使用托管c++库时出现WS2_32.dll_unloaded异常

WS2_32.dll_unloaded exception when using managed C++ library

本文关键字:WS2 dll 异常 unloaded c++      更新时间:2023-10-16

有人知道下面的崩溃意味着什么或如何调试它吗?

故障应用程序名称:MyServer.exe,版本:0.0.0.0,时间戳:0x53d885f1

故障模块名称:WS2_32.dll_unloaded,版本:0.0.0.0,时间戳:0x4ce7ca25

异常码:0xc0000005

故障偏移量:0x000007fefe67a0af

故障进程id: 0xcbc

应用程序启动时间:0x01cfabbc216f52c2

故障应用程序路径:[path]/MyServer.exe

故障模块路径:WS2_32.dll

报告Id: 61efadce-17af-11e4-8301-001517d9c80a

当我将我的exe(非托管c++)与托管c++ dll(通过dllexport暴露)链接时,会发生这种情况。从windbg来看,似乎崩溃发生在加载所有模块之后,甚至在它到达main之前:

(51d4.5 .5e50):访问违规-代码c0000005(第一次机会)在任何异常处理之前报告第一次机会异常。这种异常是可以预料和处理的。+ 0 x1a0af:000007fe fe67a0af ?? ??? 0:000> kP Child-SP RetAddr Call Site 00000000 0020dc80 00000000 002a2700 <Unloaded_WS2_32.dll>+0x1a0af 00000000 0020dc88 00000000 002a2701 0x2a2700 00000000 0020dc90 00000000 ' 00000000 0x2a2701

托管c++ dll是一个DNS库,它最终调用下面的WS2_32.dll。

奇怪的是,另一个使用相同的托管c++ dll的exe工作得很好,我没有看到源/头文件之间有任何明显的差异,这会导致这种不同的行为。如果我注释掉(1)引用这个托管dll的代码,或者(2)我的exe中除了引用托管dll的代码之外的所有其他代码,那么崩溃也不会发生。(2)工作的事实告诉我,至少所有的dll依赖项都应该在工作目录中。

这只发生在Windows Server 2008 R2上。我使用的是。net 4.5。

谁有什么建议?

我遇到过一个类似的问题,涉及到一个本机应用程序,它使用了许多dll,其中一个是托管的。应用程序无法访问主程序的事实表明DLL的DllMain返回FALSE。然而,我不知道是哪一个,尽管事件查看器中的日志显示了与op提供的类似的错误信息。对我来说幸运的是,应用程序仅在新构建的pc上失败。为尝试调试问题而执行的操作:

  • 使用加载器快照来跟踪DLL加载过程。结合WinDbg,它给出了哪个DLL失败的指示,因为一堆未初始化的DLL包括除了一个初始化器运行之外的所有DLL。它没有显示每个DLL的dlmain的实际返回值。
  • 使用Dependency Walker的分析选项来跟踪dllmain的返回值。然而,这并没有取得成果。
  • 在两台机器上使用ProcMon捕获启动并比较跟踪。在检查注册表项HKLMSoftwareMicrosoftSQMClientWindowsCEIPEnable后,这是更富有成效的,并且在两者之间显示出不同的行为。在工作情况下,CEIPEnable为0。在失败的情况下,CEIPEnable为1。这就是nop提到的Windows客户体验改进计划。禁用此选项允许应用程序在故障机器上启动。
  • 使用AppVerifier来捕获那些在DllMain中调用WSAStartup的dll(当在调试器下运行时)。这确实标记了可疑的DLL(以及其他DLL)。此行为与Richard所指出的MSDN的建议相反。

进一步观察:

    如果我在正常的机器上启用了CEIP,应用程序仍然可以成功启动。因此,一定是其他因素导致了故障。
  • 微软日本有一篇关于这个问题的文章,其中禁用CEIP被列为解决方案。链接的文章是日文的,但谷歌可以翻译。
  • 托管的DLL似乎不是问题。

我希望有人觉得这个答案有用。如果没有nop的答案,我就解决不了我的问题。

我最近遇到了完全相同的症状(和相同的调用堆栈),但是没有其他DLL干扰ws2_32.dll,所以我只是把这个放在这里以防其他人有同样的问题:在我的情况下,罪魁祸首是Windows的客户体验改进程序,如果启用,使我的应用程序崩溃。把它关掉就没事了。

我对这种行为没有深入的解释,只是你在函数名中看到的"Sqm"与此服务有关,当客户体验改进程序被禁用时,ws2_32.dll内将采用不同的代码路径(有一个函数检查Sqm选项在WSAStartup期间调用)。

经过调试我找到了原因。我能够将问题减少到两个API调用:

void __cdecl main(int argc, __in_ecount(argc)  char* argv[])
{
    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);
    HRESULT hr = TS_STATUS_OK;
    /***** A *****/
    hr = InitializeDllA();
    ASSERT(SUCCEEDED(hr)); 
    /***** B *****/
    hr = InitializeDllB();
    ASSERT(SUCCEEDED(hr));   
}

两个dll都依赖于WS2_32.dll但是发生的事情是,在A的dlmain()中,有一个对WSAStartup()的调用,它最终卸载了WS2_32.dll。在模块加载/卸载时设置断点后,来自WinDBG的堆栈跟踪确认了这一点:

0:000> k
Child-SP          RetAddr           Call Site
00000000`001ae0c8 00000000`771f2773 ntdll!ZwUnmapViewOfSection+0xa 
[e:obj.amd64freminkernelntdlldaytonaobjfreamd64usrstubs.asm @ 484]
00000000`001ae0d0 00000000`771f3b5a ntdll!LdrpUnloadDll+0x3c6 [d:win7sp1_gdrminkernelntdllldrapi.c @ 1672]
00000000`001ae1f0 000007fe`fd822dd5 ntdll!LdrUnloadDll+0x4a [d:win7sp1_gdrminkernelntdllldrapi.c @ 1743]
00000000`001ae220 000007fe`fee5a0af KERNELBASE!FreeLibrary+0x1d [d:win7sp1_gdrminkernelkernelbasemodule.c @ 1193]
00000000`001ae250 000007fe`fee54a68 WS2_32!Ws2SqmGetFileVersionInfo+0x12f [d:w7rtmminiosocketswinsock2sqmlibws2sqm.c @ 308]
00000000`001ae2c0 000007fe`fee473df WS2_32!Ws2SqmInit+0xd678
00000000`001ae510 000007fe`d7c01042 WS2_32!WSAStartup+0x2fd [d:w7rtmminiosocketswinsock2ws2_32srcstartup.cpp @ 301]

有一篇MSDN文章是关于这个:WSAStartup函数的。

WSAStartup函数通常会导致特定于协议的helper正在加载dll。因此,WSAStartup函数不应该是从应用程序DLL中的DllMain函数调用。这可以可能导致死锁。有关更多信息,请参阅DLL主要功能。

这解释了为什么程序在进入main()之前崩溃,因为崩溃发生在DLL加载期间。