We’re nearing the end!
Part I focused on the very fundamentals of C and C++, making sure that you understand the build system and the very basics of the syntax.
Part II expanded on this to teach you all the C++ you’ll need to do basic work in the language, including a few useful parts of the standard library, such as vectors and strings.
You now know all the basics we need, and the actual Win32 API should now be very simple to deal with. Not elegant or consistent, but comprehensible as long as you keep a close eye on the documentation and take nothing for granted.
First of all, the documentation can be found here. As you probably already know, Microsoft’s own search capabilities are nonexistent, and to find the function you need, you’ll typically want to use Google. But sometimes, the complete reference is useful, so here it is.
To teach you how to use the Win32 API, I wil, run you through a pair of functions with some very basic functionality: retrieving the last error message.
Should be simple, right? You’d think so, if you’re new to Win32.
The operation consists of two steps. First we have to retrieve the last error code, and then we have to ask Windows for the associated message as a string.
And the first step is indeed easy. We just have to call the GetLastError function. Let’s start with the complete code:
#include <windows.h>
int main() {
DWORD error = GetLastError();
}
Feel free to run this in the debugger and see which code the function returns. (Most likely you’re going to get a 0, since no error has actually occurred at this point).
Now let’s look at the actual documentation. They describe the function as having this signature:
DWORD WINAPI GetLastError(void);
which looks like nothing we’ve seen so far. Let’s take the easy parts first. The function’s name is GetLastError. It takes a parameter of type void, or so it seems. This is actually a throwback to C, and means that the function takes no parameters. Both GetLastError() and GetLastError(void) is legal in C++. In C, the two used to have subtly different meanings. (void) properly declared a function which took no parameters, while () declared a function which took any number of parameters, but simply didn’t access them. But that was C. In C++, the two are identical, and we usually use () to indicate a function that takes no parameters.
Next, we have the return type at the far left. DWORD is short for Double Word (a word is the “natural” data size on the CPU, which, back in the old days, was a 16-bit integer. Hence, a double word is a 32 bits wide. Microsoft has defined DWORD as a macro alias for portability. Under the hood it is simply an unsigned int, but that may change some day. If it does, they will redefine the DWORD macro to stand for some other type. So if you use DWORD when they tell you to, your code will still compile when it happens. It is easiest to just nod and accept this. It doesn’t make a big difference for us, but if and when we need to, we know that we can cast a DWORD to an unsigned int
That leaves the last name, WINAPI, which exists for pretty much the same purpose. It is another macro, and stands for the calling convention. The calling convention for a function specifies how parameters should be passed to it, and where it should place its return value. If we don’t know the calling convention of a function, we can not call it. Normally, we’re happy to use the default calling convention, but the Windows API has to be specific, so they add the WINAPI macro. And again, they use a macro so that if they one day decide to change the underlying calling convention, they can simply redefine this macro, and everyone’s code should still compile with no problems.
Following this, they describe the parameters and return value in detail. This is always worth reading in detail, because often, some parameters may or must be NULL. Likewise, the return value may have several meanings, and there is no single consistent convention. Some functions return zero on success, others return non-zero, or a positive value on succes. Some don’t return a success code at all. And some functions returns NULL on error, and actual data on success. Always, always read this section carefully.
In this case, we’re lucky. It simply returns the currently active error code, although it does ramble on about all the inconsistencies caused by other functions.
The remarks section tells us other information that doesn’t fit under one specific parameter or under the return value. Again, this should never be skipped. This is where all the inconsistencies and special cases are often listed.
Some functions then have a link to an example usage.
Finally, the documentation shows us where and when the function is defined. In this case, we need at least Windows 2000, and the function is defined in WinBase.h (but we should just include windows.h).
And it is defined in the Kernel32.Lib library. This library is included by default, so we don’t have to worry about this.
So far, it hasn’t been too bad, has it? It should be clear already that it’s not a pretty API, but as long as we stick to the documentation it’s pretty straightforward.
So let’s move on to the FormatMessage function. Follow that link, and take a look… I’ll be here waiting.…
Done? Good. Now this looks scary. And no, this time I can’t give you a simple explanation. This function truly is scary. Of course, this is one of the reasons why I picked it for this example. This is about as bad as the Win32 API gets.
The page lists the following function prototype:
DWORD WINAPI FormatMessage(
__in DWORD dwFlags,
__in_opt LPCVOID lpSource,
__in DWORD dwMessageId,
__in DWORD dwLanguageId,
__out LPTSTR lpBuffer,
__in DWORD nSize,
__in_opt va_list *Arguments
);
__in, __in_opt and __out are Microsoft-specific extensions, and are mainly used for documentation and for static code verification. It tells us which parameters are used for input, and which ones are for output, as well as which ones are optional.
LPCVOID is another Microsoft macro. Microsoft spent a decade or two promoting Hungarian Notation before they had to admit what an astonishingly bad idea it actually was. But of course Win32 is stuck with it.
The LP prefix stands for “Long Pointer”, and you can pretty much ignore the “Long” part. That dates back to 16-bit computers, where you actually had different types of pointers (far and near pointers). All we need to know is that it is a pointer. The C is for constant. In other words, this is a constant pointer to void, or const void*. (Of course, void isn’t a very meaningful thing to point to. A void pointer is essentially used as a pointer to an unknown type.)
LPTSTR is another adventure in Hungarian Notation. You already know LP. STR is probably obvious too. It’s a string. (Of course, since this is a C API, we’re talking about a C string, or a char pointer, which also explains the presence of the LP part. That leaves the T. What can that mean? I’m not sure. It might be “Template” or similar. It was introduced when Microsoft realized that they’d have to support Unicode. As I mentioned previously, Windows uses wchar_t for unicode text, and so their API had to accept wchar_t pointers when working with Unicode strings. But they still had to be backwards compatible as well, and be able to handle plain char pointers as well.
So they invented a new set of macros The T essentially stands for “whichever character type is currently active”.
If you enter your project’s properties, you’ll see the option to enable or disable Unicode on the General tab. It should be enabled by default.
As long as Unicode is enabled, any macro including this T will be mapped to the equivalent macro using a W (for Wide). If Unicode is disabled, the macro will instead point to a similarly named macro without this character.
In other words:
- LPTSTR -> LPWSTR or LPSTR
- LPTCSTR -> LPWCSTR or LPCSTR
- TCHAR -> WCHAR or CHAR
And all of these again point to the types you would probably now expect. LPWSTR is a pointer to a wide string (wchar_t*). And LPCSTR is a const pointer to a string, or const char*. And WCHAR is a wchar_t.
As if this wasn’t complicated enough, the function itself is also a macro. Two versions of the function actually exist:
FormatMessageAis the old ASCII version, using plaincharstrings.FormatMessageWis the “new” Unicode version, usingwchar_tstrings.
FormatMessage is not itself a function, but simply a macro, which is resolved by the preprocessor to one of these two. (C doesn’t allow overloaded functions, so they had to settle for this ugly hack to allow multiple definitions of the same function).
This also means that we can actually call these two names directly. If we call FormatMessageW, we’ll get the Unicode version regardless of whether Unicode is enabled in project settings. This makes it safe for us to use wchar_t strings directly, rather than mess around with TCHAR strings which might be one or the other.
Going back to the function declaration, the last parameter, va_list, looks a bit out of place. It’s not capitalized, and it doesn’t have these ugly prefixes. It is used in C to indicate a variable number of arguments, commonly known as varargs. As I mentioned in part I, printf uses varargs as well, and this throws away all hope of type safety, or even knowing how many parameters are pased to the function. Hopefully we won’t need to mess with this. (it’s marked as __in_opt, so it should be optional. Let’s hope we won’t have to use it then).
Anyway, there’s nothing for it. Let’s dive in. First parameter:
Ok, so this is a DWORD flag, and seems to store a combination of two values. The second table is shorter, so let’s take that first. There are three options here. A zero just means to preserve whatever line breaks exist in the message by default. the constant FORMAT_MESSAGE_MAX_WIDTH_MASK seems to preserve hardcoded linebreaks, but removes “regular” ones. I have no clue what the difference is.
The last option (mentioned just under the table) is to store any other number into the value. This then specifies the maximum line width. We’re happy to just use the default line breaks though, so we’ll settle for the zero value. That leaves the first table.
Looking through the options there, it seems that we need FORMAT_MESSAGE_FROM_SYSTEM. FORMAT_MESSAGE_ALLOCATE_BUFFER seems potentially interesting as well, but this table doesn’t really explain what happens if this flag is not enabled. If the system doesn’t allocate a buffer for us, who does? Looking down further, at the input parameter nSize we see that:
If the FORMAT_MESSAGE_ALLOCATE_BUFFER flag is not set, this parameter specifies the size of the output buffer, in TCHARs.
In other words, if we don’t use this flag, we have to provide a buffer. But we don’t know the length of the message we’re trying to retrieve, so this seems a bad idea. (Of course we could just provide a buffer of 64KB, which the documentation mentions is the maximum size, but this seems silly).
Finally, if we skip down to the “Security Remarks”, it says to add FORMAT_MESSAGE_IGNORE_INSERTS if we’re going to pass “arbitrary system error codes”, which we are. Most API’s try to ensure that the simplest action is the correct one. Win32 seems to be designed for the opposite case, ensuring that that correct usage should only be possible if you have already read the entire documentation page, very carefully, and at least three times. But that won’t stop us.
So the dwFlags parameter should then be the combination of these flags: FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | 0‘, although of course the 0 can be omitted.
Next, we have lpSource. Luckily, this is marked optional, and it is stated that this is ignored unless one of the two listed dwFlags values are set, which they’re not in our case. So we ignore it and simply pass NULL.
Then we have the message ID. This must be the value we got from GetLastError. Then we have the language ID. Rather than going searching for possible values to pass here, we can see that if we just pass a zero, it’ll try to pick a sensible default. So let’s do that.
Now comes the pointer to the output buffer. Read what it says here carefully:
If dwFlags includes FORMAT_MESSAGE_ALLOCATE_BUFFER, the function allocates a buffer using the LocalAlloc function, and places the pointer to the buffer at the address specified in lpBuffer.
So the parameter lpBuffer is a pointer to the pointer to the buffer. That is, we must pass it a pointer to the pointer it should set to point to the allocated buffer.
It also mentions that the buffer is allocated with LocalAlloc, and must be freed by us with LocalFree. Better remember this, or we’ll leak memory. Note that Windows defines several different memory allocation functions. This time they chose to use LocalAlloc. C++‘s new and delete are implemented in terms of some of these, but who knows which?.
Now comes nSize. It allows us to specify the minimum number of characters to allocate? Why would we care about that? Let’s just pass zero and hope for the best. It’s just a minimum after all.
Finally, we have Arguments. We already specified that the system should ignore inserts, so it seems like it shouldn’t actually care about these arguments. They’re also specified as optional, so let’s pass a big fat NULL here.
And that should be it! Now we just have to handle the return value:
- zero on failure, or
- the number of TCHARs stored in the output buffer, not counting the terminating
NULL
And… we’re through. Now let’s try putting the pieces together and see what happens:
#include <windows.h>
#include <iostream>
int main() {
DWORD error = GetLastError();
wchar_t* buffer;
DWORD length = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
0,
(wchar_t*)&buffer,
0,
NULL);
std::wcout << buffer << std::endl;
LocalFree(buffer);
}
Note the ugly cast we need to on the buffer. This is necessary because the argument may be either a pointer to a pre-allocated buffer, or (as in our case), a pointer to a pointer we’d like to be set to point to the system-allocated buffer. But the function expects a pointer to a string buffer, not a pointer to a pointer to a string buffer, so if we want to pass it the latter, we have to cast it to the former type.
Note that I’m calling the W version of the function specifically, and using wchar_t instead of TCHAR. The reason is simple. I want the Unicode version, regardless of Unicode setting in the project. Part of the reason is that it’s a lot easier to print out the string when we know what type it is. In particular, the standard library requires us to use cout for regular character strings, and wcout for wide strings. If we’re given a string of TCHAR’s, do we call cout or wcout to print it? Easier to just be specific and make sure we have wide characters.
Well, that’s it. Try running it. It should print out that “the operation completed successfully”. Gee, thanks. That really makes it all feel worthwhile, doesn’t it? Make sure you understand what our code means (in particular, why the cast is necessary, and how wcout is able to print out the string and know where it ends, when all it has is a pointer to a character.
Anyway, you’ve now seen some of the worst the Win32 API has to offer. And you’re still alive. Many of the functions you might want to call are far simpler than this.




