HRESULT Information in Managed Code
The interaction between managed code and COM can present issues when dealing with HRESULT return values.
In a COM interface, an HRESULT return value can play two roles:
To deliver error information (for example, E_INVALIDARG).
To deliver status information about normal program behavior.
When COM calls into managed code, HRESULTs can cause two problems:
COM functions that return HRESULT values less than zero (failure codes) generate exceptions.
Because many of the Visual Studio SDK COM functions either return HRESULT values less than zero or return different success codes, the Visual Studio SDK interop assemblies have been written so that the method signatures are preserved. All Visual Studio SDK interop methods are of int type. HRESULT values are passed through the interop layer without alteration and without generating exceptions.
Because a COM function returns an HRESULT to the managed method that calls it, the calling method must check the HRESULT and throw exceptions as necessary.
When you call a COM interface from managed code, you should examine the HRESULT value and throw an exception if necessary. The ErrorHandler class contains the ThrowOnFailure method, which throws a COM exception, depending on the value of the HRESULT passed to it.
By default, ThrowOnFailure throws an exception whenever it is passed an HRESULT with a value less than zero. In cases where one or more such HRESULTs are acceptable values and no exception should be thrown, the values of additional HRESULTS should be passed to ThrowOnFailure after the values are tested. If the HRESULT being tested matches any HRESULT values explicitly passed to ThrowOnFailure, no exception is thrown.
The VSConstants class contains constants for common HRESULTS, such as S_OK and E_NOTIMPL, and Visual Studio HRESULTS, such as VS_E_INCOMPATIBLEDOCDATA and VS_E_UNSUPPORTEDFORMAT. VSConstants also provides the Succeeded and Failed methods, which correspond to the Succeeded method and Failed macros in COM.
For example, consider the following function call, in which E_NOTIMPL is an acceptable return value but any other HRESULT less than zero represents an error:
int hr = MyInterface.MyFunction(cmdID); ErrorHandler.ThrowOnFailure(hr, VSConstants.E_NOTIMPL);
If there is more than one acceptable return value, additional HRESULT values can simply be appended to the list in the call to ThrowOnFailure:
int hr = MyInterface.MyFunction(cmdID); ErrorHandler.ThrowOnFailure(hr, VSConstants.E_NOINTERFACE, VSConstants.E_NOTIMPL);
If no exception occurs, managed code returns S_OK to the COM function that called it. COM interop supports common exceptions that are strongly typed in managed code. For example, a method that receives an unacceptable null argument throws an ArgumentNullException.
If you are not certain which exception to throw, but you know the HRESULT you would like to return to COM, you can use the ThrowExceptionForHR method to throw an appropriate exception. This works even with a nonstandard error, such as VS_E_INCOMPATIBLEDOCDATA. ThrowExceptionForHR attempts to map the HRESULT passed to it to a strongly typed exception. If it cannot, it throws a generic COM exception instead. The ultimate result is that the HRESULT you pass to ThrowExceptionForHR from managed code is returned to the COM function that called it.
Exceptions compromise performance and are intended to indicate abnormal program conditions. Conditions that occur often should be handled inline, rather than with a thrown exception.