【问题标题】:VC++ 6.0 access violation when run in debugger在调试器中运行时 VC++ 6.0 访问冲突
【发布时间】:2008-12-17 14:35:23
【问题描述】:

我正在尝试对一个已有 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

问候,

--- 阿利斯泰尔。

【问题讨论】:

    标签: c++ debugging visual-c++-6 access-violation


    【解决方案1】:

    您可以使用_CrtSetDbgFlag() 来启用一堆有用的堆调试技术。还有许多其他的CRT debugging functions 可以帮助您找出问题所在。

    【讨论】:

    • 非常感谢所有这些答案。我稍后会尝试看看这个。我想我已经解决了这个问题,导致我想在调试器中运行它,所以它的优先级已经下降。尽管我显然担心发布代码中可能存在隐藏的损坏。
    【解决方案2】:

    从调试器运行时,使用不同的堆;这被称为调试堆。这与在调试器外部使用的堆具有不同的行为,并且可以帮助您捕获此类问题。

    请注意,Win32“调试堆”不同于 VC++“调试堆”;但是,两者都旨在做或多或少相同的事情。请参阅this article,它描述了在调试器下运行应用程序时的行为差异。

    在这种情况下,您可能在调用此函数之前损坏了堆,方法是注销堆块的结尾或开头。

    【讨论】:

    • 调试堆与释放堆的使用取决于应用程序链接的 C 运行时库的类型,而不是代码在调试器中运行的事实,即运行调试构建命令行中的应用程序将使用调试堆。
    • AFAIK,您可以使用 API 在调试堆和非调试堆之间切换,因此很可能是调试器调用 API 将其切换到 degug 版本。除此之外 - 是的,您可能在某处损坏了堆。
    【解决方案3】:

    最简单的方法(假设您的应用程序没有过多地使用内存)是启用整页堆检查(这将在提供分配的内存页之后放置所谓的保护页,这反过来又会查明代码中发生损坏的确切位置)。

    鉴于您有 Windows debugging tools 方便,运行以下 gflags 命令来配置整页堆:

    gflags[.exe] /p /enable yourapp.exe /full

    注意,您应该提供可执行文件名称单独(即没有路径前缀!)
    然后只需在调试器下运行它 - 它会在第一次尝试破坏堆时中断。此处的区别在于,堆损坏大多是延迟缺陷,当(可能)有效的堆操作生效时,这些缺陷会在以后显现。

    注意,还有:

    gflags[.exe] /p /enable yourapp.exe /full /backwards

    在分配之前会另外放置一个保护页。

    单独使用 /p 开关运行将显示当前有效的堆页面选项。

    【讨论】:

      【解决方案4】:

      您可能有堆损坏错误。您的应用程序可能在调用 open_new_log() 之前损坏了堆。

      【讨论】:

        【解决方案5】:

        我怀疑 jmattias 是对的。问题的根本原因可能不在崩溃的地方。

        很多事情都可能导致堆损坏。

        • 写入已分配内存块的末尾(或开头)。
        • 释放指针两次,或释放未分配的指针。
        • 多线程程序并与单线程(非线程安全)RTL 链接。
        • 从一个堆分配内存并将其释放到另一个堆。
        • 等等,等等,

        由于您使用的是 Visual C++,因此您可以使用调试堆的 _CrtSetDbgFlag() 功能来打开错误检查。您可以将其设置为在每次调用 malloc 或 free 时检查堆的完整性。这将运行得非常缓慢,但它应该为您指出错误所在。

        在编译器文档中搜索 _CrtSetDbgFlag。

        【讨论】:

          【解决方案6】:

          我怀疑存在一个使用与应用程序其余部分不同的 C++ 运行时版本编译的 DLL。这通常会导致“地址 XXX 的内存无法被‘读取’/‘写入’”违规。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-09-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多