메시지 및 메시지 큐 사용

다음 코드 예제에서는 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가 호출되지 않습니다. 그 이유는 TranslateAcceleratorIsDialogMessage 가 메시지를 번역하고 디스패치하는 데 필요한 모든 작업을 수행하기 때문입니다.

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; 
        } 
    } 
} 

GetQueueStatusGetInputState를 비롯한 다른 함수도 스레드의 메시지 큐 내용을 검사할 수 있습니다. GetQueueStatus 는 큐의 메시지 유형을 나타내는 플래그 배열을 반환합니다. 를 사용하는 것이 큐에 메시지가 포함되어 있는지 여부를 검색하는 가장 빠른 방법입니다. 큐에 마우스 또는 키보드 메시지가 포함된 경우 GetInputStateTRUE를 반환합니다. 이러한 두 함수를 모두 사용하여 큐에 처리해야 하는 메시지가 포함되어 있는지 여부를 확인할 수 있습니다.

메시지 게시

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. 
    // 
 
}