非托管和混合(非托管+托管)应用程序之间的 DebugBreak 行为有何不同?

发布于 2024-09-18 04:35:41 字数 2988 浏览 6 评论 0原文

获取以下简单源代码(将其命名为 test.cpp):

#include <windows.h>

void main()
{
DebugBreak();
}

使用以下命令编译并链接它:

cl /MD /c test.cpp
link /debug test.obj

如果现在运行 TEST.EXE(在 64 位 Windows 7 系统上),您将看到以下对话框:

非托管应用程序中的 DebugBreak

现在添加以下源文件(将其命名为 test2.cpp):

void hello()
{
}

并将其与第一个源一起编译和链接,例如this:

cl /MD /c       test.cpp
cl /MD /c /clr  test2.cpp
link test.obj test2.obj

请注意,我们甚至没有调用 hello 函数,我们只是将其链接进来。

现在再次运行 TEST.EXE(在同一个 64 位 Windows 7 系统上)。您得到的不是上面显示的对话框,而是:

混合模式应用程序中的 DebugBreak

显然,.Net 框架中的链接使得DebugBreak 的行为有所不同。 这是为什么呢?我怎样才能再次恢复旧的 DebugBreak 行为? 这可能是 Windows 7 或 64 位特有的行为吗?

附带说明一下,我想使用 DebugBreak 的原因:我们有一个自定义断言框架(类似于 John Robbin 的《调试 Windows 应用程序》书中的 SuperAssert),并且我使用 DebugBreak 函数,以便开发人员可以跳转到调试器(或打开一个新的调试器)如果有问题。现在只有简单的弹出窗口,无法再跳转到调试器。

作为替代解决方案,我可以执行除以零或写入无效地址,但我发现这是一个不太干净的解决方案。

编辑: 这是第二个测试中的调用堆栈(简单对话框):

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

Take the following simple source (name it test.cpp):

#include <windows.h>

void main()
{
DebugBreak();
}

Compile and link this using the following commands:

cl /MD /c test.cpp
link /debug test.obj

If TEST.EXE is now run (on a 64-bit Windows 7 system), you get the following dialog:

DebugBreak in unmanaged application

Now add the following source file (name it test2.cpp):

void hello()
{
}

And compile and link this together with the first source, like this:

cl /MD /c       test.cpp
cl /MD /c /clr  test2.cpp
link test.obj test2.obj

Notice that we didn't even call the hello-function, we just linked it in.

Now run TEST.EXE again (on the same 64-bit Windows 7 system). Instead of the dialog shown above, you get this:

DebugBreak in mixed-mode application

Apparently, linking in the .Net framework makes DebugBreak behave differently.
Why is this? And how can I get the old DebugBreak behavior back again?
Is this possibly a Windows 7 or 64-bit specific behavior?

A side-remark to make clear why I want to use DebugBreak: we have a custom assert-framework (something like the SuperAssert from John Robbin's Debugging Windows Applications book), and I use the DebugBreak function so the developer can jump into the debugger (or open a new debugger) if there is a problem. Now there is only the simple popup and no possibility to jump to the debugger anymore.

As an alternative solution I could perform a divide-by-zero or a write to invalid address, but I find this a less clean solution.

EDIT:
This is the call stack in the second test (the simple dialog):

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()   

This is the call stack in the first test (dialog with choices "close" and "debug"):

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()  

The difference starts in ntdll.dll!Executehandler2@20. In a non-.net application it calls ntdll.dll!@_EH4_CallFilterFunc. In a .net application is calls clr.dll!__except_handler4.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

秋心╮凉 2024-09-25 04:35:41

我在以下页面找到了解决方案: http://www.codeproject.com/KB /debug/DebugBreakAnyway.aspx

您必须在 __try/__except 构造之间嵌入 DebugBreak 调用,而不是仅仅编写 DebugBreak,如下所示:

__try
   {
   DebugBreak();
   }
__except (UnhandledExceptionFilter(GetExceptionInformation()))
   {
   }

显然,UnhandledExceptionFilter 函数默认处理 DebugBreak 异常,这似乎在混合模式应用程序中被否决。

现在您再次获得原始对话框。

I found the solution on the following page: http://www.codeproject.com/KB/debug/DebugBreakAnyway.aspx.

Instead of just writing DebugBreak, you have to embed the DebugBreak call between a __try/__except construction, like this:

__try
   {
   DebugBreak();
   }
__except (UnhandledExceptionFilter(GetExceptionInformation()))
   {
   }

Apparently, the UnhandledExceptionFilter function handles the DebugBreak exception by default, which seems to be overruled in a mixed-mode appliation.

Now you get the original dialog back again.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文