COM interop is one of the messy places where error by return value vs exception meet head on. COM was built on the basis of HRESULT return values while .NET tends to use exceptions. To compensate for that, the CLR helps out by mapping failure HRESULTs into .NET exceptions during COM Interop. It does this by the following process
1) Discard the HRESULT return value
2) Convert any [out, retval] components to the method return value
This is helpful in a lot of cases. You can make you're COM calls happily and exceptions will be thrown when an error occurs. There are a lot of cases where this isn't enough information for the developer. The main one are the semi-successful HRESULT return values. The CLR will only throw exceptions for failed HRESULTS  but will not for values such as S_FALSE.
Values such as S_FALSE hold a lot in certain COM components and cannot be ignored in many situations. But because the CLR disregards this value there is no way to get to that value via this method. To be able to access the HRESULT in COM component return values you have to turn off the exception mapping. Unfortunately you will not be able to use the type library importer for this.
1) Modify your Managed Interafec to match the COM signature (add the int,uint return value and readd the [out,retval] component to the paramaters).
2) Decorate the method with the [PreserveSig] attribute.
This will allow you to access the HRESULT for the return value good or bad. The downside is that the CLR will no longer translate bad HRESULTS into exceptions and your back to using SUCCEEDED and FAILED methods.
List of Mapped HRESULTs to .NET Exceptions
 The documentation on MSDN says it will throw for anything but S_OK. You can verify that isn't correct by using a component that returns S_FALSE
"This posting is provided "AS IS" with no warranties, and confers no rights"