Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should managed code return an error or throw exceptions to unmanaged code?

I am about to expose a service written in C# to a legacy C++ application using COM. What is the best approach to report errors to the unmanaged client? Throwing exceptions or simply return an error value?

Thanks, Stefano

like image 470
Stefano Ricciardi Avatar asked May 07 '09 08:05

Stefano Ricciardi


4 Answers

You should throw Exceptions. Exceptions are mapped to HRESULTS by the Framework, and HRESULTs are the standard way to return errors to COM clients, so this is the way to go.

Each Exception type has an HResult property. When managed code called from a COM Client throws an exception, the runtime passes the HResult to the COM client. If you want application-specific HRESULT codes, you can create your own custom Exception types and set the Exception.HResult property.

One point to note is that the call stack information will be lost when an Exception is thrown to a COM client. It can therefore be a good idea to log exceptions before propagating to the COM client.

One technique I sometimes use is the following: explicitly implement a ComVisible interface for COM clients that logs and rethrows exceptions. COM clients use the ComVisible interface that logs exceptions before propagating them. .NET clients use the concrete class and are expected to make their own arrangements for exception handling. It's a bit long-winded to write but can be helpful when you're subsequently troubleshooting.

Another advantage of this approach is that you can have an API tailored to the restrictions of COM for COM clients, and a more standard API for standard .NET clients. For example, COM clients are restricted to passing arrays by reference, whereas passing by reference is discouraged for .NET clients.

Example:

[
ComVisible(true),
GuidAttribute("..."),
Description("...")
]
public interface IMyComVisibleClass
{
    // Text from the Description attribute will be exported to the COM type library.

    [Description("...")]
    MyResult MyMethod(...);

    [Description("...")]
    MyOtherResult MyArrayMethod([In] ref int[] ids,...);
}
...
[
ComVisible(true),
GuidAttribute("..."),
ProgId("..."),
ClassInterface(ClassInterfaceType.None),
Description("...")
]
public class MyComVisibleClass : IMyComVisibleClass
{
    public MyResult MyMethod(...)
    {
        ... implementation without exception handling ...
    }

    public MyOtherResult MyArrayMethod(int[] ids,...)
    {
        ... input parameter does not use ref keyword for .NET clients ...
        ... implementation without exception handling ...
    }

    MyResult IMyComVisibleClass.MyMethod(...)
    {
        // intended for COM clients only
        try
        {
            return this.MyMethod(...);
        }
        catch(Exception ex)
        {
            ... log exception ...
            throw;   // Optionally wrap in a custom exception type
        }
    }

    MyOtherResult IMyComVisibleClass.MyArrayMethod(ref int[] ids, ...)
    {
        // intended for COM clients only
        try
        {
            // Array is passed without ref keyword
            return this.MyArrayMethod(ids, ...);
        }
        catch(Exception ex)
        {
            ... log exception ...
            throw;   // Optionally wrap in a custom exception type
        }
    }

}
like image 186
Joe Avatar answered Nov 11 '22 16:11

Joe


I agree with the others that this is not a "yes or no" answer without knowing your project intimately.

It will depend on a number of factors such as:

  • Security (i.e. what should your client know about your exception)
  • Efficiency (i.e. is the processing time critical)
  • Maintainability (i.e. can you alter the legacy C++ code to parse your exception conditions)

Here's a good blog post that discusses a number of subtle points about exception processing.

The author recommends either one of two approaches:

Either:

  • Return an error document.

or:

  • Log all information about the exception on the server.
  • Create a new exception that references the logged information.
  • Send the new exception to the client for client side processing and reporting.

Personally, I think you should avoid tightly coupling your C# service with your C++ application. In other words, write your C# service so that it could theoretically be used by any consumer. Likewise, your C++ code should be written so that it doesn't rely on the internal workings of the C# service, so changes or additions to the exceptions (or error codes) do not break the consumer.

like image 3
Daniel Robinson Avatar answered Nov 11 '22 16:11

Daniel Robinson


If your COM application supports the IErrorInfo interfaces when calling into your C# service and it's an entirely internal project then throwing exceptions is likely the best bet as it captures the most information. However COM has traditionally relied on HR results to communicate status results and may be better if the service is to be published to other sources.

EDIT: I like Joe's answer better.

like image 3
Paul Alexander Avatar answered Nov 11 '22 17:11

Paul Alexander


I think that depends on how the legacy application will react. If it understands error return values then go with that approach. If it doesn't, then you'll have to throw exceptions and hope it handles them appropriately.

Also, if it's a bad reference (null reference for example) or other critical error, I would always throw an exception. Exceptions should, however, be avoided for things the consumer cannot check beforehand (e.g. a search that comes up empty shouldn't throw an exception).

like image 1
lc. Avatar answered Nov 11 '22 16:11

lc.