• ベストアンサー

ボタンが押された事を検知するには?

<プログラム環境> Windows XP VC++6.0 MFC AppWizard(exe) ダイアログベース <目的> 1.ダイアログにボタン1を配置する 2.処理A(無限ループ)を開始する 3.処理Aの先頭でボタン1が押されたか判断する 4.ボタン1が押された場合処理Aを終了し、処理Bを行う というプログラムを作成する。 <質問> 目的のプログラムを作成するには、ボタン1が押された事を検知する 必要があると思うのですが、その方法が分かりません。 ボタンが押された事を検知するにはどうしたら良いのでしょうか? 宜しければご指摘お願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.6

> while(i != 1){//無限ループ の次の行あたりに メッセージポンプを挿入してみましょう

meeyooyoo
質問者

お礼

redfox63様 有難うございます。 ご指摘頂いた通りにコーディングすると、目的の処理ができました! まだ何をコーディングしたのか、よく理解できていませんが、 ネットや本で勉強して理解します。 有難うございました。

すると、全ての回答が全文表示されます。

その他の回答 (5)

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.5

> ボタン2を押して無限ループを実行している最中にダイアログの > ボタン1を押すと、砂時計のアイコンが出て、フリーズしました。 当然でしょう ループで待っている部分にメッセージポンプが無いからです WaitForSingleObjectの返り値が時間切れの場合に MSG msg; while( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) {   AfxGetApp()->PumpMessage(); } といった具合でメッセージポンプを組みましょう MFCアプリで CreateThradなど使うのは避けたほうがいいようです MFCでマルチスレッドをしたいなら CWinThreadクラスを使いましょう

meeyooyoo
質問者

補足

redfox63様、有難うございます。 ご指摘頂いた通りに、コーディングしたのですが、 症状に変化はありませんでした・・。 データをPCに送信する機器の故障が無い限り、 データは延々と連続して送られてくるので、 WaitForSingleObjectの戻り値はWAIT_OBJECT_0になります。 もしくは、ReadFileの戻り値が0以外となって、受信成功です。 (この場合はWaitForSingleObjectは実行されない) WaitForSingleObjectの戻り値が時間切れになる事は、まず無いので、 MSG msg; while( ::PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) {   AfxGetApp()->PumpMessage(); } という処理には入らないと思います。 シングルスレッドでは不可能なのでしょうか? 宜しければ、ご指摘お願い致します。

すると、全ての回答が全文表示されます。
回答No.4

うーん、申し訳ありません。チョッと判りませんが、こんな感じ? //グローバルハンドル static HANDLE g_hEvent = 0; //スレッド関数 DWORD WINAPI ThreadProcA(void* p) { HANDLE hCom; //COMポートのハンドル unsigned char Buf[4]; //受信バッファ DWORD dwOldTime = ::timeGetTime(); for( ; ; ) { //一秒経過 if(::timeGetTime() - dwOldTime > 1000) { dwOldTime = 0; return; } if(::ReadFile(hCom,Buf,1,NULL,&old) == 0) { return; } } } void ボタン1関数() { //スレッドは既に停止していた if(g_hEvent == 0)return; //スレッドの停止 ::CloseHandle(g_hEvent); g_hEvent = 0; //B処理 } void 起点() { DWORD dw; g_hEvent = ::CreateThread(NULL, 0, &::ThreadProcA, NULL, 0, &dw); }

meeyooyoo
質問者

補足

machongola様、有難うございます。 スレッド関数を使った事が無いので良く理解できていないのですが、 void 起点() というのが、ボタン2関数の事ですよね? g_hEvent = ::CreateThread(NULL, 0, &::ThreadProcA, NULL, 0, &dw); というコードで、スレッド関数が実行されるということですか? スレッド関数のコードでは 1秒経過すると受信が終わってしまうのではないでしょうか? 延々と受信を行い、任意にボタン1を押して、そのタイミングで 受信を止めたいと考えております。 if(::ReadFile(hCom,Buf,1,NULL,&old) == 0) { return; } というのは、受信が失敗した場合にスレッドを終了する、という 事ですよね? データがPCに送られてくるのが遅れて、 少し(数ms)待たないと受信完了しない場合があるので、 ReadFileの戻り値が0の場合でも、WaitForSingleObjectなどで 受信完了まで少し待った方がいいと思うのですが、いかがでしょうか? 宜しければご指摘お願い致します。

すると、全ての回答が全文表示されます。
  • mtaka2
  • ベストアンサー率73% (867/1179)
回答No.3

挙げられたソースだけではなんとも判断できませんが、 ・処理Bは無限ループ?それともAに戻ってくる?  Aに戻ってこないなら、Bの中でもイベント処理をしないと応答無しになってしまいます。 ・Flagは0に戻してますか?  無限ループ処理内で Flag==1であることを関知したら、Flag=0に戻しましょう。  そうしないと、一度クリックしたら、いつまでもクリック時の処理を行うことになっていしまいます。

meeyooyoo
質問者

補足

mtaka2様、有難うございます。 ソースが未完全で申し訳ありませんでした。 処理Bは Flag = 0; break; です。 よってAには戻りません。 Bの中でもイベント処理をする必要があるとの事ですが、 どのようにコーディングしてイベント処理をするのでしょうか? 宜しければご指摘お願い致します。

すると、全ての回答が全文表示されます。
  • mtaka2
  • ベストアンサー率73% (867/1179)
回答No.2

マルチスレッドにして、通常のメッセージループと無限ループ処理を別スレッドにするのがいいと思います。 あとは、「ボタン1がクリックされたかどうか」を表す変数を用意しておいて、 ボタン1のクリックイベントで変数に代入 無限ループ処理内では、その変数の内容チェック を行えばいいでしょう。 あるいは、シングルスレッドでも、 無限ループ内でメッセージ処理してもいいでしょう。 http://msdn.microsoft.com/ja-jp/library/3dy7kd92(VS.80).aspx シングルスレッド方式の場合、無限ループ処理内で定期的にメッセージ処理を行う必要があります。 変数を介してクリック情報を取得するのは同じ。

meeyooyoo
質問者

補足

mtaka2様、有難うございます。 マルチスレッドの知識がまだ無いので、シングルスレッド方式で メッセージ処理するようにコーディングしました。 ボタン2を押すと無限ループの処理(ボタン2関数)に入ります。 ボタン2関数では延々とデータを受信するように なっています。(簡略に記述してます) BYTE Flag = 0;//グローバル変数 ボタン2関数(){ HANDLE hCom;//COMポートのハンドル unsigned char Buf[4];//受信バッファ OVERLAPPED old;//オーバーラップ構造体 int i =0; old.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); while(i != 1){//無限ループ if(Flag == 1)処理B; if(ReadFile(hCom,Buf,1,NULL,&old) == 0){ if(WaitForSingleObject(old.hEvent,1000) == WAIT_TIMEOUT){ return; } } } } ボタン1関数(){ Flag = 1; } ボタン2を押して無限ループを実行している最中にダイアログの ボタン1を押すと、砂時計のアイコンが出て、フリーズしました。 なぜでしょうか? 宜しければご指摘お願い致します。

すると、全ての回答が全文表示されます。
  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.1

Windowsプログラミングするのであれば 無限ループによる処理は避けましょう 待ちを期待する処理ならWaitForSingleObjectなど使ってメッセージポンプを止めないような処理が必要です 無限ループなどの処理をした場合 コントロールで起きたイベントは 独自のメッセージポンプを実装しない限り 永遠に取得できません ボタンが押された場合は BN_CLICKEDメッセージを処理するようにします ダイアログエディタで ボタンをダブルクリックすればイベントハンドラを作成してくれます

meeyooyoo
質問者

補足

redfox63様、有難うございます。 無限ループはご指摘頂いた通りWaitForSingleObjectを使っています。 非同期でCOMポートからデータ受信を行い、1秒でタイムアウトする 設定です。タイムアウトしない限り延々とデータを受信します。 ダイアログエディタでイベントハンドラを作成すると、 Onから始まる名前の関数が作成されますが、 この関数では何も処理を行わないつもりです。 BN_CLICKEDメッセージを処理したいのですが、どのようにコーディング するのでしょうか? if(BN_CLICKED == TRUE){ 処理 } という感じでしょうか? また、BN_CLICKEDだけでは、どのボタンが押されたか分からないのでは ないでしょうか? 宜しければご指摘お願い致します。

すると、全ての回答が全文表示されます。

関連するQ&A