托管C++ .NET应用程序中未处理的托管异常访问冲突问题

Joh*_*ohn 15 .net c++ mixed-mode exception-handling

这实际上是一个已经解决的问题,但它太深奥了,我以为我会为其他用户分享它.

也许其他人可能会提出理由?

无论如何,我正在研究用托管C++编写的"混合模式".NET应用程序,但是它与现有的本机库有很多链接.

问题是,未处理的托管异常最终成为Win32访问冲突.我的意思是,不是显示漂亮的.NET对话框,而是使用未处理的托管异常,而不是我将获得旧样式"未处理的win32异常发生在......"消息.

这是有趣的事情:如果我在调试器中启动应用程序,则会正确地拾取抛出的托管异常.即调试器向我显示该行.

但是,正常执行时,它将变为此访问冲突.在那一点附加调试器将产生很少的有用信息(它甚至不会显示合理的堆栈跟踪).

所以,对我而言,它表明在未处理的托管异常到达异常处理程序之前,本机代码中正在发生某些事情.

所以无论如何,我设法通过将我的项目与Visual Studio 2008生成的干净的新C++托管项目进行比较来解决问题.

解决方法是执行以下操作:

  1. 将/ SUBSYSTEM标志(项目属性 - >链接器 - >系统 - >子系统)/SUBSYSTEM:WINDOWS 更改为 "未设置"

  2. 从使用旧样式WinMain()切换到使用新样式main().

也就是过去

  int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
Run Code Online (Sandbox Code Playgroud)

它现在是

int main(array<System::String ^> ^args)
Run Code Online (Sandbox Code Playgroud)

[为什么我使用这个奇怪的_tWinMain?这是多年前在创建示例混合模式Windows应用程序时由Visual Studio .NET IDE生成的.它一直很好(直到现在)所以我从来没有打扰过它._tWinMain只是WinMain的一个宏]

我做了这个改变,问题就消失了.未处理的.NET异常现在被正确捕获,因此我现在可以实际调试它们.

我还对干净的示例C++应用程序进行了相反的更改,并证明了它是原因.

所以,我真正的问题是,到底发生了什么?

是不是只使用了旧式WinMain而不是新款main(array <String^>^)

我是否应该向微软报告此事(任何人都会关心;-))?

met*_*att 0

我没有 .NET 或托管代码方面的经验,但我在本机方面有相当多的经验。

我不明白更改入口点(main vs WinMain)或子系统(win32 vs console vs none)如何或为何会影响这一点。我并不是说没有,只是说这不是根本原因。(旁白:我不知道子系统:“未设置”是什么意思。我认为子系统是链接器知道并标记到可执行文件中的东西,必须设置为某些东西,并且如果入口点是main(),它可能设置为控制台。控制台子系统应用程序仍然可以以所有正常方式与 GUI 交互,但它们也始终附加一个控制台窗口,如果您从控制台窗口外部启动它们,它们会创建一个控制台窗口,因此对于您要向除您自己之外的任何人发送的应用程序来说,这通常不是一个好的选择。)

在 Win32 API 级别,这种行为由以下方式控制:

也许是旧的 WinMain() 中的某些内容,或者从那里调用的某些内容,或者是在 WinMain 之前运行的某些 .NET 托管内容(即使在本机 C/C++ 中,使用默认的 MSVC 设置,WinMain 也不是真正的入口点链接器所关心的——MSVC 的 C 运行时库提供了一个包装函数,它是真正的入口点,它调用 WinMain——我希望 .NET 做同样的事情,但更重要),正在调用 SetUnhandledExceptionFilter(NULL) ,而在您的固定版本中,这种情况不再发生。

如果您知道哪个函数负责建立“带有未处理的托管异常的良好 .NET 对话框”,您可以将其传递给 SetUnhandledExceptionFilter(),但在非托管代码中调用它可能不是一个好主意。

所有这一切的另一种理论:“旧式“未处理的 win32 异常发生在...””对话框实际上说的是访问冲突,并且是这个意思吗?.NET 异常处理程序也可能启动,然后由于某种原因实际上崩溃了;如果是这种情况,应该可以对其进行调试,尽管这需要更多信息。