Raise positive VB style error codes via COM intero

2019-05-10 02:25发布

问题:

I have a base library created in VB6 that exposes a standard COM interface that is used in a number of applications. This also exposed a number of error code constants, used with Err.Raise to indicate certain conditions.

Public Enum IOErrors
  IOErrorBase = 45000
  IOErrorConnectionFailed
  IOErrorAuthFailed
  IOErrorNotConnected
  IOErrorInvalidPortDirection
  IOErrorGettingValue
  IOErrorNoValueYet
End Enum

Come on 10 years and we're creating C# objects implementing the same set of interfaces and want to throw exceptions in a way that the calling application will recognise them.

I can only find two relevant classes, Win32Exception and COMException.

Throwing Win32Exception((int)IOErrors.IOErrorConnectionFailed, "Connect failed") passes the message back correctly but the error code is ignored and Err.Number is &H80004005.

Throwing COMException("Connect failed", IOErrors.IOErrorConnectionFailed) results in no error being picked up in the calling application, presumably because the error code is not an HRESULT and is positive, meaning success.

TL;DR How can I throw an exception from C# such that COM interop will translate it into one of the recognised (positive) error codes above?

回答1:

The "positive" VB style error numbers are translated to HRESULTs with a "FAILURE" severity and a facility of FACILITY_CONTROL/0xA, i.e. 0x800AAFC9.

You can get a suitable HRESULT using:

int HResult = (int)(0x800A0000 | (int)errorCode);

This can then be raised back to the calling process using a plain COMException, or by throwing your own subclass of COMException:

/// <summary>
/// Exception that returns an ICIO error wrapped in an exception.
/// </summary>
internal class ICIOErrorException : COMException {
    internal ICIOErrorException(ICIO.IOErrors errorCode, string message)
        : base(message) {
        this.HResult = (int)(0x800A0000 | (int)errorCode);
    }
}