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?