2

少し前に、この [Web サイト] [1] からデリゲートとイベントについて (まったく別の理由で) 読んでいましたが、そこで、イベントに十分な時間がかかる場合、別のスレッドが生成されるという印象を受けました。それで、修正できないように見えるバグについて考えさせられました。そこで、RS232 ポート経由で通信する MSR デバイス用のキーボード ウェッジ プログラムを作成しています。入力を処理するためにこのクラスを作成しました。

    public ComPortInput(SerialPort sp, int delay)
        : this(delay)
    {
        if (sp == null)
            throw new System.ArgumentNullException("Serial port can not be null");

        serialPort = sp;
    }

この ComPortInput クラスを開くと、DataReceived イベントをサブスクライブします。私が正しく推測している場合、遅延を十分に高く設定すると、データイベントによって新しいスレッドが作成されます。私のコードを見ると、問題が最もよく説明されると思います。

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()エラーでプログラムがクラッシュする

OLE 呼び出しを行う前に、現在のスレッドをシングル スレッド アパートメント (STA) モードに設定する必要があります。Main 関数に 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}");
    }

わからない..私は困惑しています。この問題に取り組む気がある人はいますか?

編集

だから助けを借りて、私はこれを私の解決策として思いつきました。SerialPortWedge はクラスであり、コントロールではないため、Invoke メソッドを呼び出すことができませんでした。SynchronizationContext.CurrentSerialPortWedgeに渡す必要がありました。したがって、私のメイン フォームでは、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);
    }

希望どおりに動作するようになりました。みんなの助けに感謝します。:)

4

1 に答える 1