通常メッセージループはウィンドウを制御するために使いますが、実は各スレッドが1つのメッセージキューを持つことができ、そのメッセージキューの内容をスレッドメッセージを使って制御することができます。
スレッドメッセージキューにメッセージを送る際には
専用のPostThreadMessage関数が使われますが、メッセージを取得する側は通常のメッセージループがスレッド内にあるだけでほとんど違いはありません。
違うとすれば、PeekMessage関数とSleep関数を使って
メッセージキューを作成するところだけです。
コード: 全て選択
Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" (lpEventAttributes As *SECURITY_ATTRIBUTES, bManualReset As Long, bInitialState As Long, lpName As *Char) As HANDLE
Declare Function SetEvent Lib "kernel32" (hEvent As HANDLE) As Long
Declare Function ResetEvent Lib "kernel32" (hEvent As HANDLE) As Long
Sub MessageThreadProc(hEvent As HANDLE)
/* スレッドメッセージキューの作成 */
Dim msg As MSG
Do
PostThreadMessage(GetCurrentThreadId(),WM_USER,0,0)
If PeekMessage(msg,NULL,WM_USER,WM_USER,PM_REMOVE)=TRUE Then Exit Do
Sleep(0)
Loop
/* メインスレッドに準備の完了を通知 */
If hEvent<>NULL Then SetEvent(hEvent)
/* スレッドメッセージループ */
While GetMessage(msg,0,0,0)>0
Select Case msg.message
Case WM_USER+1
/*ここに処理を記述 1*/
Case WM_USER+2
/*ここに処理を記述 2*/
Case WM_USER+・・・
/*ここに処理を記述 ・・・*/
End Select
Wend
/* メインスレッドに終了の完了を通知 */
If hEvent<>NULL Then SetEvent(hEvent)
/* メインスレッドの終了 */
ExitThread(&HE0F)
End Sub
Class CMessageThread
Private
m_hThread As HANDLE
m_idThread As DWord
m_hEvent As HANDLE
Public
Sub CMessageThread()
/* イベントを作成 */
m_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL)
/* スレッドを作成 */
m_hThread=CreateThread(ByVal NULL,0,AddressOf(MessageThreadProc),m_hEvent,0,VarPtr(m_idThread))
/* スレッドメッセージキュー作成まで待機 */
If m_hEvent<>NULL and m_hThread<>NULL Then WaitForSingleObject(m_hEvent,INFINITE)
End Sub
Sub ~CMessageThread()
If m_hEvent<>NULL Then ResetEvent(m_hEvent)
/* スレッドへ終了を通知 */
SendMsg(WM_QUIT,0,0)
/* スレッド終了まで待機 */
If m_hEvent<>NULL and m_hThread<>NULL Then WaitForSingleObject(m_hEvent,INFINITE)
/* ハンドルを破棄 */
If m_hEvent<>NULL Then CloseHandle(m_hEvent)
If m_hThread<>NULL Then CloseHandle(m_hThread)
End Sub
Function SendMsg(dwMsg As DWord, wParam As DWord, lParam As DWord) As Long
/* スレッドにメッセージを送信 */
SendMsg=PostThreadMessage(m_idThread,dwMsg,wParam,lParam)
End Function
End Class
コードを見ていただくと、MessageThreadProc内に
コード: 全て選択
Case WM_USER+1
/*ここに処理を記述 1*/
これを見て分かるように、WM_USER+1というようにメッセージによって処理を振り分けています。
このプログラムを動かしたい場合
コード: 全て選択
***.SendMsg(WM_USER+1,0,0)
WPARAMやLPARAMといった追加情報も同時に送信できたり、同時に複数のメッセージが送信されてもメッセージキューに貯められて逐次実行されるのでメモリ関連のエラーもあまり気にすることはありません。
ただ、メッセージを送信しても戻り値を取得できないという問題はあります。。。
何か良いアイデアをお持ちの方は改良に挑戦してください。
サンプル(MCIを使った別スレッドでの再生) [ここをクリックすると内容が表示されます]
私はゲーム開発でこれをよく使います。
コード: 全て選択
Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" (lpEventAttributes As *SECURITY_ATTRIBUTES, bManualReset As Long, bInitialState As Long, lpName As *Char) As HANDLE
Declare Function SetEvent Lib "kernel32" (hEvent As HANDLE) As Long
Declare Function ResetEvent Lib "kernel32" (hEvent As HANDLE) As Long
Const MEM_SOUND1 = WM_USER+1
Const MEM_SOUND2 = WM_USER+2
Sub PMciEngine(hEvent As HANDLE)
Dim Msg As MSG
Do
PostThreadMessage(GetCurrentThreadId(),WM_USER,0,0)
If PeekMessage(Msg,NULL,WM_USER,WM_USER,PM_REMOVE)=TRUE Then Exit Do
Sleep(0)
Loop
If hEvent=NULL Then
ExitThread(&HE0F)
Exit Sub
End If
SetEvent(hEvent)
/* MCIデバイスを開く */
Dim mop[ELM(2)] As MCI_OPEN_PARMS
mop[0].lpstrElementName="sound1.wav"
mop[1].lpstrElementName="sound2.wav"
mciSendCommand(0,MCI_OPEN,MCI_OPEN_ELEMENT,mop[0])
mciSendCommand(0,MCI_OPEN,MCI_OPEN_ELEMENT,mop[1])
/* スレッドメッセージを使ってMCIデバイスを再生 */
Dim mpp As MCI_OPEN_PARMS
While GetMessage(Msg,0,0,0)>0
Select Case Msg.message
Case MEM_SOUND1
mciSendCommand(mop[0].wDeviceID,MCI_PLAY,MCI_FROM,mpp)
Case MEM_SOUND1
mciSendCommand(mop[1].wDeviceID,MCI_PLAY,MCI_FROM,mpp)
End Select
Wend
/* MCIデバイスを閉じる */
Dim mgp As DWord
mciSendCommand(mop[0].wDeviceID,MCI_CLOSE,0,mgp)
mciSendCommand(mop[1].wDeviceID,MCI_CLOSE,0,mgp)
If hEvent<>NULL Then SetEvent(hEvent)
ExitThread(&HE0F)
End Sub
Class CMciEngine
Private
m_hThread As HANDLE
m_idThread As DWord
m_hEvent As HANDLE
Public
Sub CMciEngine()
m_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL)
m_hThread=CreateThread(ByVal NULL,0,AddressOf(PMciEngine),m_hEvent,0,VarPtr(m_idThread))
If m_hEvent<>NULL and m_hThread<>NULL Then WaitForSingleObject(m_hEvent,INFINITE)
End Sub
Sub ~CMciEngine()
If m_hEvent<>NULL Then ResetEvent(m_hEvent)
SendMsg(WM_QUIT,0,0)
If m_hEvent<>NULL and m_hThread<>NULL Then WaitForSingleObject(m_hEvent,INFINITE)
If m_hEvent<>NULL Then CloseHandle(m_hEvent)
If m_hThread<>NULL Then CloseHandle(m_hThread)
End Sub
Function SendMsg(dwMsg As DWord, wParam As DWord, lParam As DWord) As Long
SendMsg=PostThreadMessage(m_idThread,dwMsg,wParam,lParam)
End Function
End Class
Dim me As *CMciEngine
'-----------------------------------------------------------------------------
' ウィンドウメッセージを処理するためのコールバック関数
Function MainWndProc(hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM) As DWord
' TODO: この位置にウィンドウメッセージを処理するためのコードを記述します。
' イベントプロシージャの呼び出しを行います。
MainWndProc=EventCall_MainWnd(hWnd,dwMsg,wParam,lParam)
End Function
'-----------------------------------------------------------------------------
' ここから下は、イベントプロシージャを記述するための領域になります。
Sub MainWnd_Destroy()
Delete me
MciEngine_DestroyObjects()
PostQuitMessage(0)
End Sub
Sub MainWnd_Create(ByRef CreateStruct As CREATESTRUCT)
me=New CMciEngine
End Sub
Sub MainWnd_CommandButton1_Click()
me->SendMsg(MEM_SOUND1,0,0)
End Sub
Sub MainWnd_CommandButton2_Click()
me->SendMsg(MEM_SOUND2,0,0)
End Sub