none
.NET ボタンクリックでイベント処理中に他のボタンを有効に RRS feed

  • 質問

  • はじめまして

    現在VS2008 C++/CLIで作成しています。
    比較的正確な時間を取得させるためにQueryPerformanceCounterとQueryPerformanceFrequencyを使い
    一定カウントに達したらTextBoxへ追加書きを行うものです。
    フォームには、START、STOP、CLOSEの3つのボタンとマルチラインのTEXTBOXを配置

    STARTボタンをクリックしたら、USBポートへの接続を開始し
    デリゲートを使ってUSBからのデータ取得と、TextBoxへの追加書きを行う。
    デリゲート関数に渡すものは、USBからのデータの格納ハンドラとCLOSEボタンクリックで発生したflg
    private: System::Void button_start_Click(System::Object^  sender, System::EventArgs^  e) {
          :
       UsbDataDelegate^    dlgte = gcnew    UsbDataDelegate( this,  &USBdataGet::Form1::UsbData);
       this->textBox->Invoke(dlgte, USBhandle,flg);
    }

    デリゲート関数内では、一定時間間隔に達したら、TextBoxに追加書きさせる。
    private: delegate void    UsbDataDelegate(Int32 USBhandle,int flg);
    void    UsbDataReceived(Int32 ljhandle,int usbdataget_flg){
        // 現在のカウントが取得できる事を確認
        if(QueryPerformanceCounter((LARGE_INTEGER *)&ctr1)!= 0 ){
            oldctr   = ctr1;
            while(flg!=1){ // Closeボタンを押したときにflg=1となる。
                 QueryPerformanceCounter((LARGE_INTEGER *)&newctr);
                 if((newctr - oldctr)  >= checktime){ //checktimeの時間経過したら
                     //この中でtexBoxへの表示
                     textBox->AppendText((newctr - oldctr).ToString("QPC = "+"0000000")+"\n");
                 }
            }
        }
    }

    上記のように記載した場合、CLOSEボタンクリックで発生させたイベント flg=1 が有効にならず
    while内で無限ループとなってしまいます。
    また、デリゲートするのをWhle文内のTextBoxへの書き込みだけにしても同じ状況です。

    何か有効な手段等ありましたら教えていただきたくお願い致します。
    2010年2月22日 4:05

回答

  • UsbDataReceivedはUIスレッドで動いているんでしょうか?(textBoxに直接書き込んでいるという事はそうだと思いますが)
    UsbDataReceivedをUIのスレッドとは別のスレッドで動かせば良いはずです。その際は、flagにvolatileを付けないと、最適化で正常に動かないかもしれません。

    2010年2月22日 4:21
  • ただ、STOPボタンを2度押ししなければならない。

    1回目で、ボタンにフォーカスが行き、2度目で初めてイベントを発生させられる状況です。
    これを改善する方法は何かあるのでしょうか?

    囚人さんが既に指摘されている通り、UsbData() の処理を別スレッドで処理するのがいいと思います。
    ボタンへのフォーカス設定もボタンのクリックも、それぞれが UI スレッドへのメッセージ通知なので、
    UsbData() の処理が UI スレッドを長時間ブロックしてしまう以上、ボタンにメッセージが通知されず、イベント ハンドラーも走りません。
    while の中に DoEvents() を入れると処理が進むのはまやかしに過ぎません。(そういう意味で一歩前進でなくて、後退しています。)

    # 間違っても DoEvents() を連続で呼び出して逃げてはいけませんよ。
    2010年2月22日 5:42
  • >今のプログラムで
    >volatileもなくても可能であることを確認できました。

    最適化していない状態で実行可能かどうか確認しても意味ないですよ。volatileは最適化の抑制なので。
    whileブロック内でflagを更新していない以上

    while(true){
    ・・・
    }

    と書き換えられるかもしれないので(というか多分そうなります)。
    2010年2月22日 6:10
  • デリゲートでは別スレッドになっていないということでしょうか?
    あまり詳しく解っていないもので。

    Control.Invoke はあくまで、コントロールが所属するスレッドで指定されたデリゲートを実行するだけであり、別スレッドで実行するための手法ではありません。(提示されているサンプルでは同じスレッドで実行しているだけになる)
    非同期で実行したいのであれば、Delegate.BeginInvoke を使うことになりますが、それだけだと、TextBox などのコントロールにアクセスしたときに例外がスローされます。
    このため、コントロールを操作する部分を Control.Invoke で元のスレッドで実行するという流れになります。

    http://msdn.microsoft.com/ja-jp/library/2e08f6yc.aspx
    http://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.invoke.aspx

    Delegate.Invoke の他に、ThreadPool.QueueUserWorkItem とか、Thread クラスとか、BackgroundWorker クラスであるとか、そういったものを使っても良いと思います。
    目的に応じて良さそうなものを選んでください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年2月22日 13:43
    モデレータ

すべての返信

  • UsbDataReceivedはUIスレッドで動いているんでしょうか?(textBoxに直接書き込んでいるという事はそうだと思いますが)
    UsbDataReceivedをUIのスレッドとは別のスレッドで動かせば良いはずです。その際は、flagにvolatileを付けないと、最適化で正常に動かないかもしれません。

    2010年2月22日 4:21
  • 記載ミスがありました。
    誤 void    UsbDataReceived(Int32 ljhandle,int usbdataget_flg){
    正 void    UsbData(Int32 ljhandle,int usbdataget_flg){
    です。申し訳ございません。

    while内のif文の後にDoEvents()を入れることで
    Close、Stopボタンは有効になりクリック可能となりました。(ひとつ前に進められました。)

    しかし、Stopボタンによる停止ができない状況。

    デバックモードで見ていると
    Stopボタンを押して
      private: System::Void button_stop_Click(System::Object^  sender, System::EventArgs^  e) {
           flg=1;
       }
    flg=1となるのですが
    while文に戻ったところでflg=0となってしまいます。
    意図的にflg=0を入れているところはなく
    flgの宣言をprivateでもpublicでも行ってみましたが
    while文でflg=0となります。
    単純なところで自分が解っていないように思えるのですが
    教えていただけると助かります。よろしくお願いいたします。
    2010年2月22日 5:09
  • ありがとうございました。
    一部単純なコーディングミスでした。(スペルミス他の変数と一緒になってしまっていました。)

    今のプログラムで
    volatileもなくても可能であることを確認できました。

    ただ、STOPボタンを2度押ししなければならない。

    1回目で、ボタンにフォーカスが行き、2度目で初めてイベントを発生させられる状況です。
    これを改善する方法は何かあるのでしょうか?
    2010年2月22日 5:23
  • ただ、STOPボタンを2度押ししなければならない。

    1回目で、ボタンにフォーカスが行き、2度目で初めてイベントを発生させられる状況です。
    これを改善する方法は何かあるのでしょうか?

    囚人さんが既に指摘されている通り、UsbData() の処理を別スレッドで処理するのがいいと思います。
    ボタンへのフォーカス設定もボタンのクリックも、それぞれが UI スレッドへのメッセージ通知なので、
    UsbData() の処理が UI スレッドを長時間ブロックしてしまう以上、ボタンにメッセージが通知されず、イベント ハンドラーも走りません。
    while の中に DoEvents() を入れると処理が進むのはまやかしに過ぎません。(そういう意味で一歩前進でなくて、後退しています。)

    # 間違っても DoEvents() を連続で呼び出して逃げてはいけませんよ。
    2010年2月22日 5:42
  • >今のプログラムで
    >volatileもなくても可能であることを確認できました。

    最適化していない状態で実行可能かどうか確認しても意味ないですよ。volatileは最適化の抑制なので。
    whileブロック内でflagを更新していない以上

    while(true){
    ・・・
    }

    と書き換えられるかもしれないので(というか多分そうなります)。
    2010年2月22日 6:10
  • 囚人さん、totojoさん
    ありがとうございます。

    デリゲートでは別スレッドになっていないということでしょうか?
    あまり詳しく解っていないもので。

    MFCなどであるワーカースレッドを使う必要があるということなのでしょうか。
    AfxBeginThread()でいいのでしょうか。

    最適化の件了解です。
    宣言時に、volatile int flg:と記載しておきます。
    2010年2月22日 6:47
  • デリゲートでは別スレッドになっていないということでしょうか?
    あまり詳しく解っていないもので。

    Control.Invoke はあくまで、コントロールが所属するスレッドで指定されたデリゲートを実行するだけであり、別スレッドで実行するための手法ではありません。(提示されているサンプルでは同じスレッドで実行しているだけになる)
    非同期で実行したいのであれば、Delegate.BeginInvoke を使うことになりますが、それだけだと、TextBox などのコントロールにアクセスしたときに例外がスローされます。
    このため、コントロールを操作する部分を Control.Invoke で元のスレッドで実行するという流れになります。

    http://msdn.microsoft.com/ja-jp/library/2e08f6yc.aspx
    http://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.invoke.aspx

    Delegate.Invoke の他に、ThreadPool.QueueUserWorkItem とか、Thread クラスとか、BackgroundWorker クラスであるとか、そういったものを使っても良いと思います。
    目的に応じて良さそうなものを選んでください。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年2月22日 13:43
    モデレータ
  • Azuleanさん 囚人さん totojoさん
    ありがとうございます。
    時間が無くなってしまった為、とりあえずは動作させるためにtimer_Tickで不正確な時間ではありますが対応させて
    後に、教えていただいた方法を検討しようと思います。
    2010年2月23日 0:03
  • こんにちは。フォーラムオペレーターの高橋春樹です。

    囚人さん、totojoさん、Azuleanさん
    アドバイスの投稿、有難うございました。

    みゅーみゅーさん
    MSDNフォーラムのご利用有難うございます。
    今回、囚人さん、totojoさん、Azuleanさんのアドバイスが有用な情報だと思いましたので、
    回答マークを付けさせてもらいました。

    疑問点がありましたら、新たに疑問点を投稿して頂きたいと思います。

    今後ともMSDNフォーラムを宜しくお願いします。


    マイクロソフト株式会社 フォーラム オペレーター 高橋春樹
    2010年2月24日 1:13