使用托管 C++ 库时出现 WS2_32.dll_unloaded 异常
Posted
技术标签:
【中文标题】使用托管 C++ 库时出现 WS2_32.dll_unloaded 异常【英文标题】:WS2_32.dll_unloaded exception when using managed C++ library 【发布时间】:2014-07-30 21:28:40 【问题描述】:有没有人知道以下崩溃意味着什么或如何调试它?
错误应用程序名称: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.5e50):访问冲突 - 代码 c0000005(第一次机会)
在任何异常处理之前报告第一次机会异常。
可以预期并处理此异常。
+0x1a0af:
000007fefe67a0af ?? ???
0:000> kP
Child-SP RetAddr Call Site
00000000
0020dc80 00000000002a2700 <Unloaded_WS2_32.dll>+0x1a0af
00000000
0020dc88 00000000002a2701 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。这样的呼叫总是与 AVE 一起崩溃。从问题中无法猜测 ws2_32.dll 是如何被卸载的。劣质反恶意软件往往是环境问题。 感谢汉斯的回复。结果证明它与反恶意软件无关,并且与我的应用程序试图加载的另一个 DLL 有关,该 DLL 无意中卸载了 WS2_32.dll。 对其他 DLL 做无法形容的事情的劣质 DLL 在我的列表中排名第二。恶意软件:) 【参考方案1】:我有一个类似的问题,涉及使用许多 DLL 的本机应用程序,其中一个是受管理的。应用程序无法访问 main 的事实表明 DLL 的 DllMain 返回 FALSE。但是,尽管事件查看器中的日志显示了与 OP 提供的类似的错误消息,但我不知道是哪一个。对我来说幸运的是,该应用程序仅在新构建的 PC 上失败。为尝试调试问题而执行的操作:
使用加载程序快照来跟踪 DLL 加载过程。与 WinDbg 一起,这给出了哪个 DLL 失败的指示,因为未初始化的一堆 DLL 包括除已运行其初始化程序的所有 DLL 之外的所有 DLL。但它并没有显示每个 DLL 的 DllMain 的实际返回值。 使用 Dependency Walker 的分析选项来跟踪 DllMains 的返回值。然而,这挂了所以没有结果。 在两台机器上使用 ProcMon 捕获启动并比较跟踪。在检查了注册表项HKLM\Software\Microsoft\SQMClient\Windows\CEIPEnable
后,这更有成效,并显示出两者之间的不同行为。在工作情况下,CEIPEnable
为 0。在失败情况下,CEIPEnable
为 1。结果证明这是 nop 提到的 Windows 客户体验改善计划。禁用此选项可让应用程序在发生故障的机器上启动。
使用 AppVerifier 捕获那些在其 DllMain 中调用 WSAStartup
的 DLL(在调试器下运行时)。这确实标记了可疑的 DLL(以及其他)。正如 Richard 所指出的,这种行为与 MSDN 的建议相反。
进一步观察:
如果我在好机器上启用了 CEIP,应用程序仍然可以成功启动。因此,造成故障的原因肯定是其他因素。 Microsoft Japan 有一个关于此问题的article,其中将禁用 CEIP 列为解决方法。链接的文章是日文,但 Google 可以翻译。 托管 DLL 似乎不是问题。我希望有人觉得这个答案有用。如果没有 nop 的答案,我将无法解决我的问题。
【讨论】:
【参考方案2】:我最近遇到了完全相同的症状(和相同的调用堆栈),但没有其他 DLL 与 ws2_32.dll 混淆,所以我只是把它放在这里以防其他人有同样的问题:在我的情况下罪魁祸首是 Windows 的客户体验改善计划,如果启用,它会使我的应用程序崩溃。只需禁用它就可以了。
我对此行为没有深入的解释,只是您在函数名称中看到的“Sqm”与此服务相关,并且在客户体验改善计划时将在 ws2_32.dll 中采用不同的代码路径已禁用(在 WSAStartup 期间调用了一个检查 Sqm opt in 的函数)。
【讨论】:
@VipB(对不起,我没有足够的声望直接评论你的答案)我没有提到它,但是 CEIP 成为一个问题 只有在它运行一次之后(如果我没记错的话,它通过在当天晚些时候启动的计划任务运行)。因此,如果您在启用/禁用 CEIP 后立即运行测试,结果很可能是相同的,您需要让 CEIP 运行一次以重现我们遇到的问题。【参考方案3】:经过一番调试,我找到了原因。我能够将问题减少到两个 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 的 DllMain() 中,有一个对 WSAStartup() 的调用,最终卸载了 WS2_32.dll。在模块加载/卸载设置断点后,来自 WinDBG 的堆栈跟踪证实了这一点:
0:000> k
Child-SP RetAddr Call Site
00000000`001ae0c8 00000000`771f2773 ntdll!ZwUnmapViewOfSection+0xa
[e:\obj.amd64fre\minkernel\ntdll\daytona\objfre\amd64\usrstubs.asm @ 484]
00000000`001ae0d0 00000000`771f3b5a ntdll!LdrpUnloadDll+0x3c6 [d:\win7sp1_gdr\minkernel\ntdll\ldrapi.c @ 1672]
00000000`001ae1f0 000007fe`fd822dd5 ntdll!LdrUnloadDll+0x4a [d:\win7sp1_gdr\minkernel\ntdll\ldrapi.c @ 1743]
00000000`001ae220 000007fe`fee5a0af KERNELBASE!FreeLibrary+0x1d [d:\win7sp1_gdr\minkernel\kernelbase\module.c @ 1193]
00000000`001ae250 000007fe`fee54a68 WS2_32!Ws2SqmGetFileVersionInfo+0x12f [d:\w7rtm\minio\sockets\winsock2\sqmlib\ws2sqm.c @ 308]
00000000`001ae2c0 000007fe`fee473df WS2_32!Ws2SqmInit+0xd678
00000000`001ae510 000007fe`d7c01042 WS2_32!WSAStartup+0x2fd [d:\w7rtm\minio\sockets\winsock2\ws2_32\src\startup.cpp @ 301]
有一篇关于此的 MSDN 文章:WSAStartup function。
WSAStartup 函数通常会导致特定于协议的助手 正在加载的 DLL。因此,WSAStartup 函数不应 从应用程序 DLL 中的 DllMain 函数调用。这个可以 可能导致死锁。有关详细信息,请参阅 DLL 主要功能。
这解释了为什么程序在输入main()
之前就崩溃了,因为崩溃发生在 DLL 加载期间。
【讨论】:
以上是关于使用托管 C++ 库时出现 WS2_32.dll_unloaded 异常的主要内容,如果未能解决你的问题,请参考以下文章