程序崩溃并显示0xC000000D错误,没有异常信息 - 我该如何调试?

5
我有一个使用第三方库的Visual C++ 9 Win32应用程序。当以一组特定参数调用该库中的函数时,程序会崩溃并显示“异常代码0xC000000D”。
我尝试附加Visual Studio调试器 - 没有抛出任何异常(无论是C ++还是结构化的访问冲突),也没有调用terminate()。仍然,程序默默地结束。
为什么程序会异常结束但不在调试器中停止?如何定位问题?

它是多线程还是单线程的? - Simone
@Simone:一个工作线程,由RPC生成多个服务线程。我们已经彻底测试了同步,多线程不太可能是问题所在。 - sharptooth
只是一个想法,你的库是否可能破坏了某些内存部分,导致程序崩溃和异常终止。如果你有像BoundsChecker这样的工具,或者类似的执行分析器,它可能会捕捉到问题。你还可以检查链接器中是否开启了DEP,并查看在关闭DEP的情况下问题是否仍然存在,同样也可以在编译选项中进行运行时检查。 - SmacL
2
很多好的想法 - 所有这些都将被WinDbg捕获,您将准确地知道发生了什么。另一种捕获所有这些错误的方法是使用AppVerifier - 无需支付BoundsChecker的费用。 - Ana Betts
以前没听说过AppVerifier,而且我的BoundsChecker许可证已经很久没有更新了,甚至升级都很昂贵。这是一个值得关注的工具,谢谢保罗! - SmacL
显示剩余4条评论
3个回答

5
这是STATUS_INVALID_PARAMETER(无效参数)错误,请使用WinDbg跟踪引发该错误的程序(即附加WinDbg,输入“sxe eh”,然后输入“g”)。

1
OP已经说过“附加VS调试器-不会抛出任何异常”... WinDbg会如何改变这种情况? - Martin Ba
搜索发现有些函数会抛出此异常,但也有些函数将STATUS_INVALID_PARAMETER用作返回值。所以也许根本没有抛出任何异常... - Martin Ba
@Martin VS会隐藏它不知道如何处理的某些类型的异常(很少发生)。使用WinDbg可以完全排除任何类型的异常。 - Ana Betts
如果我们无法从库供应商那里得到帮助,我会尝试这个。我还发现了一篇有关如何在这种情况下使用WinDbg的有希望的知识库文章:http://support.microsoft.com/kb/313109/en-gb - sharptooth
@AnaBetts 我不认为它会,你有关于你的说法的来源吗? - Zimano
@Zimano 我曾在微软工作,负责用户模式异常处理程序。 - Ana Betts

2

其他回答和评论对这个问题有很大帮助。以下是我的做法。

我注意到如果在Visual Studio调试器下运行程序,它会默默地结束,但如果不使用调试器运行它,它会崩溃并出现一个消息框(通常的Windows消息框,说我丢失了未保存的数据,每个人都非常抱歉)。

因此,我在没有调试器的情况下启动程序,让它崩溃,然后 - 当消息框仍然存在时 - 附加调试器并点击“中断”。以下是调用堆栈:

ntdll.dll!_KiFastSystemCallRet@0()  
ntdll.dll!_ZwWaitForMultipleObjects@20()  + 0xc bytes   
kernel32.dll!_WaitForMultipleObjectsEx@20()  - 0x48 bytes   
kernel32.dll!_WaitForMultipleObjects@16()  + 0x18 bytes 
faultrep.dll!StartDWException()  + 0x5df bytes  
faultrep.dll!ReportFault()  + 0x533 bytes   
kernel32.dll!_UnhandledExceptionFilter@4()  + 0x55c bytes
//SomeThirdPartyLibraryFunctionAddress
//SomeThirdPartyLibraryFunctionAddress
//SomeThirdPartyLibraryFunctionAddress
//SomeThirdPartyLibraryFunctionAddress
//OurCodeInvokingThirdPartyLibraryCode

很明显这是第三方库内部出现了问题。根据MSDN的说法,UnhandledExceptionFilter() 在致命情况下被调用,显然是由于库代码中的某些问题而导致的。因此我们将首先与库供应商一起解决这个问题。


1

如果您没有第三方库的源代码和调试信息,您将无法使用调试器进入其中。我认为,您的选择有:

  • 编写一个简单的测试用例来说明崩溃情况,并将其发送给库开发人员
  • 在自己的代码中包装该库函数,检查非法参数并在应用程序传递这些参数时抛出异常/返回错误代码
  • 重写不起作用的库部分或使用替代方法

提供对象形式的代码很难修复。

编辑 您还可以尝试在主消息循环周围使用__try __finally更优雅地退出,例如:

int CMyApp::Run() 
{
    __try
    {
        int i = CWinApp::Run();
        m_Exitok = MAGIC_EXIT_NO;
        return i;
    }
    __finally
    {
        if (m_Exitok != MAGIC_EXIT_NO)
            FaultHandler();
    }
}

@Paul,可以,但只能使用没有PDB的汇编语言。有了PDB但没有源代码,您可以从源代码中获得带有标签的漂亮注释汇编代码。在我看来,这些很少值得手动调试,因为修复必须在汇编语言中完成,这将耗费时间并引起未来的维护问题。 - SmacL
我认为 OP 的问题不是进入代码,而是捕获异常,他说这并没有发生。 - Ori Osherov
但他不想手动调试汇编代码,他想要一个调用堆栈(即使是错误的),以找到故障点。如果故障实际上是由操作系统引起的,他将拥有相关符号。 - Ana Betts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接