托管 clr 并捕获线程异常

2024-04-07

我正在尝试编写一个可以加载托管插件的插件系统。如果出现任何异常,主机应该能够卸载插件。 对于我的 poc,我有一个 C# 示例代码库,它会抛出这样的异常...

 public static int StartUp(string arguments)
 {
       Console.WriteLine("Started exception thrower with args {0}", arguments);
       Thread workerThread = new Thread(() => 
            {
                Console.WriteLine("Starting a thread, doing some important work");
                Thread.Sleep(1000);
                throw new ApplicationException();
            }
         );
         workerThread.Start();
         workerThread.Join();
         Console.WriteLine("this should never print");
        return 11;
    }

然后我有像这样的本机 win32 控制台应用程序..

int _tmain(int argc, _TCHAR* argv[])
{
    ICLRMetaHost *pMetaHost       = NULL;
    HRESULT hr; 
    ICLRRuntimeInfo *runtimeInfo = NULL;    
    __try
    {
        hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost);
        hr = pMetaHost->GetRuntime(L"v4.0.30319",IID_ICLRRuntimeInfo,(LPVOID*)&runtimeInfo);
        ICLRRuntimeHost *runtimeHost  = NULL;
        hr = runtimeInfo->GetInterface(CLSID_CLRRuntimeHost,IID_ICLRRuntimeHost, (LPVOID*)&runtimeHost);    
        ICLRControl* clrControl = NULL;
        hr = runtimeHost->GetCLRControl(&clrControl);
        ICLRPolicyManager *clrPolicyManager = NULL;
        clrControl->GetCLRManager(IID_ICLRPolicyManager, (LPVOID*)&clrPolicyManager);
        clrPolicyManager->SetDefaultAction(OPR_ThreadAbort,eUnloadAppDomain);   
        hr = runtimeHost->Start();
        DWORD returnVal = NULL;         
        hr = runtimeHost->ExecuteInDefaultAppDomain(L"ExceptionThrower.dll",L"ExceptionThrower.MainExceptionThrower",L"StartUp",L"test",&returnVal);        
        runtimeHost->Release();
    }
    __except(1)
    {
        wprintf(L"\n Error thrown %d",e);
    }
    return 0;
}

问题是,如果我使用上面的代码,主机将完成运行托管代码(“这不应该打印”行将最终打印) 如果我删除 clrPolicyManager->SetUnhandledExceptionPolicy(eHostDeterminedPolicy),那么主机进程将崩溃。

在非托管主机中可以做些什么来优雅地从运行时删除错误的应用程序并继续工作吗?


首先,如果您想使用上面的代码防止应用程序崩溃,您需要使用设置未处理异常过滤器 http://msdn.microsoft.com/en-us/library/windows/desktop/ms680634%28v=vs.85%29.aspx, 像这样:

LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionInfo)
{
    // do something useful
    return EXCEPTION_EXECUTE_HANDLER; // prevent crash
}

int _tmain(int argc, _TCHAR* argv[])
{
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
        ...
}

但这可能不是您真正想要的。一种解决方案(我相信由 Poity 提出)是创建一个中间 AppDomain,它可以轻松捕获所有未处理的异常。您可以在 C# 中执行此操作,如下所示:

public class PluginVerifier
{
    public static int CheckPlugin(string arguments)
    {
        AppDomain appDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString());
        appDomain.UnhandledException += AppDomainUnhandledException;
        object obj = appDomain.CreateInstanceAndUnwrap("ExceptionThrower", "ExceptionThrower.MainExceptionThrower");
        object ret = obj.GetType().InvokeMember("Startup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, obj, new object[] { arguments });
        AppDomain.Unload(appDomain);
        return (int)ret;
    }

    private static void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        AppDomain appDomain = (AppDomain)sender;
        // the following will prevent "this should never print" to happen
        AppDomain.Unload(appDomain);
    }
}

然而,为了使其能够工作,您需要对插件类进行两处更改:

  • 它们必须从 MarshalByRefObject 派生
  • 插件方法不能是静态的(静态方法调用不经过AppDomain过滤器)

所以你的类应该这样写:

public class MainExceptionThrower: MarshalByRefObject
{
    public int StartUp(string arguments)
    {
    ...
    }
 }

如果这样做,您可以删除对 SetUnhandledExceptionPolicy、SetActionOnFailure 或 SetDefaultAction 的调用,而只需替换引导代码,如下所示:

    hr = runtimeHost->ExecuteInDefaultAppDomain(L"PluginSystem.dll", L"PluginSystem.PluginVerifier", L"CheckPlugin", L"test", &returnVal);        

如果您使用上面的启动代码尝试此操作,则此调用将返回 hr=0x80131604,即 COR_E_TARGETINVOCATION (TargetInitationException)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

托管 clr 并捕获线程异常 的相关文章

随机推荐

  • 删除元素时映射迭代器如何失效? [复制]

    这个问题在这里已经有答案了 使用擦除方法时 迭代器何时以及如何在映射中失效 例如 std map lt int int gt aMap aMap 33 1 aMap 42 10000 aMap 69 100 aMap 666 1 std m
  • WordPress 到 Jekyll 脚本抛出错误

    我已经为这个问题苦苦挣扎了一天左右 但无法弄清楚发生了什么 我正在尝试运行一个脚本将我的 Wordpress sql 导入 Jekyll 并将所有内容转换为 Markdown 但每次我运行脚本 在 Mac 和 Ubuntu 上 时都会收到错
  • Python定时器回调方法

    from threading import Timer class test timer def init self self awesum hh self timer Timer 1 self say hello args WOW def
  • 在 Grails 中使用 Groovy++ 的经验

    有人尝试过在幕后实现一个包含 Groovy 的 Grails 解决方案吗 Groovy 使用 Groovy 1 7 5 并包含将 G 与 Grails 集成的插件
  • 当键盘在 Flutter TextField 中可见时,键盘顶部有大量空白

    这是我的代码 build BuildContext context return new Scaffold body new SafeArea child new ListView builder itemBuilder itemBuild
  • 动态更改从数组创建的按钮的按钮标签和标题(快速)

    我的环境 下面的代码是在我公司 iPad 上的 swift Playgrounds 4 中编写的 项目目标 我正在努力改进学校的课堂管理工具 它基本上是一个纸质交通灯 每个学生都有一个衣夹 我想要一个应用程序 其中我的 28 个学生都由一个
  • pthread_mutex_t 可以在内存中移动吗?

    我想构建一个动态分配的 pthread mutex 数组 该数组将随着时间的推移而增长 添加更多互斥体 我的问题是 如果数组通过 realloc 移动 它们是否仍然有效 我担心的是 pthread mutex init 可能会以某种方式设置
  • 更新具有一个字段的当前最高(最大值)值的行[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我有这个 SQL 查询 这是错误的
  • 在Java中,如何从资源构造文件?

    File 在其构造函数中采用 String 和 URI getClass getResource 返回 URL 和getResourceAsStream 返回输入流 有没有办法从资源构造文件 你不能 至少在资源位于 Jar 内的标准情况下不
  • JPA/Hibernate - 与子实体一起分离实体

    我使用 JPA 2 和 Hibernate 3 6 8 作为实现 假设我们有一个实体Foo Entity public class Foo OneToOne private Bar bar 我需要将整个实体图从会话中分离出来 当我这样做时e
  • 使用 Java 将数据从 Cassandra 中的一个表复制到另一个表

    我正在尝试将所有数据从一个列族 表 移动到另一个列族 由于两个表都有不同的描述 我必须从 table 1 中提取所有数据并为 table 2 创建一个新对象 然后执行批量 aync 插入 我的 table 1 有数百万条记录 因此我无法直接
  • 如何使用Lua脚本语言打开Web套接字?

    作为初学者 我想在基于 Linux 的服务器上使用 Lua 打开一个 Web 套接字 该服务器应允许 Android 客户端连接到它 你能给我一些用Lua打开网络套接字的示例代码吗 您两周前已经问过同样的问题并得到了回答 LUA 脚本 We
  • 在 html5 Canvas 上移动对象

    我使用 fillText 选项在 html5 canvas 对象上放置了一个文本 问题是我需要移动文本位置或更改已渲染文本的颜色 很快我需要知道如何操作画布元素的特定子元素 这将在画布上移动一个小圆圈 var can document ge
  • 为什么 ng-bind-html 不显示任何内容?

    我正在显示一个包含 HTML 代码的字符串 div style font size 14px div put 它显示 HTML 代码而不是解释元素 当我使用ng bind and ng bind unsafe 它什么也没显示 如何获取要解析
  • 使用 df.merge 填充 df 中的新列给出奇怪的匹配

    我刚刚发现导致此问题的 2 个问题 请参阅下面的解决方案 我想基于另一个数据帧在我的数据帧 df 中创建一个新列 基本上 df2 包含我想要插入 df 的更新信息 为了复制我的真实情况 gt 1m 行 我将用简单的列填充两个随机 df 我使
  • 如何使用app.config.from_envvar? (烧瓶)

    如何使用app config from envvar 我查看了 Flask 文档并搜索了这个主题 我所知道的就是这样做 DATABASE flaskr db DEBUG True SECRET KEY development key app
  • 在 iOS 中将电子邮件/密码保存到钥匙串

    我对 iOS 开发非常陌生 所以如果这是一个新手问题 请原谅我 我的应用程序有一个简单的身份验证机制 它需要用户的电子邮件地址和密码 我还有一个开关 上面写着 记住我 如果用户打开该开关 我想保留他们的电子邮件 密码 以便将来可以自动填充这
  • 确定设备公网ip

    有谁知道如何获取 Android 设备的公共 IP 地址 我正在尝试运行服务器套接字 只是尝试简单的 p2p 这需要告知本地和远程用户彼此的公共IP 我确实找到了这个线程如何从代码中获取设备的IP地址 https stackoverflow
  • Perl:比较两个 CSV 文件并打印差异

    我是 Perl 的菜鸟 而且我很难完成这件事 我有两个单列 CSV 文件 我正在尝试将差异打印到第三个文件 File1 123 124 125 126 File2 123 124 127 Expected Output 125 126 12
  • 托管 clr 并捕获线程异常

    我正在尝试编写一个可以加载托管插件的插件系统 如果出现任何异常 主机应该能够卸载插件 对于我的 poc 我有一个 C 示例代码库 它会抛出这样的异常 public static int StartUp string arguments Co