DebugBreak の動作は、アンマネージド アプリケーションと混合 (アンマネージド + マネージド) アプリケーションで異なりますか?
-
26-09-2019 - |
質問
次の単純なソースを考えてみましょう (test.cpp という名前を付けます)。
#include <windows.h>
void main()
{
DebugBreak();
}
次のコマンドを使用してこれをコンパイルし、リンクします。
cl /MD /c test.cpp
link /debug test.obj
TEST.EXE を実行すると (64 ビット Windows 7 システム上で)、次のダイアログが表示されます。
ここで、次のソース ファイルを追加します (test2.cpp という名前を付けます)。
void hello()
{
}
次のように、これを最初のソースとコンパイルしてリンクします。
cl /MD /c test.cpp
cl /MD /c /clr test2.cpp
link test.obj test2.obj
hello 関数を呼び出しているわけではなく、リンクしているだけであることに注意してください。
次に、TEST.EXE を再度実行します (同じ 64 ビット Windows 7 システム上で)。上記のダイアログの代わりに、次のダイアログが表示されます。
どうやら、.Net Framework でリンクすると、DebugBreak の動作が異なるようです。どうしてこれなの?また、古い DebugBreak の動作を再び戻すにはどうすればよいでしょうか?これはおそらく Windows 7 または 64 ビット固有の動作でしょうか?
DebugBreak を使用する理由を明確にするための余談:カスタムのアサート フレームワーク (John Robbin の『Windows アプリケーションのデバッグ』の本にある SuperAssert のようなもの) があり、問題が発生した場合に開発者がデバッガにジャンプできる (または新しいデバッガを開くことができる) ように DebugBreak 関数を使用しています。現在は単純なポップアップのみが表示され、デバッガにジャンプすることはできなくなりました。
別の解決策として、ゼロ除算や無効なアドレスへの書き込みを実行することもできますが、これはあまりクリーンな解決策ではないと思います。
編集:これは 2 番目のテスト (単純なダイアログ) のコール スタックです。
ntdll.dll!_NtRaiseHardError@24() + 0x12 bytes
ntdll.dll!_NtRaiseHardError@24() + 0x12 bytes
clrjit.dll!Compiler::compCompile() + 0x5987 bytes
clr.dll!RaiseFailFastExceptionOnWin7() + 0x6b bytes
clr.dll!WatsonLastChance() + 0x1b8 bytes
clr.dll!InternalUnhandledExceptionFilter_Worker() + 0x29c bytes
clr.dll!InitGSCookie() + 0x70062 bytes
clr.dll!__CorExeMain@0() + 0x71111 bytes
msvcr100_clr0400.dll!@_EH4_CallFilterFunc@8() + 0x12 bytes
msvcr100_clr0400.dll!__except_handler4_common() + 0x7f bytes
clr.dll!__except_handler4() + 0x20 bytes
ntdll.dll!ExecuteHandler2@20() + 0x26 bytes
ntdll.dll!ExecuteHandler@20() + 0x24 bytes
ntdll.dll!_KiUserExceptionDispatcher@8() + 0xf bytes
KernelBase.dll!_DebugBreak@0() + 0x2 bytes
test_mixed.exe!01031009()
これは、最初のテスト (「閉じる」と「デバッグ」を選択したダイアログ) のコール スタックです。
ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15 bytes
ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15 bytes
kernel32.dll!_WaitForMultipleObjectsExImplementation@20() + 0x8e bytes
kernel32.dll!_WaitForMultipleObjects@16() + 0x18 bytes
kernel32.dll!_WerpReportFaultInternal@8() + 0x124 bytes
kernel32.dll!_WerpReportFault@8() + 0x49 bytes
kernel32.dll!_BasepReportFault@8() + 0x1f bytes
kernel32.dll!_UnhandledExceptionFilter@4() + 0xe0 bytes
ntdll.dll!___RtlUserThreadStart@8() + 0x369cc bytes
ntdll.dll!@_EH4_CallFilterFunc@8() + 0x12 bytes
ntdll.dll!ExecuteHandler2@20() + 0x26 bytes
ntdll.dll!ExecuteHandler@20() + 0x24 bytes
ntdll.dll!_KiUserExceptionDispatcher@8() + 0xf bytes
KernelBase.dll!_DebugBreak@0() + 0x2 bytes
test_native.exe!00af1009()
違いは ntdll.dll!Executehandler2@20 から始まります。.net 以外のアプリケーションでは、 ntdll.dll!@_EH4_CallFilterFunc
. 。.net アプリケーションでは呼び出しがあります clr.dll!__except_handler4
.
解決
次のページで解決策を見つけました。 http://www.codeproject.com/KB/debug/DebugBreakAnyway.aspx.
DebugBreak を記述するだけではなく、次のように __try/__Except 構造の間に DebugBreak 呼び出しを埋め込む必要があります。
__try
{
DebugBreak();
}
__except (UnhandledExceptionFilter(GetExceptionInformation()))
{
}
どうやら、UnhandledExceptionFilter 関数はデフォルトで DebugBreak 例外を処理しますが、混合モードのアプリケーションでは無効になるようです。
これで、元のダイアログが再び表示されます。