2010-08-12 4 views
0

J'essaie de passer un tableau d'interfaces de C# à C++/CLI. Voici le code:Passer un tableau d'interfaces de 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() { } } 
} 

Comme indiqué ici, le code s'exécute et se compile proprement. Cependant, lors de l'exécution de la partie "zone XX", j'obtiens un System.Runtime.InteropServices.SEHException.

Le code XX est juste une ligne unique qui appelle une méthode générée automatiquement acceptant un pointeur SAFEARRAY. Voici la déclaration de cette méthode (à partir d'un fichier .tlh):

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

je pense que cette méthode convertit le SAFEARRAY retour à un tableau .NET - tout cela fait partie d'un projet de conversion ma société est en cours d'exécution à la temps. Il n'y a donc pas d'alternative à l'utilisation d'un SAFEARRAY.

Quoi qu'il en soit, cela me surprendrait vraiment si le code sans la partie XX est sans bug; Je suis un novice quand il s'agit de C++. Pouvez-vous m'aider à identifier certains des problèmes? Si quelqu'un peut suggérer une meilleure façon de tester la validité de la SAFEARRAY, ce serait également une aide.

(Soit dit en passant, c'est une variante plus complexe de la question SafeArrayPutElement method throws System.AccessViolationException, où je ne faisais que passer un tableau de ints de C# à C++/CLI.)

+0

Pourquoi avez-vous besoin d'un SAFEARRAY de vos interfaces - que fait le code XX avec ce tableau? –

+0

Merci de votre demande, Saxon Druce. J'ai édité la question pour qu'elle la décrit maintenant plus précisément. – user181813

+0

Avez-vous accès au code source de l'objet avec la méthode put_SafeArray()? Je pense que cela dépendra du type de SAFEARRAY attendu par le code. –

Répondre

3

Plusieurs problèmes. Pour un, vous ne stockez pas réellement un VARIANT dans le tableau. Cela ne va nulle part, SafeArray ne peut pas stocker de références aux objets gérés. Le garbage collector déplace les objets, il ne peut pas voir les références détenues par le code non managé, donc il ne peut pas mettre à jour la référence. Au mieux, vous pouvez créer un tableau de VT_UNKNOWN ou de VT_DISPATCH au mieux. Mais vous ne pouvez pas obtenir le pointeur d'interface COM pour ces objets gérés, ils ne sont pas [ComVisible]. Lorsque vous corrigez cela, vous devez utiliser Marshal.GetIDispatchForObject() ou Marshal.GetIUnknownForObject() pour obtenir le pointeur d'interface à stocker dans le tableau.

+0

Merci! J'ai essayé de mettre en œuvre vos suggestions (et j'ai mis à jour le code indiqué dans la question). Cependant, il échoue quand j'appelle la méthode put_SafeArray(). Existe-t-il un moyen plus simple de décider si le SAFEARRAY a été initialisé et copié correctement? – user181813

+0

Je ne sais pas ce que "ça échoue encore" veut dire. –

+0

L'appel de la méthode put_SafeArray() renvoie toujours une exception SEHException. – user181813

Questions connexes