Day 8: VARIANT_BOOL, BOOL, bool, HRESULTs, and managed Exceptions

God, what a mess.

  true false
VARIANT_BOOL -1 0
bool (C#/C++) true false
BOOL (C++) not 0 0
HRESULT >= 0 < 0

I ran into this mess while creating COM interfaces in C# that were being used in C++ (via tlbexp). I discovered some silly behavior with regards to how C# or tlbexp marshals bool return types, which was the cause of a couple bugs:

C# COM Interface:

[Guid("91B57DDB-5CCF-4cb5-9A26-A7F9559BAFFF")]
public interface IFoo
{
    // by default bool is marshalled as VARIANT_BOOL
    bool Bar();
    void Goo();
}

Seriously? Why is it defaulted to VARIANT_BOOL and not BOOL? VARIANT_BOOL is a Visual Basic concept (and a retarded one at that). Looking at the table above, it is the complete opposite behavior of COM HRESULTs. The fix:

[Guid("91B57DDB-5CCF-4cb5-9A26-A7F9559BAFFF")]
public interface IFoo
{
    [return: MarshalAs(UnmanagedType.Bool)]
    bool Bar();
    void Goo();
}

Another issue that bothered me was that these COM calls actually look like the following:

virtual HRESULT __stdcall raw_Bar (
  /*[out,retval]*/long* pRetVal ) = 0;

virtual HRESULT __stdcall Goo () = 0;

All COM calls are returning HRESULTs behind the scenes, which is expected. However, what happens when the C# code throws an exception? You would expect the marshaller to maybe catch it and return an HRESULT failure? Nope. On .NET CF (and maybe even in the desktop version too), the application crashes (without any chance for recovery) in native code. Beautiful. This basically requires that your C# COM methods have a try/catch wrap around all operation, as an exception would be fatal. I guess I can understand why you wouldn't want to have the COM interop handling arbitrarily catch all exceptions, but it is quite tedious to have to do it yourself.

2 comments:

Peter Foot said...

You can cut out the exception handling and just return the original HRESULT using the PreserveSigAttribute:-

[PreserveSig]
int SomeMethod();


Peter

Koush said...

Awesome! I was hoping it was just developer ignorance on my part.