December 9th, 2021

If you’re going to configure a header file, you have to do it before you include the header file

In Windows, if you want the windows.h header file to default to the Unicode character set, you need to define the UNICODE symbol:

#define UNICODE
#include <windows.h>

There’s no point defining the symbol after the horse has left the barn:

// Code in italics is wrong
#include <windows.h>
#define UNICODE

Related: Don’t forget to #define UNICODE if you want Unicode.

Now, sure, a mistake like that is pretty obvious and easy to spot.

But other mistakes might not be so easy to find.

#include <stdio.h>
#include <contoso.h>
#include <fastmalloc.h>
#define UNICODE
#include <windows.h>

extern HWND g_mainWindow;

void UpdateTitle(PCWSTR title)
{
  SetWindowText(g_mainWindow, title);
}

This looks perfectly fine: We defined the UNICODE symbol before we included windows.h, but we still get compiler errors that suggest that the symbol didn’t take effect:

error C2664: 'BOOL SetWindowTextA(HWND,const char *)': cannot convert argument 2 from 'const wchar_t *' to 'const char *'
note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

The error message tells us that the compiler thinks that Set­Window­Text redirects to Set­Window­TextA, and that function expects an ANSI string, not a Unicode one. But how can that be? We did the right thing and defined UNICODE symbol immediately before including windows.h.

Yes, you defined it immediately before you included windows.h. But your inclusion of windows.h isn’t the one that counted.

Somewhere in the chain of #include files that came before you included windows.h is somebody that did their own #include <windows.h>. That first inclusion did not have the UNICODE symbol defined, so the result was that all of the Windows macros redirected to the ANSI versions. You reconfigured the windows.h header afterward, but by then it was too late.

You’ll need to move the definition to somewhere that occurs before the point at which the windows.h header is included for the first time. The safest place is to do it before including anything.

#define UNICODE
#include <stdio.h>
#include <contoso.h>
#include <fastmalloc.h>
#include <windows.h>

Bonus chatter: If you want to find out who is doing the early inclusion of windows.h, you can pass the /showIncludes command line option to the Microsoft Visual C++ compiler. This can be configured in Visual Studio under ProjectConfiguration PropertiesC/C++AdvancedShow Includes = Yes (/showIncludes). This will generate a hierarchy tree of every included file, and you can search it to see who included windows.h.

For gcc, you don’t even need to do that much. You can reuse a trick we saw a little while ago: Give a macro a conflicting definition and wait for the fireworks.

#define CreateFile who_includes_windows_h_first
#define UNICODE
#include <stdio.h>
#include <contoso.h>
#include <fastmalloc.h>
#include <windows.h>

You will then get told

in file included from /sdk/fileapifromapp.h:19:
                 from /sdk/winbase.h:42:
                 from /sdk/windows.h:171:
                 from /contoso/inc/internal/config.h:84:
                 from /contoso/inc/contoso.h:20:
/sdk/fileapi.h:84: warning: "CreateFile" redefined

Bingo, it’s contoso.h, via its helper include file internal/config.h.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • 紅樓鍮 · Edited

    I wonder if contoso.h even supports using windows.h in Unicode mode. In my opinion the more robust solution is to always use the W-suffixed function names and explicit wchar_t-based type names (à la C++/WinRT). There should also be a supercharged version of NOMINMAX that disables all redirection macros.

    • MGetz

      Believe it or not you can't get rid of all the redirection macros, unfortunately there is a bug (feature?) in quite a few APIs that have symbols identical to GDI names (ex: ID2D1RenderTarget::DrawText collides with GDI DrawText) Such that the actual name in the vtable is actually so if you try to compile this without redirection macros enabled you end up with symbol errors unfortunately... I've tried because it annoyed me. I'm fairly sure...

      Read more