Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET中非托管线程上的异常_C#_.net_Winapi_Unmanaged_Unhandled Exception - Fatal编程技术网

C# .NET中非托管线程上的异常

C# .NET中非托管线程上的异常,c#,.net,winapi,unmanaged,unhandled-exception,C#,.net,Winapi,Unmanaged,Unhandled Exception,我如何处理我的应用程序终止时的情况,在终止前使用回调 .NET处理程序在以下场景中不工作,SetUnhandledExceptionHandler是正确的选择吗?它似乎有以下讨论的缺点 脚本 我想在.net应用程序中向我们的服务发送消息和错误报告,以响应所有应用程序终止的情况 然而,我有一个WPF应用程序,其中两个测试人员得到未处理的异常,绕过: AppDomain.UnhandledException(最重要的) Application.ThreadException Dispatcher.

我如何处理我的应用程序终止时的情况,在终止前使用回调

.NET处理程序在以下场景中不工作,SetUnhandledExceptionHandler是正确的选择吗?它似乎有以下讨论的缺点

脚本 我想在.net应用程序中向我们的服务发送消息和错误报告,以响应所有应用程序终止的情况

然而,我有一个WPF应用程序,其中两个测试人员得到未处理的异常,绕过:

  • AppDomain.UnhandledException(最重要的)
  • Application.ThreadException
  • Dispatcher.UnhandledException
它们被标记为SecuirtyCriticalhandleProcessCorruptedStateExceptionlegacyCorruptedStateExceptionsPolicy在app.config中设置为true

我在野外的两个例子
  • 在某个地方初始化WPF时,运行widows10的VirtualBox在某些vboxd3d.dll中抛出(关闭VBOx3D accel“修复它”)
  • Win8机器在系统上下文菜单中具有“在图形卡A/B上运行”的可疑选项,在WPF启动过程中的某个地方(:/)发生崩溃,但仅在应用了防破解工具时发生
无论哪种方式,当应用程序处于活动状态时,应用程序必须在终止前对此类故障做出响应

我可以用一个非托管异常重现这种情况,该异常发生在.net中PInvoked方法的非托管线程中:

test.dll

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

DWORD WINAPI myThread(LPVOID lpParameter)
{
    long testfail = *(long*)(-9022);
    return 1;
}

extern "C" __declspec(dllexport) void test()
{
    DWORD tid;
    HANDLE myHandle = CreateThread(0, 0, myThread, NULL, 0, &tid);
    WaitForSingleObject(myHandle, INFINITE);
}
app.exe

class TestApp
{
    [DllImport("kernel32.dll")]
    static extern FilterDelegate SetUnhandledExceptionFilter(FilterDelegate lpTopLevelExceptionFilter);

    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    delegate int FilterDelegate(IntPtr exception_pointers);

    static int Win32Handler(IntPtr nope)
    {
        MessageBox.Show("Native uncaught SEH exception"); // show + report or whatever
        Environment.Exit(-1); // exit and avoid WER etc
        return 1; // thats EXCEPTION_EXECUTE_HANDLER, although this wont be called due to the previous line
    }

    [DllImport("test.dll")]
    static extern void test();

    [STAThread]
    public static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        SetUnhandledExceptionFilter(Win32Handler);
        test(); // This is caught by Win32Handler, not CurrentDomain_UnhandledException
    }
    [SecurityCritical, HandleProcessCorruptedStateExceptions ]
    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Exception ex = e.ExceptionObject as Exception;
        MessageBox.Show(ex.ToString()); // show + report or whatever
        Environment.Exit(-1); // exit and avoid WER etc
    }
}
这将处理裸WPF测试应用程序中vboxd3d.dll中的故障,当然,该应用程序还注册了WCF Dispatcher和WinForms应用程序(为什么不注册)异常处理程序

更新
  • 在我试图使用它的生产代码中,处理程序似乎被其他调用程序覆盖了,我可以通过每100ms调用一次方法来解决这个问题,这当然是愚蠢的。
    • 在存在vbox3d.dll问题的计算机上,执行上述操作会将异常替换为clr.dll中的异常
    • 在崩溃时,传递到内核32的托管函数指针不再有效。使用本机帮助程序dll设置处理程序(该dll调用内部的本机函数)似乎可以正常工作。托管函数是一个静态方法-我不确定固定是否适用于这里,也许clr正在终止
    • 实际上,正在收集托管委托。没有对处理程序进行“覆盖”。我添加了一个答案…不确定该接受什么,也不确定这里的SO惯例是什么

可以说,我不得不处理不可预测的非托管库

如果要对非托管代码进行p/调用,则可能会出现问题。我发现在非托管代码周围使用C++和CLI包装更容易,在某些情况下,我在写C++或CLI。

之前,已经在库周围写了另一组非托管C++包装器。 你可能会想,“你到底为什么要写两套包装纸?”

首先,如果隔离非托管代码,则可以更容易地捕获异常并使它们更容易接受

第二个是纯粹实用的—如果您有一个使用stl的库(而不是dll),您会发现该链接将神奇地提供stl函数的所有代码(托管和非托管)CLI实现。防止这种情况发生的最简单方法是完全隔离使用stl的代码,这意味着每次通过非托管代码中的stl访问数据结构时,最终都会在托管代码和非托管代码之间进行多次转换,性能会下降。你可能会想,“我是一个一丝不苟的程序员——我会非常小心地把
#pragma managed
和/或
#pragma unmanaged
包装放在正确的位置,我已经准备好了。”不,不,不。这不仅是困难和不可靠的,当(不是如果)你做不好的时候,你将没有一个好的方法来检测它

和往常一样,您应该确保您编写的任何包装都是粗体的,而不是健谈的

下面是处理不稳定库的典型非托管代码块:

try {
    // a bunch of set up code that you don't need to
    // see reduced to this:
    SomeImageType *outImage = GetImage();
    // I was having problems with the heap getting mangled
    // so heapcheck() is conditional macro that calls [_heapchk()][1]
    heapcheck();
    return outImage;
}
catch (std::bad_alloc &) {
    throw MyLib::MyLibNoMemory();
}
catch (MyLib::MyLibFailure &err)
{
    throw err;
}
catch (const char* msg)
{
    // seriously, some code throws a string.
    throw msg;
}
catch (...) {
    throw MyLib::MyLibFailure(MyKib::MyFailureReason::kUnknown2);
}

无法正确处理的异常总是会发生,并且无论您如何努力从内部保护该进程,该进程都可能意外死亡。但是,您可以从外部监视它

让另一个进程监视您的主进程。如果主进程突然消失而没有记录错误或优雅地报告情况,第二个进程可以这样做。第二个过程可以简单得多,根本没有非托管调用,因此它突然消失的可能性要小得多


作为最后手段,当您的进程开始时,请检查它们是否已正确关闭。如果没有,您可以报告严重关机。如果整个机器死亡,这将非常有用。

问题中的代码问题如下:

SetUnhandledExceptionFilter(Win32Handler);
由于委托是自动创建的,因此它适用于:

FilterDelegate del = new FilterDelegate(Win32Handler);
SetUnhandledExceptionFilter(del);
问题是,GC可以在它的最终引用之后的任何时候收集它和创建的本机->托管thunk。因此:

SetUnhandledExceptionFilter(Win32Handler);
GC.Collect();
native_crash_on_unmanaged_thread();
当传递到kernel32.dll的处理程序不再是有效的函数指针时,总是会导致严重的崩溃。这可以通过不允许GC收集以下信息来解决:

public class Program
{
    static FilterDelegate mdel;
    public static void Main(string[] args)
    {
        FilterDelegate del = new FilterDelegate(Win32Handler);
        SetUnhandledExceptionFilter(del);
        GC.KeepAlive(del);  // do not collect "del" in this scope (main)
        // You could also use mdel, which I dont believe is collected either
        GC.Collect();
        native_crash_on_unmanaged_thread(); 
    }
}

其他答案也是一个巨大的资源;现在不确定该标记什么作为答案。

这没有什么意义。您不能使用CreateProcess()来“启动方法”。这将创建一个进程,进程内的任何异常都无法在进程中进行编码。您对非托管异常的堆栈感兴趣的是什么,如果是,那么为什么不使用Windbg,它将为您提供Win32线程的堆栈跟踪。事实上,windows调试工具做得很好。如果你有相关的非托管代码和pdb文件,那么就启用相关的EXE