Search code examples
c#exceptioncomcom-interophresult

Setting specific COM HRESULT value from .NET


I'm creating a .NET assembly that needs to be COM callable from e.g. VB6 etc.

Most of the stuff works fine - I'm fine-tuning my error handling right now.

What I'd like to do is create specific HRESULT values for my exceptions - e.g. use values like 0x88880001 etc. for my exception cases. I'd like to use a common "prefix" (like 0x8888) and then add my internal error codes (running from decimal 1001 up) to that number.

So my internal error 1001 should become HRESULT = 0x888803E9 and so forth.

I have my own custom .NET exception class, and I know I can set the protected HResult property on the base ApplicationException class - but somehow I can't manage to get my "prefix" plus my error code to work....

using System;

public class CustomException : ApplicationException
{
    public CustomException(string message, int errorCode) : base(message)
    {
        base.HResult = (uint) 0x88880000 + errorCode;
    }
}

No matter what I try here - I just cannot get a int value (for base.HResult, which is an int) that represents 0x888803E9 to be stored into my base class...

What am I missing here??


Solution

  • The C# compiler just hassles you to stop you from tripping an overflow bug. Justifiably, a lot of this kind of code cannot survive overflow checking, not the kind of mishap you ever want in error handling code :) Note how the code in the upvoted answer keels over with Project > Properties > Build > Advanced > "Check for arithmetic overflow/underflow" option ticked. An option that doesn't get used enough.

    You have to use the unchecked keyword to suppress this runtime check:

      base.HResult = unchecked((int)(0x88880000U + errorCode));
    

    Or you can suppress it at compile time:

      const int ErrorBase = unchecked((int)0x88880000);
      base.HResult = ErrorBase + errorCode;
    

    Excruciatingly: the HRESULT you'll generate is not a valid one, it doesn't have a correct facility code. That code provides a hint where the error came from. Interop code should use 0x80040000 as the error base to select FACILITY_ITF. Not so sure you'll ever get any grief over 0x88880000 though, the message string you generate is most important. Unfortunately ISupportErrorInfo tends to be skipped in client code so there's no great guarantee that anybody actually sees it.