Search code examples
c#c++exceptionpinvokeseh

Reading native win32 exception data/arguments in .NET


I'm trying to receive data about a exception I throw in native (c++/win32) in a catch block of managed (.net/c#).

For example, in c++, we can:

void MyFunc()
{
    const char* data = "my exception info";
    ULONG_PTR args[1] { data };
    RaiseException(0,0,1,args);
}

This function could be called via P/Invoke from c#:

[DLLImport("MyLib.dll")]
extern public static void MyFunc();

static void Main()
{
    try { MyFunc(); }
    catch(SEHException seh)
    {
        // Where is my exception info? :(
    }
}

The SEHException doesn't contain my string from the imported DLL function.

The SEHException.ErrorCode always appears to be E_FAIL, but I would like to marshal richer data than an int in any case.

Am I missing something, or is there another way to get this string data back via an exception?

The purpose is to re-raise native exceptions with a native stack trace, by setting native filters like with SetUnhandledExceptionFilter, FYI.


Solution

  • SEH exceptions don't normally have arguments, and very little in the way of data. Really all you can get is the numeric exception code and the processor state when the exception happened (including the instruction pointer and stack pointer).

    What C++ exception handling uses is an SEH exception used to launch the handler, but the C++ exception object is actually stored elsewhere (the details are internal to the compiler and its runtime library). Getting access to that from another language is nearly impossible; even other C++ compilers have a terrible time. All C++ exceptions share a single exception code.

    Calling RaiseException manually, like you are doing, does allow you to store the exception parameters just like the OS exceptions EXCEPTION_ACCESS_VIOLATION and EXCEPTION_IN_PAGE_ERROR, see EXCEPTION_RECORD on MSDN. With the Microsoft C++ compiler, you can access those via GetExceptionInformation. And you assuredly should not combine C++ objects like std::string with RaiseException -- you're missing the extra magic the C++ compiler adds for lifetime management of those objects. Passing string data as const char* to a string literal should be fine, though..

    Then you're faced with the problem of retrieving this information in .NET. Even this limited (exception code, context, and record, no C++ objects) information which is there I don't think you can get from the .NET SEHException object. The reason you're seeing E_FAIL is a result of this note in the docs, not the exception code.

    The SEHException class handles SEH errors that are thrown from unmanaged code, but that have not been mapped to another .NET Framework exception. The SEHException class also responds to the HRESULT E_FAIL, which has the value 0x80004005.