我还没确定我已经把这个问题钉了下来,但这就是我所看到的以及我认为正在进行的事情。
我有一个Win32程序,主要用C语言加载C ++ DLL。该DLL通过COM对象将数据从C程序传递到另一个应用程序 - 这可能是由DLL本身实例化的。所有这一切显然在至少Windows XP和Windows 7(可能是Win95和Win98,我需要回顾代码历史以找出何时引入此接口)上工作正常,但在Windows 10中程序崩溃在FreeLibrary()调用此DLL期间。
在调试器中检查时,DLL_DETACH_PROCESS似乎处理成功(处理该消息时不执行代码)。崩溃发生在离开入口点的代码之后(或同时)。
如果我继续Step In,我最终会出现一个名为utilcls.h的头文件,它似乎是Borland C Builder 6头文件之一。我相信其中的模板代码与被拆除的COM对象有关。一个Unbind()调用传递,这是我在崩溃之前可以执行的最后一行代码。
如果我使用调试器的CPU窗口并继续步进,那么剩下的一切似乎都与崩溃前的内存释放有关,但是要实现这一目标需要相当多的CPU步骤。
崩溃引发APPCRASH,异常为0xc0000602,返回Combase.dll。
只是不为该DLL调用FreeLibrary允许应用程序成功关闭,但我的假设是FreeLibrary调用很重要。
在FreeLibrary()调用之前,数据共享应用程序释放COM对象,允许该应用程序关闭。我现在的假设是,在较新的操作系统中,这种取消链接的某些方式发生了不同,这导致了崩溃,但我不知道如何确定。
我的问题:
如果对于那些更了解自己正在做什么的其他人来说很明显,是什么导致这次崩溃?
尝试调试此操作的后续步骤是什么?我已经用尽了我所使用的调试环境的知识,并且不太了解COM或DLL,知道下一个问题是什么。
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
现在正在处理其余部分,但我的猜测是我需要弄清楚如何在COM对象上正确清理?也许是为了响应DLL_DETACH_PROCESS?
答案 0 :(得分:2)
崩溃引发APPCRASH,异常为0xc0000602,返回参考 Combase.dll
combase.dll
仅使用来自
0xc0000602
(STATUS_FAIL_FAST_EXCEPTION
)代码
void CrashProcessWithWERReport();
(使用此代码调用RaiseFailFastException
)
CrashProcessWithWERReport
仅在2个条件下从DecrementMTAUsageHelper
调用 - CoDecrementMTAUsage
调用次数超过CoIncrementMTAUsage
或(,我几乎可以肯定 )DecrementMTAUsageHelper
在调用线程保持Loader临界区时调用 - 所以在DLL加载或卸载过程中。来自MSDN
请勿在进程关闭或内部时调用CoDecrementMTAUsage 的DllMain 即可。您可以在调用start之前调用CoDecrementMTAUsage 关机过程。
所以我的猜测 - 你的DLL卸载过程中的一些代码调用CoDecrementMTAUsage
(当你调用FreeLibrary
时)
你的DLL无法直接调用CoIncrementMTAUsage
/ CoDecrementMTAUsage
因为这个新的API存在从win 8开始(也检查你的代码在win 8.1上 - 我认为也会崩溃),但是这个api可以从其他系统组件间接调用。
我可以假设您的DLL不直接释放一些使用过的资源,或者当DLL仍然保留一些资源时调用FreeLibrary
(因此您在没有正确清理DLL的情况下调用FreeLibrary
)并因此获得此资源在卸载过程中开始免费(CoDecrementMTAUsage
)
尝试调试它的后续步骤是什么?
你需要使用符号文件调试(比如winDbg)。在DecrementMTAUsageHelper
,CoDecrementMTAUsage
设置断点,可能是CoIncrementMTAUsage
- 我正确地呼吁RtlIsCriticalSectionLockedByThread
返回TRUE
(这个api从{{1}开始调用}})。
无论如何在DecrementMTAUsageHelper
呼叫点(就在崩溃之前)发布线程调用堆栈,也可能在DecrementMTAUsageHelper
上发布
----------------------编辑--------------------- ---- 强>
通过视图堆栈跟踪可见您的DLL从DllMain调用CoIncrementMTAUsage
。
DestroyWindow
这只是出于两个原因的错误 - 首先 - 阅读this article -
获取DLL_PROCESS_DETACH通知的线程不是 必须是获得DLL_PROCESS_ATTACH通知的那个。您 在您的DLL_PROCESS_ATTACH或中,无法对线程相关性做任何事情 DLL_PROCESS_DETACH处理程序,因为您无法保证哪些 将调用线程来处理这些进程通知。该 这个经典的例子,我告诉开发人员支持团队 进入惊人的频率,是一个在其中创建窗口的DLL DLL_PROCESS_ATTACH处理程序并在其DLL_PROCESS_DETACH中销毁它 处理程序。
但是你的崩溃是由于另一个原因,未在文章中列出 - DllMain有很多restrictions,其内部无法调用。尽管apphelp!DWM8AND16BitHook_DestroyWindow
没有直接列在这里,但是如你所知 - 这是非法的调用(即使我们调用同一个线程,在这个窗口上创建) - 当你的窗口被销毁时DestroyWindow
被调用
imm32.CtfImmNotify(msctf!TF_Notify)
结果CoUninitialize从DllMain 调用!
来自MSDN
不要从中调用CoInitialize,CoInitializeEx或CoUninitialize DllMain功能。
此处名为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
的 FINAL CoUninitialize内部,它通过调用DecrementMTAUsage
和RtlIsCriticalSectionLockedByThread
确定我们在加载程序锁定内部。
解决方案?
当然最好的是修复DLL,但如果这是不可能的 - 想想下一个“hack”将会起作用
CrashProcessWithWERReport
这个CoUninitialize当然会从HRESULT hr = CoInitialize(0); // asume that we in STA
FreeLibrary(hDLL);
if (0 <= hr) CoUninitialize();
调用,但这将是 NOT FINAL 未初始化,结果imm32!CtfImmCoUninitialize
将不会被调用