2010-08-12 70 views
0

我想从C#接口数组传递给C++/CLI。这里是代码:从C#到C++/CLI传递一组接口

// *** 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() { } } 
} 

正如这里写的,代码运行和编译干净。但是,在运行“区域XX”部分时,我得到一个System.Runtime.InteropServices.SEHException

XX代码只是一个调用接受SAFEARRAY指针的自动生成方法的单行。下面是这个方法的声明(从.tlh文件):

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

其实,我觉得这个方法可转换SAFEARRAY回.NET阵列 - 这是我的公司在运行的转换项目的一部分时间。所以没有其他选择使用SAFEARRAY。

无论如何,如果没有XX部分的代码是无bug的,那真的会让我感到惊讶;当谈到C++时,我是一个新手。你能帮我发现一些问题吗?如果任何人都可以提出更好的方法来测试SAFEARRAY的有效性,那也是一种帮助。

(顺便说一句,这是问题SafeArrayPutElement method throws System.AccessViolationException,其中我只是路过整数数组从C#到C++/CLI的更复杂的变化。)

+0

为什么你需要一个界面的SAFEARRAY - XX代码对这个数组做什么? – 2010-08-12 06:05:41

+0

谢谢你,萨克森德鲁士。我编辑了这个问题,以便更精确地描述这个问题。 – user181813 2010-08-12 06:57:59

+0

您是否有权使用put_SafeArray()方法访问对象的源代码?我认为这取决于代码所期待的SAFEARRAY类型。 – 2010-08-12 07:12:11

回答

3

几个问题。首先,你实际上并没有在数组中存储一个VARIANT。这最终不会发生,SafeArray不能存储对托管对象的引用。垃圾回收器移动对象,无法看到由非托管代码持有的引用,因此无法更新引用。

充其量,您可以创建一个VT_UNKNOWN或VT_DISPATCH数组。但是你无法获得这些管理对象的COM接口指针,它们不是[ComVisible]。当你解决这个问题时,你可以使用Marshal.GetIDispatchForObject()或Marshal.GetIUnknownForObject()来获取存储在数组中的接口指针。

+0

谢谢!我试图实现你的建议(并且我更新了问题中显示的代码)。但是,当我调用put_SafeArray()方法时,它仍然失败。是否有更简单的方法来确定SAFEARRAY是否已被初始化并正确复制? – user181813 2010-08-13 09:49:36

+0

我不知道“它仍然失败”的意思。 – 2010-08-13 10:52:05

+0

调用put_SafeArray()方法仍会引发SEHException。 – user181813 2010-08-13 19:19:30