c++ - _CrtSetAllocHook - 未处理的异常
问题描述
我使用 Windows 10 x64 和 Visual Studio 2019。这是一个简单的程序 (x86-Debug),它应该打印内存分配大小:
#include <iostream>
int MyAllocHook(int allocType, void* userData, std::size_t size, int blockType, long requestNumber,
const unsigned char* filename, int lineNumber)
{
std::cout << "Alloc: " << size << std::endl;
return 1;
}
int main()
{
_CrtSetAllocHook(MyAllocHook);
void* ptr = malloc(128);
if (ptr)
{
free(ptr);
}
system("pause");
return 0;
}
当我运行程序时,我得到 Exception Unhandled 错误:
Unhandled exception at 0x7A6A2B19 (ucrtbased.dll) in testcpp.exe: 0xC00000FD: Stack overflow (parameters: 0x00000000, 0x00EB2000). occurred
怎么了 ?如何解决?
解决方案
“堆栈溢出”(哈哈)是我的赠品。
钩子中的 I/O 和字符串操作导致它自己的内存分配,因此钩子被一次又一次地调用,直到它溢出堆栈。
您必须查看blockType
并明确排除运行时本身的调用,如此处所述: https ://docs.microsoft.com/en-us/visualstudio/debugger/allocation-hooks-and-c-run-time-内存分配?view=vs-2019
int MyAllocHook(int allocType, void* userData, std::size_t size,
int blockType, long requestNumber,
const unsigned char* filename, int lineNumber)
{
// Do not hook calls from the C Runtime itself
if (blockType == _CRT_BLOCK) return TRUE;
std::cout << "Alloc: " << size << std::endl;
return 1;
}
这样,来自 I/O 库本身的内存分配调用不会导致递归调用。
编辑啊哈,你不只是被 C 运行时调用挂断,它也来自你自己的“正常”字符串和 I/O 调用,它们不是来自运行时。基本上,您是在处理程序中间接调用malloc()
和/或调用new
,这是不允许的。
这个测试程序可以让你看到被分配的大量内存:
#include <iostream>
struct allocinfo {
int size;
const char *filename;
int lineno;
int alloctype;
int blocktype;
};
struct allocinfo allocs[256], *allocp = allocs;
volatile bool capturing = true;
static const char *printableAllocType(int t)
{
switch (t)
{
case _HOOK_ALLOC: return "_HOOK_ALLOC";
case _HOOK_REALLOC: return "_HOOK_REALLOC";
case _HOOK_FREE: return "_HOOK_FREE";
default: return "?";
}
}
static const char *printableBlockType(int t)
{
switch (t)
{
case _FREE_BLOCK: return "_FREE_BLOCK";
case _NORMAL_BLOCK: return "_NORMAL_BLOCK";
case _CRT_BLOCK: return "_CRT_BLOCK";
case _IGNORE_BLOCK: return "_IGNORE_BLOCK";
case _CLIENT_BLOCK: return "_CLIENT_BLOCK";
default: return "?";
}
}
int MyAllocHook( int allocType, void* userData, std::size_t size,
int blockType, long requestNumber,
const unsigned char* filename, int lineNumber)
{
if (blockType == _CRT_BLOCK) return 1;
if (capturing)
{
allocp->size = (int)size;
allocp->lineno = lineNumber;
allocp->blocktype = blockType;
allocp->alloctype = allocType;
allocp->filename = (const char *)filename;
allocp++;
}
static bool firstTime = true;
if (firstTime)
{
firstTime = false;
std::cout << "Alloc : " << size << std::endl;
}
return 1;
}
int main()
{
_CrtSetAllocHook(MyAllocHook);
void* ptr = malloc(128);
if (ptr)
{
free(ptr);
}
capturing = false;
for (struct allocinfo *ap = allocs; ap < allocp; ap++)
printf("%-15s %-15s Size %d file %s line %d\n",
printableAllocType(ap->alloctype),
printableBlockType(ap->blocktype),
ap->size, ap->filename, ap->lineno);
return 0;
}
它以一种不分配任何内存(应该是安全的)的方式记录所有非C 运行时调用,并在最后报告您的请求生成了您预期的明显 128 字节,以及来自处理程序中的字符串代码:
Alloc : 128
_HOOK_ALLOC _NORMAL_BLOCK Size 128 file (null) line 0 <-- EXPECTED
_HOOK_ALLOC _NORMAL_BLOCK Size 16 file (null) line 0 <-- SURPRISE!
_HOOK_ALLOC _NORMAL_BLOCK Size 16 file (null) line 0 <-- SURPRISE!
_HOOK_FREE _NORMAL_BLOCK Size 0 file (null) line 0
_HOOK_FREE _NORMAL_BLOCK Size 0 file (null) line 0
_HOOK_FREE _NORMAL_BLOCK Size 0 file (null) line 0
代码本身完全被破解在一起,但它演示了正在发生的事情。
注意:此代码不能正确重入,如另一个答案中所述。
摘要:即使您正确排除了 C 运行时内存,您也无法在直接或间接分配用户内存的内存挂钩中执行任何操作。
有趣的事实:在捕获信息后_CRT_BLOCK
移动检查,您会看到 C 运行时有多忙。
编辑- 我刚刚修改了我的小测试程序,发现调用malloc()
和new
都被视为“正常”内存块,而不是运行时,所以这表明你根本不能在内存挂钩处理程序中使用它们。
推荐阅读
- javascript - 未定义将变量分配给回调函数的输出
- c# - 我想将对象的实例转换为仅在运行时才知道的类型的实例
- php - 如何使用 PHP 发送 HTTP 请求以使用 Jaxon 连接 PostgreSQL 数据库?
- mysql - Mysql 获取包含匹配另一个条件的所有数据
- r - 如何使用leaflet.esri 包添加ArcGIS 服务器层?
- kotlin - Kotlin:lambda 运行替代方案
- java - 如何强制 Spring Scheduled 并行执行单个方法?
- sql-server - 如何捕获诸如“包验证错误”之类的错误
- c# - 如何在 Simple Injector 中创建标记的 Litetime?
- xcode - 并行运行 xcode ui 测试时 Xcpretty 不起作用