0

Here is my C# code:

static void Main(string[] args)
{
    CrappyCOMService service = new CrappyCOMService();
    CrapStructure crapStructure = new CrapStructure();
    try
    {
        service.TestCrap(1337, "This is bananas.", out crapStructure);
    }
    catch (Exception exception)
    {
        Console.WriteLine(exception.ToString());
    }
    Console.WriteLine(crapStructure.ErrorCode);
    Console.WriteLine(crapStructure.ErrorMessage);
}

Here is my IDL:

[
    object,
    uuid(61B0BFF7-E9DF-4D7E-AFE6-49CC67245257),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface ICrappyCOMService : IDispatch{
    typedef
        [
            uuid(C65F8DE6-EDEF-479C-BD3B-17EC3F9E4A3E),
            version(1.0)
        ]
    struct CrapStructure {
        INT ErrorCode;
        BSTR ErrorMessage;
    } CrapStructure;
    [id(1)] HRESULT TestCrap([in] INT errorCode, [in] BSTR errorMessage, [out] CrapStructure *crapStructure);
};
[
    uuid(763B8CA0-16DD-48C8-BB31-3ECD9B9DE441),
    version(1.0),
]
library CrappyCOMLib
{
    importlib("stdole2.tlb");
    [
        uuid(F7375DA4-2C1E-400D-88F3-FF816BB21177)      
    ]
    coclass CrappyCOMService
    {
        [default] interface ICrappyCOMService;
    };
};

Here is my implementation:

STDMETHODIMP CCrappyCOMService::TestCrap(INT errorCode, BSTR errorMessage, CrapStructure *crapStructure) {
    memset(crapStructure, 0, sizeof(CrapStructure));
    crapStructure->ErrorCode = errorCode;
    crapStructure->ErrorMessage = errorMessage;
    return -1;
}

The Problem: When I return -1 in the above implementation of the TestCrap method, this results in an exception in the C# world. When this happens, crapStructure.ErrorCode is set to 0 and crapStructure.ErrorMessage is null, despite clearly being set to a value of 1337 and, "This is bananas," respectively.

The Question: How can I see the values of crapStructure.ErrorCode and crapStructure.ErrorMessage when TestCrap returns -1? I have verified that similar code works perfectly fine from C++ or if P/Invoking directly from C# instead of using a COM reference over C#.

Furthermore: If I return S_OK (otherwise known as 0) in the above implementation, then I get back 1337 and, "This is bananas," which is what I would expect/want to see even when HRESULT is not 0.

Alexandru
  • 12,264
  • 17
  • 113
  • 208
  • 4
    Don't return -1. For one thing it is not a valid HRESULT value. Second, you should be returning S_OK since your method is actually succeeding. Also, your method should not be declaring `crapStructure` as `[out]`, that implies the method allocates a new object that the caller takes ownership of, but that is not the case here. It should be declared as `[in, out]` instead. – Remy Lebeau Jul 20 '17 at 00:53
  • @RemyLebeau So, say you're programming some COM method...and you need to tell the caller whether you have succeeded or failed, and if you fail you need to return them an error message, what is the standard way you would determine if a function succeeded or not, and how would you return them an error message? Because from C++, I can simply use `raw_interfaces_only` and return HRESULT as -1. – Alexandru Jul 20 '17 at 00:57
  • 1
    implement `IErrorInfo` and `ISupportErrorInfo`. Have the method call `SetErrorInfo()` before exiting with a suitable error HRESULT (not -1). The caller can check the HRESULT and use `GetErrorInfo()` to get the details. See [Error Handling Interfaces](https://msdn.microsoft.com/en-us/library/windows/desktop/ms221510.aspx) and [Error Handling in COM](https://msdn.microsoft.com/en-us/library/windows/desktop/ff485842.aspx) on MSDN. – Remy Lebeau Jul 20 '17 at 01:01
  • I would read the wikipedia page for [`HRESULT`](https://en.m.wikipedia.org/wiki/HRESULT), which describes the standard way to pass additional data with an error. – Yakk - Adam Nevraumont Jul 20 '17 at 01:04
  • @RemyLebeau I followed along with this article and implemented it as it said: http://thrysoee.dk/InsideCOM+/ch06c.htm but now how do I get back the error details from C#? I don't see the interface for `IErrorInfo` in the managed wrapper its generated for me. – Alexandru Jul 20 '17 at 01:32
  • @Alexandru https://stackoverflow.com/questions/17103995/ – Remy Lebeau Jul 20 '17 at 02:02
  • @RemyLebeau https://stackoverflow.com/questions/45203898/how-do-you-properly-set-and-read-back-the-ierrorinfo-message-of-a-com-method-in A follow up, if you please sir. It should work according to your link but it doesn't appear to display the error message in my case, and I'm not sure what I'm doing wrong... – Alexandru Jul 20 '17 at 02:06
  • @RemyLebeau I think you need to revisit https://msdn.microsoft.com/en-us/library/windows/desktop/ff485842.aspx because you are wrong when you say -1 is not a valid HRESULT. It most definitely is. A successful HRESULT is just non-negative, but I can return anything I want as an error code for the HRESULT, and this is the ONLY way to set the error code in the managed world to what you'd like to see. Please visit this link: https://stackoverflow.com/questions/45203898/getting-the-error-message-of-a-com-method-called-from-c-sharp/45209749?noredirect=1#comment77392724_45209749 – Alexandru Jul 20 '17 at 11:42
  • @HansPassant A follow-up question for you, probably its up your alley: https://stackoverflow.com/questions/45215419/why-does-this-code-throw-system-accessviolationexception-when-called-from-a-c-sh – Alexandru Jul 20 '17 at 13:16
  • @Alexandru an HRESULT of -1 has the NT status and X bits set, which are reserved, and the facility is set to 2047, which is undefined, and an error code of 65535, which is not an NT status code. So no, -1 is not a valid HRESULT, because the resulting bits are nonsense. – Remy Lebeau Jul 20 '17 at 16:59
  • @RemyLebeau Then R/N/X bits can just be overwritten by the COM layer if need be (although I have yet to see this happen), so whether you set them to 0 or 1, what's it matter? The outcome will probably be the same from now until the end of COM. – Alexandru Jul 20 '17 at 17:03
  • @RemyLebeau There you go: `return MAKE_HRESULT(result != 0 ? 1 : 0, FACILITY_NULL, (result & 0xFFFF));` My error code, anyways, won't be above 0xFFFF. – Alexandru Jul 20 '17 at 21:01
  • 1
    @Alexandru for custom HRESULT values, you should use FACILITY_ITF instead of FACILITY_NULL – Remy Lebeau Jul 20 '17 at 21:45
  • @RemyLebeau Thanks Remy, good point! – Alexandru Jul 20 '17 at 21:49

0 Answers0