【问题标题】:Access Violation issue with unhandled managed Exceptions in managed C++ .NET application托管 C++ .NET 应用程序中未处理的托管异常的访问冲突问题
【发布时间】:2010-11-02 02:17:39
【问题描述】:

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

也许其他人可能会提出原因?

无论如何,我正在开发一个用托管 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)

现在是

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

[为什么我要使用这个奇怪的 _tWinMain?这是多年前 Visual Studio .NET IDE 在您创建示例混合模式 Windows 应用程序时生成的。它一直运行良好(直到现在),所以我从不费心改变它。 _tWinMain 只是 WinMain 的一个宏]

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

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

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

难道只是我使用的是旧风格的 WinMain 而不是新的main(array &lt;String^&gt;^)

我是否应该向 Microsoft 报告此情况(有人会关心 ;-))吗?

【问题讨论】:

  • 你可以把解决方案拿出来,让它成为答案并接受它(这是自学的东西)

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


【解决方案1】:

我并不感到惊讶。

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

如果您编写自己的本机 WinMain,则没有 .net 代码调用此函数,因此也没有托管代码能够处理托管异常。当一个托管异常被抛出时,它在操作系统看来就像任何其他本机 Windows 异常一样。

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

【讨论】:

  • 但是过去使用 WinMain DID 总是可以的。事实上,正如我所说,这是 Visual Studio 2008 之前的规范方法 - 这就是 VS 生成示例主对象的方式。
  • @John:对....现在我看到你确实提到了这一点。在旧的 Visual Studio(哪个版本?)和 2008 上检查调试器中的调用堆栈会很有趣。
【解决方案2】:

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

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

在 Win32 API 级别,这种行为受以下因素控制:

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

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

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

【讨论】:

    猜你喜欢
    • 2021-07-13
    • 2015-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多