When will GetMessage return -1?

A source of great consternation is the mysterious -1 return value from Get­Message:

If there is an error, the return value is −1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer.

That paragraph has caused all sorts of havoc, because it throws into disarray the standard message pump:

MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {

But don't worry, the standard message pump is safe. If your parameters are exactly

  • a valid pointer to a valid MSG structure,
  • a null window handle,
  • no starting message range filter,
  • no ending message range filter,

then Get­Message will not fail with -1.

Originally, the Get­Message function did not have a failure mode. If you passed invalid parameters, then you invoked undefined behavior, and you probably crashed.

Later, somebody said, "Oh, no, the Get­Message function needs to detect invalid parameters and instead of crashing, it needs to fail gracefull with some sort of error code." (This was before "Fail-Fast" came into fashion.)

The problem is that Get­Message's return value of BOOL was already specified not as a success/failure code, but rather a "Has a WM_QUIT message been received?" code. So return FALSE wouldn't work.

The solution (if that's what you want to call it) was to have Get­Message return the not-really-a-BOOL-but-we'll-pretend-it-is value -1 to signal an invalid parameter error.

And that's what threw everybody into a tizzy, because now every message loop looks buggy.

But you can calm down. The standard message loop is fine. All the parameters are hard-coded (and therefore valid by inspection), save for the &msg parameter, which is still valid by inspection. So that case is okay. It has to be, for compatibility.

The people who need to worry are people who pass a variable as the window handle filter (because that window handle may no longer be valid), or pass dynamically-allocated memory as the lpMsg (because the pointer may no longer be valid), or who pass a nontrivial message filter (because the filter parameters may be invalid).

In practice, the memory for the lpMsg is nearly always a stack variable (so the pointer is valid), and the message range filters are hard-coded (so valid by inspection). The one to watch out for is the window handle filter. But we saw earlier that a filtered Get­Message is a bad idea anyway, because your program will not respond to messages that don't meet the filter.

Comments (16)
  1. Dumb question: why does this function return -1 if lpMsg is invalid?  How can GetMessage know that the pointer into my highly-proprietary custom heap is valid?

    [The definition of "invalid" here is the same used elsewhere: "raises an exception when kernel tries to write the result into it." -Raymond]
  2. RP7 says:

    Where the documentation says "If the function retrieves a message other than WM_QUIT, the return value is nonzero", should it really say "the return value is 1"?  -1 is a nonzero value, but is presumably not among the possibilities when there's no error.  Or, does nonzero implicitly refer to nonnegative nonzero values?

  3. Grumpy says:

    True, false, FILE_NOT_FOUND – I see no problems here, these are all valid boolean values. ;-)

  4. @RP7:

    The thing is, TRUE may be defined in the headers as 1, but a function may not necessarily return 1 for success.

    You would be better reading that as more of a non specific to specific return value order. This really makes the values that it can obtain on success INT_MIN to -2 and then 1 to INT_MAX. The fact that -1 was given as a specific value means that it is automatically removed from the success values.

  5. AsmGuru62 says:

    In fact, specifying a valid HWND of a main application window in a call to GetMessage (main message loop) means, that

    this application will never terminate. Specifying NULL, however, ends application properly.

  6. Random832 says:

    @Crescens2k "The fact that -1 was given as a specific value means that it is automatically removed from the success values."

    That's not necessarily something you can infer. There are functions in the C standard where the only reliable difference between an error and success is errno. Two come to mind easily: strtol and mktime (the latter not on windows, since years before 1970 are not accepted by MSVCRT's mktime) can each return their respective error values [LONG_MIN and LONG_MAX, and (time_t)-1, respectively] as valid results.

    GetMessage could have, in principle, been defined in a similar way, wherein you must call SetLastError(ERROR_SUCCESS) before calling it, and inspect the result of GetLastError if the returned value == -1. In practice, I suspect it always returns 1 for success.

  7. @Random832 says:

    "In practice, I suspect it always returns 1 for success."

    But is not contractually obliged to in Windows9, or after any security patch in Windows8 or earlier.

    Don't rely on undocumented functionality. "It works this way now, so will work this way forever" is an bad assertion to make.

  8. @Random832:

    Surely if that was the case, it would also document it.

    For example, GetWindowLongPtr:

    If the function fails, the return value is zero. To get extended error information, call GetLastError.

    If SetWindowLong or SetWindowLongPtr has not been called previously, GetWindowLongPtr returns zero for values in the extra window or class memory.

  9. @AsmGuru62: because WM_QUIT seems not to be window-specific, so specifying a particular HWND filters the message out.

  10. Me says:

    Silly question: Wouldn't it have been more intelligent, to fail with FALSE and QUIT…?

  11. cheong00 says:

    @Me: That means when such error is hit, the user will see the application suddenly disappear without reason. There won't even be a chance to write diagnostic logs somewhere.

  12. foo says:

    @me. I get the impression that the function used to return non-zero on invalid parameter, so altering it to return 0 would cause apps that previously got lucky (passed invalid parameters, but no noticable side effects) to run their exit sequence without the user asking them to. But yeah, something like return FALSE with GetLastError() being ERROR_INVALID_PARAMETER vs ERROR_SUCCESS or something would seem like a better work-around otherwise.

  13. Yuhong Bao says:

    Yea, Win16 did not have any memory protection at all. Similar reasons are why WaitMessage was changed to return BOOL in Win32 too.

  14. JDT says:

    Raymond, you might want to file a bug with MSDN; the GetMessage page says that you should avoid the "while(GetMessage(…))" form at all costs.

    [Read it again. It says that you should avoid "while (GetMessage(lpMsg, hWnd, 0, 0)". Note the filter. Nevertheless, MSDN should call out that subtlety. -Raymond]
  15. Danny says:

    What's this function again? I never heard of it (same way I never heard of WinMe for example). PeekMessage (especially with a while) and the counter-part PostMessage are all I need. GetMessage / SendMessage can become deprecated as far as I care. I live in a multi-thread world, and functions who will stop my show are a No-No.

  16. Random832 says:

    @Danny, see how far you get by posting WM_GETTEXT.

    Also, if you live in a multi-thread world, why does your UI thread need to do anything but checking messages? Having other things to do than check messages is the only reason I can think of not to want to block on GetMessage – PeekMessage is fundamentally for a single-thread-multitasking programming model (i.e. you have other things to do than check messages in your main loop).

Comments are closed.