메시지 및 메시지 큐 사용

다음 코드 예제에서는 Windows 메시지 및 메시지 큐와 연결 된 다음 작업을 수행 하는 방법에 설명 합니다.

메시지 루프 만들기

시스템은 각 스레드에 대한 메시지 큐를 자동으로 만들지 않습니다. 대신, 시스템은 메시지 큐가 필요한 작업을 수행하는 스레드에 대해서만 메시지 큐를 만듭니다. 스레드가 하나 이상의 창을 만드는 경우 메시지 루프를 제공해야 합니다. 이 메시지 루프는 스레드의 메시지 큐에서 메시지를 검색하고 적절한 창 프로시저로 디스패치합니다.

시스템은 메시지를 애플리케이션의 개별 창으로 전달하므로 스레드는 메시지 루프를 시작하기 전에 하나 이상의 창을 만들어야 합니다. 대부분의 애플리케이션에는 창을 만드는 단일 스레드가 포함되어 있습니다. 일반적인 애플리케이션은 주 창에 대한 창 클래스를 등록하고, 주 창을 만들고 표시한 다음, WinMain 함수에서 메시지 루프를 시작합니다.

GetMessageDispatchMessage 함수를 사용하여 메시지 루프를 만듭니다. 애플리케이션이 사용자로부터 문자 입력을 받아야 하는 경우 루프에 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 또는 IsDialogMessage가 TRUE를 반환하면(메시지가 처리되었음을 나타낸) 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 함수를 사용하여 긴 작업 중에 메시지 큐를 검사할 수 있습니다. PeekMessage는 GetMessage 함수와 비슷합니다. 둘 다 필터 조건과 일치하는 메시지에 대한 메시지 큐를 확인한 다음 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는 큐의 메시지 유형을 나타내는 플래그 배열을 반환합니다. 를 사용하는 것이 큐에 메시지가 포함되어 있는지 여부를 검색하는 가장 빠른 방법입니다. 큐에 마우스 또는 키보드 메시지가 포함된 경우 GetInputState는 TRUE를 반환합니다. 이러한 두 함수를 모두 사용하여 큐에 처리해야 하는 메시지가 포함되어 있는지 여부를 확인할 수 있습니다.

메시지 게시

PostMessage 함수를 사용하여 메시지 큐에 메시지를 게시할 수 있습니다. PostMessage는 스레드의 메시지 큐 끝에 메시지를 배치하고 스레드가 메시지를 처리할 때까지 기다리지 않고 즉시 반환합니다. 함수의 매개 변수에는 창 핸들, 메시지 식별자 및 두 개의 메시지 매개 변수가 포함됩니다. 시스템은 이러한 매개 변수를 MSG 구조에 복사하고, 구조체의 시간pt 멤버를 채우고, 구조체를 메시지 큐에 배치합니다.

시스템은 PostMessage 함수와 함께 전달된 창 핸들을 사용하여 메시지를 수신할 스레드 메시지 큐를 결정합니다. 핸들이 HWND _ TOPMOST인 경우 시스템은 모든 최상위 창의 스레드 메시지 큐에 메시지를 게시합니다.

PostThreadMessage 함수를 사용하여 특정 스레드 메시지 큐에 메시지를 게시할 수 있습니다. PostThreadMessage는 PostMessage와유사합니다. 단, 첫 번째 매개 변수는 창 핸들이 아닌 스레드 식별자입니다. 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. 
    // 
 
}