Last time, we learned about the tumultuous history of the FORMAT_
MESSAGE_
ALLOCATE_
BUFFER
flag in Windows Store UWP apps.
But why was this flag disallowed for so long?
It’s nothing particularly profound. Rather, it was just bad luck.
The buffer allocated by the FORMAT_
MESSAGE_
ALLOCATE_
BUFFER
flag needs to be freed by calling LocalÂFree
, but LocalÂFree
was not one of the functions that can be called from a Windows Store app.
Why not?
Because LocalÂAlloc
and LocalÂFree
are legacy functions that hang around for backward compatibility with 16-bit Windows. New programs shouldn’t be using them. It’s not like your new program needs to be backward compatible with 16-bit Windows 3.1.
But this left the FORMAT_
MESSAGE_
ALLOCATE_
BUFFER
flag in a bit of a pickle, because despite being something new for Win32, the flag continues to use that old and busted legacy function for memory allocation.
There was some discussion within the team about how to address the problem. One school of thought was to document enough of the internals of the LocalÂFree
function so that you could call HeapÂFree
to free it. You can see remnants of this approach in the comments of the winbase.h
header file:
// // FORMAT_MESSAGE_ALLOCATE_BUFFER requires use of HeapFree //
For a while, there was also this paragraph of the FORMAT_
MESSAGE_
ALLOCATE_
BUFFER
documentation that says a bunch of stuff in a rather confusing way.
Windows 10: LocalAlloc() has different options: LMEM_FIXED, and LMEM_MOVABLE. FormatMessage() uses LMEM_FIXED, so HeapFree can be used. If LMEM_MOVABLE is used, HeapFree cannot be used.
(Fortunately, the confusing paragraph isn’t there any more.)
The decision that won the day was to accept that legacy code will never die. The team just held their nose and added LocalAlloc
and LocalFree
to the list of functions that are permitted to be called by Windows Store app.
But please, promise to use it only for situations that absolutely require it for compatibility purposes, like freeing the message string allocated by FormatÂMessage
. Don’t use it as your go-to memory allocation function.
While you seem to know much about FormatMessage. Can it be made more efficient with regards to its locale? Every call to FormatMessage looks up the current culture in the Registry. This becomes a major bottleneck once you try to open up the Windows Event Viewer which seems to have gotten no love since a long time.
See https://aloiskraus.wordpress.com/2020/07/20/ms-performance-hud-analyze-eventlog-reading-performance-in-realtime/ for the full background why it should definitely made faster since more and more security software...
This looks like a horrible api. How was this allowed to ship?
I remember calling GlobalAlloc a few years ago for something because nobody had gotten around to documenting that HeapAlloc survives the dll it was allocated from being freed. (under some circumstances malloc doesn't).
Also, .net provides AllocHGlobal and FreeHGlobal as it's only fixed byte buffer allocators. (Ever try to keep a pin alive through a callback? It's hard.) I have seriously considered writing an API that allocates with GlobalAlloc just so a .net caller can free...
The lifetime of HeapAlloc-allocated memory is tied to the heap, not the DLL. DLLs don't own heaps. It's not like HeapAlloc walks the stack looking for the return address. (Never occurred to me that this would need to be documented. Who expects the system to auto-destroy heaps on DLL unload? Especially if you allocated from the process heap - that thing is called the "process" heap for a reason.)
The malloc behavior is the same: The...
With malloc, the problem could be that the DLL uses a different runtime library. So you may have no way to free this memory after the DLL is unloaded.
Other options:
a) adding FORMAT_MESSAGE_ALLOCATE_BUFFER_EX and documenting to use HeapFree.
b) adding FORMAT_MESSAGE_ALLOCATE_BUFFER_EX and documenting to use FormatMessageFree.
The decision that won the day was to accept that legacy code will never die.
And I'm sure it was the wrong decision. 😉
Fortunately, the confusing paragraph isn’t there any more.
Believe me, there are more confusing paragraphs there. For example, this:
If the length of the formatted message exceeds 128K bytes, then FormatMessage will fail and a subsequent call to GetLastError will return ERROR_MORE_DATA.
128K bytes? Is that 128 kilobytes (128 kB) or...
SI prefixes are case sensitive. The SI prefix for 1000 is lowercase `k`. Capital `K` is not an SI prefix.
For more than half a century, capital `K`, in a computing context, has meant 1024. So 128 KB is 131,072 bytes and 128 kB is 128,000 bytes. Many user interfaces, bits of documentation, and marketing materials aren't careful about maintaining this distinction, but, in this case, the documentation is fine.
Admittedly, when you...