locked
Invoke .NET method via COM, with a ref array RRS feed

  • Question

  • Hi all,

    I have a .NET DLL that exposes a method that looks like this in C#:

    void TestMethod(ref TestClass[] parameter)

    Where TestClass is a regular C# class that has a COM-visible interface, and I'm supposed to call the method with an array with one element. ​I can't figure out how to invoke this via COM from a C++ program.

    That parameter becomes a SAFEARRAY**. Currently, I'm doing this, where m_Object is a smart pointer to the class that has TestMethod:

    SAFEARRAY *ret = SafeArrayCreateVector(VT_VARIANT, 0, 1);
    VARIANT* current = NULL;
    HRESULT hrx = SafeArrayAccessData(ret, (LPVOID*)&current);
    ITestClassPtr item;
    item.CreateInstance(__uuidof(TestClass));
    current->vt = VT_UNKNOWN;
    current->punkVal = item.GetInterfacePtr();
    SafeArrayUnaccessData(ret);
    HRESULT hr = (*m_Object)->TestMethod(&ret);

    ​This results in 0x80131533 "A mismatch has occurred between the runtime type of the array and the sub type recorded in the metadata".

    I have also tried:

    SAFEARRAY* ret = SafeArrayCreateVector(VT_UNKNOWN, 0, 1);
    ITestClassPtr item;
    item.CreateInstance(__uuidof(TestClass));
    long i = 0;
    SafeArrayPutElement(ret, &i, item);
    HRESULT hr = (*m_Object)->TestMethod(&ret);

    Which results in an unhelpful 0x80131600.

    I do not build the DLL, and I can't debug into it. I've searched around the web, and I can find lots of examples for how to invoke a C# DLL via COM, but none that covers passing ref arrays of classes.

    Any idea?

    Tuesday, June 23, 2020 5:12 PM

All replies

  • Show us the C# declaration for TestClass and its COM visible interface.

    Is the C# TestMethod function also a member function of a COM visible interface?

    • Edited by RLWA32 Tuesday, June 23, 2020 5:28 PM added question
    Tuesday, June 23, 2020 5:20 PM
  • public interface ITestClass { string AField {get;set;} } public class TestClass : ITestClass { public string AField {get;set;} }

    public interface ITestObject
    { void TestMethod(ref TestClass[] parameters); }

    public class TestObject : ITestObject
    { public void TestMethod(ref TestClass[] parameters){} }

    The real-world situation is more complicated, but it boils down to that. The entire assembly is ComVisible(true).
    Wednesday, June 24, 2020 6:51 AM
  • This is what I used to create a SAFEARRAY of VT_DISPATCH which was successfully marshaled to the C# COM server.

    The example function takes an array of interface pointers and a count of items in the array.  The ISimpleObject interface in the example corresponds to your ITestClass interface.

    SAFEARRAY * SafeArrayFromIFaces(ISimpleObject **ppdisp, ULONG cItems)
    {
    	SAFEARRAY *psa = NULL;
    	ULONG nElements = cItems;
    	SAFEARRAYBOUND bound = { nElements, 0 };
    	HRESULT hr;
    
    	psa = SafeArrayCreate(VT_DISPATCH, 1, &bound);
    
    	if (psa)
    	{
    		for (LONG i = 0; i < cItems; i++)
    		{
    			hr = SafeArrayPutElement(psa, &i, ppdisp[i]);
    			if (FAILED(hr))
    				printf_s("SafeArrayPutElement failed with HRESULT 0x%X\n", hr);
    		}
    	}
    
    	return psa;
    }
    

    The C# COM object in my example that corresponds to your TestObject class is SimpleParent (ISimpleParent interface).  So the call to pass the array of interface pointers from unmanaged C++ to the C# COM server was

    	ISimpleObjectPtr ptrTest(__uuidof(SimpleObject)), ptrTest2(__uuidof(SimpleObject));
    	ISimpleParentPtr pParent(__uuidof(SimpleParent));
    
    	if (pParent)
    	{
    		ISimpleObject *soArray[2]{};
    
    		ptrTest->put_FloatProperty(1.0f);
    		ptrTest2->put_FloatProperty(2.0f);
    
    		soArray[0] = ptrTest;
    		soArray[1] = ptrTest2;
    
    		SAFEARRAY *psa = SafeArrayFromIFaces(soArray, 2);
    		if (psa)
    		{
    			pParent->TestMethod(&psa);
    			hr = SafeArrayDestroy(psa);
    		}
    	}
    

    Wednesday, June 24, 2020 11:09 AM