COM interop: C++ invoke the C# method with ref or out parameter

Answered COM interop: C++ invoke the C# method with ref or out parameter

  • Sunday, March 04, 2012 1:52 PM
     
     

    Hi,

    I use the COM to communicate between C# and C++. C# is the server, and C++ is the client.  in the server side:

    [C#]

    public interface IManaged
     {
            public string GetValue(string name, ref string refParam, out bool outParam);
    }                                                                                                                                                                                                                           Then how to invoke it at C++ side? I tried it with tlb things, failed. 

    Regards,

    Ming


    FM

All Replies

  • Monday, March 05, 2012 9:33 AM
     
     Answered Has Code

    Hello FFMM,

    1. The C# Interface and Implementation.

    1.1 I assume that you are aware of the ComVisibleAttribute and that you need to apply it to the IManaged interface as described in the OP.

    1.2 I have provided a sample implementation for IManaged for illustrative purposes :

        [ComVisible(true)]
        [Guid("5A735DF3-BDC5-42fa-A3CA-08C31429FECB")]
        [InterfaceType(ComInterfaceType.InterfaceIsDual)]
        public interface IManaged
        {
            string GetValue(string name, ref string refParam, out bool outParam);
        }
        [ComVisible(true)]
        [Guid("F3B78D8C-3901-4d20-BC09-C3C1B31E9A0E")]
        [ClassInterface(ClassInterfaceType.None)]
        public class ManagedImplementation : IManaged
        {
            public string GetValue(string name, ref string refParam, out bool outParam)
            {
                Console.WriteLine("Param [name]     : [{0:S}]", name);
                Console.WriteLine("Param [refParam] : [{0:S}]", refParam);
                refParam = "Return string";
                outParam = true;
                Console.WriteLine("Return [refParam] : [{0:S}]", refParam);
                Console.WriteLine("Return [outParam] : [{0}]", outParam);
                return "Finished";
            }
        }

    1.3 Note that I have declared IManaged as a dual interface so that it can be called from unmanaged C++ via IUnknown and IDispatch.

    1.4 I also assume that you have already registered the output assembly using REGASM.EXE.

    1.5 For my sample C# COM Server (above), my class library assembly is named "AFMClassLib.dll" and I have used REGASM.EXE to register the COM-visible parts. The output type library is AFMClassLib.tlb.

    1.6 When viewed using OLEVIEW.EXE, the IDL for the IManaged::GetValue() method is as follows :

        [
          odl,
          uuid(5A735DF3-BDC5-42FA-A3CA-08C31429FECB),
          version(1.0),
          dual,
          oleautomation,
            custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "AFMClassLib.IManaged")    
        ]
        interface IManaged : IDispatch {
            [id(0x60020000)]
            HRESULT GetValue(
                            [in] BSTR name, 
                            [in, out] BSTR* refParam, 
                            [out] VARIANT_BOOL* outParam, 
                            [out, retval] BSTR* pRetVal);
        };

    1.7 Later, when we #import AFMClassLib.tlb into a C++ client app, we will see that the signature for the IManaged::GetValue() is listed as follows :

          virtual HRESULT __stdcall GetValue (
            /*[in]*/ BSTR name,
            /*[in,out]*/ BSTR * refParam,
            /*[out]*/ VARIANT_BOOL * outParam,
            /*[out,retval]*/ BSTR * pRetVal ) = 0;

    2. C++ Client Code Using IUnknown.

    2.1 I have provided a sample C++ client function to call the ManagedImplementation class' implementation of the IManaged::GetValue() method :

    void DoTest_IUnknown()
    {
    	::CoInitialize(NULL);
    	
    	// Create an instance of the IManaged interface 
    	// using the implementation of ManagedImplementation.
    	IManagedPtr spIManaged = NULL;
    	
    	spIManaged.CreateInstance(__uuidof(ManagedImplementation));
    	
    	// Allocate various variables to be used in the call to
    	// IManaged::GetValue().
    	BSTR name = ::SysAllocString(L"AFM");
    	BSTR refParam = ::SysAllocString(L"Original string");
    	BSTR returnedBSTR = NULL;
    	VARIANT_BOOL outParam = VARIANT_FALSE;
    	
    	// Call the IManaged::GetValue() method.
    	spIManaged -> GetValue
    	(
            name,
            &refParam,
            &outParam,
            &returnedBSTR
        );
       
        // At this point, the interop marshaler will have changed 
        // "refParam" from "Original string" to "Return string".
        // The interop marshaler will first free the original BSTR
        // and then assign a new one.
        //
        // "outParam" will be assigned by IManaged::GetValue() to
        // VARIANT_TRUE.
        //
        // "returnedBSTR" will contain the string "Finished" which
        // is the return value of IManaged::GetValue().
        
        // Free all variables.
        ::SysFreeString(name);
        name = NULL;
        ::SysFreeString(refParam);
        refParam = NULL;
        ::SysFreeString(returnedBSTR);
        returnedBSTR = NULL;
    	
    	::CoUninitialize();    
    }

    2.2 Please read the comments that I have added to the code above.

    3. Client C++ Code Using IDispatch.

    3.1 I have provided a sample C++ client function to call the ManagedImplementation class' implementation of the IManaged::GetValue() method using IDispatch :

    void DoTest_IDispatch()
    {
    	::CoInitialize(NULL);
    	// Create an instance of the IDispatch interface 
    	// using the implementation of ManagedImplementation.	
    	IDispatchPtr spIDispatch = NULL;
    	
    	spIDispatch.CreateInstance(__uuidof(ManagedImplementation));
    	
    	DISPPARAMS dp;
    	memset(&dp, 0, sizeof(DISPPARAMS));
    	dp.cArgs = 3;
    	VARIANT VariantArg[3];
    	dp.rgvarg = VariantArg;
    	memset(VariantArg, 0, (sizeof(VARIANT))*(dp.cArgs));
    	// Note that paramater passing via IDispatch must be
    	// performed in reverse order : i.e. with the last
    	// parameter at 0 index position, etc.
    	V_VT(&(VariantArg[2])) = VT_BSTR;
    	V_BSTR(&(VariantArg[2])) = ::SysAllocString(L"AFM");
    	BSTR refParam = ::SysAllocString(L"Original string");
    	V_VT(&(VariantArg[1])) = (VT_BYREF | VT_BSTR);
    	V_BSTRREF(&(VariantArg[1])) = &refParam;
    	
    	VARIANT_BOOL outParam = VARIANT_FALSE;
    	V_VT(&(VariantArg[0])) = (VT_BYREF | VT_BOOL);
    	V_BOOLREF(&(VariantArg[0])) = &outParam;
    	// Define a VARIANT	that will hold the return value
    	// from the method call.
    	VARIANT varResult;
    	VariantInit(&varResult);
    	VariantClear(&varResult);
    	// Finally make the call
    	OLECHAR FAR* rgszNames = L"GetValue";
    	DISPID dispid = 0;
    	EXCEPINFO excepinfo;
    	// Always call IDispatch::GetIDsOfNames()
    	// to obtain the Dispatch ID of the method
    	// to be invoked.
    	//
    	// This is especially important for COM 
    	// methods that are implemented in managed
    	// code.
    	spIDispatch -> GetIDsOfNames
    	( 
    		IID_NULL,                  
    		&rgszNames,  
    		1,          
    		LOCALE_SYSTEM_DEFAULT,                   
    		&dispid          
    	);
    	UINT nErrArg = 0;
    	memset (&excepinfo, 0, sizeof(excepinfo));
    	HRESULT hr = spIDispatch -> Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dp, &varResult, &excepinfo, &nErrArg);
    	
    	BSTR returnedBSTR = V_BSTR(&varResult);
    	
    	// Always clear all VARIANTs at the end of the function.
    	for (int i = 0; i < 3; i++)
    	{
    		VariantClear(&VariantArg[i]);
    	}
    	
    	VariantClear(&varResult);
    	      	
    	::CoUninitialize();
    }

    3.2 When you use IDispatch::Invoke(), always call IDispatch::GetIDsOfNames() to get the dispatch ID of the intended method to call. This is especially important for methods that are implemented by managed code.

    3.3 Please read the various comments that I have added to the code above.

    4. I hope you will benefit from the examples given.

    4.1 Post again if you need furher clarifications.

    4.2 Best of luck.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/