在Win 10中卸载DLL时调试崩溃,但不是Win 7 [英] Debugging a crash while unloading DLL in Win 10, but not Win 7

查看:1790
本文介绍了在Win 10中卸载DLL时调试崩溃,但不是Win 7的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不完全确定我已经解决了这个问题,但这里是我看到的,我认为正在发生。


$ b $我有一个Win32程序主要用C编写,加载一个C ++ DLL。该DLL通过COM对象将数据从C程序传递给另一个应用程序 - 可能由DLL本身实例化。所有这一切通过至少Windows XP和Windows 7(可能是Win95和Win98,我需要更深入地查看代码历史记录以了解此界面的介绍),显然已经正常工作,但在Windows 10中,程序崩溃在该DLL的FreeLibrary()调用期间。



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



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



如果我使用调试器的CPU窗口并继续步进,一切这个遗漏似乎与崩溃之前释放内存有关,但是到达那里是相当多的CPU步进。



崩溃引发了APPCRASH,异常为0xc0000602,请参阅Combase.dll。



只需不调用FreeLibrary即可使该DLL成功关闭,但我的假设是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
0019f A94 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 00000 000 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?

解决方案


崩溃引发APPCRASH异常0xc0000602,返回到
Combase.dll


combase.dll used 0xc0000602 STATUS_FAIL_FAST_EXCEPTION )仅代码



void CrashProcessWithWERReport();



(其中调用 RaiseFailFastException 使用此代码)



CrashProcessWithWERReport 仅在中调用DecrementMTAUsageHelper 2条件 - CoDecrementMTAUsage 的次数比 CoIncrementMTAUsage 或(我几乎肯定在这个原因在DLL加载或卸载过程中调用。从MSDN


在进程关闭期间不要调用CoDecrementMTAUsage
dllmain
内。您可以在开始
的关闭进程之前调用CoDecrementMTAUsage。


所以我的猜测 - 一些代码调用 CoDecrementMTAUsage (当您调用 FreeLibrary



您的DLL可以不直接致电 CoIncrementMTAUsage / CoDecrementMTAUsage 因为这个新的API,存在从win 8开始(也检查你的代码在胜利8.1 - 我认为也将崩溃),但是这个api可以从其他系统组件间接调用。



我可以假设你的DLL不直接释放一些使用的资源,或者你打电话当DLL仍然拥有一些资源(所以你调用$ code> FreeLibrary 而没有适当的清理调用DLL)时,$ code> FreeLibrary 卸载过程中开始免费( CoDecrementMTAUsage


尝试下一步是什么要调试这个?


你需要使用符号文件进行调试(比如说winDbg)。设置断点在 DecrementMTAUsageHelper CoDecrementMTAUsage ,可能是 CoIncrementMTAUsage 我正确的是调用 RtlIsCriticalSectionLockedByThread return TRUE (这个api从$ code开始调用DecrementMTAUsageHelper )。



无论如何发布线程调用堆栈在 DecrementMTAUsageHelper 调用点在崩溃之前)和可能在 CoIncrementMTAUsage



----------- -----------编辑-------------------------



通过视图堆栈跟踪可见,您的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

作为结果 CoUninitialize 是从DllMain 调用的



从MSDN


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




这里的 FINAL CoUninitialize 称为 DecrementMTAUsage 这决定了我们内部的加载程序锁定通过调用 RtlIsCriticalSectionLockedByThread 调用CrashProcessWithWERReport



解决方案



当然最好的是修复DLL,但是如果这是不可能的 - 想下一个hack将会工作

  HRESULT hr = CoInitialize(0); // asume that我们在STA 
FreeLibrary(hDLL);
if(0< = hr)CoUninitialize();

与此 CoUninitialize 当然无论如何,将从 imm32!CtfImmCoUninitialize 但这将是 NOT FINAL 未初始化,因此 DecrementMTAUsage 将不被调用


Not totally sure that I've got this problem nailed down yet, but here's what I'm seeing and what I think is going on.

I have a Win32 program mostly written in C that loads a C++ DLL. That DLL passes data from the C program to another application via a COM object--one that's probably instantiated by the DLL itself. All of this has apparently worked fine through at least Windows XP and Windows 7 (possibly Win95 and Win98, I'd need to look back deeper into the code history to find out when this interface was introduced), but in Windows 10 the program crashes during the FreeLibrary() call for this DLL.

While checking this in the debugger, DLL_DETACH_PROCESS seems to be handled successfully (no code is executed when handling that message). The crash occurs after (or while) leaving the code from the Entry Point.

If I continue to Step In, I end up in a header file called utilcls.h, which seems to be one of the Borland C Builder 6 header files. I believe the template code therein is related to the COM object being torn down. An Unbind() call passes, and this is the last line of code I can step to before the crash.

If I use the debugger's CPU window and continue stepping, everything that remains appears to be related to freeing memory before the crash comes, but it's quite a lot of CPU-stepping to get there.

The crash raises APPCRASH with exception 0xc0000602, referring back to Combase.dll.

Simply not calling FreeLibrary for that DLL allows the application to close successfully, but my assumption is that the FreeLibrary call is important.

The COM object is released by the data-sharing application prior to the FreeLibrary() call, which allows that application to close. My assumption at the moment is that some of this unlinking is happening differently in the newer operating system and this is causing the crash, but I don't know how to be find out for sure.

My question(s):

  • if it's obvious to someone else who better knows what they're doing, what causes this crash?

  • what are the next steps in trying to debug this? I've exhausted my knowledge of the debugging environment I'm using and don't know COM or DLLs well enough to know what the next question to ask is.


Some of the debugger output RbMm requested:

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)

At that point, the stack looks roughly like this:

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

Working on the rest now, but my guess is I need to figure out how to do the cleanup properly on the COM object? Maybe in response to DLL_DETACH_PROCESS?

解决方案

The crash raises APPCRASH with exception 0xc0000602, referring back to Combase.dll

combase.dll used 0xc0000602 (STATUS_FAIL_FAST_EXCEPTION) code only from

void CrashProcessWithWERReport();

(which called RaiseFailFastException with this code)

CrashProcessWithWERReport called only from DecrementMTAUsageHelper on 2 conditions - CoDecrementMTAUsage called more times than CoIncrementMTAUsage or (and i almost sure in this reason) DecrementMTAUsageHelper called when calling thread hold Loader critical section - so while DLL loading or unloading process. from MSDN

Don't call CoDecrementMTAUsage during process shutdown or inside dllmain. You can call CoDecrementMTAUsage before the call to start the shutdown process.

so my guess - some code call CoDecrementMTAUsage in your DLL unloading process (when you call FreeLibrary)

your DLL can not direct call CoIncrementMTAUsage / CoDecrementMTAUsage because this new API, exist begin from win 8 (also check your code on win 8.1 - i think also will be crash), but this api can be indirect called from other system components.

i can assume that your DLL not direct free some used resources or you call FreeLibrary when DLL still holding some resources (so you call FreeLibrary without proper cleanup calls fro DLL) and as result this resources begin free (CoDecrementMTAUsage) in unloading process

what are the next steps in trying to debug this?

you need use debugging with symbols files (say with winDbg). set breakpoints at DecrementMTAUsageHelper, CoDecrementMTAUsage and may be CoIncrementMTAUsage - are i right that call toRtlIsCriticalSectionLockedByThread return TRUE (this api called from begin of DecrementMTAUsageHelper).

in any case post the thread call stack at DecrementMTAUsageHelper call point(just before crash) and possible on CoIncrementMTAUsage too

---------------------- EDIT -------------------------

by view stack trace visible that your DLL call DestroyWindow from DllMain.

apphelp!DWM8AND16BitHook_DestroyWindow

this is the bug just by 2 reasons - at first - read this article -

The thread that gets the DLL_PROCESS_DETACH notification is not necessarily the one that got the DLL_PROCESS_ATTACH notification. You can't do anything with thread affinity in your DLL_PROCESS_ATTACH or DLL_PROCESS_DETACH handler since you have no guarantee about which thread will be called upon to handle these process notifications. The classic example of this, which I'm told the Developer Support team run into with alarming frequency, is a DLL that creates a window in its DLL_PROCESS_ATTACH handler and destroys it in its DLL_PROCESS_DETACH handler.

but your crash here by another reason, not listed in article - DllMain have many restrictions, what can not be called inside it. despite DestroyWindow not direct listed here, but as show your case - this is illegal call (even if we called on same thread, on which this window was created) - while your window is destroyed imm32.CtfImmNotify(msctf!TF_Notify) is called

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

and as result CoUninitialize is called from DllMain !

from MSDN

do not call CoInitialize, CoInitializeEx, or CoUninitialize from the DllMain function.

here inside FINAL CoUninitialize called DecrementMTAUsage which determinate that we inside loader lock by call RtlIsCriticalSectionLockedByThread and CrashProcessWithWERReport called.

Solution ?

of course the best is fix DLL, but if this is impossible - think next "hack" will be work

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

with this CoUninitialize of course anyway will be called from imm32!CtfImmCoUninitialize but this will be NOT FINAL Uninitialize and as result DecrementMTAUsage will be not called

这篇关于在Win 10中卸载DLL时调试崩溃,但不是Win 7的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆