Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WndProc calling mechanism (WinAPI)

I'm trying to understand how a windows application works.

There is a WndProc function, wherein message processing occurs.

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {   
    switch (msg) {
        case WM_KEYDOWN:
            if (wParam == VK_ESCAPE) {                                              
                if (MessageBox(0, L"Are you sure?", L"Exit?", MB_YESNO |     MB_ICONQUESTION) == IDYES)
                    //Release the windows allocated memory  
                    DestroyWindow(hwnd);
            }
            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

This function can be called in two cases:

A) Called by DispatchMessage(&msg) function in a message loop cycle:

while (true){                       
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
            if (msg.message == WM_QUIT)    
                break;   
            TranslateMessage(&msg);                                     
            DispatchMessage(&msg);
        }
    }

B) Called by Windows when receiving a non-queued message.

How does this work? How does Windows immediately call the WndProc function without using parallelism? Could you please describe in detail the mechanism of function calls?

The official MSDN documentation says:

Nonqueued messages are sent immediately to the destination window procedure, bypassing the system message queue and thread message queue. The system typically sends nonqueued messages to notify a window of events that affect it. For example, when the user activates a new application window, the system sends the window a series of messages, including WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR. These messages notify the window that it has been activated, that keyboard input is being directed to the window, and that the mouse cursor has been moved within the borders of the window. Nonqueued messages can also result when an application calls certain system functions. For example, the system sends the WM_WINDOWPOSCHANGED message after an application uses the SetWindowPos function to move a window.

It turns out that non-queued messages appear only during initialization of the window, and all subsequent non-queued messages can only be the result of calling WinAPI functions in my program?

like image 514
r1d1 Avatar asked Jan 06 '23 16:01

r1d1


1 Answers

There's nothing particularly magic, if SendMessage is called from the same thread that created the window then the window procedure gets called directly by SendMessage, otherwise a request is queued and SendMessage() waits until the message loop processes the request. This is documented behavior:

SendMessage function

If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message.

PeekMessage function

Dispatches incoming sent messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).

...

During this call, the system delivers pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. Then the first queued message that matches the specified filter is retrieved. The system may also process internal events.

GetMessage function

Retrieves a message from the calling thread's message queue. The function dispatches incoming sent messages until a posted message is available for retrieval.

...

During this call, the system delivers pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. Then the first queued message that matches the specified filter is retrieved. The system may also process internal events.

The only magic is that:

  • some messages aren't "really" queued, but are synthesized by GetMessage if there's nothing better to do (→ repaint, mouse move messages, timers, etc);
  • the message dispatch handles Unicode conversion for messages that the system "knows"; a window is "Unicode" or "ANSI" depending from whether it was registered through RegisterWindowW or RegisterWindowA, and the sent message is "Unicode" or "ANSI" depending from whether it was sent through SendMessageW/PostMessageW/... or SendMessageA/PostMessageA/.... If the two don't match the system converts the message appropriately.

No parallelism is involved, the nice thing about window procedures is that they are always invoked from the thread that created the window.

like image 163
Matteo Italia Avatar answered Jan 14 '23 19:01

Matteo Italia