【问题标题】:Prevent graceless crash from occurring in application calling library via P/Invoke通过 P/Invoke 防止应用程序调用库发生无优雅崩溃
【发布时间】:2013-02-06 19:08:32
【问题描述】:

我有一个 C# 应用程序 App A,它通过 P/Invoke 调用 C++ (Qt) DLL App B。假设我不能以任何方式编辑App B,但我知道App B 在一组条件下抛出Out of Memory Exception,在将输入从App A 传递到@ 之前,我可以复制但不能准确测试987654327@。此异常不会对App A 构成任何威胁,如果App B 可以以某种方式“重置”,则可以忽略此异常,但是当App B 崩溃时,它会调用abort(),这反过来又会导致App A 的运行时终止。

我如何防止App B 不可避免的、不可预测的和通常平凡的崩溃影响App A

注意事项:

  • 抛出此错误时,已实现的UnhandledExceptionHandler 将被忽略。
  • App B 因 QtWebkit 中的错误而崩溃,App B 可以通过删除过大的对象并返回 null 来处理该错误。
  • App A 不会报告为内存不足,并且机器有足够的内存来多次执行App B 的操作,无论错误如何,但无论出于何种原因,该内存显然都没有分配给App B .

【问题讨论】:

    标签: c# c++


    【解决方案1】:

    您创建了一个 shim 进程,其唯一目的是加载 App B 并使用某种 IPC 机制与 App A 通信。应用 A 生成 shim,如果 shim 崩溃,则应用 A 采取适当的操作(重新生成,传达错误给用户等)。

    【讨论】:

    • 不错的选择,但如果可能的话,我更愿意在单个应用程序的上下文中执行此操作。如果没有任何结果可以以这种方式适当地解决问题,我将接受此作为答案并将其作为解决方案实施。
    【解决方案2】:

    您可以处理来自非托管代码的 SIGABRT,但它有点混乱。有可能它可以完全在 C# 中完成,但我的方法需要 C++/CLI 程序集才能使其工作(至少它在一个进程中)。

    第一步:创建一个 C++/CLI 类库项目,在 *.h 文件中放入以下函数

    namespace ClassLibrary1 {
    
        public ref class Class1
        {
        public:
            static void callaborter();
        };
    }
    

    第2步:在cpp文件中输入以下代码:

    // This is the main DLL file.
    
    #include "stdafx.h"
    #include <csetjmp>
    #include <csignal>
    #include <cstdlib>
    #include <iostream>
    #include "ClassLibrary1.h"
    #include "..\Win32Project1\Win32Project1.h"
    
    
    #pragma managed(push, off) 
    jmp_buf env;
    
    void on_sigabrt (int signum)
    {
        longjmp (env, 1);
    }
    
    void run()
    {
        if (setjmp (env) == 0) {
                signal(SIGABRT, &on_sigabrt);
                fnWin32Project1();
        }
        else {
                std::cout << "aborted\n";
        }
    }
    
    #pragma managed(pop)
    
    void ClassLibrary1::Class1::callaborter()
    {
        run();
    }
    

    我将我的类库和类的名称保留为默认值,我的中止函数是调用 fnWinProject1()。您需要将有问题的 DLL 直接链接到该项目(就像在 C++ 中一样)

    在您的 C# 类中,包含对 C++/CLI 程序集的引用并调用“callaborter”方法。

    运行中的代码将为 SIGABRT 设置一个捕获器(由 abort() 调用)并在 on_sigabrt() 中对其进行处理,这将导致整个函数退出。然后您可以清理并重试。

    我将把它作为练习让 OP 选择有用的名称。

    注意:在调试中,您仍然会看到中止对话框,只需按忽略继续。在 Release 中,您不会看到该对话框。

    附:我应该提到我基于这个 stackoverflow 问题构建了我的回复:How to Handle SIGABRT signal?

    【讨论】:

    • 我将在 C# 本身内实现类似的东西。感谢您为我提供了坚实的工作路线图!
    • @dambriso ...不客气。完成后您介意发布您的 C# 版本吗?
    • 会的。如果可以的话,我会为这个问题添加一个额外的答案并在其中标记你。
    猜你喜欢
    • 1970-01-01
    • 2011-10-13
    • 2016-03-15
    • 2010-10-17
    • 1970-01-01
    • 2014-05-26
    • 1970-01-01
    • 2016-01-10
    • 2017-04-30
    相关资源
    最近更新 更多