FAQ2-3

Q. 処理中に,「中断」ボタンで中断するには

 大きなファイルの読み込み中に,「中断」ボタンを使って中断させたいのですが,読み込みルーチンが走っているときはマルチタスクが停止してしまい,せっかくボタンを用意したのに読み込み中に押すことができません.
 これを解決するにはタイマーを使用して断続的に読み込みをするしかないのでしょうか?


A. PeekMessageを使用する

 タイマーを使用しなくても,処理のあいまにPeekMessageを入れることによって他のタスクに制御を渡すことができます.
  MSG msg;
  for(・・・){
          :
      [時間のかかる処理]
          :
      if( PeekMessage(&msg,NULL,0,0,PM_REMOVE) ){
          TranslateMessage(&msg);
          DispatchMessage(&msg);
      }
  }
 PeekMessage関数はメッセージキューに自分のアプリケーションに関するメッセージがあるときにMSG構造体にメッセージを格納して返します.他のアプリケーションのキューにメッセージがあるときは他のアプリケーションのメッセージループなどで処理されます.
 最後のパラメータにPM_REMOVEを指定すると,キューからメッセージは削除されます.そこでメッセージループと同じようにTranslateMessageとDispatchMessageを呼んでウィンドウプロシージャに渡すようにすることで,自分自身のメッセージも[時間のかかる処理]のforループの中において処理することができるようになります.

GetMessageとPeekMessageの違い

 GetMessage関数との一番大きな違いは,PeekMessageはキューに自分のメッセージがなくても帰っくるのに対し,GetMessageはキューに自分のメッセージがないと帰ってこないところです.ですから,PeekMessageを使用しないと,たとえ他のタスクに制御を渡せたとしても,[時間のかかる処理]が止まってしまうので意味がなくなってしまいます.
 あとは中断ボタンが押されたときにグローバル変数などを利用してforループを抜けるようにすれば,「ほぼ」完成です.

処理途中のアプリケーションが終了したときの対処法

 なぜ「ほぼ」なのかというと,このままでは[時間のかかる処理]の途中でもアプリケーションを終了できてしまうので,その対処をしなくてはいけません.
 対処法として,あらかじめアプリケーションを終了できないようにしておくか,もしくは終了したときは「中断」と同じようにすることです.後者の場合,さらに注意しなくてはいけないことがあります.
 通常,アプリケーションを終了した場合,キューにWM_QUITが来ることで,WinMainにあるメッセージループを抜けてタスクが終了しますが,

 PeekMessage(・・・,PM_REMOVE)

としてWM_QUITを拾った場合,WinMainのメッセージループにWM_QUITが永遠に来ることはなくなり,ウィンドウは閉じてもタスクが終了しないという事態におちいります.
 リスト2-5はそういった対処などをした例です.この例の場合,WM_QUITが来た場合,再度PostQuitMessage(0);を呼んで,WinMainのメッセージループも終了可能なようにしています.

[リスト2-5] :

BOOL fAbort = FALSE;	/*←グローバル変数*/

/*** 時間のかかる処理 ***/
	MSG msg;
	for(・・・){
			:
		[時間のかかる処理]
			:
		if(fAbort) break;
		
		if( PeekMessage(&msg,NULL,0,0,PM_REMOVE) ){
			if(msg.message==WM_QUIT){
				fAbort = TRUE;
				PostQuitMessage(0);
				break;
			}
		 	TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

/*** ウィンドウプローシージャ ***/
		:
		:
	case WM_DESTROY :
		fAbort = TRUE;
		PostQuitMessage(0);
		return(0);
		
	case WM_COMMAND:
		switch(wParam){
			case ID_TYUUDAN:	/*中断ボタンが押された*/
				fAbort = TRUE;
				return(0);
		}
		break;
		:
		:

Back to FAQ main page