Quick caveat; I am not a hardcore COM guy. I do work with COM quite a bit but most of it is hobbyist work at home. So if I make any COM errors here please let me know and I will correct them. Now onto the fun stuff.
Pure COM uses HRESULTS to indicate the success or failure of a method. HRESULTS are just DWORDs under the cover. They use the highest bit to indicate success or failure (0 is success) and the rest of the code is used to indicate the reason for failure/success. Managed code on the other hand prefers to use Exceptions. The CLR merges these somewhat conflicting paradigms by coverting failing HRESULTS into COM Exceptions when an interop call is made. You can access the HRESULT from the exception.
Typically when a COMException occurs you will usually get a generic error message like the following.
"Error HRESULT E_FAIL has been returned from a call to a COM component."
Seeing this you would expect that the CLR just adds a generic error message to all COMExpcetions using a format string. However, if you've done much interop programming, you've noticed that sometimes you'll get error codes like E_FAIL but a completely different message (and usually its pretty helpful). So how does that rich error information get in there?
As COM programmers surely know, rich error descriptions have been around in COM since OLE through the use of ISupportErrorInfo, IErrorInfo. and the GetErrorInfo()/SetErrorInfo() pair of methods.
When thet CLR encounters a failing HRESULT it will first grab error information via GetErrorInfo(). If error information is present it will then query the COM component on which the call was made and QueryInterface for ISupportErrorInfo. If the coclass supports this interface the CLR will then populate the COMException from the information contained in IErrorInfo.
Note that the original target of the COM call must implement ISupportErrorInfo for this to work. If you have the following situation ...
Managed | Native
Client -> IStore::GetPrice() ---> IDatalayer::LookupItem()
Assume that the call to IDatalayer::LookupItem() fails with E_FAIL and it sets a rich error message via SetErrorInfo() and implements ISupportErrorInfo. IStore::GetPrice() recieving this code, passes it back to the caller. Assume also that IStore does not implement ISupportErrorInfo. In this case, the CLR will fail in its attempt to QueryInterface on IStore and will return a generic error message.
If IStore did implement ISupportErrorInfo, the CLR would use the IErrorInfo structure to create the exception.