在Win 10中卸载DLL时debugging崩溃,但不是Win 7

不完全确定我已经确定了这个问题,但是这里是我所看到的和我所想的

我有一个主要用C语言编写的加载C ++ DLL的Win32程序。 该DLL通过COM对象将C程序中的数据传递给另一个应用程序 – 这可能是由DLL本身实例化的。 至lessWindows XP和Windows 7(可能是Win95和Win98,我需要更深入地回顾代码历史logging以了解何时引入此接口),但是在Windows 10中,程序崩溃在FreeLibrary()调用此DLL期间。

debugging器中检查这个时,DLL_DETACH_PROCESS似乎被成功处理(处理该消息时没有执行代码)。 崩溃发生在离开入口点的代码之后(或同时)。

如果我继续步入,我最终在一个名为utilcls.h,这似乎是Borland C Builder 6头文件之一的头文件。 我相信其中的模板代码与COM对象被拆除有关。 一个Unbind()调用会通过,这是在崩溃之前我可以进入的最后一行代码。

如果我使用debugging器的CPU窗口并继续步进,那么在崩溃到来之前,所有剩下的事情似乎都与释放内存有关,但是到了那里,CPU执行了很多步骤。

崩溃引发APPCRASHexception0xc0000602,指的是Combase.dll。

简单地说,不要为FreeLibrary调用这个DLL就可以让应用程序成功closures,但我的假设是FreeLibrary调用很重要。

COM对象在FreeLibrary()调用之前由数据共享应用程序释放,从而允许该应用程序closures。 我目前的假设是,这种不连贯的一些在新的操作系统中发生了不同的事情,这导致了崩溃,但是我不知道如何被发现。

我的问题:

  • 如果对其他人更了解他们正在做的事情是显而易见的,那么导致这次事故的原因是什么?

  • 试图进行debugging的下一步是什么? 我已经用尽了我所使用的debugging环境的知识,不知道COM或DLL是否足够了解下一个要问的问题。


一些debugging器输出RbMm请求:

0:000:x86> t ntdll_77b40000!RtlIsCriticalSectionLockedByThread+0x1b: 77b7256b c20400 ret 4 0:000:x86> t combase!DecrementMTAUsageHelper+0x5b: 7527a2d6 85c0 test eax,eax 0:000:x86> r eax eax=00000001 0:000:x86> t combase!DecrementMTAUsageHelper+0x5d: 7527a2d8 0f859a000000 jne combase!DecrementMTAUsageHelper+0xfd (7527a378) [br=1] 0:000:x86> t combase!DecrementMTAUsageHelper+0xfd: 7527a378 e89e9e0f00 call combase!CrashProcessWithWERReport (7537421b) 

在这一点上,堆栈看起来大致如下:

 ChildEBP RetAddr Args to Child 0019f9b8 7527a37c 063f4248 753d8448 00000000 combase!CrashProcessWithWERReport+0x35 0019f9e8 75292bfc 753d8448 7529257e 00000000 combase!DecrementMTAUsageHelper+0x101 (Inline) -------- -------- -------- -------- combase!DecrementMTAUsage+0x9 0019f9f0 7529257e 00000000 00000000 00000000 combase!CDllHost::MTAUninitializeApartmentOnly+0xe 0019fa08 7527543a 00000000 063f4248 00712410 combase!CDllHost::ClientCleanupFinish+0x4d 0019fa30 75276361 00000000 0019fa8c 00000000 combase!DllHostProcessUninitialize+0xa0 0019fa58 7527a452 000d06f6 00712410 00000000 combase!ApartmentUninitialize+0xe4 0019fa70 752c2a1e 000d06f6 00712e18 00712e80 combase!wCoUninitialize+0xd0 0019fa94 74ed3e58 00000003 74c17ff1 a6d0e607 combase!CoUninitialize+0x7e 0019fa9c 74c17ff1 a6d0e607 000b0792 74ed48f0 imm32!CtfImmCoUninitialize+0x48 0019fb7c 74809ea6 00050004 000d06f6 00000000 msctf!TF_Notify+0x581 0019fb98 748080dc 00050004 000d06f6 00000000 user32!CtfHookProcWorker+0x36 0019fbe0 74807fa6 0019fc34 0019fc24 00000000 user32!CallHookWithSEH+0x5c 0019fc08 77bb0006 0019fc24 00000018 0019fc80 user32!__fnHkINDWORD+0x26 0019fc38 710623fb 000b0792 04ff11aa 05480e70 ntdll!KiUserCallbackDispatcher+0x36 0019fc50 050364e4 000b0792 050376d8 05480e70 apphelp!DWM8AND16BitHook_DestroyWindow+0x2b 0019fc8c 05051007 00000000 05055034 00000001 myDLL!myCOMObject_tlbFinalize+0x408a4 0019fcb4 050511c6 0019fcd0 00000001 04ff1318 myDLL!myCOMObject_tlbFinalize+0x5b3c7 0019fcd8 04ff13d3 05055034 77badcce 04ff0000 myDLL!myCOMObject_tlbFinalize+0x5b586 0019fd00 77b807c6 04ff1318 04ff0000 00000000 myDLL+0x13d3 0019fd50 77b6aa5e 00000000 00000000 259704e5 ntdll!LdrpCallInitRoutine+0x43 0019fdb8 77b6e6c8 00000000 0071dd60 00000000 ntdll!LdrpProcessDetachNode+0xbb 0019fdd8 77b6e5af 25970745 0071e560 c000022d ntdll!LdrpUnloadNode+0x100 0019fe18 77b6e4f6 004afcc4 004ae3a4 04ff0000 ntdll!LdrpDecrementModuleLoadCountEx+0xa7 0019fe38 746e9d56 04ff0000 006e33c5 00000000 ntdll!LdrUnloadDll+0x86 0019fe4c 0049261c 04ff0000 00000000 00493034 KERNELBASE!FreeLibrary+0x16 0019fe64 00441895 004afc98 fffffffe 0019fee8 rpopdbg!_GetExceptDLLinfo+0x914bf 

现在rest,但我的猜测是我需要弄清楚如何正确清理COM对象? 也许在响应DLL_DETACH_PROCESS?

Solutions Collecting From Web of "在Win 10中卸载DLL时debugging崩溃,但不是Win 7"

崩溃引发APPCRASH异常0xc0000602,指的是Combase.dll

combase.dll使用0xc0000602STATUS_FAIL_FAST_EXCEPTION )代码

void CrashProcessWithWERReport();

(这个代码调用了RaiseFailFastException

CrashProcessWithWERReport仅在2种情况下调用DecrementMTAUsageHelperCoDecrementMTAUsage调用次数多于CoIncrementMTAUsage或者( 我几乎可以肯定的是这个原因 )当调用线程保持Loader临界区时调用的DecrementMTAUsageHelper CoIncrementMTAUsage – 所以在DLL加载或卸载过程中。 从MSDN

在进程关闭或dllmain内不要调用CoDecrementMTAUsage 。 您可以在调用之前调用CoDecrementMTAUsage来启动关机过程。

所以我的猜测 – 一些代码调用CoDecrementMTAUsage在你的DLL卸载过程中(当你调用FreeLibrary

你的DLL不能直接调用CoIncrementMTAUsage / CoDecrementMTAUsage因为这个新的API,存在从win 8开始(也请检查你的代码在win 8.1上 – 我想也会崩溃),但是这个api可以间接从其他系统组件中调用。

我可以假设你的DLL不直接释放一些使用过的资源,或者当DLL仍然持有一些资源时调用FreeLibrary (所以你调用FreeLibrary没有对DLL进行适当的清理调用),并且结果这个资源在卸载过程中开始释放( CoDecrementMTAUsage

试图进行调试的下一步是什么?

你需要使用调试符号文件(比如winDbg)。 在DecrementMTAUsageHelperCoDecrementMTAUsage设置断点,并可能是CoIncrementMTAUsage – 我正确的调用RtlIsCriticalSectionLockedByThread返回TRUE (这个API从DecrementMTAUsageHelper开始调用)。

在任何情况下在DecrementMTAUsageHelper调用点(在崩溃之前)发布线程调用堆栈,并且也可能在CoIncrementMTAUsageCoIncrementMTAUsage

———————-编辑————————-

通过查看堆栈跟踪可见,您的DLL从DllMain调用DestroyWindow

apphelp!DWM8AND16BitHook_DestroyWindow

这只是2个原因的错误 – 起初 – 阅读这篇文章 –

获取DLL_PROCESS_DETACH通知的线程不一定是得到DLL_PROCESS_ATTACH通知的线程。 您不能在DLL_PROCESS_ATTACH或DLL_PROCESS_DETACH处理程序中使用线程关联来执行任何操作,因为您无法保证将调用哪个线程来处理这些进程通知。 这个经典的例子,我被告知开发者支持团队以惊人的频率运行,是一个DLL,它在DLL_PROCESS_ATTACH处理程序中创建一个窗口,并在DLL_PROCESS_DETACH处理程序中销毁该窗口。

但是由于另外一个原因你的崩溃在这篇文章中没有列出–DllMain有很多限制 ,里面什么都不能调用。 尽管DestroyWindow没有直接在这里列出,但作为显示你的情况 – 这是非法调用(即使我们调用相同的线程,在这个窗口创建) – 当你的窗口被破坏imm32.CtfImmNotify(msctf!TF_Notify)被称为

 0019fa9c 74c17ff1 a6d0e607 000b0792 74ed48f0 imm32!CtfImmCoUninitialize+0x48 0019fb7c 74809ea6 00050004 000d06f6 00000000 msctf!TF_Notify+0x581 0019fb98 748080dc 00050004 000d06f6 00000000 user32!CtfHookProcWorker+0x36 0019fbe0 74807fa6 0019fc34 0019fc24 00000000 user32!CallHookWithSEH+0x5c 

结果从DllMain调用 CoUninitialize !

从MSDN

不要从DllMain函数调用CoInitialize,CoInitializeEx或CoUninitialize。

这里面的FINAL CoUninitialize里面调用了DecrementMTAUsage ,通过调用RtlIsCriticalSectionLockedByThreadCrashProcessWithWERReport来确定我们里面的loader加载器被锁定了。

解决方案

当然最好的是修复DLL,但如果这是不可能的 – 认为下一个“黑客”将是工作

 HRESULT hr = CoInitialize(0); // asume that we in STA FreeLibrary(hDLL); if (0 <= hr) CoUninitialize(); 

这个CoUninitialize当然imm32!CtfImmCoUninitialize会被imm32!CtfImmCoUninitialize调用,但是这不会是最终的初始化,并且结果是DecrementMTAUsage不会被调用