2011-01-15 8 views
3

Ich denke, das wird für C++/CLI-Gurus einfach sein.Marshalling einfache und komplexe Datentypen zu/von Objekt ^%/void *

Ich erstelle einen Wrapper, der leistungsstarke C++ - Klassen für C# WinForms-Anwendungen verfügbar macht. Alles ging gut mit einfachen bekannten Objekten und ich könnte auch eine Callback-Funktion zu delegieren. Aber jetzt bin ich ein bisschen verwirrt.

Die native C++ Klasse hat eine folgende Methode:

int GetProperty(int propId, void* propInOut) 

Zuerst dachte ich, ich Leere verwenden könnte * als IntPtr, aber dann fand ich heraus, dass ich es von C# zugreifen müssen. Also dachte ich über eine Wrapper-Methode:

int GetProperty(int propId, Object^ propInOut) 

aber als ich durch die C++ Quelle sah, fand ich heraus, dass das Verfahren die Objekte ändern muss. So offensichtlich brauche ich:

int GetProperty(int propId, Object^% propInOut) 

Jetzt kann ich nicht Objekte zu nativen Methoden übergeben, damit ich wissen müssen, wie sie in der Verpackung zu behandeln. Da der Anrufer immer wissen sollte, welche Art von Daten, die er/sie ist vorbei/Empfangen, erklärte ich einen Wrapper:

int GetProperty(int propId, int dataType, Object^% propInOut) 

Ich denke, ich habe es Arten Referenz- und Wert zu übergeben kann, zum Beispiel, ein int wie dies:

Object count = 100; // yeah, I know boxing is bad but this will not be real-time call anyway 
myWrapper.GetProperty(Registry.PROP_SMTH, DATA_TYPE_INT, ref count); 

habe ich nur noch ein paar Konstanten für alle datatype Datentypen ich brauche:

DATA_TYPE_INT, DATA_TYPE_FLOAT, DATA_TYPE_STRING, DATA_TYPE_DESCRIPTOR, DATA_TYPE_BYTE_ARRAY 

(DATA_TYPE_DESCRIPTOR ist eine einfache Struktur mit zwei Feldern: int Id und wstring Beschreibung - Dieser Typ wird ebenfalls umgebrochen, also denke ich, dass das Marshalling Daten einfach hin und her kopiert; Alle systemeigenen Strings sind Unicode).

Nun ist die Frage - wie die Wrapper-Methode für alle diese 5 Arten zu implementieren? Wenn ich einfach Object ^% auf etwas werfen kann (ist int, float sicher, das zu tun?) Und an die native Methode übergeben, wann muss ich pin_ptr verwenden und wann brauche ich ein komplexeres Marshalling auf native und zurück?

int GetProperty(int propId, int dataType, Object^% propInOut) 
{ 
    if(dataType == DATA_TYPE_INT) 
    { 
     int* marshaledPropInOut = ??? 
     int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut); 
     // need to do anything more? 
     return result; 
    } 
else 
    if(dataType == DATA_TYPE_FLOAT) 
    { 
     float* marshaledPropInOut = ??? 
     int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut); 
     // need to do anything more ? 
     return result; 
    } 
else 
    if(dataType == DATA_TYPE_STRING) 
    { 
     // will pin_ptr be needed or it is enough with the tracking reference in the declaration? 
     // the pointers won't get stored anywhere in C++ later so I don't need AllocHGlobal 
     int result = nativeObject->GetProperty(propId, (void*)marshaledPropInOut); 
     // need to do anything more? 
     return result; 
    } 
else 
    if(dataType == DATA_TYPE_BYTE_ARRAY) 
    { 
     // need to convert form managed byte[] to native char[] and back; 
     // user has already allocated byte[] so I can get the size of array somehow 

     return result; 
    } 
else 
    if(dataType == DATA_TYPE_DESCRIPTOR) 
    { 
     // I guess I'll have to do a dumb copying between native and managed struct, 
     // the only problem is pinning of the string again before passing to the native 

     return result; 
    } 

    return -1; 
} 

P.S. Vielleicht gibt es eine elegantere Lösung, um diese void * -Methode mit vielen möglichen Datentypen zu umhüllen?

Antwort

0

Es ist nicht unbedingt sinnvoll, ein C# -Objekt mit einem void * gleichzusetzen. Es gibt keine Möglichkeit, beliebige Daten zu marshalieren. Selbst mit einem Objekt weiß C# immer noch, um welchen Typ es sich darunter handelt, und für das Marshalling - das bedeutet eine Umwandlung von der C++ - Welt in C# oder umgekehrt - muss der Typ der Daten bekannt sein. Ein void * ist nur ein Zeiger auf den Speicher eines völlig unbekannten Typs. Wie würden Sie es in ein Objekt umwandeln, in dem der Typ bekannt sein muss?

Wenn Sie eine begrenzte Anzahl von Typen, wie Sie beschreiben, die aus der C# -Welt übergeben werden könnten, ist es am besten, mehrere Überladungen in Ihrem C++/CLI-Code, von denen jeder einen dieser Typen und dann Sie können den übergebenen Typ feststecken (falls erforderlich), ihn in ein void umwandeln, an Ihre C++ - Funktion übergeben, die ein void * annimmt, und dann entsprechend für den Typ zurückmarschieren.

Sie können eine case-Anweisung wie angegeben implementieren, aber was machen Sie, wenn Sie den übergebenen Typ nicht verarbeiten können?Die Person, die die Funktion von C# aufruft, kann nicht wissen, welche Typen akzeptabel sind, und der Compiler kann Ihnen nicht helfen, herauszufinden, dass Sie etwas falsch gemacht haben.