December 21st, 2023

How do I get access to the wParam and lParam of the WM_QUERY­END­SESSION method from my MFC message handler?

The Microsoft Foundation Classes (MFC) framework provides a set of macros for building message handler tables. The handler for the WM_QUERY­END­SESSION message has the signature

BOOL OnQueryEndSession();

and it does not have access to the wParam and lParam parameters.¹ How can you get the values of those parameters?

Don’t be afraid to learn how things work. That often leads to insights on how to use them better.

In our case, ON_WM_QUERY­END­SESSION goes like this:

#define ON_WM_QUERYENDESSION() \
    { WM_QUERYENDSESSION, 0, 0, 0, AfxSig_bv, \
        (AFX_PMSG)(AFX_PMSGW)(BOOL (AFX_MSG_CALL CWnd::*)(void))
        &OnQueryEndSession },

If you read the comment block at the top of the file, it says that message map entries can take many forms, including

7) constant windows messages
    nMessage, 0, 0, 0, signature type, member function
    (eg: WM_PAINT, 0, ...)

From the structure of the table and the pattern of the macros, and the internal documentation, it’s apparent that the message map is a table of message number and instructions on how to handle the message. The signature AfxSig_bv is documented in the header file as

    Afx_bv = Afx_wv,    // BOOL (void)

Apparently, what happens is that the message dispatcher looks through the table for a matching message number, and once it finds a match, it calls the associated function using the specified signature, and then returns the result as the message result.

We can therefore just create our own entry for WM_QUERY­END­SESSION that uses a different signature. And it turns out that the ON_MESSAGE macro exists specifically for generic messages:

#define ON_MESSAGE(message, memberFxn) \
    { message, 0, 0, 0, AfxSig_lwl, \
        (AFX_PMSG)(AFX_PMSGW)(BOOL (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM))
        &memberFxn },

Solution: Treat WM_QUERY­END­SESSION as a custom message:

    ON_MESSAGE(WM_QUERYENDSESSION, OnQueryEndSession)

    LRESULT AFX_MSG_CALL OnQueryEndSession(WPARAM wParam, LPARAM lParam);

Bonus chatter: There apparently used to be a CWnd::GetCurrentMessage() function for getting a copy of the current message being handled by MFC. (I don’t know this for sure, but I see references to it.) It doesn’t exist anymore (or maybe never existed), so it’s not really an option.

¹ The reason is that at the time that MFC was written, there was no information encoded in those parameters.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Ismo Salonen

    Also AfxGetCurrentMessage exists, ( VS2019). I can confirm that GetMessage() works also, I’ve been using it in one special case and it still compiles correctly ( also works correctly).

  • Tobias Käs

    > There apparently used to be a CWnd::GetCurrentMessage() function for getting a copy of the current message being handled by MFC. (I don’t know this for sure, but I see references to it.) It doesn’t exist anymore (or maybe never existed), so it’s not really an option.

    Well, CWnd::GetCurrentMessage certainly seems documented: https://learn.microsoft.com/en-us/cpp/mfc/reference/cwnd-class?view=msvc-170#getcurrentmessage and I see it in the afxwin header at the top of the CWnd class as a protected member, so not sure where...

    Read more