What’s the story with the parameters to the WM_INPUT_DEVICE_CHANGE message?

Date:September 2, 2011 / year-entry #211
Tags:history
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20110902-00/?p=9743
Comments:    13
Summary:A customer found these strange macros in winuser.h: #if (_WIN32_WINNT >= 0x0601) #define GET_DEVICE_CHANGE_WPARAM(wParam) (LOWORD(wParam)) #elif (_WIN32_WINNT >= 0x0501) #define GET_DEVICE_CHANGE_LPARAM(lParam) (LOWORD(lParam)) #endif /* (_WIN32_WINNT >= 0x0601) */ According to the documentation for the WM_INPUT_DEVICE_CHANGE message, the wParam is the operation code and the lParam is a handle to the device that changed. Given that...

A customer found these strange macros in winuser.h:

#if (_WIN32_WINNT >= 0x0601)
#define GET_DEVICE_CHANGE_WPARAM(wParam)  (LOWORD(wParam))
#elif (_WIN32_WINNT >= 0x0501)
#define GET_DEVICE_CHANGE_LPARAM(lParam)  (LOWORD(lParam))
#endif /* (_WIN32_WINNT >= 0x0601) */

According to the documentation for the WM_INPUT_DEVICE_CHANGE message, the wParam is the operation code and the lParam is a handle to the device that changed. Given that definition, the correct macro would be GET_DEVICE_CHANGE_WPARAM. What's up with the bogus GET_DEVICE_CHANGE_LPARAM macro?

The macro was incorrectly defined in Windows Vista. In the Windows 7 version of the Platform SDK, the correct macro was added, but in order to avoid introducing a breaking change to existing code, the old broken macro remains in place in order to retain bug-for-bug compatibility with existing code.

Even though the macro didn't work, there is a good possibility that code exists which relied on it anyway. For example, people may have read the documentation, read the macro, realized that the macro was wrong, and worked around the bug like this:

case WM_INPUT_DEVICE_CHANGE:
 return OnInputDeviceChange(GET_DEVICE_CHANGE_LPARAM(wParam),
                            (HANDLE)lParam);

To avoid breaking this code, the old broken definition remains in the header file. But at least it's defined only if you say that you want the Windows Vista version of the header file, so at least people won't use the bad macro going forward.


Comments (13)
  1. Mc says:

    So should the winuser.h header file have a comment in there explaining what that was all for?  If it was my code, it would have an explanation alongside "the hack".  Or even just a  "see KB-99999" or something.

  2. Vilx- says:

    OK, so my C/C++ is a bit rusty, but I'm pretty sure that the "wParam"/"lParam" in this case is the parameter to the macro itself. So what's the difference how it is named, and why should changing it break any code?

  3. Vilx- says:

    Oh, wait, should learn to read first. :P The name of the macro itself changed too.

  4. John says:

    Seriously?  I'm all for backward compatibility (run-time more than compile-time), but this is taking it a bit extreme for my tastes.  I mean really, how many times could this bogus macro possibly be used?  Also, why invent it in the first place?  Why not just use LOWORD directly?  The documentation doesn't even talk about splitting the wParam.  Perhaps it was from a beta release of longhorn or something.

    On a related topic, how stringent is Microsoft in surrounding functionality with #ifdefs?  I've been burned a few times where some function was not implemented on the target I was compiling against.  Lo and behold, there were no #ifdefs to be found.

    Also, please harass somebody to fix this ridiculous bug where your comment gets lost by the blogging system if you don't post it in a certain amount of time.

  5. configurator says:

    I'm not a C++ expert, but wouldn't this definition do the same thing? Why not use this simpler version? I'm sure there's a really good reason that I'm completely missing.

    #define GET_DEVICE_CHANGE_WPARAM LOWORD

  6. David Walker says:

    Here is a case where we REALLY need that time machine.

  7. voo says:

    @Evan Nice to learn something new. The only thing I'm now worried about is that we basically started fighting over the preprocessor which I'm somehow sure will end in tears earlier or later ;)

  8. Bob says:

    @EvanED: You can also do (min)(a, b) to prevent function macro expansion. I guess MACRO_EXPANSION_BLOCKER is to show your intent to maintenance programmers.

    I wonder how GET_DEVICE_CHANGE_LPARAM could come about. Did the API developer not even write a testcase and notice this instantly?

    [Perhaps the parameter was originally in the lParam (so the testcase looked just fine), then later a design change moved it to wParam. Just speculating as to how this could've happened. (You folks need to be more creative.) -Raymond]
  9. EvanED says:

    @configurator:

    I'm not sure this is the reason (I'd argue that the actual version is more readable, and that's probably closer to the reason; Haskell folks may disagree), but those two aren't *exactly* the same

    If GET_DEVICE_CHANGE_WPARAM appears in a context where the next token is NOT a (, the actual version will not undergo macro replacement but your version will.

    I know of one place where this can arguably be used in a productive manner. Suppose you are writing code that should work in a context where "someone" impolitely defines 'min' and 'max' as a macro. [Yes, I know about NOMINMAX or whatever it's called, but you don't always have that control.] The following will allow you to use those functions anyway:

     #define MACRO_EXPANSION_BLOCKER

     …

     min MACRO_EXPANSION_BLOCKER (1, 2)

    The MACRO_EXPANSION_BLOCKER goes away by the time the compiler sees things, so it just sees "min (1, 2)", but it inhibits macro expansion of the min.

  10. Evan says:

    BTW, credit where it's due; I learned about the MACRO_EXPANSION_BLOCKER from Boost. (They call it BOOST_PREVENT_MACRO_SUBSTITUTION.)

    And it's actually got a bit wider applicability, e.g. http://www.boost.org/…/cmath.hpp uses it to inhibit expansion on "functions" from <cmath> that may actually be macros. (Boost defines it http://www.boost.org/…/suffix.hpp here. In my defense, the comment in that file *also* ignores the <cmath> macro problem. :-))

  11. denis bider says:

    I haven't been able to find a proper way to submit this bug report for the Windows 7 console subsystem. None of the categories available through Microsoft Connect seems suitable.

    Perhaps you will care about this problem and forward it to the right person.

    In Windows 7, console support was moved from CSRSS.EXE to CONHOST.EXE. In the process of implementing this change, a bug was introduced which causes premature closing of the active console screen buffer. This causes applications that use screen buffers, such as less.exe or telnet.exe, to fail when running under some types of console-based terminal servers.

    I described the bug in detail here:

    fogbugz.bitvise.com/default.asp

    The page also provides source code to a set of minimal test programs that reliably reproduce this problem in Windows 7.

    I hope the Windows console subsystem is currently maintained by someone, and that you can forward this issue to them.

  12. TC says:

    +1 Mc. The MS codebase must contain thousands of fixes etc. whose purpose is not clear just from reading the code. I've often wondered how all of those are documented. Raymond sometimes says, "we added a note describing why" (for some particular fix). Is there a fixed policy for how to document fixes? Do you do always do it inine (in the source), or by reference to a big black book, or at the coder's discretion, or what?

  13. Joshua says:

    @denis bider: To file a bug in core Windows components, you have to open a support request (spend $$). If they agree it's a bug, you get your money back.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index