Issue
I was looking at some code recently that did call GetLastError()
after HeapAlloc()
had failed. But, according to the HeapAlloc function MSDN docs:
If the function fails, it does not call SetLastError. An application cannot call GetLastError for extended error information.
The Internet is littered with code snippets that are calling GetLastError()
after a failed HeapAlloc()
, and I assume most coders (including myself) are programmed to naturally call GetLastError()
when an API fails.
To test, I created this example to allocate the max amount of memory:
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
int main() {
HANDLE hHeap = GetProcessHeap();
if (hHeap == NULL) {
printf("Failed to get the process heap.\n");
return 1;
}
SetLastError(0); // Clear the last error
// Attempt to allocate a very large amount of memory
SIZE_T largeSize = SIZE_MAX;
LPVOID pMemory = HeapAlloc(hHeap, 0, largeSize);
if (pMemory == NULL) {
DWORD dwError = GetLastError();
printf("HeapAlloc failed: ");
switch (dwError) {
case ERROR_NOT_ENOUGH_MEMORY:
printf("Not enough memory available.\n");
break;
case ERROR_OUTOFMEMORY:
printf("Insufficient memory to satisfy the request.\n");
break;
default:
printf("Error code: %lu\n", dwError);
}
}
else {
printf("Memory allocation successful.\n");
HeapFree(hHeap, 0, pMemory);
}
return 0;
}
The output shows:
HeapAlloc failed: Not enough memory available.
Question
Why does GetLastError()
appear to work? Is it just luck, so everyone should avoid calling it because relying on GetLastError()
for detailed error information may not be reliable?
Update
The official documentation for HeapAlloc()
notes:
If the function fails, it does not call SetLastError. An application cannot call GetLastError for extended error information.
However, from the comments below, the internal implementation of HeapAlloc()
updates the LastErrorValue
in the TEB, but is not documented for external developers.
Here’s an example of HeapAlloc
updating LastErrorValue
in the TEB with ERROR_NOT_ENOUGH_MEMORY
8.
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <intrin.h>
typedef struct _MY_TEB {
// The TEB is highly stable across Windows versions.
uint8_t Padding[0x68];
ULONG LastErrorValue;
// ...
} MY_TEB;
int main() {
SetLastError(0);
printf("LastErrorValue (TEB): %lu\n", ((MY_TEB*)__readgsqword(0x30))->LastErrorValue);
printf("GetLastError (API): %lu\n", GetLastError());
HeapAlloc(GetProcessHeap(), 0, SIZE_MAX);
printf("LastErrorValue (TEB): %lu\n", ((MY_TEB*)__readgsqword(0x30))->LastErrorValue);
printf("GetLastError (API): %lu\n", GetLastError());
return 0;
}
which outputs
LastErrorValue (TEB): 0
GetLastError (API): 0
LastErrorValue (TEB): 8
GetLastError (API): 8
To sum up, I’ll stick with the official documentation to NOT call GetLastError()
. Thanks.
Solution
To test, […]
You cannot "test" whether a contract is in effect.
Contracts are.
The combination of preconditions and postconditions is called a contract. An implementation is entitled to loosen its preconditions and strengthen its postconditions. When that happens, it is called an implementation detail.
Implementation details aren’t part of the contract.
If the documentation for HeapAlloc
states that
An application cannot call
GetLastError
for extended error information.
then that is the contract pertaining to its postcondition. Don’t call GetLastError
when HeapAlloc
returns NULL
.
Answered By – IInspectable
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0