2010-10-29 3 views
1

Dans le Compact Framework 3.5, je tente d'appeler un objet ActiveX qui a une signature de la fonction IDL:.Net Compact Framework - Appel objet ActiveX qui utilise [out] SAFEARRAY (float) *

HRESULT MyFunc([out] SAFEARRAY(float) *var) 

Le génération Interop crée le MSIL

[out] class [mscorlib]System.Array& marshal(safearray float32) 

Ce qui semble assez raisonnable, mais je continue à obtenir un « NotSupportedException ». Selon un article intitulé "Interop: Problèmes courants et techniques de débogage" (je ne peux pas poster plus d'un hyperlien, c'est le premier résultat google pour cette phrase), dans le premier point sous le titre "Marshaling", le cadre compact ne rassemble pas SAFEARRAYs correctement.

J'ai tenté de contourner ce problème, en manipulant la réponse décrite dans ce post sur le forum MSDN (Dernière entrée décrit sa méthode): http://social.msdn.microsoft.com/forums/en-US/clr/thread/6641abfc-3a9c-4976-a523-43890b2b79a2/

Alors, je l'ai créé la définition suivante:

[StructLayout(LayoutKind.Sequential)] 
struct SafeArray 
{ 
    public ushort dimensions;  // Count of dimensions in the SAFEARRAY 
    public ushort features;  // Flags to describe SAFEARRAY usage 
    public uint elementSize; // Size of an array element 
    public uint locks;   // Number of times locked without unlocking 
    public IntPtr dataPtr;  // Pointer to the array data 
    public uint elementCount; // Element count for first (only) dimension 
    public int lowerBound;  // Lower bound for first (only) dimension 
} 

Et redéfinie le IDL pour la signature de fonction:

HRESULT MyFunc([out] long *var) 

Et alors il utilise le code suivant:

IntPtr safeArrayPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(SafeArray))); 
SafeArray safeArray; 
safeArray.dimensions = 1; 
safeArray.features = 0; 
safeArray.elementSize = (uint)(Marshal.SizeOf(typeof(float))); 
safeArray.locks = 0; 
safeArray.elementCount = 6; 
safeArray.lowerBound = 0; 
safeArray.dataPtr = Marshal.AllocCoTaskMem((int)(safeArray.elementCount * safeArray.elementSize)); 

Marshal.StructureToPtr(safeArray, safeArrayPtr, false); 
int iTmp = safeArrayPtr.ToInt32(); 
MyFunc(out iTmp) 

Alors que le code semble réussir, lorsque je tente de relire les valeurs de données, en utilisant la fonction Marshal.Copy (DataPtr, myFloatArr, faux), je reçois tous les 0 pour les données, ce qui me dit que le pointeur que la DLL ActiveX obtient est probablement totalement faux et il est écrit dans l'oubli.

Avez-vous des suggestions sur ce que j'ai peut-être raté dans ces définitions, ou des suggestions pour d'autres façons d'aborder ce problème?

Merci à l'avance ...

Répondre

1

Eh bien, j'ai résolu celui-ci.
J'espère que ma réponse aidera les autres qui rencontrent le même problème. Le problème que je rencontrais était que la balise [out] dans une déclaration COM tlb signifie que tout ce que je passe sera écrasé par l'objet créé dans la bibliothèque COM. Une version assez compliquée du classique (et très élémentaire) "Passer par référence vs passer par valeur"

Donc, le marshaling correct est d'utiliser la définition de SafeArray que j'ai posté ci-dessus. Ne touchez pas la signature IDL elle-même - ce n'est pas une manière très propre de le faire.Au lieu de cela, utilisez ildasm sur la bibliothèque Interop générée pour modifier le il de:

[out] class [mscorlib]System.Array& marshal(safearray float32) 

à

[out] native int& 

puis réassembler avec ilasm qui produira une signature de la fonction C#

void MyFunc(out IntPtr var) 

Le le code appelant devient alors:

IntPtr ip; 
SafeArray safeArray; 
float []arrFloats; 
MyFunc(out ip); 
//Marshal the structure itself 
Marshal.StructureToPtr(safeArray, ip, false); 

//Marshal the data over to .NET 
float []arrFloats = new float[safeArray.elementCount]; 
Marshal.Copy(safeArray.dataPtr, arrFloats, 0, (int)safeArray.elementCount); 

Enfin, nous devons libérer la mémoire (souvenez-vous, nous avons changé la signature de la fonction afin que nous ne donnions pas suffisamment d'informations à .NET pour libérer la mémoire par elle-même.

//Don't forget to free both the structure and the object 
Marshal.FreeCoTaskMem(safeArray.dataPtr); 
Marshal.FreeCoTaskMem(ip); 
Questions connexes