What is the default version of a header file?

Date:April 10, 2007 / year-entry #124
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20070410-00/?p=27313
Comments:    34
Summary:The general rule with Windows header files is that if you don't specify which version of the header file you want, you get the latest version. For example, if you have the Windows XP Platform SDK header files and you #include , you're going to get the Windows XP function prototypes, the Windows XP structures, the the Windows XP...

The general rule with Windows header files is that if you don't specify which version of the header file you want, you get the latest version. For example, if you have the Windows XP Platform SDK header files and you #include <windows.h>, you're going to get the Windows XP function prototypes, the Windows XP structures, the the Windows XP flags, all that stuff. And unless you're careful, the program you get as a result will most likely run only on Windows XP.

If you call a function that is new for Windows XP, then your program won't run on earlier versions of Windows because the import can't be resolved.†

If you use a structure that changed for Windows XP, then your program won't run on earlier versions of Windows because the structure size will be wrong.

Even if the structure size didn't change, using a flag that was introduced in Windows XP will create difficulties for your program when run on earlier versions of Windows because those earlier versions don't support the flag you're passing. Depending on how the function in question was written, it may ignore the "flag from the future" or it may reject it as invalid.

If you want your program to run on older versions of Windows, you have a few options. First, you can explicitly "downgrade" your header file by defining an appropriate symbol or symbols before including the windows.h header file.

#define WINVER         0x0400
#define _WIN32_WINNT   0x0400
#define _WIN32_WINDOWS 0x0400
#define _WIN32_IE      0x0400

#include <windows.h>
#include <commctrl.h>
#include <shlobj.h>
...

Oh yuck, now we have the messy world of "So what's the difference between _WIN32_WINNT, _WIN32_WINDOWS, _WIN32_IE, and WINVER?" We'll pick up this topic next time, but you're not going to like the answer.

Nitpicker's corner

†That statement is from the operating system's‡ point of view. You can of course use techniques like Visual Studio linker's delay-load feature to avoid creating an import dependency, but that's outside the operating system.‡

‡s/operating system/Windows operating system/


Comments (34)
  1. Tom says:

    Ooh!  Finally, some technical dirt on those macros!  Wait a minute…wasn’t dirt yesterday’s post :)

  2. S says:

    Shouldn’t that be:

    s/operating system/Windows operating system/g

    Thank you.

  3. asmguru62 says:

    Good (read ‘smart’) Win32 code should run on any 32-bit Windows.

    That means, using different headers will not work. The code should

    be morphed at run-time to take advantage of services provided by API,

    like LFH and other goodies.

  4. Joseph Bruno says:

    Especially as Windows XP only has a 10% market share. 90% of PC users use Windows, not Windows XP, Windows Vista, Windows 2000 or Windows 98.

    If you tell them to go away and check which version of Windows they have before buying, they’ll obey the first half of your request.

  5. Leo Davidson says:

    According to this http://news.softpedia.com/news/XP-Reigns-Supreme-in-2006-43442.shtml the vast majority of (85%) PCs run Windows XP. That’s 85% of all PCs, too, not just Windows PCs.

    Factor in the assumption that people still using 95/98 aren’t going to be interested in many types of software (if they were they wouldn’t be running 95/98 anymore) and the pain of writing, maintaining and testing code which has to contain wrappers or workarounds for so many missing APIs, not to mention separate Unicode and ANSI builds, and I seriously question the assertion that "good" code should work on all Win32 versions, any more than "good" code should work on every OS including Linux and OS X as well as Windows. You have to decide who you are writing code for before the quality of what you produce is even a factor, and often you will produce better code if you narrow your scope.

    It seems easy enough to write code that works on Win2k and up since, unless you’re doing certain things, there are only a few APIs to wrap up. 95/98/ME are a real pain, though and given how few people use them, and that those people probably only do email and use an ancient copy of Works, it doesn’t seem worth supporting them.

    For header files, I set my defines to pull in the headers for the minimum version of Windows I want to support. I then use runtime linking (i.e. LoadLibrary/GetProcAddress) to get to newer APIs, and if there’s a particular structure that I need (which is quite rare in my experience) I’d rather copy it to my project than define my project to use newer headers.

    The problem with using newer headers when you want your code to run on older versions is it’s very difficult (unless there’s a tool I don’t know of) to tell if you’re accidentally using an API, structure or flag that isn’t going to work on all versions. Copying things over and writing wrappers instead of using delay-loading is messy and wrong, but it seems the lesser of two evils.

  6. denis bider says:

    Raymond doesn’t appear to bless this strategy, but what I do is, I define the highest Windows version macro for the APIs and structures I need, and then I consciously avoid directly using any API functions that aren’t available on all the Windows versions I’m supporting. Instead, I access such APIs dynamically via LoadLibrary()/GetProcAddress(), which is conditional on an OS version check.

    This appears to have worked fine for the past several years. This way, if one branch of your OS conditional code needs type definitions that only exist for newer Windows versions, it can use the type definitions from the Windows header files without redeclaring them.

    The converse, which Raymond appears to be recommending, is to use version macros for the lowest common denominator OS, which in my case means dumbing down to Windows NT 4 with no service packs. Then, if you want to make conditional use of any new features, you have to duplicate the definitions of any requisite types. That sounds rather tedious and error prone to me, and it is not something I would like to see Microsoft officially require.

    I think, instead, Microsoft should keep the type definitions in header files backwards compatible at least to the extent that you can use type definitions from newer header files and it will still work with older Windows versions if you abstain from using any of the features not present in those older OSs.

    [The purpose of this article was not to endorse any particular strategy; I apologize if it appeared that any such endorsement was made. -Raymond]
  7. John says:

    I haven’t investigated this thoroughly, but it would appear that the macros are used thusly:

    _WIN32_WINNT = Windows NT-specific definitions (i.e. functions in NT not in 9x)

    _WIN32_WINDOWS = Windows 9x-specific definitions (i.e. functions in 9x not in NT)

    _WIN32_IE = Internet Explorer-specific definitions (i.e. functions that live in files distributed with IE, such as the Shell Lightweight Utility Functions)

    WINVER = definitions common between 9x and NT

    Of course that would make too much sense and I’m sure backwards compatibility throws a wrench in there somewhere.

    I’m afraid I have to nitpick your nitpicks, though.  Ignoring the potential legalities of it, you can run Windows applications on WINE under Unix derivatives.  WINE provides its own implementations of the Windows libraries, so I don’t think it would be correct to say that WINE’s implemenation of import function resolution is part of the Windows operating system (particularly when Windows is not even running).  Technically it should probably be something like “s/Windows operating system/Windows executable loader/”.  That way you can account for executable loaders that process Windows executables on other operating systems.  Sorry in advance; please don’t yell at me.

    [“X applies to Y” does not contradict “X applies to Y and Z”. I only talk about Y. -Raymond]
  8. Marc K says:

    I like the method of setting the version defines to indicate the lowest version of Windows that the program will run on, but it seems like the SDK designers don’t like that approach.

    While it’s no problem to dynamically load API functions, many of the headers don’t allow the program to know about types and data structures that pertain to those APIs.

    For example, If I want my program to run on Windows NT4 & Windows 95, but also support multiple monitors on newer versions of Windows, it’s no problem to dynamically load functions like GetMonitorInfo(), but I also have to redefine structures like MONITORINFO and types like HMONITOR because the headers balk at my version defines of 0x0400.

  9. Adrian says:

    I recently "discovered" the NTDDI_VERSION macros.  Will you be explaining what DDI stands for and why these are the "preferred" macros to replace the "legacy" macros you listed in your example?

    http://msdn2.microsoft.com/en-us/library/aa383745.aspx

    And are the NO<api> macros (e.g., NOCOMM) still supported?  I’ve searched the headers for several of them and find they don’t seem to be used anymore.

  10. Marcel says:

    "Raymond doesn’t appear to bless this strategy, but what I do is, I define the highest Windows version macro for the APIs and structures I need, and then I consciously avoid directly using any API functions that aren’t available on all the Windows versions I’m supporting."

    Beware, you can still run into problems this way. Sometimes structure sizes increase with a new release and when you do the standard

     ZeroMemory(&xyz, sizeof(xyz));

     xyz.dwSize = sizeof(xyz);

    in your code, the API running on an earlier Windows release will check dwSize and refuse to work because it doesn’t match any sizes it knows. Been there, done that…

  11. Name required says:

    What’s with the U+2020 (DAGGER) and U+2021 (DOUBLE DAGGER)?

  12. Norman Diamond says:

    The general rule with Windows header files is

    that if you don’t specify which version of

    the header file you want, you get the latest

    version.

    Which development tools obey the general rule?  In Visual Studio if I forget to declare some macros like _WIN32_WINNT then I get compilation errors instead of the latest version.

    Tuesday, April 10, 2007 2:51 PM by Leo Davidson

    Factor in the assumption that people still

    using 95/98 aren’t going to be interested in

    many types of software (if they were they

    wouldn’t be running 95/98 anymore)

    Factor in the assumption that there are a lot of countries where middle class people can afford to buy used PCs that are powerful enough to run 98 but aren’t powerful enough to run pirated copies of XP.  I even saw one where the vendor had copied XP’s default wallpaper onto the machine so the customer thought they were running XP.  Anyway, I installed AVG Free onto several computers running 98, and I’m very glad that this was still possible.

    On the other hand, try to get Windows Update working on a fresh installation of 98.  Try to get Internet Explorer 6 downloaded onto a fresh installation of 98.  Just try it.  Try either or try both, they catch-22 each other and they catch-22 themselves.  Sure there are no new security patches for 98, but it sure would be nice to get the old ones on.  I don’t think Linux will ever be teachable to the general public, but this experience made me want to try.

  13. Dean Harding says:

    In Visual Studio if I forget to declare some macros like

    _WIN32_WINNT then I get compilation errors

    What version of Visual Studio is that? I’ve never seen compilation just because I forgot to include a _WIN32_WINNT macro… In fact,

    #include <windows.h>

    on its own is a perfectly valid .cpp file and will compile fine for me.

    @Name required: Dagger and Double Dagger are footnote markers. They’re not used in printing as much nowadays, since a simple numbering scheme is more convenient, but if you only have a couple of footnotes, its fine.

  14. Dean Harding says:

    > Factor in the assumption that there are a lot of countries where

    > middle class people can afford to buy used PCs that are powerful

    > enough to run 98 but aren’t powerful enough to run pirated copies of XP

    Factor in your own willingness to try to sell software to people who cannot afford newer computers and only run pirated copies of Windows anyway. Are people who can only afford low-end, second-hand PCs and pirate copies of Windows going to PAY for your software anyway? Seems like they’d be more like to pirate your software as well, so why would you be wanting to pander to that particular market at all?

    Just because that market is LARGE does not automatically mean it is a market you want to sell to.

    [Hey guys, could we like talk about version numbers in Windows header files? Just saying. -Raymond]
  15. Yuhong Bao says:

    WINVER came from Windows 3.1.

    Win32 introduced _WIN32_WINNT for specifing the target Windows NT version.

    With Windows 95, Microsoft introduced _WIN32_WINDOWS to specifing the version of non-NT versions of Windows. As well it also used WINVER to do so.

    NT 4.0 added compatiblity with Windows 95, but introduced new features not supported in 95. The answer is to use _WIN32_WINNT to specify NT 4.0. Meanwhile the common control library was taken over by the IE team as part of IE 3 and so they need a way to specify a version of IE, so _WIN32_IE was introduced. IE 4 take over the shlwapi library and also the shell32 library, but the new shell32 library was part of the Windows Desktop Update, which was optional on 95 and NT 4, and this (along with the update themselves) created a mess which I am not going into here, other than the fact causing the Windows team to take back these libraries. Regardless of that, _WIN32_IE still exists for compatiblity.

    Later on however WINVER got overloaded to describe both lines of Windows (NT and non-NT), which also created a mess. Thankfully both 95, NT 4 and IE 4 is obsolete, and the two lines were merged with XP, which makes the whole mess obsolete.

  16. Yuhong Bao says:

    And yes, the shell themselves also was taken over by the IE team for IE 4 together with the shlwapi and shell32 libraries and the new version of the shell developed by the IE team was included in the Windows Desktop Update, which is an optional part of IE 4 and included Active Desktop, and was part of Windows 98. But the Windows 2000 team as part of the development of Windows 2000 realized the mess this caused so the shell was taken back by that team and as a result, the shell is no longer updated as part of IE, though shlwapi still is updated.

  17. Name still required says:

    Thanks Dean for the explanation.

    I found an article about it:

    http://en.wikipedia.org/wiki/Dagger_(typography)

    Quote:

    “However, the dagger is only used as a second footnote when an asterisk is already used. Third footnote employs the double daggers.”

    Raymond, did you have a first footnote (*) in mind to compliment the second (†) and third (‡)?

    [I understand there are a lot of violations of formal writing style over on LiveJournal, too; you might want to let them know. -Raymond]
  18. Norman Diamond says:

    Wednesday, April 11, 2007 12:53 AM by Dean Harding

    >> In Visual Studio if I forget to declare

    >> some macros like _WIN32_WINNT then I get

    >> compilation errors

    >

    > What version of Visual Studio is that?

    I think it was Visual Studio 2005 SP1.  I don’t remember if I had to make similar adjustments to projects that I started compiling under Visual Studio 2005 RTM.

    > I’ve never seen compilation just because I

    > forgot to include a _WIN32_WINNT macro…

    Well of course, Visual Studio isn’t psychic, you have to click “ăƒ“ăƒ«ăƒ‰” – “ă‚œăƒȘăƒ„ăƒŒă‚·ăƒ§ăƒłăźăƒ“ăƒ«ăƒ‰”.

    Meanwhile the NTDDI_VERSION macros look a bit useless to me.  Suppose you can run on W2000 SP3 and later, and WXP SP1 and later, but not WXP RTM.  The MSDN page recommends tests that look pretty useless.

    [The old macros were just as useless. What’s your point? -Raymond]
  19. I thought doing only

    #define WINVER         0x0400

    automatically defined the rest of them to be compatible versions.

  20. GregM says:

    Norman and Dean, at least at warning level 4, VS 2003 will throw a warning if you don’t define _WIN32_WINNT to something.  If you then have warnings as errors, it becomes an error.

    Since we use both of these (/W4 /WX), not setting _WIN32_WINNT in the project settings for one of our projects is an error.

    I seem to recall Norman saying that he did the same.

  21. kbiel says:

    Name still required:  Wow, you didn’t know what a dagger was but now you’re going to slap Raymond for not using it according to Wikipedia’s uncited rules?  You’re probably one of those people who believe that it’s also incorrect to use split infinitives or end a sentence in a preposition also.  No wonder Raymond has begun to nitpick his own nitpicks.

  22. dmurillo says:

    Hi Raymond,

    I’m on the side of the silent majority.

    I love your posts and always learn something even if the subject is already known to me.

    I hope that the nitpickers will go nitpick elsewhere in the near future.  They waste your precious time.

    Again, thank you Mr. Chen!

    DM

  23. It’s a confusing story.

  24. David Walker says:

    Joseph Bruno:  

    "Especially as Windows XP only has a 10% market share."

    I don’t believe it.  Where did you get this number?

  25. GregM says:

    David, you have to read the other half of that sentence:

    90% of PC users use Windows, not Windows XP, Windows Vista, Windows 2000 or Windows 98.

    or phrased a little differently:

    Especially as only 10% of PC users know that they’re running Windows XP.  The other 90% of PC users don’t know or care what version of Windows they are running.

    (I’m not saying that this number is correct, just explaining what was meant.)

  26. Norman Diamond says:

    The old macros were just as useless. What’s

    your point? -Raymond

    (1)  I have to SET not use the old macros.  I do set the old macros.  The result is not useless.  But that wasn’t my point then, that’s my point now in response to one of your statements here.

    (2)  On Tuesday, April 10, 2007 5:28 PM, Adrian commented about the new macros.  I read about the new macros.  I kind of wonder what their purpose was supposed to be, but that was my "obvious" point then.  I can’t see any use for them, which was my stated point then.

  27. Norman Diamond says:

    Re: re: re:

    > The old macros were just as useless. What’s

    > your point? -Raymond

    Just now I figured out two things that you might have meant.  (1) SETTING the macros could be included in the meaning of USING the macros.  (2) It’s equally impossible to set the old macros with the necessary precision as it is to set the new macros with the necessary precision.

    The only settings that I had to make were in order to specify either Windows 2000 RTM or newer, or Windows Vista RTM or newer.  So I hadn’t noticed until now that they are indeed insufficiently precise.

    I still can’t help wondering, since some people surely already knew what the problems were with the old macros, what was the purpose in inventing another way to provide the exact same problems.  Probably no one knows the answer though.  (And even if anyone knows, the answer will be useless because even knowing the answer will still leave the problems unchanged.)

    [“In an attempt to make some sense out of this disaster”. Instead of having to set four macros (and make sure you set them consistently), you just set one, and it sets the other four. I don’t know why I’m writing this; I’m just repeating the paragraph in this comment. -Raymond]
  28. Norman Diamond says:

    > Instead of having to set four macros (and

    > make sure you set them consistently), you

    > just set one, and it sets the other four.

    Um, if it did exactly that, then your answer would probably be more obvious.  Two reasons why that wasn’t obvious are:  (1) the cited MSDN page makes it look like programs should use the NTDDI macros instead of setting them, and (2) the NTDDI macros cloud things up by pretending to be able to set minimum versions (e.g. Windows 2000 SP3 or later, and Windows XP SP1 or later, which only in our dreams would we be able to specify syntactically), whereas the old macros didn’t cloud things up that way because they didn’t even try.

    > I’m just repeating the paragraph in this comment.

    Well yeah in this comment you repeated a paragraph from a later blog entry.

    Anyway I was trying to answer your “What’s your point?” question.

    [, Nitpicker’s corner:  a comma doesn’t override operator priorities the way parentheses do, but if I properly parenthesized English expressions using the or and and operators then it would be harder to read than the commandeered syntax.]

    [Documentation does not relieve you of the responsibility to use your brain. Why would a program test the NTDDI macros? -Raymond]
  29. Norman Diamond says:

    > Why would a program test the NTDDI macros?

    Why would a program test __STDC__?  Nonetheless that’s the direction the language design went.

    Why do programs have to do tons of other nutty stuff?  Because
    the API makes them do it.  Sometimes MSDN says why, sometimes not.

    [I still can’t tell if you’re playing the naif
    just to taunt me or if you’re genuinely confused. I’m going to assume
    you’re just taunting me. -Raymond
    ]
  30. Norman Diamond says:

    I’m going to assume you’re just taunting me.

    Um, when I posted this:

    > Why would a program test STDC?

    > Nonetheless that’s the direction the

    > language design went.

    I didn’t think it was a taunt.  If you don’t see that this is exactly the same as how the MSDN page on the NTDDI macros looked when I read that page, then I think maybe I owe you a taunt.  Congratulations on your self-fulfilling buttumption.

  31. stdcall says:

    What a fuzz about some stupid macros. Just read the header files, judge from there what to do.

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