Handling unhandled exceptions

B

Bob Altman

I've written a DLL in "interop" mode (compiled with /clr). This DLL is a
wrapper for a managed library. It exposes native C-style entry points, calls
into the managed library, and marshals the results (if any) back to the native
caller.

Here's the short version of the question: Is there an easy way for my library
to catch unhandled managed exceptions (short of putting a try/catch block around
each top-level routine in my library)? As I explain below, I tried wiring up
the AppDomain.CurrentDomain.UnhandledException event, but that didn't work.

Here's the longer story:

If the managed code called by my library throws an exception that is ultimately
unhandled then my library's caller dies a horrible death. That horrible death
includes a dialog box that basically tells the user that the program died, but
offers little by way of useful information about unhandled exception (like the
exception name and the call stack). I want to display my own custom dialog in
response to an unhandled exception (before I kill the caller).

I have an existing class called UnhandledExceptionHandler in a managed library
(authored in VB) whose constructor wires up the
AppDomain.CurrentDomain.UnhandledException event. That class decodes the
unahandled exception and puts up a custom dialog.

I tried constructing an instance of my UnhandledExceptionHandler class in my
wrapper library, but it dies a horrible death. I'm just assuming (with no
really good evidence to support it) that /clr doesn't give me enough AppDomain
context to do this sort of thing.

TIA - Bob
 
C

Carl Daniel [VC++ MVP]

Bob Altman said:
I've written a DLL in "interop" mode (compiled with /clr). This DLL is a
wrapper for a managed library. It exposes native C-style entry points,
calls into the managed library, and marshals the results (if any) back to
the native caller.

Here's the short version of the question: Is there an easy way for my
library to catch unhandled managed exceptions (short of putting a
try/catch block around each top-level routine in my library)?

No. The best way to implement this kind of layer is to explicitly catch all
exceptions in your wrapper functions and do not let them propagate across
architecture boundaries (Managed -> C, C++ -> COM, etc).

Make sure that your wrapper layer code is compiled with /EHa as well - I
think VC2005 and later enforces this, but it's worth checking to make sure
that no exception handling code will be inadvertantly optimized out by the
C++ compiler.

Your experiments with AppDomain.UnhandledException aside, you really don't
want to use that solution anyway. You'd be catching unhandled exceptions
from the entire app, not just from the code exposed through your wrapper
layer.

-cd
 
B

Bob Altman

I've written a DLL in "interop" mode (compiled with /clr). This DLL is a
No. The best way to implement this kind of layer is to explicitly catch
all exceptions in your wrapper functions and do not let them propagate
across architecture boundaries (Managed -> C, C++ -> COM, etc).

Bummer, but that was about what I suspected. Thanks Carl!

Bob
 
B

Ben Voigt [C++ MVP]

Bob said:
Bummer, but that was about what I suspected. Thanks Carl!

The only possible alternative would be to install an error handler that
stays in the call stack. The native code would have to pass in a function
pointer, so that its main program would run from within your error handler.
 
J

Jeroen Mostert

Bob said:
I've written a DLL in "interop" mode (compiled with /clr). This DLL is a
wrapper for a managed library. It exposes native C-style entry points, calls
into the managed library, and marshals the results (if any) back to the native
caller.

Here's the short version of the question: Is there an easy way for my library
to catch unhandled managed exceptions (short of putting a try/catch block around
each top-level routine in my library)? As I explain below, I tried wiring up
the AppDomain.CurrentDomain.UnhandledException event, but that didn't work.

Here's the longer story:

If the managed code called by my library throws an exception that is ultimately
unhandled then my library's caller dies a horrible death. That horrible death
includes a dialog box that basically tells the user that the program died, but
offers little by way of useful information about unhandled exception (like the
exception name and the call stack). I want to display my own custom dialog in
response to an unhandled exception (before I kill the caller).
Actually, in addition to what others have said, this is not your
responsibility. It is the caller's responsibility in turn (and ultimately
whoever's responsible for the process as a whole) to handle unhandled
exceptions from *your* code. If the caller is/might be a pure C caller that
doesn't know about exceptions at all, then it's your job to make sure no
exceptions are ever propagated from your functions (at least not those that
can be adequately captured in error codes), so exception handlers around
every call are entirely appropriate. Otherwise, propagating them may be
acceptable, and your client will have to worry about them.

This is also why throwing up a dialog and killing the caller is something
you shouldn't do even if you could; the caller may have its own ideas about
how to handle exceptions from your library. Only if your library is calling
the shots (or is expected to call the shots) could you do something like
muck around with .UnhandledException.

If you absolutely, positively must have this level of control, you could
always make your DLL purely unmanaged and host the CLR yourself. This will
allow you to customize exception handling to your heart's content, but it's
not something you should do lightly (for one thing, this will simply not
work if your DLL is ever used in a context where the CLR is already hosted,
and it obviously makes interacting with managed code a lot less convenient).
 
J

Jeffrey Tan[MSFT]

Hi Bob,

The community members have provided good replies to you now. I wanted to
add some comment:

Just as Jeroen pointed out, as a library, your responsibility is throwing
the exception for bad condition. It is the application developer's
responsibility to set a top-level unhandled exception handler to report all
the information. If they did not do that, it is their problem. Your
responsibility is documenting all the possible or expected exceptions for
easy methods/routines in your library documents like what MSDN does.

Furthermore, I do not recommend you to use try...catch clause around all
the methods/routines, because this will eliminate the caller's chance to
fix the exception. For example, if one method is openning certain file and
the caller supplies a wrong path to your method. A FileNotFoundException
may be thrown from your method. The caller may use a try...catch for this
exception and notify the user with a dialog to supply a legal file path.
However, if you try...catch this in your library and just kill the
application, you did not give the caller any chance to fix it.

Is your library used in .Net Winform application? .Net has different
approaches to handle unhandled exceptions for console, Winform and Asp.net
applications. Winform's main GUI thread will use
Application.ThreadException event to handle the exception. So, if your
library is tested in .Net Winform,
AppDomain.CurrentDomain.UnhandledException event will not catch it. See the
article for more details:
"Unexpected Errors in Managed Applications"
http://msdn2.microsoft.com/en-us/magazine/cc188720.aspx

Hope it helps.

Best regards,
Jeffrey Tan
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
B

Ben Voigt [C++ MVP]

Jeffrey Tan said:

Jeffrey,

I believe you missed the statement "It exposes native C-style entry points,
calls into the managed library, and marshals the results (if any) back to
the native caller." because all your advice is inapplicable to the case of a
native caller.
 
J

Jeffrey Tan[MSFT]

Hi Ben,

Oh, thank you for pointing this out.

Yes, I did not catch that the caller is pure native C/C++ code. Exceptions
should not propagate across modules boundary unless we know the
caller&callee are using the same runtime(for example, they are your managed
code).

So, in the Bob's scenario, we'd better catch all the expected exceptions in
the managed world and translate them into error code and report to the
native caller, something like this:

//inner library file
class hidden {
int doWork();
};

//Outer exported file
HRESULT __declspec( dllexport ) DoWork(HANDLE explicitThis, int *retVal) {
try {
*retVal = ((hidden *)explicitThis))->doWork();
return S_OK;
} catch (exceptionType1) {
return HARDCODED_HRESULT_FOR_ET1;
} catch (...) {
return E_FAIL;
}
}

Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top