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.
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.
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.
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.
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 ?).
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.
Nitpicker’s corner
†Yes, that’s not exactly what GetVersion returns. You’re so smart. -Raymond]
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.
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.
Simple solution to people stealing your thunder is to not preannounce what you are going to blog about.
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.
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
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.
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.
Chris L: A separate copy of all the headers and libs sounds like a maintenance nightmare.
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.
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).
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
Win32s. I’d relegated the year of hell I spent with that in about 1996 to the back of my mind.
*shudders*
And don’t forget Windows CE adds even more version macros:
* WINCE
* _WIN32_WCE
* UNDER_CE
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
Both MSDN and my experience disagree with you. It seems to us that NT4 was version 4 but was part of the NT series.
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.
@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 );
Nitpicking, but it also breaks for NT 3.1, 3.5 and 3.51.
Are _WIN32_WINNT, _WIN32_WINDOWS and/or _WIN32_IE valid in Win64, or do they have any corresponding defines?
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.
"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.
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.
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.
"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.
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
Windows is rather famous for its ability to run applications that were written for previous versions
PingBack from http://blog.patrulleros.com.br/2007/06/05/pacote-nomade/
Recently, we came across a very interesting issue. A purely native application written in C++ was failing