Somebody has to do the hard work. As a programmer, you have elected to be that person
-- Charles Petzold, Programming Windows 95
Introduction
Windows.h must be the worst engineered header of all times. It's a meta header, designed to include several others and these are usually so bad that I'm wondering if anyone at Microsoft ever learned to program in a larger environment (ironic is it not?). One of the very first things you learn moving onto a larger project is that nameclashes are bad and hard to track down. Thus we invented namespaces in C++ (this doesn't solve the problem of link time interposing, that one you just need to handle manually). However, for oldschool C programs we don't have the namespaces. Most programmers developed the habit to prefix their stuff, e.g. if you're writing a library that is called foobar, you might prefix all your variables, functions and defines with foo_ to avoid clashes with others. Now, that is if you're concerned with playing nice. Obviously none of the windows people wants to do this, including windows.h forces you to do a number of concessions.
Must enable non-standard extensions
This might just be a part of Microsoft's lock in policy, if you're compiling stuff for Windows, of course you need to compile with their extensions. Now, I've always presumed that the reason why they didn't give their compiler away for free on the windows platform was because they didn't want to kill the compiler marker for windows and encourage other compiler vendors to port/write their compilers for the windows platform. But then again, why would they turn around and say that you must emulate all of their compiler to even parse their SDK headers? It doesn't make sense. It's also one of the reasons why the extensions are turned on by default and the poor beginners who learn C++ with this compiler with the mentality "if the compiler let it pass, it must be correct" are in for a shock when they move on to a real compiler. I've heard a lot of complaining that GCC does this and that wrong for example from people that exclusively used the MSVC compiler before. In almost all cases it turns out that GCC does the correct thing (and the correct thing is to follow the standard of course) and MSVC is just simply broken. For example, in the quest to improve compile times the advice is usually to check how many headers you include and try to minimize these by using forward declarations. Ready, set, go! Churn churn churn. A couple of hundred headers later, you might get called over to a programmer's desk to try to explain why the GCC compiler doesn't compile perfectly legal code. This could be the line you see:
enum State;
My own reaction to this was. Hey, wait. Can you actually forward declare enums nowadays? Have something changed? Turns out, no. Not yet. But the MSVC compiler has an extension to do this, even though the standard explicitly tells you that you can not forward declare an enum.
Of course the solution to the enum problem is to move the enum to a separate header that can be included for cheap instead of being embedded in a large header with a lot of redundant code.
There are a lot of more reserved words
I recently came across this gem in one of the windows headers (RpcNdr.h):
#define small char
It resulted in the very cryptic error message:
nvtt.h(179) : error C2632: 'float' followed by 'char' is illegal
Looking at the code below I had a sinking feeling that some evil programmer had defined something they shouldn't have. A couple of #undefs later I confirmed it and started to grep the headers. I found the offender and got totally derailed from my coding and wrote this article instead.
NVTT_API void setNormalFilter(float small, float medium, float big, float large);
Actually, this reminds me of another gottcha on the windows compilers. Ever tried to write a function that created a projection transform? A reasonable prototype would be:
Matrix createProjectionMatrix( float left, float right, float bottom, float top, float near, float far );
That works great unless you happen to have windows.h somewhere in your include chain. Then the following defines in windef.h kicks in:
#define far #define near
All sorts of fun compiler errors comes out of this one. Hello? 2008? When was the last time you wrote real mode programs? Anyone? (I stopped around 1995). Wonder when Microsoft let me define PLEASE_DO_NOT_STEAL_MY_NAMES before you include windows.h... (Check out near/far pointers here).
Sockets! Network!
If you've tried to program any winsock programming (and that's not == BSD sockets. Damn.) you might have run into one of the windows headers most vexing things. It assumes include order. Or at least that every include point does know the full extent of the usage. For all that is holy, don't assume that you can just write the following:
#include <windows.h> #include <winsock2.h>
Looks innocent? Looks reasonable? Oh yeah. But it will result in so many compile error that you poor stderr will work overtime. Now picture that these two includes are in vastly different headers and connected together by a complicated include chain... to track it down you need to insert the /showIncludes compiler flag and read the output carefully. Not so much fun. The solution is of course to invert the include order of the two headers (there is also a magic define you can enable before including windows.h to let it know to not include winsock 1 headers). I use the phrase "of course" very sarcastically.
In closing
In the future, if you see a naked include of windows.h, consider how that header leaks out to the rest of the codebase and forces the bad practices onto it. I've taken the habit to wrap windows.h inside another header with a heavy preamble and post include fixup (undef a whole bunch of crap).
One really nice feature of the microsoft compiler is that you can turn off extensions (/Za). I toyed with the idea to make this the default in the codebase and just enable the extensions for the files that just had to include windows.h (or as it turned out any other directx related and more). This turned out to be a huge rabbit (rabbit because geese are slower) chase and I abandoned the quest. It turned out to be a lot of special cases where I had to enable it, it broke the precompiled header structure I had as well as the compiler batching for visual studio only works for exactly the same compile line, which meant that most of my source files flew by and the ones that happen to include windows.h slowed down to a crawl. Ugh. But it annoys me to no end that I had to include windows.h in the precompiled header...
I usually try to enclose everything I write in a global namespace, e.g. for the code at home I just have a global namespace aurora in which all the code lives (this solves for one 99% of all link time interposing problems). With a few visual studio macros to help me create the preambles I virtually never have to scope anything with aurora:: or even know that it's there. I also try to prefix all my macros with AURORA_ to further wall myself off third party headers like the evil windows.h. In a perfect world, I would not need to do this and all the others would do it, but the reality is that most libraries out there are terrible and have leaky interfaces as well as headers.
Resources
- Programming Windows, the bible of Win32 programming. This explains in straight C how the Windows API actually works, without those confusing templates or C++ classes that abstracts away and hides the inner workings of Windows. Even if you are coding in MFC or ATL, or even C# forms, this is a must read for getting the background.