3

When you define a COM class in C# you typically have a signature like this:

void Login(string user,string password)

The automagic COM interop wrapper means that when calling from C++:

  • throwing an exception gets converted to a failure HRESULT
  • returning nothing gets converted to returning S_OK

But what if you want to return success codes other than S_OK? Such as S_FALSE or some custom "success, but..." value?

Note: I am defining the class in C# and wondering how to control what the COM interop does. Not implementing a class in C# based on an existing interface in IDL.

Niall
  • 30,036
  • 10
  • 99
  • 142
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
  • Possible duplicate of [Returning S\_FALSE from a C# COM dll](http://stackoverflow.com/questions/2592684/returning-s-false-from-a-c-sharp-com-dll) – user1 Oct 15 '15 at 11:37
  • It has the same title as what I'm asking but is actually a very different question (as I understand it) - he's working from IDL -> C# – Mr. Boy Oct 15 '15 at 11:39

2 Answers2

3

The HRESULT is generally an error code only datum. It is rarely used to convey success; S_FALSE being the only well defined exception to this. In the case of S_FALSE, it is often only used when the result type is the COM boolean (VARIANT_BOOL) and the HRESULT is mapped to S_FALSE when the return (out) is VARIANT_FALSE (and S_OK when VARIANT_TRUE).

Wikipedia also talks about the HRESULT for the case of the error for COM and the .Net/COM boundary (from here);

In the .NET Framework, HRESULT/IErrorInfo error codes are translated into CLR exceptions when transitioning from native to managed code; and CLR exceptions are translated to HRESULT/IErrorInfo error codes when transitioning from managed to native COM code.

Although not conclusive, it certainly does re-enforce the fact that the HRESULT is generally seen as an error code. Accessing the IErrorInfo may provide some more data, but I'm not sure it will be as you expect or desire it to be.

This MSDN article does provide more information on how to map COM errors and exceptions, again supporting the error code;

COM methods report errors by returning HRESULTs; .NET methods report them by throwing exceptions. The runtime handles the transition between the two. Each exception class in the .NET Framework maps to an HRESULT.

To support the "success code" you could use an additional [out] parameter. If the code is outside your control, you could write a thin COM object to map that for you and then use that in the .Net object.

Alternatively, you could use the PreserveSigAttribute, but as far as I know, it won't translate the errors to exceptions so you will need to do that yourself (a test to verify this is advised). Annotating the method with [PreserveSig] should do the trick.

Craig
  • 2,248
  • 1
  • 19
  • 23
Niall
  • 30,036
  • 10
  • 99
  • 142
  • 1
    You make a good case that S_FALSE is an anomaly and best avoided. I note here it specifically says "COM methods that regularly return two or more different success codes, for example, S_OK or S_FALSE, cannot be distinguished." https://msdn.microsoft.com/en-us/library/bb164625.aspx – Mr. Boy Oct 15 '15 at 11:45
2

You have to use the [PreserveSig] attribute on the method declaration. Which means literally what it says, it preserves the method signature and prevents the type library exporter from rewriting it to make it compatible with COM. The return type must be int to keep it compatible with HRESULT.

Simple for your example:

   void Login(string user, string password);

Becomes:

   [PreserveSig]
   int Login(string user, string password);

It is more convoluted when it is method with a non-void return type. You must then rewrite it the way the type library exporter does it. Adding it as an extra argument with the ref keyword. So:

   bool Login(string user, string password);

Becomes:

   [PreserveSig]
   int Login(string user, string password, ref bool success);

Keep in mind that the value of S_FALSE is 1, not 0.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • And now no automagic marshalling of .NET exceptions to HRESULTs is performed, is that right? So my C# method shouldn't throw exception in case of failure, but use a C-style approach throughout? What _would_ happen if it throws? – Mr. Boy Oct 15 '15 at 12:02