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

时间:2009-06-11 10:22:16

标签: .net c++ exception-handling mixed-mode

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

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

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

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

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

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

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

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

修复方法是执行以下操作:

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

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

  3. 即。它曾经是

      int APIENTRY _tWinMain(HINSTANCE hInstance,
                           HINSTANCE hPrevInstance,
                           LPTSTR    lpCmdLine,
                           int       nCmdShow)
    

    现在是

    int main(array<System::String ^> ^args)
    

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

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

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

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

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

    我是否应该向微软报告(有人会关心;-))?

2 个答案:

答案 0 :(得分:2)

我并不感到惊讶。

如果您在托管代码中编写顶级main,任何将冒出的托管异常都将由托管代码处理。与C#中的相同。这是因为操作系统不直接调用您的int main(array<System::String ^> ^args)函数。这是由托管代码完成的,托管代码是.net的一部分,或者在编译时插入。这是通知和处理任何转义.net异常的代码。

如果您编写自己的本地WinMain,则没有.net代码调用此函数,因此也没有托管代码能够处理托管异常。当抛出托管异常时,它在操作系统中显示为任何其他本机窗口异常。

请注意,这并不是对main / WinMain的调用方式的准确描述。根据您的问题描述和一点经验,这是一个有根据的猜测。我遗漏了一些我所知道的细节,也可能还有我完全不知道的细节。但我很确定这是故事的精髓。

答案 1 :(得分:0)

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

我不知道更改入口点(main vs WinMain)或子系统(win32 vs console vs none)的方式或原因应该如何影响这一点。我不是说它没有,只是因为它不是根本原因。 (旁白:我不知道子系统是什么:“未设置”意味着。我认为子系统是链接器知道并记入可执行文件的东西,必须设置为某些东西 ,如果入口点是main(),它可能设置为控制台。控制台子系统应用程序仍然可以通过所有常规方式与GUI交互,但它们也始终连接控制台窗口,如果从外部启动它们控制台窗口,他们创建一个,所以这通常不是一个很好的选择,你将发送给除了你自己以外的任何人。)

在Win32 API级别,此类行为由以下内容控制:

也许是旧的WinMain()中的某些东西,或从那里调用的东西,或者在WinMain之前运行的一些.NET托管goo(即使在原生C / C ++中,使用默认的MSVC设置,WinMain不是真正的入口点就链接器而言 - 有一个由MSVC的C运行时库提供的包装函数,它是真正的入口点,它调用WinMain - 我希望.NET做同样的事情,但更多的是),调用SetUnhandledExceptionFilter (NULL),并且在您的固定版本中不再发生。

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

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