What’s the difference between WINVER, _WIN32_WINNT, _WIN32_WINDOWS, and _WIN32_IE?


Okay, so there are all these different ways you can specify what version of the Windows header files you want.†

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

Let's take them in order.

The WINVER symbol is the earliest one. That's the symbol that 16-bit Windows used to control the versioning of its header files, and its use carried forward into the 32-bit header files, presumably from the people who did the initial conversion of the header files to 32-bit and who grew up with the WINVER symbol. This symbol is still used a lot in the header files that can trace their origins to 16-bit Windows, such as winuser.h, wingdi.h, and mmsystem.h.

The _WIN32_WINNT symbol came next. I'm not sure where it came from, but from its name it probably was invented by the Windows NT team in order to allow them to block off sections of the header file that are available only in the Windows NT implementation of Win32. Don't forget that in the early days, there was also Win32s, a subset of Win32 that could run on 16-bit Windows 3.1. The single WINVER symbol wasn't enough to specify exactly what you wanted to be compatible with. For example, a function available only in Windows NT 3.1 would be guarded with #if _WIN32_WINNT >= 0x030A so that programs that wanted to run on Win32s could set _WIN32_WINNT to zero and keep that function off-limits.

Similarly, both Windows 95 and Windows NT 4 identified themselves as Windows major version 4, so the WINVER symbol was insufficient to distinguish them. Functions that existed in Windows NT 4 but not in Window 95 were therefore guarded with _WIN32_WINNT.

On the other hand, there were also functions that were first introduced in Windows 95 and did not exist in the original version of Windows NT 4. The _WIN32_WINDOWS symbol let you specify that you wanted access to stuff that was new for Windows 95 and which would also be ported to Windows NT 4 and future versions of Windows NT.

The next symbol in this progression is _WIN32_IE, which lets you specify what version of Internet Explorer you require to be installed on the system. This was more important back in the days when Internet Explorer included updates to selected operating system components. For example, Internet Explorer 4 came not only with an updated comctl32.dll but also a new shell32.dll that gave you Active Desktop. (Wow, remember Active Desktop? That was when everybody thought that HTML was going to take over the world and people would write entire applications in HTML. People are still trying.)

And history repeated itself: We saw it before when we tried to puzzle out why some functions return NULL while others return INVALID_HANDLE_VALUE. Each time somebody added a new feature to Windows and had to add an #ifdef guard, it was pretty much a toss-up whether they would use WINVER, _WIN32_WINDOWS, or _WIN32_WINNT. Once Internet Explorer stopped including updates to shell components, _WIN32_IE fell into the "toss-up" bucket as well.

In an attempt to make some sense out of this disaster, the SDK and DDK teams came up with a new plan for Windows Vista header files: sdkddkver.h. There's now just one symbol you define to specify your minimum target operating system: NTDDI_VERSION. Once you set that, all the other symbols are set automatically to the appropriate values for your target operating system. (And no, I don't know what the letters NTDDI stand for, though there is one obvious candidate.) With any luck, everybody wll standardize on NTDDI_VERSION and this article will become one of those "quaint historical novelties" like all the ones about 16-bit Windows. Just "a little story about what people had to do back in the crazy days of the early 21st century. Boy am I glad we don't have to worry about that any more!"

Notes

I'd appreciate it if people would extend me the courtesy of not stepping on my announced topic. (I wonder if these are the same people who go to a comedy show and shout out the punch lines before the performer gets to say them.) I did say that I would pick up the topic today, after all. If you really want to steal my topic, at least be polite enough to post your essay on your own blog.

Nitpicker's corner

†This list is not intended to be comprehensive.

Comments (33)
  1. gkdada says:

    I have one question.

    If you declare WINVER to be 0x0500, can you dispense with _WIN32_WINNT and _WIN32_WINDOWS (not to mention _WIN32_IE)?

    Thanks for providing a column (oops…blog) to read with my morning cuppa.

    [You already have the tools available to figure this out for yourself. No need to get me involved. -Raymond]
  2. Nathan says:

    If those tricky DDK guys were involved, I’d think it is the device driver interface. How that’d relate to user level is a stretch.

  3. Adrian says:

    The link for sdkddkver.h in the article appears broken.  I did a search and found this on MSDN, which I believe is the page Raymond intended:

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

    It’s interesting (to me, anyway) that the NTDDI macros go back only to Windows 2000.  It seems with the legacy macros, you should be able to target even older versions of Windows.

  4. Active Desktop... says:

    Active Desktop… we use it!

    Almost everyone here uses it to have an handy phone list on the desktop (exporting an excel file as html).

    No other good uses I think (maybe a calculator ?).

  5. CN says:

    Just curious: Any idea why 0x30A was used for NT?

    I guess that A = 10 and the version number being 3.1(0) coulod be the explanation, but it’s still quite odd. On the other hand, I guess it’s better than an actual expansion of 3.1 in fixed-point hex, like 0x319 or 0x31999… (or 0x31A etc with rounding)

    I hope this can be considered somewhat on-topic.

    [Because that’s what GetVersion returned.

    Nitpicker’s corner
    Yes, that’s not exactly what GetVersion returns. You’re so smart. -Raymond]

  6. mikeb says:

    Totally useless trivia about Windows version numbers:

    Win 3.1 (16-bit and NT) returned 0x0A for the minor revision part.  In other words 0x030A (or reverse the bytes to get what GetVersion() returns).

    Win 5.1 (otherwise known as Windows XP) returns 0x01 for the revision.  In other words 0x0501.

    Actually, I suppose this isn’t totally useless.  It may be of use if you’re formatting version numbers you get from GetVersion() or it’s variants for display.  Otherwise you might display WinXP as version 5.01, confusing the universe and causing a rocket to explode in mid-flight.

  7. Chris L says:

    Mac OS X takes a different approach. There is a separate copy of all the headers & libs for each OS version. I guess that is more straightforward than putting #ifdef all over the place.

  8. richard says:

    Simple solution to people stealing your thunder is to not preannounce what you are going to blog about.

    [That’s what I used to do but it was only worse. -Raymond]
  9. Yuhong Bao says:

    Actually the use of _WIN32_IE began with IE 3 because it updated comctl32. _WIN32_IE continues to be used today because new IE releases still update SHLWAPI. And the Active Desktop and therefore the new version shell32 was optional in 95 and NT4.

    A general rule with WINVER: < 0x0500 means it is the version of the 9x serius, > 0x0500 means it is the version of the NT serius.

  10. Yuhong Bao says:

    I aplogise for stealing your thonder.

    By the way, the fact that 0x0501 is displayed as 5.1, not 5.01 depends on the minor version being displayed without the trailing zero

  11. The proliferation of defines does make sense given how the product lines forked so badly with NT, Win32s, & IE. But if we’re supposed to all simply check for a single NTDDI_VERSION number, to me this requires Microsoft to promise us they’ll never again split off whole lines of Windows, nor will they introduce important chunks of Windows functionality in specific product lines, but instead will always bring them out sequentially in service packs or major releases of the OS itself.

    You simply can’t account for two independent lines of code by checking for a single define being greater than some single number.

    It’s a glorious vision indeed. But I’m skeptical. Darnit.

  12. KenW says:

    Jenny Palonus: "You simply can’t account for two independent lines of code by checking for a single define being greater than some single number.

    It’s a glorious vision indeed. But I’m skeptical. Darnit."

    Re-read what Raymond wrote:

    "There’s now just one symbol you define to specify your minimum target operating system: NTDDI_VERSION. Once you set that, all the other symbols are set automatically to the appropriate values for your target operating system."

    Note the "all the other symbols are set automatically" part? So you still have the granularity you had before, but you don’t have to set all of the individual #define values yourself.

  13. David Walker says:

    Chris L:  A separate copy of all the headers and libs sounds like a maintenance nightmare.

  14. Legolas says:

    David Walker: A separate copy of all the headers and libs is indeed a maintenance nightmare. And that’s why it isn’t true: they have macro’s on osx just like on windows. Since apple was be a bit more abrupt in os9->osx, no os9 legacy went into the headers, so the defines are somewhat cleaner.

  15. Thomas says:

    Ah yes, I remember Active Desktop. A few years back I put together a simple status page to drop on everyone’s desktop at school.

    More recently, I’ve used it under Windows 2000 to keep my university timetable in a corner of the desktop. Unfortuantly WinXP breaks it slightly – Active Desktop and shadowed captions appear to be mutually exclusive.

    In some ways it’s a shame it fell by the wayside and never got used, along with web views for folders (I remember seeing examples of how a program CD could use a web view as a poor man’s autorun splash screen).

  16. Yuhong Bao says:

    Active Desktop was removed because it made the shell depend on IE.

    Anyway, why is this present in windows.h:

    #if defined(_WIN32_WINNT) && (WINVER < 0x0400) && (_WIN32_WINNT > 0x0400)

    #error WINVER setting conflicts with _WIN32_WINNT setting

    #endif

  17. Greg Neilson says:

    Win32s. I’d relegated the year of hell I spent with that in about 1996 to the back of my mind.

    *shudders*

  18. And don’t forget Windows CE adds even more version macros:

    * WINCE

    * _WIN32_WCE

    * UNDER_CE

  19. Norman Diamond says:

    For example, Internet Explorer 4 came not

    only with an updated comctl32.dll but also a

    new shell32.dll that gave you Active Desktop.

    In some cases IE4 also brought Desktop Update, which was useful even when Active Desktop was turned off.  In MSDN-English language NT4, during installation of IE4, it would ask if we wanted Desktop Update.  Clicking No would yield the same results as in Japanese NT4 (i.e. no Desktop Update), but clicking Yes would bring it in.  The same features in Desktop Update were built into Windows 98, Windows 2000, etc., including the Japanese versions.  One time I found a trick to get it to install in Japanese NT4.

    Wednesday, April 11, 2007 2:55 PM by Yuhong Bao

    A general rule with WINVER: < 0x0500 means it

    is the version of the 9x serius, > 0x0500

    means it is the version of the NT serius.

    Both MSDN and my experience disagree with you.  It seems to us that NT4 was version 4 but was part of the NT series.

  20. Chris L says:

    Legolas: I think it’s true — recent versions of XCode use "SDK’s" which are copies of the Frameworks (and other headers and libs), specific for each version of the OS.

  21. Anon says:

    @Yuhong Bao

    "A general rule with WINVER: < 0x0500 means it is the version of the 9x serius, > 0x0500 means it is the version of the NT serius"

    As Norman pointed out, that rule breaks for NT 4.0. The most significant bit of of the DWORD GetVersion returns tells you if it’s NT based or 9x based.

    DWORD dwVersion = GetVersion();

    BOOL bIsNT = ! ( dwVersion  & 0x80000000 );

  22. WinVer says:

    > As Norman pointed out, that rule breaks for NT 4.0.

    Nitpicking, but it also breaks for NT 3.1, 3.5 and 3.51.

  23. Win64 says:

    Are _WIN32_WINNT, _WIN32_WINDOWS and/or _WIN32_IE valid in Win64, or do they have any corresponding defines?

  24. Marc K says:

    NTDDI will be a great simplification some time in the future.  But now, if you want your app to run on something older than Windows 2000, there’s no choice but to use the legacy macros.

  25. Dan McCarty says:

    "Chris Peterson: And don’t forget Windows CE adds even more version macros"

    Yeah but usually CE guys who read this blog are like left-handed people watching an instructional video: they watch what’s being done and reverse the directions to suit themselves.

  26. sandman says:

    My grumble with this API technique is it is all done in C macros. Which makes it *really* hard to write code which could make use of the extra fields in a structure but able to drop back until it finds an struct size that the current OS supports.

    The best you can do is write your own declartion of those headers but there are an awful lot oof structs in windows like this.

  27. Yuhong Bao says:

    But I mean that defining WINVER as equal to 0x0400 will not include the function that are new to NT 4, onlt those that are new to 95, but defining WINVER to 0x0500 will include functions that are new to NT 5, or Windows 2000.

  28. Dan says:

    "Anyway, why is this present in windows.h:

    #if defined(_WIN32_WINNT) && (WINVER < 0x0400) && (_WIN32_WINNT > 0x0400)

    #error WINVER setting conflicts with _WIN32_WINNT setting

    #endif"

    I’m guessing because Windows 2000 (4.1) and above implement WINVER >= 0x400 functionality … it wouldn’t make sense to set WINVER lower in this context, so the headers assume it was a mistake and alert the programmer.

  29. Anthony Wieser says:

    I can’t get this to work.

    When I add this to the start of my precompiled header file:

    #define NTDDI_VERSION NTDDI_WINXPSP2

    // we build only for xp sp2 and later…

    #include <sdkddkver.h>

    I get this error message:

    NTDDI_VERSION setting conflicts with _WIN32_WINNT setting

    Looking at the code in sdkddkver.h, I see this:

    #if !defined(WIN32_WINNT) && !defined(_CHICAGO)

    #define  _WIN32_WINNT   0x0600

    #endif

    So, it looks like I have to define _WIN32_WINNT anyway.  

    Is that really the case, or am I doing something stupid?

    Anthony Wieser

    Wieser Software Ltd

  30. Windows is rather famous for its ability to run applications that were written for previous versions

  31. Recently, we came across a very interesting issue. A purely native application written in C++ was failing

Comments are closed.

Skip to main content