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
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/
- Marked As Answer by Lie YouModerator Friday, March 09, 2012 2:35 AM

