ab.com コミュニティ

ActiveBasicを通したコミュニケーション
現在時刻 - 2024年4月19日(金) 13:16

全ての表示時間は UTC+09:00 です




新しいトピックを投稿する  トピックへ返信する  [ 4 件の記事 ] 
作成者 メッセージ
投稿記事Posted: 2008年7月06日(日) 01:57 
オフライン

登録日時: 2006年6月02日(金) 18:20
記事: 106
スレッドの停止・再開について(2)

メイン・ウインドウのクリエイト部で宣言、開始されるスレッド
(ハンドル=hThread1, ID=thread1_ID)を一つ持つプログラムがあります。
これを「スレッド1」として、
以下の条件の時にそのスレッド1を停止させたいのですが、

 1.メインウインドウが非アクティブになった時。
 2.メニュー、ポップアップメニュー、更にそれらからモーダルダイアログが
開かれた時。

(※つまるところ、"ユーザーがよそ事をしているときはスレッドを止める"ということ
ですが、)

(※スレッド1再開の条件は上の条件の裏返しです。)

まずスレッド1の停止の方法自体ですが、自分の以前のこの質問板での
「スレッドの停止・再開について」と同じもの(イグトランスさんにチェックを
して頂いたもの)で、スレッド1にメッセージをポストし、スレッドの流れを
待避線に引き込むというものです。
(但し必ずしもこの方法でないといけないということはありません。
SuspendThread/ResumeThreadのような強制中断でなければ
どんなやり方でも構いません。)

 またその中で、条件1の「メインウインドウが非アクティブになった時。」について
書いています。
 そこで次に条件2「メニュー、ポップアップメニュー、更にそれらから
モーダルダイアログが開かれた時。」について以下のように書いてみたのですが、

 メイン・ウインドウのウインドウメッセージループ内で、
コード:
' ウィンドウメッセージを処理するためのコールバック関数

Function MainWndProc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As DWord
	' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。

    Select Case dwMsg

      Case WM_ENTERMENULOOP
         PostThreadMessage(thread1_ID,MYTHREADMSG_IDLE_THREAD,0,0)
      Case WM_EXITMENULOOP
         PostThreadMessage(thread1_ID,MYTHREADMSG_RESUME_THREAD,0,0)

    End Select

	' イベントプロシージャの呼び出しを行います。
	MainWndProc=EventCall_MainWnd(hWnd,dwMsg,wParam,lParam)
End Function
このようにして、とりあえず「ポップアップメニューが開いた時に停止、閉じた時に再開」
としたのですが、このようにしていくとどうもうまく行かないと分かりました。

 問題なのはまず、例えば"メニュー項目をクリックしてポップアップメニューを
開いた時、スレッド1はとりあえずきちんと停止するのですが、
そこからメインウインドウ以外の場所をクリックしてメインウインドウを非アクティブ
にします。そうすると、スレッド1は停止したままでいて欲しいのですが、実際には
"ポップアップが閉じた"としてスレッド1は再開してしまいます。

                 ポップアップを開く---->メインウインドウを非アクティブにする

スレッド1の状態(本来)   停止-------------------------------------------->

スレッド1の状態(実際)   停止--------------->再開 (ポップアップが閉じたため)
                

さらに"ポップアップメニューからの選択で更にモーダルダイアログが
開くような場合"、スレッドの停止状態としては、
「ポップアップが開いてからその後モーダルダイアログが
閉じるまでずっと停止していて欲しい」のですが、恐らくモーダルが開く直前に
ポップアップは「閉じる」訳でそのときにスレッド再開のメッセージが送られてしまい、
スレッド1は再開してしまうと思います。
 モーダルが開く時に再びスレッド停止のメッセージを送るにしても、瞬間的には
スレッドが動いてしまうように思えますし、何より「何だかこのようにいちいちそれぞれの
GUIの状態ごとに停止、再開の処理を行うのは不自然だ」、と感じます。


冒頭の条件、

 1.メインウインドウが非アクティブになった時。
 2.メニュー、ポップアップメニュー、更にそれらからモーダルダイアログが開かれた時。

これらを満たせるようなスレッドの停止、再開の方法が
ありましたら是非教えて下さい。


通報する
ページトップ
投稿記事Posted: 2008年7月12日(土) 01:39 
オフライン

登録日時: 2006年6月02日(金) 18:20
記事: 106
「SuspendThread()」、「ResumeThread()」は使わずと言っておきながら
それらの解説を読んでいて思ったのですが、
これらには「サスペンド・カウンタ」なるものがあり、それを増減してスレッドを実際に
止めるかどうかの判断にしている。
なぜこのような仕様になっているのかと考えるのですが
これは何だかFor-Nextの"入れ子"のような、
またはスタックの"FILO"(First In, Last Out )のような、そんな
構造で使われることが前提になっているような印象を受けるのです。

そこでとりあえず上のプログラムで、スレッドを止める
「PostThreadMessage(thread1_ID, MYTHREADMSG_IDLE_THREAD, 0, 0) 」を「SuspendThread(hThread1)」に換え、
同様にスレッドを再開する
「PostThreadMessage(thread1_ID, MYTHREADMSG_RESUME_THREAD, 0, 0)」を「ResumeThread(hThread1)」に換えてサスペンドカウンタの動作をみてみたのですが、
(※実際にはカウンタ役のグローバル変数を一つ用意してその値を見ています)


「ポップアップを開く」----->「メインウインドウを非アクティブ」という流れの動作では、
サスペンドカウンタは、

 1 (+1) 「ポップアップを開く」
 0 (-1) (「メインウインドウを非アクティブにする前に
         開いていたポップアップが閉じられる」)
 1  (+1) 「メインウインドウを非アクティブ」

という流れになり「サスペンド・カウンタ」が"2"になるような状況になりようがない、
という感じになってしまい、結局のとこ何でそもそもサスペンドカウンタなんで
あるんだろうか?と、つい思ってしまいます。

うーん、とあれこれ考えてはいるのですが良い考えも無く。
この「サスペンド・カウンタ」のそもそもの意義について、お知りの方が
おられましたら是非教えて下さい。


通報する
ページトップ
 記事の件名:
投稿記事Posted: 2008年7月12日(土) 12:22 
オフライン

登録日時: 2005年5月31日(火) 17:59
記事: 899
お住まい: 東京都
こう動いてくれれば,この状況でサスペンドカウンタが役立ったんですよね。
1 (+1) 「ポップアップを開く」
2 (+1) 「メインウインドウを非アクティブ」
1 (-1) 「開いていたポップアップが閉じられる」)

とりあえず,そこについて考えてみました。
まずWM_EXITMENULOOPの前には,各項目が選択されたらWM_MENUSELECTがやってきます。WM_EXITMENULOOP前の最後のWM_MENUSELECTが「モーダルダイアログを開くメニュー項目」だったらという風にしようとしましたが,Dが選択されても結局クリックされずメニューが閉じられた場合を検出できなさそうなので却下しました。

結局,こういう風にしてみました。
コード:
Const WM_CATCHCOMMAND = WM_APP + 0
Const WM_RESUME_THREAD1 = WM_APP + 1

Function MainWndProc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As LRESULT
	Select Case dwMsg
		Case WM_ENTERMENULOOP
			c++
			OutputDebugString(StrPtr("WM_ENTERMENULOOP " + Str$(c) + Ex"\r\n"))
		Case WM_EXITMENULOOP
			PostMessage(hWnd, WM_CATCHCOMMAND, 0, 0)
		Case WM_CATCHCOMMAND
			Dim msg As MSG
			If PeekMessage(msg, hWnd, WM_COMMAND, WM_COMMAND, PM_REMOVE) <> FALSE Then
				DispatchMessage(msg)
				PostMessage(hWnd, WM_RESUME_THREAD1, 0, 0)
			Else
				c--
				OutputDebugString(StrPtr("WM_CATCHCOMMAND " + Str$(c) + Ex"\r\n"))
			End If
			Exit Function
		Case WM_RESUME_THREAD1
			c--
			OutputDebugString(StrPtr("WM_RESUME_THREAD1 " + Str$(c) + Ex"\r\n"))
		Exit Function
	End Select
	' イベントプロシージャの呼び出しを行います。
	MainWndProc = EventCall_MainWnd(hWnd, dwMsg, wParam, lParam)
End Function
c++がSuspendで,c--がResumeです。OutputDebgStringは確認用なので不要なら消してください。

ダイアログより後にResumeするように工夫しています。WM_EXITMENULOOPでのResumeをやめ,代わりにWM_CATCHCOMMANDをPostしています。WM_CATCHCOMMANDではWM_COMMANDがあれば,さらにResumeを後回し (WM_RESUME_THREAD1), なければその場でResumeしています。
こういうメッセージの順序に依存することはあんまりやりたくないんですけど仕方ありません。また,WM_COMMANDが送られる順序が変わったとしても,最悪アクティブ・非アクティブの制御でスレッドは止まってくれるはずです(その場合,元通りメニューを閉じてから一瞬動いてしまいます)。


通報する
ページトップ
投稿記事Posted: 2008年7月13日(日) 02:02 
オフライン

登録日時: 2006年6月02日(金) 18:20
記事: 106
イグトランスさん、レスありがとうございます。
プログラムを書いて頂いて本当に感謝です。
引用:
こう動いてくれれば,この状況でサスペンドカウンタが役立ったんですよね。
1 (+1) 「ポップアップを開く」
2 (+1) 「メインウインドウを非アクティブ」
1 (-1) 「開いていたポップアップが閉じられる」)
そうなんです。言われる通りで、それぞれのGUIの処理が
どこか一部でも「重なる」部分があれがカウンタの恩恵に与れたと思うのですが
どうもそうでもないような感じなので、どうしたらいいのかと思っていました。
そこで書いていただいたプログラムですが、
コード:
       Case WM_EXITMENULOOP
            PostMessage(hWnd, WM_CATCHCOMMAND, 0, 0)
        Case WM_CATCHCOMMAND
            Dim msg As MSG
            If PeekMessage(msg, hWnd, WM_COMMAND, WM_COMMAND, PM_REMOVE) <> FALSE Then
                DispatchMessage(msg)
                PostMessage(hWnd, WM_RESUME_THREAD1, 0, 0)
            Else
                c--
            End If
            Exit Function
        Case WM_RESUME_THREAD1
            c--
EXITMENULOOPでWM_CATCHCOMMANDをポストし、更に
WM_CATCHCOMMANDを受けたところでまた更に WM_RESUME_THREAD1をポスト、
そこでのリジューム。むむむ。。凄いです。二段階でのメッセージ・ポスト構え。
自分では思い付きもしなかったろうと思います。(まだ完全に理解が
追いついているわけではないのですが、ゆっくり読んでいきたいと
思っています。)

ただ一点気になることが、これはイグトランスさんも指摘されている、
引用:
こういうメッセージの順序に依存することはあんまりやりたくないんですけど仕方ありません。また,WM_COMMANDが送られる順序が変わったとしても,最悪アクティブ・非アクティブの制御でスレッドは止まってくれるはずです(その場合,元通りメニューを閉じてから一瞬動いてしまいます)。
"メッセージの順序に依存する"、そうなんですよね。
自分もサスペンド・カウンタについて考えているとき、「自分のやろうとしている
方法では"必ずしも確定的だとは言い切れない"メッセージの順序に依存してしまう」
と思いました。つまり、結局のとこGUIの動作ごとにサスペンドの処理を書くのは
「正攻法」じゃないのかも、と。
ただしそうすると「サスペンド・カウンタ」なるものの存在はそもそも何のため?
そういう風に書くために用意されたものじゃないの?と悩んでいたのですが、
うーん。。これについてはまだ自分には分かりません。

「ユーザーがよそ事をしている間はスレッドを止める」というのは自分としては
「当然やらなければならない、ごく一般的な」処理だと思えるんです。
とすると、ごくありふれた、一般的な処理というものがあるんじゃないかと。。。

 いずれにしてもまだもう少し調べてみます。何か良い方法があれば
また是非教えて下さい。
 コード、書いて頂いてありがとうございました。


P.S.
クライアント・エリアに関するメッセージで処理できるのかな、と思ったりも
したんですが、どうもそれらしいメッセージは見つからず。。。


通報する
ページトップ
期間内表示:  ソート  
新しいトピックを投稿する  トピックへ返信する  [ 4 件の記事 ] 

全ての表示時間は UTC+09:00 です


オンラインデータ

このフォーラムを閲覧中のユーザー: Bing [Bot] & ゲスト[22人]


トピック投稿:  可
返信投稿:  可
記事編集: 不可
記事削除: 不可
ファイル添付: 不可

検索:
ページ移動:  
Powered by phpBB® Forum Software © phpBB Limited
Japanese translation principally by ocean