0

I am trying to pass an array of interfaces from C# to C++/CLI. Here is the code:

// *** SafeArrayTesting_PlusPlus.cpp  ***
#include "stdafx.h"  
#include <comdef.h>

using namespace System;    
using namespace System::Runtime::InteropServices;

namespace SafeArrayTesting_PlusPlus {

public ref class MyCppClass
{       
    public:
    MyCppClass();
    ~MyCppClass();
    void SetMyInterfaces(
        array<SafeArrayTesting_Sharp::MyInterface^>^ myInterfaces);
};

MyCppClass::MyCppClass(){}
MyCppClass::~MyCppClass(){}

void MyCppClass::SetMyInterfaces(array<SafeArrayTesting_Sharp::MyInterface^>^ 
     myInterfaces)
{
    // Create safearray
    SAFEARRAY  *safeArrayPointer;
    SAFEARRAYBOUND arrayDim[1];    // one dimensional array
    arrayDim[0].lLbound= 0;
    arrayDim[0].cElements= myInterfaces->Length;

    safeArrayPointer = SafeArrayCreate(VT_UNKNOWN,1,arrayDim);

    // copy ints to safearray
    for (long lo= 0;lo<myInterfaces->Length;lo++)
    {           
        IntPtr myIntPtr = Marshal::GetIUnknkownForObject(myInterfaces[lo]);
        SafeArrayPutElement(
            safeArrayPointer, 
            &lo, 
            static_cast<void*>(myIntPtr)
            ); 
    }

    // do something with the safearray here - area XX
}}

// *** SafeArrayTesting_Main.cs ***
using SafeArrayTesting_PlusPlus;
using SafeArrayTesting_Sharp;

namespace SafeArrayTesting_Main
{

class SafeArrayTesting_Main
{
    static void Main()
    {
        var myCppClass = new MyCppClass();
        MyInterface myInterface = new MyClass();
        myCppClass.SetMyInterfaces(new[]{ myInterface });
    }
}}  

// *** SafeArrayTesting_Sharp.cs ***
using System;
using System.Runtime.InteropServices;

namespace SafeArrayTesting_Sharp
{
    [ComVisible(true)]
    public interface MyInterface
    {
        int MyInt { get; set; }
        string MyString { get; set; }
        DateTime MyDateTime { get; set; }
    }

    [ComVisible(true)]
    public class MyClass : MyInterface
    {
        public int MyInt{get;set;}
        public string MyString{get;set;}
        public DateTime MyDateTime{get; set;}
    }

// Just to please the compiler; bear with me. 
class DummyClass { static void Main() { } }
}

As written here, the code runs and compiles cleanly. However, when running the "area XX" part, I get a System.Runtime.InteropServices.SEHException.

The XX code is just a single line which calls an auto-generated method accepting a SAFEARRAY pointer. Here is the declaration of this method (from a .tlh file):

virtual HRESULT __stdcall put_SafeArray (
        /*[in]*/ SAFEARRAY * pRetVal ) = 0;

I actually think this method converts the SAFEARRAY back to a .NET array - it's all part of a conversion project my company is running at the time. So there is no alternative to using a SAFEARRAY.

Anyway, it would really surprise me if the code without the XX part is bug-free; I'm quite a novice when it comes to C++. Can you help me spot some of the problems? If anyone can suggest a better way of testing the validity of the SAFEARRAY that would also be a help.

(By the way, this is a more complex variation of the question SafeArrayPutElement method throws System.AccessViolationException , in which I was just passing an array of ints from C# to C++/CLI.)

6
  • Why do you need a SAFEARRAY of your interfaces - what is the XX code doing with that array? Commented Aug 12, 2010 at 6:05
  • Thanks for asking, Saxon Druce. I have edited the question so that it now describes this more precisely. Commented Aug 12, 2010 at 6:57
  • Do you have access to the source code of the object with the put_SafeArray() method? I think it will depend on what type of SAFEARRAY that code is expecting. Commented Aug 12, 2010 at 7:12
  • Unfortunately, I don't think I have access to the source code put_SafeArray() method. Commented Aug 13, 2010 at 9:45
  • Typo: ... the source code of the put_SafeArray() method. Commented Aug 13, 2010 at 10:22

1 Answer 1

3

Several problems. For one, you don't actually store a VARIANT in the array. This is ultimately not going anywhere, a SafeArray cannot store references to managed objects. The garbage collector moves objects around, it cannot see references held by unmanaged code so it cannot update the reference.

At best, you could create an array of VT_UNKNOWN or VT_DISPATCH. But you can't get the COM interface pointer for these managed objects, they are not [ComVisible]. When you fix that, you'd use Marshal.GetIDispatchForObject() or Marshal.GetIUnknownForObject() to get the interface pointer to store in the array.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! I have tried to implement your suggestions (and I have updated the code shown in the question). However, it still fails when I call the put_SafeArray() method. Is there a simpler way to decide if the SAFEARRAY has been initialized and copied to correctly?
I don't know what "it still fails" means.
Calling the put_SafeArray() method still throws a SEHException.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.