2016-12-07 49 views
1

この問題はまだ解決していないと確信していますが、ここに私が見ているものは何ですか、だと思います。Win 10ではDLLをアンロードしているが、Win 7ではDLLをアンロードしている間にクラッシュをデバッグする

私は、C++ DLLを読み込むWin32プログラムを主にC言語で作成しています。そのDLLは、COMオブジェクトを介してCプログラムから別のアプリケーションにデータを渡します。おそらくDLL自体によってインスタンス化されるでしょう。これらのすべては明らかに少なくともWindows XPとWindows 7(おそらくWin95とWin98、このインターフェースがいつ導入されたかを調べるためにコード履歴を詳しく調べる必要があるでしょう)ではうまくいきましたが、Windows 10ではプログラムがクラッシュしますこのDLLのFreeLibrary()呼び出し中に発生します。

デバッガでこれを確認している間、DLL_DETACH_PROCESSは正常に処理されたようです(そのメッセージを処理するときにコードは実行されません)。クラッシュは、エントリポイントからコードを離した後(またはその間)に発生します。

ステップインに進むと、私は、utilcls.hというヘッダーファイルになります。これは、Borland C Builder 6ヘッダーファイルの1つと思われます。私はその中のテンプレートコードは、COMオブジェクトが解体されることに関連していると思います。 Unbind()コールが成功すると、これはクラッシュ前にステップできるコードの最後の行です。

デバッガのCPUウィンドウを使用してステッピングを続けると、クラッシュが発生する前にすべてのメモリが解放されているように見えますが、そこに到達するにはかなりのCPUステッピングがあります。

クラッシュは、Combase.dllを参照して、例外0xc0000602でAPPCRASHを発生させます。

単にそのDLLのFreeLibraryを呼び出さないと、アプリケーションは正常に終了しますが、私の前提はFreeLibrary呼び出しが重要であるということです。

COMオブジェクトは、FreeLibrary()呼び出しの前にデータ共有アプリケーションによって解放され、そのアプリケーションを閉じることができます。現時点で私の前提は、新しいオペレーティングシステムでは、このリンク解除のいくつかが違って起こっていることです。これがクラッシュの原因になっていますが、どうやって確認するのか分かりません。

私の質問(複数可):それはより良いが、彼らがやっていることを知っている他の誰かに明らかだ場合

  • 、このクラッシュの原因でしょうか?

  • これをデバッグしようとする次のステップは何ですか?私は私が使用しているデバッグ環境に関する知識を尽くしており、次の質問が何であるかを知るのに十分な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

私は自己応答を更新します.DownMainから 'DestroyWindow'が呼び出されたので簡単にクラッシュします。これが修正できない場合 - 私は最後にいくつかのハックを提案する – RbMm

+0

これは、次のカップル日のために他のタスクにバックシートを取るつもりですが、私はあなたが問題を解決するために十分に私に言ったと思います。私が知っていれば私は報告するでしょう。ありがとうございました。 –

答えて

2

クラッシュは、 Combaseを参照して、例外0xc0000602でAPPCRASHを発生させます。複数回呼び出さCoDecrementMTAUsage - DLL

combase.dllは、2つの条件でのみDecrementMTAUsageHelperから呼び出さ

void CrashProcessWithWERReport();

(このコードで RaiseFailFastExceptionとも呼ばれる)

CrashProcessWithWERReportから0xc0000602STATUS_FAIL_FAST_EXCEPTION)コードを使用しましたCoIncrementMTAUsageまたは(と私はほとんどこの理由で確信していますDecrementMTAUsageHelperスレッドホールドのLoaderクリティカルセクションを呼び出すときに呼び出されるため、DLLのロードまたはアンロード処理中に発生します。 MSDN

から

は、プロセスのシャットダウン中またはたDllMain 内CoDecrementMTAUsageを呼び出さないでください。シャットダウンプロセスを開始する呼び出しの前にCoDecrementMTAUsageを呼び出すことができます。

ので、私の推測 - あなたのDLLのアンロード処理中にいくつかのコードの呼び出しCoDecrementMTAUsageを(あなたがFreeLibraryを呼び出す)

あなたのDLLことはできませんダイレクトコールCoIncrementMTAUsage/CoDecrementMTAUsageこの新しいAPIので、また(勝利8から始める存在します勝利8.1であなたのコードをチェックしてください - 私もクラッシュされると思う)が、このAPIは間接的に他のシステムコンポーネントから呼び出すことができます。

私はあなたのDLLは、DLLは、まだいくつかのリソースを保持している(ので、あなたはDLLあちこちに適切なクリーンアップコールなしFreeLibraryを呼び出す)と、このリソースがアンロード中(CoDecrementMTAUsage)自由を開始した結果として際に、いくつかの使用リソースまたはあなたがFreeLibraryを呼び出すない直接ではないと仮定することができますプロセス

これをデバッグしようとする次のステップは何ですか?

シンボルファイルを使用してデバッグする必要があります(たとえばwinDbg)。 DecrementMTAUsageHelperCoDecrementMTAUsageにブレークポイントを設定し、CoIncrementMTAUsageになる可能性があります。RtlIsCriticalSectionLockedByThread return TRUE(このAPIはDecrementMTAUsageHelperの先頭から呼び出されます)を呼び出してください。いずれにしても

すぎCoIncrementMTAUsageDecrementMTAUsageHelperコール(ちょうどクラッシュ前)時点と可能でスレッドコールスタックを投稿

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

ビューでは、スタックトレースが表示され、DLLの呼び出しでDestroyWindowがDllMainから呼び出されます。

apphelp!DWM8AND16BitHook_DestroyWindow

これはちょうど2つの理由により、バグがある - 最初に - this article読む -

DLL_PROCESS_DETACH通知を取得するスレッドがない 必ずしもDLL_PROCESS_ATTACH通知を得たものです。 は、DLL_PROCESS_ATTACHまたは DLL_PROCESS_DETACHハンドラでスレッドアフィニティを持つものは実行できません。これらのプロセス通知を処理するために呼び出されるスレッドについては、 スレッドが保証されないためです。私がデベロッパーサポートチームに言いました のこの古典的な例は、 を警告頻度で実行していますが、その DLL_PROCESS_ATTACHハンドラーにウィンドウを作成し、DLL_PROCESS_DETACH ハンドラーで破棄するDLLです。

が、記事に記載されていない別の理由によるここにあなたのクラッシュ - のDllMainその中に何を呼び出すことはできません、restrictions多くを持っています。 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のを呼び出すことはありません。ここ

我々コールRtlIsCriticalSectionLockedByThreadCrashProcessWithWERReportによってローダーロック内部で呼び出され、確定DecrementMTAUsage呼ばCoUninitialize
FINAL 内部。

解決策?当然の

最高のDLLを修正するが、これが不可能な場合である - 「ハック」の仕事はもちろん、このCoUninitialize

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

はとにかくimm32!CtfImmCoUninitializeから呼び出されますされますが、これはなり、次を考えます未初期化と結果としてDecrementMTAUsageは呼び出されません。

+0

あなたが正しいように見えます。今すぐWinDbgを使うことを学ぶ - RtlIsCriticalSectionLockedByThread'からの戻り値を追加しました。 –

+0

@ErinAnne - このコードを試してみる - \t 'HRESULT hr = CoInitialize(0); \t FreeLibrary(hDLL); \t if(0 <= hr)CoUninitialize(); ' - この場合はクラッシュしません。 – RbMm

+1

最後にハックを試すことができる場所に到達しました。できます。そこにはクラッシュはありません。 –

関連する問題