2012-04-09 10 views
1

Ich habe ein gemeinsames Konstrukt in einem nicht verwalteten Win32 C++ DLL:Ungültige Managed/Unmanaged Typ Kombination mit Embedded, dynamisch zugeordnete Array

// FirstElemPtrContainer.h 
#include "stdafx.h" 

typedef unsigned char elem_type; // a byte 

typedef struct FirstElemPtrContainer { 
    unsigned char num_elems; 
    void *allocd_ary; 
} FirstElemPtrContainer; 

Die void * in der Struktur gemeint ist, einen Zeiger auf die erste enthalten Element eines zugewiesenen Byte-Arrays.

Die DLL, die diese Definition verwendet dann exportiert Funktionen zuzuweisen, bevölkern, und ausplanen die Struktur:

// The exported allocator function. 
extern "C" _declspec(dllexport) 
    FirstElemPtrContainer *BuildStruct(int elem_count) 
{ 
    FirstElemPtrContainer *fepc_ptr = new FirstElemPtrContainer; 
    fepc_ptr->num_elems = elem_count; 
    elem_type *ary = new elem_type[fepc_ptr->num_elems]; 
    for (int i = 0; i < fepc_ptr->num_elems; i++) 
    { 
     ary[i] = ((i + 1) * 5); // multiples of 5 
    } 
    fepc_ptr->allocd_ary = ary; 

    return fepc_ptr; 
} 

// The exported deallocator function. 
extern "C" _declspec(dllexport) void 
    DestroyStruct(FirstElemPtrContainer *fepc_ptr) 
{ 
    delete[] fepc_ptr->allocd_ary; 
    delete fepc_ptr; 
} 

Diese funktionieren gut für eine native Anrufer.

In C#, ich versuche, diese gleiche Struktur über PInvoke zu beschreiben: wie so

[StructLayout(LayoutKind.Sequential)] 
public struct FirstElemPtrContainer 
{ 
    public byte num_elems; 
    [MarshalAs(UnmanagedType.LPArray, 
     ArraySubType = UnmanagedType.U1, SizeConst = 4)] 
    public IntPtr allocd_ary; 
} 

... und beschreiben den Anruf-Schnittstelle:

public static class Imports 
{ 
    [DllImport("MyLib", CallingConvention = CallingConvention.Winapi)] 
    public static extern IntPtr BuildStruct(int elem_count); 

    [DllImport("MyLib", CallingConvention = CallingConvention.Winapi)] 
    public static extern void DestroyStruct(IntPtr fepc_ptr); 
} 

Jetzt versuche ich meine Schnittstelle zu nennen:

class Program 
{ 
    const int NUM_ELEMS = 4; 
    static void Main(string[] args) 
    { 
     IntPtr fepc_ptr = Imports.BuildStruct(NUM_ELEMS); 
     if (fepc_ptr == IntPtr.Zero) 
     { 
      Console.WriteLine("Error getting struct from PInvoke."); 
      return; 
     } 

     FirstElemPtrContainer fepc = 
      (FirstElemPtrContainer)Marshal.PtrToStructure(fepc_ptr, 
     typeof(FirstElemPtrContainer)); 
     //... 
    } 
} 

die PtrToStructure() Aufruf gibt die Fehlermeldung „Can not Marschall Feld‚ 'allocd_ary' vom Typ MyLibInvoke .FirstElemPtrContainer ': Ungültige Kombination aus verwaltetem/nicht verwaltetem Typ (Int/UInt muss mit SysInt oder SysUInt gepaart werden). "

Sie können sehen, dass ich eine bestimmte Anzahl von Elementen fest codiert habe, von denen wir annehmen, dass der Anrufer sich daran hält. Ich habe auch eine ArraySubType-Klausel hinzugefügt, obwohl es keinen Unterschied zu machen scheint. Warum die Typ-Mismatch-Beschwerde?

Antwort

2

Ihre Struktur sollte wie folgt erklärt werden:

[StructLayout(LayoutKind.Sequential)] 
public struct FirstElemPtrContainer 
{ 
    public byte num_elems; 
    public IntPtr allocd_ary; 
} 

es muss so gemacht werden, da allocd_ary ein Zeiger auf nicht verwalteten Speicher und kann nicht durch die p/aufrufen Einweiser rangieren. Um den Inhalt von allocd_ary zu lesen, können Sie Marshal.Copy verwenden.

FirstElemPtrContainer fepc = (FirstElemPtrContainer)Marshal. 
    PtrToStructure(fepc_ptr, typeof(FirstElemPtrContainer)); 
byte[] ary = new byte[fepc.num_elems]; 
Marshal.Copy(fepc.allocd_ary, ary, 0, ary.Length); 

Ich vermute, dass CallingConvention.Winapi falsch ist und dass Sie mit CallingConvention.Cdecl werden sollte.

+0

Das stimmt. Ich habe Ihren ursprünglichen Vorschlag, die PInvoke-Definition zu ändern, um ein Byte [] zu enthalten, ausprobiert und einen anderen Fehler erhalten: "Es ist ein Konflikt zwischen dem Laufzeittyp des Arrays und dem in den Metadaten aufgezeichneten Sub-Typ aufgetreten." Ich habe bereits den Marshall.Copy() in meinem Code gemacht, also war buchstäblich alles, was ich tat, um das [MarshalAs()] Kauderwelsch zu kommentieren, das ich oben hatte und alles funktionierte. Dann kam ich zurück und sah, dass Sie genau das Gleiche vorgeschlagen hatten, was ich gerade getan hatte. – Buggieboy

+0

@Buggieboy Ich entschuldige mich dafür, dich in die Irre geführt zu haben. Ich hatte die Frage beim ersten Mal nicht genau genug gelesen. Aber wie wäre es mit einer Konvention? Wenn Sie es nicht explizit in Ihrem C++ - Code festlegen, ist es "Cdecl". –

+0

David - Nun, ich musste Visual Studio 2005 ein C++ Win32 DLL Projekt machen und 'extern' C '_declspec (dllexport)' vor meinen Funktionsdefinitionen haben, wenn das irgendwas sagt. Das "CallingConvention.Winapi" schien nichts zu schaden, also habe ich es so gelassen wie es ist. – Buggieboy