FormatMessage
expanding to FormatMessageA
or LPSTR
and LPCSTR
sharing the same character representation. It is otherwise correct, though.)
[out] lpBuffer
A pointer to a buffer that receives the null-terminated string that specifies the formatted message. If
dwFlags
includesFORMAT_MESSAGE_ALLOCATE_BUFFER
, the function allocates a buffer using theLocalAlloc
function, and places the pointer to the buffer at the address specified inlpBuffer
.
(emphasis mine)
The code snippet:
void ErrorExit()
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL) == 0) {
MessageBox(NULL, TEXT("FormatMessage failed"), TEXT("Error"), MB_OK);
ExitProcess(dw);
}
MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
ExitProcess(dw);
}
On my setup - x64, Visual Studio 2022 (v143), Windows 10 SDK - these types evaluate as follows:
LPVOID
: void *
unsigned long FormatMessageW(
unsigned long dwFlags,
const void * lpSource,
unsigned long dwMessageId,
unsigned long dwLanguageId,
wchar_t * lpBuffer,
unsigned long nSize,
char ** Arguments
)
As you can see, what we pass is indeed a "pointer to a buffer": void **
. But what we cast it to is just a buffer: wchar_t *
.
And we have to, as FormatMessage
/-A/-W does not take a **
(or *&
). Going by its signature, it should allocate the buffer right at &lpMsgBuf
, write the second wchar
at (&lpMsgBuf) + sizeof(wchar_t)
and basically trash our stack.
(That is not what happens, of course: Both linked code snippets work as intended, if not as expected.)
It does seem to me that I must trick the compiler into letting me pass the wrong type into the function.
How would one write this code without using an unchecked cast?
(E.g. I'd have expected the code - after macro expansion - to look like this:
wchar_t * lpMsgBuf;
[...]
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
&lpMsgBuf,
0, NULL) == 0) {
[...]
but that of course does not compile, as &lpMsgBuf
is wchar_t **
, not wchar_t *
.)
X/Y answer: There would not happen to be a modern, UTF-8 version of this cursed API around somewhere?
This stuff is straight out of Microsoft's "let's code as alien as possible" era. Unfortunately GetLastError
and thence FormatMessage
seem to be the only way to do error handling for Windows API calls like GetProcessAffinityMask, for which currently no standard C++ equivalent exists. I'd much rather not touch this with the proverbial ten-feet pole.
FormatMessage
expanding to FormatMessageA
or LPSTR
and LPCSTR
sharing the same character representation. It is otherwise correct, though.)
[out] lpBuffer
A pointer to a buffer that receives the null-terminated string that specifies the formatted message. If
dwFlags
includesFORMAT_MESSAGE_ALLOCATE_BUFFER
, the function allocates a buffer using theLocalAlloc
function, and places the pointer to the buffer at the address specified inlpBuffer
.
(emphasis mine)
The code snippet:
void ErrorExit()
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL) == 0) {
MessageBox(NULL, TEXT("FormatMessage failed"), TEXT("Error"), MB_OK);
ExitProcess(dw);
}
MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
ExitProcess(dw);
}
On my setup - x64, Visual Studio 2022 (v143), Windows 10 SDK - these types evaluate as follows:
LPVOID
: void *
unsigned long FormatMessageW(
unsigned long dwFlags,
const void * lpSource,
unsigned long dwMessageId,
unsigned long dwLanguageId,
wchar_t * lpBuffer,
unsigned long nSize,
char ** Arguments
)
As you can see, what we pass is indeed a "pointer to a buffer": void **
. But what we cast it to is just a buffer: wchar_t *
.
And we have to, as FormatMessage
/-A/-W does not take a **
(or *&
). Going by its signature, it should allocate the buffer right at &lpMsgBuf
, write the second wchar
at (&lpMsgBuf) + sizeof(wchar_t)
and basically trash our stack.
(That is not what happens, of course: Both linked code snippets work as intended, if not as expected.)
It does seem to me that I must trick the compiler into letting me pass the wrong type into the function.
How would one write this code without using an unchecked cast?
(E.g. I'd have expected the code - after macro expansion - to look like this:
wchar_t * lpMsgBuf;
[...]
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
&lpMsgBuf,
0, NULL) == 0) {
[...]
but that of course does not compile, as &lpMsgBuf
is wchar_t **
, not wchar_t *
.)
X/Y answer: There would not happen to be a modern, UTF-8 version of this cursed API around somewhere?
This stuff is straight out of Microsoft's "let's code as alien as possible" era. Unfortunately GetLastError
and thence FormatMessage
seem to be the only way to do error handling for Windows API calls like GetProcessAffinityMask, for which currently no standard C++ equivalent exists. I'd much rather not touch this with the proverbial ten-feet pole.
How would one write this code without using an unchecked cast?
Unfortunately, you cannot, since the function takes an LPTSTR
. So, when using the FORMAT_MESSAGE_ALLOCATE_BUFFER
flag, the type-cast is required. This is what allows you to pass in an LPTSTR*
where an LPTSTR
is expected. There is simply no getting around that limitation of this API.
That being said, when compiling for C++, you should be using reinterpret_cast
instead of a C-style cast, eg:
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | ...,
...
reinterpret_cast<LPTSTR>(&lpMsgBuf),
...)
I'd have expected the code - after macro expansion - to look like this:
...
but that of course does not compile, as
&lpMsgBuf
iswchar_t **
, notwchar_t *
.
Actually, it would be a void**
not a wchar_t**
. But, just as you can't pass a wchar_t**
to a wchar_t*
, you can't pass a void**
to it, either. Had the function taken an LPVOID
instead, this would not be a problem at all.
That being said, you should declare lpMsgBuf
as LPTSTR
instead of LPVOID
, then you don't need to type-cast it when passing it to MessageBox()
at least, eg:
LPTSTR lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | ...,
...
reinterpret_cast<LPTSTR>(&lpMsgBuf),
...)
MessageBox(NULL, lpMsgBuf, TEXT("Error"), MB_OK);
There would not happen to be a modern, UTF-8 version of this cursed API around somewhere?
Technically, yes. Starting with Windows 10 v1903, if you enable the UTF-8 codepage for your app then FormatMessageA()
(and most other Win32 A
APIs) will process and return UTF-8 strings.
However, do be aware that, as of a year ago, this option was broken in FormatMessageA()
for UTF-8 encoded format strings. I don't know if that issue has been fixed yet, so use with caution.
If you need to support older Windows versions (or just don't want to trust an experimental feature) then you should use FormatMessageW()
to get the text as UTF-16 first, and then convert it to UTF-8 using WideCharToMultiByte()
or equivalent.
Unfortunately
GetLastError
and thenceFormatMessage
seem to be the only way to do error handling for Windows API calls likeGetProcessAffinityMask
, for which currently no standard C++ equivalent exists.
Actually, there is std::error_code
and std::error_condition
, both of which have a message()
method. It is up to the standard library implementation to decide whether it uses FormatMessage()
or not, but you could try it and see what happens, eg:
void ErrorExit()
{
DWORD dw = GetLastError();
int ierr = static_cast<int>(dw);
std::string msg = std::error_code(ierr, std::system_category()).message();
MessageBoxA(NULL, msg.c_str(), "Error", MB_OK);
ExitProcess(dw);
}
When calling without FORMAT_MESSAGE_ALLOCATE_BUFFER
you don't need a cast.
With it, I usually prefer casting twice, one for safety and one to satisfy the API:
LPTSTR buffer;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | ..., ...
(LPTSTR)const_cast<LPTSTR*>(&buffer), 0,
...);
Having to type that all the time is annoying, just make a helper function:
inline UINT FormatMessageAlloc(UINT Flags, UINT MsgId, LPTSTR* ppszBuffer, LPCVOID Source = NULL, UINT LangId = 0, UINT MinSize = 0)
{
Flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;
return FormatMessage(Flags, Source, MsgId, LangId, (LPTSTR)const_cast<LPTSTR*>(ppszBuffer), MinSize, NULL);
}
X/Y answer: There would not happen to be a modern, UTF-8 version of this cursed API around somewhere?
Not modern, but there is an UTF-8 "version".
Create an application.manifest
file with the following content:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<application>
<windowsSettings>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>
</assembly>
Then link this into your .exe, for example if you're using CMake:
if(WIN32)
target_sources(your_executable_target PRIVATE application.manifest)
endif()
After that FormatMessageA
will give you UTF-8. See https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page for more information.
This won't solve your unchecked cast issue though.
LPCSTR
asconst LPSTR
then it would be obvious that they truly are not meant to diverge and it would have been less code as well. – Zsar Commented Feb 4 at 14:03LPSTR
ischar*
(non-const pointer to non-const char).LPCSTR
ischar const *
(non-const pointer to const char), whereasconst LPSTR
ischar* const
(const pointer to non-const char). They are not the same thing. – Remy Lebeau Commented Feb 4 at 15:44