Я немного пораньше узнал о делегатах и событиях (совершенно по другой причине) из этого [сайта] [1] Там у меня сложилось впечатление, что если ваше событие занимает достаточно много времени генерируется. Ну, это заставило меня задуматься над ошибкой, которую я не могу исправить. Поэтому я делаю программу клина для клавиатуры для своего устройства MSR, которое обменивается данными через порт RS232. Я сделал этот класс для обработки ввода.предотвратить событие от нереста другого потока
public ComPortInput(SerialPort sp, int delay)
: this(delay)
{
if (sp == null)
throw new System.ArgumentNullException("Serial port can not be null");
serialPort = sp;
}
Я подписываюсь на событие DataReceived, когда я открываю этот класс ComPortInput. Если я правильно угадываю, то, если бы я установил задержку достаточно высоко, то мой файл данных создаст новый поток. Я думаю, что проблему лучше всего описать, глядя на мой код.
Program.cs
[STAThread]
static void Main()
{
singleton = new System.Threading.Mutex(true, "Keymon");
if (!singleton.WaitOne(System.TimeSpan.Zero, true))
{ return; }
InstantiateProgram();
System.Windows.Forms.Application.Run(main);
}
private static void InstantiateProgram()
{
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
main = new frmMain();
}
ComPortInput.cs. Только DataReceived событие
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (Delay > 0)
System.Threading.Thread.Sleep(Delay); //waits for all the data to be sent to the card.
int bytesRead = serialPort.BytesToRead;
if (bytesRead > 0)
{
byte[] buffer = new byte[bytesRead];
serialPort.Read(buffer, 0, bytesRead);
OnDataAvailable(buffer);
}
}
SerialPortWedge.cs
void Input_DataAvailable(byte[] data)
{
Sound.Play();
Output.SendData(data);
}
FormattedHexStringOutput
public override void SendData(byte[] buffer)
{
string str = "";
for(int i=0; i<buffer.Length; i++)
{
if ((i+16)%16==0)
{
str += string.Format("{1}{0}: ", i.ToString("X3"), System.Environment.NewLine);
}
str += string.Format("{0}:", buffer[i].ToString("X2"));
}
Clipboard.Clear();
Clipboard.SetText(str);
SendKeys.SendWait("^v");
SendKeys.SendWait("{ENTER}");
}
На Clipboard.Clear()
программа падает с этой ошибкой
Текущий поток должен быть установлен в режим однопоточной квартиры (STA) до того, как вызовы OLE могут быть выполнены. Убедитесь, что ваша основная функция имеет STAThreadAttribute, отмеченный на нем.
at System.Windows.Forms.Clipboard.SetDataObject(Object data, Boolean copy, Int32 retryTimes, Int32 retryDelay)
at System.Windows.Forms.Clipboard.Clear()
at SerialPortDataSender.FormattedHexStringOutput.SendData(Byte[] buffer) in c:\SerialPortDataSender\Output\FormattedHexStringOutput.cs:line 28
at SerialPortDataSender.SerialPortWedge.Input_DataAvailable(Byte[] data) in c:\SerialPortDataSender\SerialPortWedge.cs:line 34
at SerialPortDataSender.IInput.OnDataAvailable(Byte[] data) in c:\SerialPortDataSender\Input\IInput.cs:line 41
at SerialPortDataSender.ComPortInput.sp_DataReceived(Object sender, SerialDataReceivedEventArgs e) in c:\SerialPortDataSender\Input\ComPortInput.cs:line 135
at System.IO.Ports.SerialPort.CatchReceivedEvents(Object src, SerialDataReceivedEventArgs e)
at System.IO.Ports.SerialStream.EventLoopRunner.CallReceiveEvents(Object state)
at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)
Я в затруднении относительно того, почему это делается. Если я добавлю часы в текущее состояние квартиры, уверен, что это MTA. Но если я поставлю перерыв в начале программы, это говорит о том, что это STA. Так почему он переключился? Что даже пни меня больше в том, что если я использую другой класс вывода он не бросает эту ошибку
SendRawClipboardOutput.cs
public override void SendData(byte[] buffer)
{
Clipboard.Clear();
Clipboard.SetText(System.Text.ASCIIEncoding.ASCII.GetString(buffer));
SendKeys.SendWait("^v");
}
Нор делает это один
SendTrimClipboardOutput.cs
public override void SendData(byte[] buffer)
{
var str = System.Text.ASCIIEncoding.ASCII.GetString(buffer);
str = str.Replace(System.Environment.NewLine, "");
Clipboard.Clear();
Clipboard.SetText(str);
SendKeys.SendWait("^v");
SendKeys.SendWait("{ENTER}");
}
Я не знаю .. Я в тупике. Кто-нибудь может решить эту проблему?
EDIT
Так с помощью я придумал это как мое решение. Поскольку SerialPortWedge - это класс, а не элемент управления, я не смог вызвать метод Invoke. Мне пришлось пройти в SynchronizationContext.Current
на мой SerialPortWedge. Поэтому в моей основной форме у меня есть это после создания экземпляра SerialPortWedge.
msr.MainFormContext = SynchronizationContext.Current;
затем в SerialPortWedge я изменил мою Input_DataAvailable к этому
void Input_DataAvailable(byte[] data)
{
if(MainFormContext != null)
MainFormContext.Send(FireEventFromContext, data);
}
private void FireEventFromContext(object state)
{
Sound.Play();
Output.SendData((byte[])state);
}
теперь работает как хотелось бы. Спасибо всем за помощь. :)
_I сложилось впечатление, что если ваше событие занимает достаточно много времени, генерируется отдельная нить ._ - очень плохое впечатление. –
@HenkHolterman hahaha wow Я рад, что задаю вопросы. Я так многому научился. Спасибо, что объяснил это мне. –