Holy cow, I wrote a book!
Solving this next problem should be a snap with your nascent psychic powers:
I'm trying use FormatMessage to load a resource string with one insertion in it, and this doesn't work for some reason. The string is "Blah blah blah %1. Blah blah blah." The call to FormatMessage fails, and GetLastError() returns ERROR_RESOURCE_TYPE_NOT_FOUND. What am I doing wrong? LPTSTR pszInsertion = TEXT("Sample"); LPTSTR pszResult; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, //I also tried an instance handle and NULL. GetModuleHandle(NULL), IDS_MY_CUSTOM_MESSAGE, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language (LPTSTR) &pszResult, 0, (va_list*) &pszInsertion);
FormatMessage
GetLastError()
ERROR_RESOURCE_TYPE_NOT_FOUND
LPTSTR pszInsertion = TEXT("Sample"); LPTSTR pszResult; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, //I also tried an instance handle and NULL. GetModuleHandle(NULL), IDS_MY_CUSTOM_MESSAGE, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language (LPTSTR) &pszResult, 0, (va_list*) &pszInsertion);
Hint: Take a closer look at the parameter IDS_MY_CUSTOM_MESSAGE.
IDS_MY_CUSTOM_MESSAGE
Hint 2: What does "IDS_" tell you?
IDS_
Resource identifiers that begin with "IDS_" are typically string resource identifiers, not message resource identifiers. There is no strong consensus on the naming convention for message resource identifiers, although I've seen "MSG_". Part of the reason why there is no strong consensus on the naming convention for message resource identifiers is that almost nobody uses message resources! I don't understand why they were added to Win32, since there was already a way of embedding strings in resources, namely, string resources.
MSG_
That's why you're getting ERROR_RESOURCE_TYPE_NOT_FOUND. There is no message resource in your module. If you're not going to use a message resource, you'll have to use the FORMAT_MESSAGE_FROM_STRING flag and pass the format string explicitly.
FORMAT_MESSAGE_FROM_STRING
DWORD_PTR rgdwInsertions[1] = { (DWORD_PTR)TEXT("Sample") }; TCHAR szFormat[256]; LoadString(hInstance, IDS_MY_CUSTOM_MESSAGE, szFormat, 256); LPTSTR pszResult; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, szFormat, 0, 0, (LPTSTR) &pszResult, 0, (va_list*) &rgdwInsertions);
I also made a slight change to the final parameter. When you use FORMAT_MESSAGE_ARGUMENT_ARRAY, the last parameter must be an array of DWORD_PTRs. (The parameter must be cast to va_list* to keep the compiler happy.) It so happens that the original code got away with this mistake since sizeof(DWORD_PTR) == sizeof(LPTSTR) and they both have the same alignment requirements. On the other hand, if the insertion were a DWORD, passing (va_list*)&dwValue is definitely wrong and can crash if you're sufficiently unlucky. (Determining the conditions under which your luck runs out is left as an exercise.)
FORMAT_MESSAGE_ARGUMENT_ARRAY
DWORD_PTR
va_list*
sizeof(DWORD_PTR) == sizeof(LPTSTR)
DWORD
(va_list*)&dwValue