提问人:vengy 提问时间:11/16/2023 最后编辑:vengy 更新时间:11/18/2023 访问量:134
在 HeapAlloc() 失败后调用 GetLastError() 是否可靠?
Is it reliable to call GetLastError() after a failed HeapAlloc()?
问:
我最近在看一些代码,这些代码在失败后确实调用了。但是,根据 HeapAlloc 函数 MSDN 文档:GetLastError()
HeapAlloc()
如果函数失败,则不会调用 SetLastError。应用程序无法调用 GetLastError 以获取扩展的错误信息。
互联网上充斥着在失败后调用的代码片段,我假设大多数编码人员(包括我自己)都被编程为在 API 失败时自然调用。GetLastError()
HeapAlloc()
GetLastError()
为了测试,我创建了这个示例来分配最大内存量:
#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;
}
输出显示:
HeapAlloc 失败:可用内存不足。
问题
为什么看起来有效?难道只是运气好,所以大家都应该避免打电话,因为依靠详细的错误信息可能不可靠?GetLastError()
GetLastError()
更新
笔记的官方文档:HeapAlloc()
如果函数失败,则不会调用 SetLastError。应用程序无法调用 GetLastError 以获取扩展的错误信息。
但是,从下面的注释中可以看出,TEB 中的内部实现更新了,但未为外部开发人员记录。HeapAlloc()
LastErrorValue
下面是在 TEB 中使用 8 进行更新的示例。HeapAlloc
LastErrorValue
ERROR_NOT_ENOUGH_MEMORY
#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;
}
哪个输出
LastErrorValue (TEB): 0
GetLastError (API): 0
LastErrorValue (TEB): 8
GetLastError (API): 8
总而言之,我会坚持使用官方文档,不要调用.谢谢。GetLastError()
答:
要测试,[...]
您无法“测试”合同是否有效。
合同是。
前提条件和后置条件的组合称为合同。实施有权放宽其前提条件并加强其后置条件。发生这种情况时,它被称为实现细节。
实现细节不是合同的一部分。
如果 HeapAlloc
的文档指出
应用程序无法调用扩展的错误信息。
GetLastError
那么这就是与其后置条件有关的合同。返回时不要打电话。GetLastError
HeapAlloc
NULL
评论
SetLastError
当然没有调用(因为 HeapAlloc == ntdll.dll 中的 RtlAllocateHeap),而是直接设置在 中,如果它失败了。RtlAllocateHeap
LastErrorValue
TEB
LastErrorValue
RtlAllocateHeap
LastErrorValue
LocalAlloc
RtlAllocateHeap
ERROR_NOT_ENOUGH_MEMORY