Search code examples
c#c++comunmanagedmanaged

call c# from c++, return value


I am trying to call C# code from C++. So I chose the COM Interop way. Now I have C# code:

namespace ToBeCalled
{
    [Guid("9329feaf-b293-4093-a7d8-6128f52b30a6")]
    [ComVisible(true)]
    public interface IInterface
    {
        int Write(string toWrite);
    }
}

namespace ToBeCalled
{
    [Guid("e4105e40-2d6b-4b7c-ae42-d2f9c405a2a0")]
    [ComVisible(true)]
    public class ClassYouWantToUse : IInterface
    {
        public int Write(string toWrite)
        {
            System.Console.WriteLine(toWrite);
            return 1;
        }
    }
}

And c++ code

#import "...\\ToBeCalled.tlb"

int _tmain(int argc, _TCHAR* argv[])
{
    // Initialize COM.
    HRESULT hr = CoInitialize(NULL);

    // Create the interface pointer.
    ToBeCalled::IInterfacePtr piTest(__uuidof(ToBeCalled::ClassYouWantToUse));

    long lResult = 0;

    // Call the Add method.
    piTest->Write("hi", &lResult);

    wprintf(L"The result is %d\n", lResult);

    // Uninitialize COM.
    CoUninitialize();
    return 0;
}

When I try this compile, of course it says that Write doesn't take 2 arguments. I saw the MSDN, where they have example like this and they take the return value this way. So my question is, how can I obtain the return value of the function? When I update the call to

piTest->Write("hi");

than execution fails on Unhandled Exception. When I tried this example without returning value, so declaration of the return value for method is void, than everything works ok.


Solution

  • Your server code is OK and you don't need to use an out parameter.

    The client code should look simply like this:

    long result = piTest->Write("hi");
    

    If you look inside generated .tlh file, you will see that signature is something like:

    long Write(_bstr_t toWrite);
    

    This is a generated wrapper code, and you are probably confusing it with raw COM call which is also a part of the tlh file and looks like this:

    virtual HRESULT __stdcall raw_Write(BSTR toWrite, long* pRetVal) = 0;
    

    The wrapper is more convenient: it calls the raw COM call internally and handles the return value - thus, the return value is normally returned from the function (instead of as an [out, retval] argument), and if the result of raw call denotes an error (a failed HRESULT) a _com_error exception is thrown.

    For details, you can take a look at another generated file, with tli extension, which contains the implementation of wrapper method.