.net - CLR 在非 CLR 创建的线程中托管异常处理

标签 .net c++ exception-handling clr clr-hosting

问题:

线程中的未处理异常进入 CLR来自非托管代码不会触发“正常”未处理的异常 CLR 处理。

在下面的代码中,使用 C++ 调用 CSSimpleObject.GetstringLength()

  • “1”在调用线程(非CLR创建的线程)中抛出异常,
  • “2”在 new Thread()(CLR 创建的线程)中引发异常。

如果是“1”

  • CurrentDomain_UnhandledException() 永远不会被调用。
  • Application Domain并且该进程将保持加载并运行,您只会收到 FAILED。

情况“2”(预期行为)

  • CurrentDomain_UnhandledException() 被调用。
  • 进程被杀死。

问题:

必须做什么才能获得“正常”行为?

示例代码:

以下代码基于 Visual Studio 2010“CppHostCLR”代码示例 来自“all interop and fusion samples”。

运行时主机(C++):

PCWSTR pszStaticMethodName = L"GetStringLength";
PCWSTR pszStringArg = L"1";
//PCWSTR pszStringArg = L"2";

hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(pszAssemblyPath,
    pszClassName, pszStaticMethodName, pszStringArg, &dwLengthRet);
if (FAILED(hr))
{
    wprintf(L"Failed to call GetStringLength w/hr 0x%08lx\n", hr);
    goto Cleanup;
}

托管代码 (C#):

public class CSSimpleObject
{
    public CSSimpleObject()
    {
    }
    //------8<----------
    public static int GetStringLength(string str)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        switch (str)
        {
            case "1":
                throw new Exception("exception in non-CLR-created thread");
            case "2":
                Thread thread = new Thread(new ThreadStart(WorkThreadFunction));
                thread.Start();
                break;
        }
        return str.Length;

    }
    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine("CurrentDomain_UnhandledException:" + e.ToString());
        Console.ReadKey();
    }
    public static void WorkThreadFunction()
    {
        throw new Exception(""exception in CLR-created thread"");
    }

研究至今:

MSDN 最初暗示非 CLR 创建的线程中未处理的异常应该或多或少“自然地”表现 - 请参阅“Exceptions in Managed Threads

The common language runtime allows most unhandled exceptions in threads to proceed naturally. In most cases this means that the unhandled exception causes the application to terminate."

“大多数”意味着在 CLR 创建的内部线程中,线程中止和应用程序域卸载异常的处理方式不同。在非 CLR 线程中

"they proceed normally, resulting in termination of the application."

进一步的研究使我找到了“Unhandled Exception Processing In The CLR”,在那里我发现了以下内容:

"if the exception was not handled ... in the managed method, the exception would exit the CLR but continue to propagate up the stack as a native SEH exception (managed exceptions are represented as native SEH exceptions) ... The OS unhandled exception filter (UEF) mechanism may not always result in triggering the CLR's unhandled exception processing. Under normal circumstances, this will work as expected and the CLR's unhandled exception processing will be triggered. However, in certain instances this may not happen."

上面的代码有什么问题或者怎么改才能触发CLR的未处理异常处理?

更新(2011-05-31):

我刚刚发现了一个旧的错误报告,“当从非托管调用托管代码并引发异常时,未调用 UnhandledExceptionEventHandler - http://tinyurl.com/44j6gvu”,Microsoft 确认这是一个“错误”行为:

Thank you for taking the time to report this problem. The behavior is indeed a bug caused by the CLR execution engine and the CRT competing for the UnhandledExceptionFilter. The architecture of the CLR has been revised in the 4.0 version supporting this scenario.

更新(2011-06-06):

为什么正确地做到这一点很重要?

  • 如果您正在创建一个托管环境,您的开发人员希望在异常处理中保持一致的行为
  • 除非有办法在 native 线程中触发“正常的 CLR 异常处理”,否则这意味着您必须始终将执行转移到托管线程(例如在线程池中排队)
  • 仍有一小部分代码将执行从 native 线程转移到托管线程......必须捕获所有异常并以不同方式处理这种情况

注意: 通过 SetActionOnFailure() 更改 CLR 行为会使事情变得更糟,因为它最终会掩盖原始异常(即,而不是 out of内存你最终看到 threadAborts - 不知道原始错误凸轮来自哪里)!

最佳答案

更新意味着您可能在这里找到了解决方案。但是,它的解决方案并非在所有情况下都有效,因此这里有更多信息。

如果您更喜欢 CLR 未处理异常行为,则将其设为最外层程序并仅调用 native 代码以执行特定功能。这将确保 CLR 控制未处理的异常过滤器。

如果你想保留你当前的结构并且你的 C++ 代码很小,你可以完全停止使用 CRT(这会让你失去一堆有用的东西,包括静态构造函数和 C++ 异常处理)。这将确保 CLR 获得未处理的异常过滤器。

当然,您可以简单地自己调用 SetUnhandledExceptionFilter 并获得您想要的行为。

但是,我认为在这种情况下,最好的建议是在任何线程的调用堆栈上放置一个带有 catch block 的实际函数,以便在发生异常时执行某些操作,而不是依赖 UEF 机制——因为在组件系统的上下文中,由于多个用户竞争它,它总是很脆弱。

马丁

关于.net - CLR 在非 CLR 创建的线程中托管异常处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6124631/

相关文章:

c# - Socket.EndAccept() 错误 10054

c# - 如何保护 .Net exe 免遭反编译/破解

c++ - 卡住使用 QTcpSocket 发送大文件

java - 处理一些运行时 HTTP 异常的好的做法是什么?

python - 为什么 "except Exception"没有捕捉到 SystemExit?

c# - DevExpress:将自定义栏添加到表单

C++ 删除对象作为引用

c++ - c++ 中数据类型的动态更改?

node.js - Nodejs + 要求 instanceof 行为

.net - Fiddler TLS 版本详细信息