Reputation: 5473
EDIT: I found a similar question, and the answers are basically that windows.h is bad and you must either rename your functions or #undef the macros: Other's library #define naming conflict
However, I believe mine is still different due to the conflicting behavior of LoadLibrary under debug and release builds.
I am programming on Windows using Visual Studio and I ran into a few peculiar issues with preprocessor directives used by windows.h and the headers it includes.
Our project had a function in its own namespace, MyProject::FileManager::CreateFile()
. After including windows.h our code failed to compile due to a linker error stating that it could not resolve MyProject::FileManager::CreateFileW (note the W at the end of the function name). This was not a static function, it was a member function of a FileManager
object that was being called with file_manager.CreateFile(...)
.
When highlighting the function in Visual Studio a tooltip displayed the following:
#define CreateFile CreateFileW
We were puzzled but just renamed the function as a workaround. However later we ran into a similar issue with the LoadLibrary
function we were trying to use from the Windows API. Compiling in Debug mode, LoadLibrary
was defined as LoadLibraryW()
which took an LPCWSTR (wide string) as a parameter. When I tried building in Release mode this function was now defined as LoadLibraryA()
which takes a normal LPCSTR. This broke our build because the code was written under the assumption that LoadLibrary
took an LPCWSTR.
So, my question is, how should a programmer deal with this? Should I just wrap my calls to LoadLibrary with #ifdef's checking for Debug or Release mode? Or is there a more simple solution?
Also, I found an interesting header file on github which appears to have been created for the sole purpose of #undef'ing all these function names:
https://github.com/waTeim/poco/blob/master/include/Poco/UnWindows.h
Upvotes: 0
Views: 1890
Reputation: 76315
The problem here is that windows.h tries to support two different string models: strings consisting of single-byte characters and strings encoded in unicode (defined by microsoft as two-byte characters). Almost all of the Windows API functions have two different versions, one that takes single-byte character strings and one that takes two-byte character strings. You're supposed to write your code with the generic names (such as CreateFile
, LoadLibrary
, etc.) and let windows.h take care of mapping those names to the actual API functions. For single-byte characters those two are CreateFileA
and LoadLibraryA
; for two-byte characters they are CreateFileW
and LoadLibraryW
. And there are a bajillion more, of course. You choose the model at compile time by defining the macro UNICODE
in every compilation unit.
Incidentally, the 'A'
suffix stands for "ANSI", and the 'W'
suffix stands for "wide character".
This is particularly insidious when you write code that tries to isolate the Windows dependencies into a handful of source files. If you write a class that has a member function named CreateFile
, it will be seen in source files that don't use windows.h as CreateFile
, and in source files that do use windows.h as CreateFileA
or CreateFileW
. Result: linker errors.
There are a several ways around this problem:
always #include <windows.h>
in every header file; that's a compiler performance killer, but it will work. (windows.h was a major motivator for precompiled headers in early C++ compilers targeting windows)
always use the doctored name, either CreateFileA
or CreateFileW
; that will work, but at the cost of losing the flexibility of being able to change the underlying string model when you're making API calls; whether that matters is up to you.
don't use any of the Windows API names; potentially painful if you use the same naming convention as Windows; not at all painful if you use snake case, i.e., all lower-case with underbars to separate words. For example, create_file
. Alternatively, use a local prefix or suffix: MyCreateFile
or CreateFileMine
.
Upvotes: 2
Reputation: 12485
There are few things I generally do to cope with this:
win/filesystem.h
and win/filesystem.cpp
to wrap all the calls. (This is also a good place to convert Win32 errors into std::system_error
exceptions, remove unneeded/obsolete/reserved parameters, and generally make the Windows API more C++ friendly.)DWORD
, LPTSTR
and BOOL
to infiltrate all levels of your code makes dealing with Windows.h
that much more difficult. (And porting too.) The only files that should #include <Windows.h>
should be your wrapper C++ files.CreateFileW
or CreateFileA
directly instead of relying on the macro. That way, you don't need to depend on the Unicode/Multi-byte project setting.Example
win/filsystem.h
might contain definitions like this:
namespace win32
{
class FileHandle
{
void* raw_handle_;
public:
// The usual set of constructors, destructors, and accessors (usually move-only)
};
FileHandle CreateNewFile(std::wstring const& file_name);
FileHandle OpenExistingFile(std::wstring const& file_name);
// and so on...
}
Any part of your code can include this file to access the file system API. Since win/filesystem.h
does not itself include <Windows.h>
, the client code will be uncontaminated by the various Win32 macros.
Upvotes: 4