メッセージループについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
きゃりーわんわん
記事: 34
登録日時: 7年前

メッセージループについて

#1

投稿記事 by きゃりーわんわん » 7年前

メッセージループの挙動を確認する過程で
以下のようなコードを実装したところ、
PostThreadMessage()で送信したメッセージは受信できているのに
DispatchMessage()を実行してもウィンドウプロシージャ(MyWindowProc)が実行されないという現象が発生しました。
RegisterWindow()、CreateWindow()は共に成功していますし
原因が分からない状況です。
RegisterWindow()やCreateWindow()のパラメータを間違えているのでしょうか?

コード:

#include <windows.h>
#include <stdio.h>
#include <process.h>

#define EVENT_NAME "EVENT_NAME"
#define MY_MESSAGE (WM_APP + 1)

// _beginthreadex()から実行されるAPI
unsigned int WINAPI func(void *vp);
 
// ウィンドウプロシージャ
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

// Windowを作成
BOOL MyCreateWindow(LPTSTR lpszAppName, HINSTANCE hinst);

int main(void)
{
	char buf[64];
	HANDLE hEvent;
	HANDLE hThread;

	printf("【0x%08X】main thread\n", GetCurrentThreadId());

	// サスペンド状態でスレッドを作成
	hThread = (HANDLE)_beginthreadex(NULL, 0, func, NULL, CREATE_SUSPENDED, NULL);

	// EVENT名にスレッドIDを使用し、EVENT作成後にスレッドを開始する
	sprintf_s(buf, sizeof(buf), "%s-0x%08X", EVENT_NAME, GetThreadId(hThread));
	hEvent = CreateEvent(NULL, FALSE, FALSE, buf);
	ResumeThread(hThread);

	// GUIスレッドになるまで待つ
	WaitForSingleObject(hEvent, INFINITE);
	printf("【0x%08X】GUIスレッドになったため、スレッドに対してメッセージを送信\n", GetCurrentThreadId());

	// 作成したスレッドにメッセージを送る
	if(PostThreadMessage(GetThreadId(hThread), MY_MESSAGE, 0, 0) == FALSE)
	{
		printf("【0x%08X】PostThreadMessage() == FALSE、GetLastError() = 0x%08X\n", GetCurrentThreadId(), GetLastError());
		return 0;
	}

	// 作成したスレッドが終了するまで待つ
	WaitForSingleObject(hThread, INFINITE);


	return 0;
}

unsigned int WINAPI func(void *vp)
{
	char buf[64];
	HANDLE hEvent;
	MSG msg;

	printf("【0x%08X】sub thread", GetCurrentThreadId());

	// GUIスレッドにする
	if(IsGUIThread(TRUE) == FALSE)
	{
		printf("ERROR");
	}

	// Windowを作成
	if(MyCreateWindow("WinProc", GetModuleHandle(NULL)) == FALSE)
	{
		printf("FALSE\n");
	}

	// EVENT名を作成し、EVENTをシグナル状態にする
	sprintf_s(buf, sizeof(buf), "%s-0x%08X", EVENT_NAME, GetCurrentThreadId());
	hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, buf);
	SetEvent(hEvent);

	// メッセージループ開始
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		printf("【0x%08X】msg.message = 0x%08X, msg.lParam = 0x%08X, msg.wParam = 0x%08X\n", GetCurrentThreadId(), msg.message, msg.lParam, msg.wParam);
		printf("【0x%08X】DispatchMessage()開始\n", GetCurrentThreadId());
		DispatchMessage(&msg);
		printf("【0x%08X】DispatchMessage()終了\n", GetCurrentThreadId());
	}

	return 0;
}

// Windowの作成
BOOL MyCreateWindow(LPTSTR lpszAppName, HINSTANCE hinst)
{
	HWND       hwnd;
	WNDCLASSEX wc;

	memset(&wc, 0, sizeof(wc));
	
	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = MyWindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = NULL;
	wc.hCursor       = NULL;
	wc.hbrBackground = NULL;
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = lpszAppName;
	wc.hIconSm       = NULL;
	
	if (RegisterClassEx(&wc) == 0)
		return FALSE;

	hwnd = CreateWindowEx(0, lpszAppName, lpszAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return FALSE;

	return TRUE;
}

LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	printf("【0x%08X】MyWindowsProc thread、Msg = 0x%08X\n", GetCurrentThreadId(), uMsg);

	switch (uMsg) 
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

きゃりーわんわん
記事: 34
登録日時: 7年前

Re: メッセージループについて

#2

投稿記事 by きゃりーわんわん » 7年前

すみません、確認した環境を記載していませんでした。

こちらではVisual Studio 2008 SP1でビルドし、
Windows Vista 32bitのPCで動作確認しました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: メッセージループについて

#3

投稿記事 by softya(ソフト屋) » 7年前

TranslateMessage()が見当たりませんが?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

きゃりーわんわん
記事: 34
登録日時: 7年前

Re: メッセージループについて

#4

投稿記事 by きゃりーわんわん » 7年前

softya(ソフト屋) さんが書きました: TranslateMessage()が見当たりませんが?
説明不足ですみません。
独自のメッセージを送信するつもりですので
TranslateMessage()は不要と考えておりました。
// 文字メッセージを送信する場合にTranslateMessage()が必要との認識です。

試しにTranslateMessage()を実行しても結果は変わらずでした。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: メッセージループについて

#5

投稿記事 by softya(ソフト屋) » 7年前

表示されていないのが原因ですかね。
hwnd = CreateWindowEx(0, lpszAppName, lpszAppName, WS_OVERLAPPEDWINDOW|WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
とすれば表示されますしメッセージも回ります。
ただ、ウィンドウ表示するとTranslateMessage()は必要じゃないかなぁと。
文字入力とかしないといらないはずですが、まぁつけておいて問題ないからぐらいですかね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: メッセージループについて

#6

投稿記事 by YuO » 7年前

ウィンドウプロシージャはウィンドウクラスに紐付いているため,PostThreadMessageで送られたメッセージはウィンドウプロシージャには送られません。
ウィンドウに対してのメッセージをPostしてやれば,ウィンドウプロシージャが走るでしょう。
# WM_CREATEの分は走っているはず。

なお,メッセージを取り扱うだけのウィンドウであるなら,Message-Only Windowsとするのがよいと思います

きゃりーわんわん
記事: 34
登録日時: 7年前

Re: メッセージループについて

#7

投稿記事 by きゃりーわんわん » 7年前

softya(ソフト屋) さん、YuO さん
教えてくださいありがとうございます。

Message-Only Windowにすることで
UIを表示することもなく、メッセージを送信することができました。
以下の手順で実現しました。
①:YuOさんから提示していただいたリンク先に記載してある方法でCreateWindowEx()を実行し、
   Message-Only Windowを作成
②:メッセージ送信側でFindWindowEx()を実行し、①で作成したWindowのHWNDを取得
③:②で取得したHWNDを使用してPostMessage()

ここまできて、ようやくウィンドウプロシージャを動作させることができたのですが
独自定義のメッセージだけ送信する場合、DispatchMessage()/WindowProc()を実行させる意味ってあるのでしょうか?
GetMessage()で取得した後に普通にif文なりswitch文で分岐した方が早いような気がしました。

なお、上に記載した手順を実装したのが以下となります。

コード:

#include <windows.h>
#include <stdio.h>
#include <process.h>
 
#define EVENT_NAME "EVENT_NAME"
#define MY_MESSAGE (WM_APP + 1)
 
// _beginthreadex()から実行されるAPI
unsigned int WINAPI func(void *vp);
 
// ウィンドウプロシージャ
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
// Windowを作成
BOOL MyCreateWindow(LPTSTR lpszAppName, HINSTANCE hinst);
 
int main(void)
{
    char buf[64];
    HANDLE hEvent;
    HANDLE hThread;
 
    printf("【0x%08X】main thread\n", GetCurrentThreadId());
 
    // サスペンド状態でスレッドを作成
    hThread = (HANDLE)_beginthreadex(NULL, 0, func, NULL, CREATE_SUSPENDED, NULL);
 
    // EVENT名にスレッドIDを使用し、EVENT作成後にスレッドを開始する
    sprintf_s(buf, sizeof(buf), "%s-0x%08X", EVENT_NAME, GetThreadId(hThread));
    hEvent = CreateEvent(NULL, FALSE, FALSE, buf);
    ResumeThread(hThread);
 
    // 作成したスレッドがGUIスレッドになるまで待つ
    WaitForSingleObject(hEvent, INFINITE);
    printf("【0x%08X】GUIスレッドになり、Message-Only Windowが作成できたので、作成したWindowに対してメッセージを送信\n", GetCurrentThreadId());
 
	// HWNDを取得
	HWND hWnd = FindWindowEx(HWND_MESSAGE, NULL, TEXT("WinProc"), NULL);

	// 取得したHWNDに対してメッセージを送る
    if(PostMessage(hWnd, MY_MESSAGE, 0, 0) == FALSE)
    {
        printf("【0x%08X】PostThreadMessage() == FALSE、GetLastError() = 0x%08X\n", GetCurrentThreadId(), GetLastError());
        return 0;
    }
 
    // 作成したスレッドが終了するまで待つ
    WaitForSingleObject(hThread, INFINITE);
 
 
    return 0;
}
 
unsigned int WINAPI func(void *vp)
{
    char buf[64];
    HANDLE hEvent;
    MSG msg;
 
    printf("【0x%08X】sub thread", GetCurrentThreadId());
 
    // GUIスレッドにする
    if(IsGUIThread(TRUE) == FALSE)
    {
        printf("【0x%08X】ERROR:IsGUIThread\n", GetCurrentThreadId());
		exit(0);
    }
 
    // Windowを作成
    if(MyCreateWindow("WinProc", GetModuleHandle(NULL)) == NULL)
    {
        printf("【0x%08X】ERROR:MyCreateWindow\n", GetCurrentThreadId());
		exit(0);
    }
 
    // EVENT名を作成し、EVENTをシグナル状態にする
    sprintf_s(buf, sizeof(buf), "%s-0x%08X", EVENT_NAME, GetCurrentThreadId());
    hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, buf);
    SetEvent(hEvent);
 
    // メッセージループ開始
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        printf("【0x%08X】msg.message = 0x%08X, msg.lParam = 0x%08X, msg.wParam = 0x%08X\n", GetCurrentThreadId(), msg.message, msg.lParam, msg.wParam);
        printf("【0x%08X】DispatchMessage()開始\n", GetCurrentThreadId());
		DispatchMessage(&msg);
        printf("【0x%08X】DispatchMessage()終了\n", GetCurrentThreadId());
    }
 
    return 0;
}
 
// Windowの作成
BOOL MyCreateWindow(LPTSTR lpszAppName, HINSTANCE hinst)
{
    HWND       hwnd;
    WNDCLASSEX wc;
 
    memset(&wc, 0, sizeof(wc));
    
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = MyWindowProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hinst;
    wc.hIcon         = NULL;
    wc.hCursor       = NULL;
    wc.hbrBackground = NULL;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = lpszAppName;
    wc.hIconSm       = NULL;
    
    if (RegisterClassEx(&wc) == 0)
        return NULL;
 
    hwnd = CreateWindowEx(0, lpszAppName, lpszAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, hinst, NULL);
    if (hwnd == NULL)
        return NULL;
 
    return TRUE;
}
 
LRESULT CALLBACK MyWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    printf("【0x%08X】MyWindowsProc thread、Msg = 0x%08X\n", GetCurrentThreadId(), uMsg);
 
    switch (uMsg) 
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
 
	case MY_MESSAGE:
		PostQuitMessage(0);
		return 0;

    default:
        break;
 
    }
 
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

閉鎖

“C言語何でも質問掲示板” へ戻る