我正在尝试为4岁的VC ++ 6.0程序添加增强功能。调试版本从命令行运行,但不在调试器中运行:它在printf()内部发生访问冲突。如果我跳过printf,那么它会在malloc()中崩溃(从fopen()中调用)并且我不能跳过它。
这意味着我无法在调试器中运行并且必须依赖旧的printf语句来查看正在发生的事情。这显然使它变得更加困难。
知道为什么printf()和malloc()在VC ++调试器下运行时会失败?我不擅长这种低级别的东西!
以下是访问冲突后的调用堆栈:
_heap_alloc_dbg(unsigned int 24, int 2, const char * 0x0046b3d8 `string', int 225) line 394 + 8 bytes
_nh_malloc_dbg(unsigned int 24, int 0, int 2, const char * 0x0046b3d8 `string', int 225) line 242 + 21 bytes
_malloc_dbg(unsigned int 24, int 2, const char * 0x0046b3d8 `string', int 225) line 163 + 27 bytes
_lock(int 2) line 225 + 19 bytes
_getstream() line 55 + 7 bytes
_fsopen(const char * 0x00468000 `string', const char * 0x00466280 `string', int 64) line 61 + 5 bytes
fopen(const char * 0x00468000 `string', const char * 0x00466280 `string') line 104 + 15 bytes
open_new_log(const char * 0x00468000 `string') line 66 + 14 bytes
log_open(const char * 0x00468000 `string', int 0) line 106 + 9 bytes
Xlog_open(const char * 0x00468000 `string', int 0) line 51 + 13 bytes
service_start(unsigned long 1, char * * 0x009a0e50) line 3152 + 12 bytes
service_init2(char * 0x00471fcc char * NTPROGRAM, char * 0x004723c4 char * NTSERVICE, char * 0x00466540 `string', unsigned long 1, char * * 0x009a0e50) line 508 + 13 bytes
service_init(char * 0x00471fcc char * NTPROGRAM, char * 0x004723c4 char * NTSERVICE, unsigned long 2, char * * 0x009a0e50) line 548
main(unsigned long 2, char * * 0x009a0e50) line 3131
mainCRTStartup() line 206 + 25 bytes
KERNEL32! 7c817067()
以下是直到失败的操作的调试反汇编:
0041EA7E jmp _heap_alloc_dbg+2B3h (0041eb23)
0041EA83 mov edx,dword ptr [_lTotalAlloc (004b4294)]
0041EA89 add edx,dword ptr [nSize]
0041EA8C mov dword ptr [_lTotalAlloc (004b4294)],edx
0041EA92 mov eax,[_lCurAlloc (004b429c)]
0041EA97 add eax,dword ptr [nSize]
0041EA9A mov [_lCurAlloc (004b429c)],eax
0041EA9F mov ecx,dword ptr [_lCurAlloc (004b429c)]
0041EAA5 cmp ecx,dword ptr [_lMaxAlloc (004b42a0)]
0041EAAB jbe _heap_alloc_dbg+249h (0041eab9)
0041EAAD mov edx,dword ptr [_lCurAlloc (004b429c)]
0041EAB3 mov dword ptr [_lMaxAlloc (004b42a0)],edx
0041EAB9 cmp dword ptr [_pFirstBlock (004b4298)],0
0041EAC0 je _heap_alloc_dbg+25Fh (0041eacf)
0041EAC2 mov eax,[_pFirstBlock (004b4298)]
0041EAC7 mov ecx,dword ptr [pHead]
0041EACA mov dword ptr [eax+4],ecx
这是我们调用fopen()的源代码,在malloc()
中失败FILE *open_new_log( const char *logfile )
{
FILE *fp;
int retry = 0;
while( ( fp = fopen( logfile, "w" ) ) == NULL && ++retry < 300 )
Sleep( 1000 );
return( fp );
}
我得到的错误是
Unhandled exception inPISCOOP.exe: 0xC00000005: Access Violation
此致
--- Alistair。
答案 0 :(得分:3)
从调试器运行时,使用不同的堆;这被称为调试堆。这与调试器外部使用的堆有不同的行为,可以帮助您捕获类似这样的问题。
请注意,Win32“调试堆”与VC ++“调试堆”不同;然而,两者都旨在或多或少地做同样的事情。请参阅this article,其中介绍了在调试器下运行应用时的行为差异。
在这种情况下,您可能在调用此函数之前损坏了堆,可能是通过注销堆块的结束或者从堆块的开头开始。
答案 1 :(得分:3)
您可以使用_CrtSetDbgFlag()
启用一堆有用的堆调试技术。还有许多其他CRT debugging functions可用于帮助您找出问题所在。
答案 2 :(得分:2)
最简单的方法(假设您的应用程序没有过多地使用内存)是启用整页堆检查(在提供分配的内存页之后会出现所谓的保护页,反过来,确定代码中发生损坏的确切位置)。
假设您有Windows debugging tools方便,请运行以下gflags命令来配置整页堆:
gflags[.exe] /p /enable yourapp.exe /full
注意,您应该提供可执行文件名单独(即没有路径前缀!)
然后在调试器下运行它 - 它会在第一次尝试破坏堆时中断。这里的区别在于堆损坏主要是延迟缺陷,当(可能的)有效堆操作生效时,这些缺陷会在以后显现出来。
另请注意:
gflags[.exe] /p /enable yourapp.exe /full /backwards
还会在分配之前放置一个保护页面。
单独使用/ p开关运行将显示当前有效的堆页面选项。
答案 3 :(得分:1)
您可能遇到堆损坏错误。在调用open_new_log()
之前,您的应用程序可能已损坏堆。
答案 4 :(得分:1)
我怀疑jmattias是对的。问题的根本原因可能是崩溃的其他地方。
很多事情都可能导致堆损坏。
由于您使用的是Visual C ++,因此可以使用调试堆的_CrtSetDbgFlag()功能打开错误检查。您可以将其设置为在每次调用malloc或free时检查堆的完整性。这将运行得非常缓慢,但它应该确定错误的位置。
在编译器文档中搜索_CrtSetDbgFlag。
答案 5 :(得分:0)
我怀疑是否存在使用与应用程序其余部分不同的C ++运行时版本编译的DLL。这通常会导致“地址XXX的记忆不能被'读'/'写'”违规。