다음 코드 예제에서는 Windows 메시지 및 메시지 큐와 연결된 다음 작업을 수행하는 방법을 보여 줍니다.
메시지 루프 만들기
시스템은 각 스레드에 대한 메시지 큐를 자동으로 만들지 않습니다. 대신 시스템은 메시지 큐가 필요한 작업을 수행하는 스레드에 대해서만 메시지 큐를 만듭니다. 스레드가 하나 이상의 창을 만드는 경우 메시지 루프를 제공해야 합니다. 이 메시지 루프는 스레드의 메시지 큐에서 메시지를 검색하고 적절한 창 프로시저로 디스패치합니다.
시스템에서 애플리케이션의 개별 창으로 메시지를 보내므로 스레드는 메시지 루프를 시작하기 전에 하나 이상의 창을 만들어야 합니다. 대부분의 애플리케이션에는 창을 만드는 단일 스레드가 포함되어 있습니다. 일반적인 애플리케이션은 주 창에 대한 창 클래스를 등록하고, 주 창을 만들고 표시한 다음, 메시지 루프를 시작합니다( WinMain 함수에서 모두).
GetMessage 및 DispatchMessage 함수를 사용하여 메시지 루프를 만듭니다. 애플리케이션이 사용자로부터 문자 입력을 가져와야 하는 경우 루프에 TranslateMessage 함수를 포함합니다. TranslateMessage 가상 키 메시지를 문자 메시지로 변환합니다. 다음 예제에서는 간단한 Windows 기반 애플리케이션의 WinMain 함수의 메시지 루프를 보여 줍니다.
HINSTANCE hinst;
HWND hwndMain;
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
WNDCLASS wc;
UNREFERENCED_PARAMETER(lpszCmdLine);
// Register the window class for the main window.
if (!hPrevInstance)
{
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL,
IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL,
IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MainMenu";
wc.lpszClassName = "MainWndClass";
if (!RegisterClass(&wc))
return FALSE;
}
hinst = hInstance; // save instance handle
// Create the main window.
hwndMain = CreateWindow("MainWndClass", "Sample",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,
(HMENU) NULL, hinst, (LPVOID) NULL);
// If the main window cannot be created, terminate
// the application.
if (!hwndMain)
return FALSE;
// Show the window and paint its contents.
ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);
// Start the message loop.
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Return the exit code to the system.
return msg.wParam;
}
다음 예제에서는 가속기를 사용 하 고 모덜리스 대화 상자를 표시 하는 스레드에 대 한 메시지 루프를 보여 줍니다. TranslateAccelerator 또는 IsDialogMessageTRUE 반환하는 경우(메시지가 처리되었음을 나타남) TranslateMessageDispatchMessage 호출되지 않습니다. 그 이유는 TranslateAccelerator 및 IsDialogMessage가 필요한 모든 메시지를 번역하고 디스패치하기 때문입니다.
HWND hwndMain;
HWND hwndDlgModeless = NULL;
MSG msg;
BOOL bRet;
HACCEL haccel;
//
// Perform initialization and create a main window.
//
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
if (hwndDlgModeless == (HWND) NULL ||
!IsDialogMessage(hwndDlgModeless, &msg) &&
!TranslateAccelerator(hwndMain, haccel,
&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
메시지 큐 검사
경우에 따라 애플리케이션은 스레드의 메시지 루프 외부에서 스레드의 메시지 큐의 내용을 검사해야 합니다. 예를 들어 애플리케이션의 창 프로시저가 긴 그리기 작업을 수행하는 경우 사용자가 작업을 중단하도록 할 수 있습니다. 애플리케이션이 마우스 및 키보드 메시지에 대해 작업하는 동안 메시지 큐를 주기적으로 검사하지 않는 한 작업이 완료될 때까지 사용자 입력에 응답하지 않습니다. 그 이유는 창 프로시저가 메시지 처리를 완료할 때까지 스레드의 메시지 루프에서 DispatchMessage 함수가 반환되지 않기 때문입니다.
PeekMessage 함수를 사용하여 긴 작업 중에 메시지 큐를 검사할 수 있습니다. PeekMessageGetMessage 함수와 비슷합니다. 둘 다 필터 조건과 일치하는 메시지에 대한 메시지 큐를 확인한 다음 메시지를 MSG 구조에 복사합니다. 두 함수 간의 주요 차이점은 필터 조건과 일치하는 메시지가 큐에 배치될 때까지 GetMessage 반환되지 않는 반면, PeekMessage 메시지가 큐에 있는지 여부에 관계없이 즉시 반환된다는 것입니다.
다음 예제에서는 PeekMessage 사용하여 긴 작업 중에 마우스 클릭 및 키보드 입력에 대한 메시지 큐를 검사하는 방법을 보여 줍니다.
HWND hwnd;
BOOL fDone;
MSG msg;
// Begin the operation and continue until it is complete
// or until the user clicks the mouse or presses a key.
fDone = FALSE;
while (!fDone)
{
fDone = DoLengthyOperation(); // application-defined function
// Remove any messages that may be in the queue. If the
// queue contains any mouse or keyboard
// messages, end the operation.
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
switch(msg.message)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
//
// Perform any required cleanup.
//
fDone = TRUE;
}
}
}
GetQueueStatus및 GetInputState비롯한 다른 함수도 스레드의 메시지 큐의 내용을 검사할 수 있습니다. GetQueueStatus 큐의 메시지 유형을 나타내는 플래그 배열을 반환합니다. 를 사용하는 것이 큐에 메시지가 포함되어 있는지 여부를 검색하는 가장 빠른 방법입니다. GetInputState 큐에 마우스 또는 키보드 메시지가 포함된 경우 TRUE 반환합니다. 이러한 두 함수를 사용하여 큐에 처리해야 하는 메시지가 포함되어 있는지 여부를 확인할 수 있습니다.
메시지 게시
PostMessage 함수를 사용하여 메시지 큐에 메시지를 게시할 수 있습니다. PostMessage 스레드의 메시지 큐 끝에 메시지를 배치하고 스레드가 메시지를 처리할 때까지 기다리지 않고 즉시 반환합니다. 함수의 매개 변수에는 창 핸들, 메시지 식별자 및 두 개의 메시지 매개 변수가 포함됩니다. 시스템은 이러한 매개 변수를 MSG 구조에 복사하고, 시간 채우고, 구조체의 pt 멤버를 채우고, 구조체를 메시지 큐에 배치합니다.
시스템은 PostMessage 함수와 함께 전달된 창 핸들을 사용하여 메시지를 수신해야 하는 스레드 메시지 큐를 결정합니다. 핸들이 HWND_TOPMOST경우 시스템은 모든 최상위 창의 스레드 메시지 큐에 메시지를 게시합니다.
PostThreadMessage 함수를 사용하여 특정 스레드 메시지 큐에 메시지를 게시할 수 있습니다. PostThreadMessagePostMessage비슷합니다. 단, 첫 번째 매개 변수는 창 핸들이 아닌 스레드 식별자입니다. GetCurrentThreadId 함수를 호출하여 스레드 식별자를 검색할 수 있습니다.
PostQuitMessage 함수를 사용하여 메시지 루프를 종료합니다. PostQuitMessage 현재 실행 중인 스레드에 WM_QUIT 메시지를 게시합니다. 스레드의 메시지 루프가 종료되고 WM_QUIT 메시지가 발생하면 시스템에 컨트롤을 반환합니다. 애플리케이션은 일반적으로 다음 예제와 같이 WM_DESTROY 메시지에 대한 응답으로 PostQuitMessage 호출합니다.
case WM_DESTROY:
// Perform cleanup tasks.
PostQuitMessage(0);
break;
메시지 보내기
SendMessage 함수는 창 프로시저에 직접 메시지를 보내는 데 사용됩니다. SendMessage 창 프로시저를 호출하고 해당 프로시저가 메시지를 처리하고 결과를 반환할 때까지 기다립니다.
메시지를 시스템의 모든 창으로 보낼 수 있습니다. 필요한 것은 창 핸들입니다. 시스템은 핸들을 사용하여 메시지를 받아야 하는 창 프로시저를 결정합니다.
다른 스레드에서 보낸 메시지를 처리하기 전에 창 프로시저는 먼저 InSendMessage 함수를 호출해야 합니다. 이 함수가 TRUE반환하는 경우 창 프로시저는 다음 예제와 같이 스레드가 제어를 생성하는 함수 앞에 ReplyMessage 호출해야 합니다.
case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);
DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;
대화 상자의 컨트롤에 많은 메시지를 보낼 수 있습니다. 이러한 컨트롤 메시지는 컨트롤의 모양, 동작 및 콘텐츠를 설정하거나 컨트롤에 대한 정보를 검색합니다. 예를 들어 CB_ADDSTRING 메시지는 콤보 상자에 문자열을 추가할 수 있으며 BM_SETCHECK 메시지는 확인란 또는 라디오 단추의 확인 상태를 설정할 수 있습니다.
SendDlgItemMessage 함수를 사용하여 컨트롤의 식별자 및 컨트롤이 포함된 대화 상자 창의 핸들을 지정하여 메시지를 컨트롤에 보냅니다. 대화 상자 프로시저에서 가져온 다음 예제에서는 콤보 상자의 편집 컨트롤에서 해당 목록 상자에 문자열을 복사합니다. 이 예제에서는 SendDlgItemMessage 사용하여 CB_ADDSTRING 메시지를 콤보 상자에 보냅니다.
HWND hwndCombo;
int cTxtLen;
PSTR pszMem;
switch (uMsg)
{
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDD_ADDCBITEM:
// Get the handle of the combo box and the
// length of the string in the edit control
// of the combo box.
hwndCombo = GetDlgItem(hwndDlg, IDD_COMBO);
cTxtLen = GetWindowTextLength(hwndCombo);
// Allocate memory for the string and copy
// the string into the memory.
pszMem = (PSTR) VirtualAlloc((LPVOID) NULL,
(DWORD) (cTxtLen + 1), MEM_COMMIT,
PAGE_READWRITE);
GetWindowText(hwndCombo, pszMem,
cTxtLen + 1);
// Add the string to the list box of the
// combo box and remove the string from the
// edit control of the combo box.
if (pszMem != NULL)
{
SendDlgItemMessage(hwndDlg, IDD_COMBO,
CB_ADDSTRING, 0,
(DWORD) ((LPSTR) pszMem));
SetWindowText(hwndCombo, (LPSTR) NULL);
}
// Free the memory and return.
VirtualFree(pszMem, 0, MEM_RELEASE);
return TRUE;
//
// Process other dialog box commands.
//
}
//
// Process other dialog box messages.
//
}