イグトランスさん、レスありがとうございます。リストの整理に
手間取ってしまい返信を遅らせてしまいました。すみません。
メイン部分のソースを書きます。
プログラムは「マウスで左クリックした所を中心に、同心円を
外側へ向かって20個、さざ波が広がるようにゆっくり描いていく」
というものです。
右クリックでポップアップメニューを表示して、その選択により
描画色を変える。(ポップアップには「赤に変更」、「緑に変更」、
「青に変更」、の3つのメニュー項目)
ただし、もし20個の同心円の描画中に色が変えられた場合、
今描画している20個はそのままの色で描き、色の変更は次回から
反映させるものとする。
(プログラムはAB 4.04のプロジェクトで作っています)
ソースリスト1(ここをクリック) [ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]
コード: 全て選択
'-------------------------------------------------------------
' ウィンドウメッセージを処理するためのコールバック関数
Function MainWndProc(hWnd As DWord, dwMsg As DWord, wParam As DWord, lParam As DWord) As DWord
' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。
'WM_NULLを受け取るとポップアップメニューを表示
If dwMsg=WM_NULL Then ShowPopUpMenu()
' イベントプロシージャの呼び出しを行います。
MainWndProc=EventCall_MainWnd(hWnd,dwMsg,wParam,lParam)
End Function
'
'●メイン・スレッド
'
Function MainOperation(dwDummy As DWord)
While 1
'↓マウスポインタの位置、左右クリックを取得
GetMousePositionAndClick()
'↓右クリックが押されたら「WM_NULL」をSendMessage
If rightClickDown Then
SendMessage(hMainWnd,WM_NULL,0,0)
'↑初めはここで直接「TrackPopUpMenu」を実行したが
'それではポップアップは表示されなかった
End If
'↓左クリックが押されたらその位置に同心円を外側に向かって表示していく
If leftClickDown Then
Dim I As Long
For I=0 to 19
DrawCircle (hMemDC,mousePosition.x,mousePosition.y,4+I*2)
InvalidateRect(hMainWnd,ByVal NULL,FALSE)
Sleep (100)
Next I
End If
Wend
End Function
'
'●右クリック・ダウンでのポップアップメニューの表示
'
Sub ShowPopUpMenu()
Dim cPos As POINTAPI
'↓右クリックメニューの表示
GetCursorPos(cPos)
popUpMenuRet=TrackPopupMenu(hMenu1,TPM_LEFTALIGN Or TPM_TOPALIGN Or TPM_RETURNCMD,cPos.x,cPos.y,0,hMainWnd,ByVal NULL)
'↓ポップアップメニューの戻り値があるならSelectCasePopUpMenu()へ
If popUpMenuRet Then SelectCasePopUpMenu()
'↓左クリックがUpになるまで待つ
'(これを待たないとメニュー選択時の左クリックをメインスレッドで
'拾ってしまい、そこで円を描いてしまう)
'ただし、時々左クリックがUpになっていないのにこの判定ループを
'すり抜ける時がある
While GetAsyncKeyState(1) And &H8000
Wend
End Sub
'
'●右クリックポップアップメニューの分岐
'
Sub SelectCasePopUpMenu()
Select Case popUpMenuRet
Case 1000
penColor=RGB(255,0,0) ':赤に変更
Case 1001
penColor=RGB(0,255,0) ':緑に変更
Case 1002
penColor=RGB(0,0,255) ':青に変更
End Select
popUpMenuRet=0
End Sub
主な流れは、
1,メインのスレッド「MainOperation」でループ
マウスの位置、クリックを取得し、もし左クリックが
押されたらその位置に同心円を序々に表示。
右クリックならポップアップメニューを表示する
為に「WM_NULL」をSendMessage。
2,コールバック関数「MainWndProc()」で
そのWM_NULLを拾ったらShowPopUpMenu()をコール。
3,ShowPopUpMenu()ではTrackPopupMenuで
ポップアップメニューを表示し、その戻り値を
popUpMenuRetというグローバル変数に入れ、
SelectCasePopUpMenu()でその値を元に
それぞれの色に変更。
4,色の変更処理が終わったらメインスレッドの
SendMessageをした次の命令からメインループ再開。
となります。
この方法でとりあえずは出来たのですが、
問題は「For-Nextで同心円を20個描画している最中は
右クリックの取得判定をしていないのでポップアップが
表示されない」ということです。
もちろん単にFor-Nextのループ中にも
GetAsyncKeyState()などを置いてやってキメ細かく取れば
いいようなものですが、やっぱりそれでは根本的な解決に
ならないので他のやり方を試すことにしました。
それが次のプログラムです。
ソースリスト2(ここをクリック) [ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]
コード: 全て選択
'
'●メイン・スレッド
'
Function MainOperation(dwDummy As DWord)
While 1
'↓マウスポインタの位置、左右クリックを取得
GetMousePositionAndClick()
'↓何らかのウインドウメッセージがあったならそれに対処
If myMainWndMsg Then
SelectCaseMyMainWndMsg()
myMainWndMsg=0
End If
'↓左クリックが押されたらその位置に同心円を外側に向かって表示していく
If leftClickDown Then
Dim I As Long
For I=0 to 19
DrawCircle (hMemDC,mousePosition.x,mousePosition.y,4+I*2)
InvalidateRect(hMainWnd,ByVal NULL,FALSE)
Sleep (100)
Next I
End If
Wend
End Function
'
'●ウインドウメッセージによる分岐
'
Sub SelectCaseMyMainWndMsg()
Select Case myMainWndMsg
Case WM_RBUTTONDOWN
SelectCasePopUpMenu()
End Select
End Sub
'
'●右クリックポップアップメニューの分岐
'
Sub SelectCasePopUpMenu()
Select Case popUpMenuRet
Case 1000
penColor=RGB(255,0,0) ':赤に変更
Case 1001
penColor=RGB(0,255,0) ':緑に変更
Case 1002
penColor=RGB(0,0,255) ':青に変更
End Select
popUpMenuRet=0
End Sub
'
'●右クリックダウン・イベント
'
Sub MainWnd_RButtonDown(flags As Long, x As Integer, y As Integer)
'スレッドの一時停止
SuspendThread(hMainThread)
'↓マウスポインタがクライアントエリア内ならポップアップメニューを表示
If mousePosition.x>=0 And mousePosition.x<clientRc.right And mousePosition.y>=0 And mousePosition.y<clientRc.bottom Then
'ポップアップメニューの表示
ShowPopUpMenu()
'メインスレッド内で扱えるようにウインドウメッセージを格納
myMainWndMsg=WM_RBUTTONDOWN
End If
'スレッドの再開
ResumeThread(hMainThread)
End Sub
'
'●右クリックダウンでのポップアップメニューの表示
'
Sub ShowPopUpMenu()
Dim cPos As POINTAPI
'↓右クリックメニューの表示
GetCursorPos(cPos)
popUpMenuRet=TrackPopupMenu(hMenu1,TPM_LEFTALIGN Or TPM_TOPALIGN Or TPM_RETURNCMD,cPos.x,cPos.y,0,hMainWnd,ByVal NULL)
'↓左クリックがUpになるまで待つ
'(これを待たないとメニュー選択時の左クリックをメインスレッドで
'拾ってしまい、そこで円を描いてしまう)
'ただし、時々左クリックがUpになっていないのにこの判定ループを
'すり抜ける時がある
While GetAsyncKeyState(1) And &H8000
Wend
End Sub
右クリックはイベントとして取得し、右クリックが
あったときはメインスレッドの状態にかかわらず、
必ずMainWnd_RButtonDown()へ飛ぶ。
そこでメインスレッドを一時停止させて
ポップアップメニューを表示させる。
ポップアップメニューの戻り値はpopUpMenuRetに入れ、
また右クリックが押されたということをスレッド内で
把握できるように「myMainWndMsg」というグローバル変数を
用意し、それにWM_RBUTTONDOWNを入れておく。
スレッドを再開させる。
再開されたスレッドの中では、myMainWndMsgを参照し
何かメッセージがあればSelectCaseMyMainWndMsg()へ飛ぶ。
メッセージがWM_RBUTTONDOWNであれば
ポップアップの戻り値の分岐であるSelectCasePopUpMenu()へ。
SelectCasePopUpMenu()で色を変更。
すべて変更が終わってメインループへ戻る。
となっています。
これで、初めの指針に沿った動作はしてくれるのですが、
一つ気になるのが「右クリックを押してポップアップメニューが
表示されたとき、(マウスポインタを動かして)更にそこで右クリックを
押した時の動作」です。
エクスプローラなどでは新しい位置で改めてポップアップが立ち上がりますが、
このプログラムではそうはならず、ポップアップメニューは元の位置に
表示されたままです。
イベントでMainWnd_RButtonDown()へ飛んだあと、そのサブ内で
処理中にさらに右クリック・ダウンが発生した場合、処理はどうなるのでしようか?
(自分で試したみたところでは、改めてMainWnd_RButtonDown()が呼ばれる
ているようです。)
その場合、このプログラムではSuspendThreadでスレッドを止めています。
サスペンドカウンタはどうなるのでしょうか。
(これも実際に取って見ましたが、まず最初の右クリックで
呼び出し前のサスペンド カウントの値=0が返ります。それから更に
右クリックすると今度は1が返ります。ただしそれ以降何度右クリック
してもサスペンドカウンタは1のままです。)
このあたりが良く分からずにいます。宜しければ御教授下さい。
プログラムのほうでも、「こう組んだ方がいい」というのがありましたら
是非教えてください。
長くなってすみません。よろしくお願いします。
----------------------------------------------------------
この返信を書き終えた後にTomorrowさんのレクを確認してしまったので
ちょっと間が抜けた感じになってしまいましたが、改めて、
Tomorrowさん、レスありがとうございます。
教えていただいた通り第2引数にTPM_RIGHTBUTTONを
セットしてやってみたら、ポップアップは望んだ通りの動きを
してくれました。しかもこれを設定すると右クリックを何度
押してもスレッドカウンタは0のまま変わらず、安心して呼び出せる
ようにもなりました。(何故かはまだ分かってないのですが…)
ありがとうございました。またよろしくお願いします。