Callbacks von native C++ in C++/CLI
-
Hallo,
ich muß ein SDK anbinden und in diesem Zug eine Callbackfunktion in einer nativen C++-KLasse ein Callback in .NET senden lassen. Normal programmiere ich in C#, mache aus dem Grund nun aber einen Ausflug nach C++/CLI, also falls die Frage blöd sein sollte entschuldigt *g*- Mittlerweile habeich rausgefunden, das ich aus einem Delegate einen Funktionszeiger machen kann und diesen (theoretisch) in die unmanaged Welt schicken kann und ihn dort aufrufen kann, also habe ich folgenden Testcode implementiert:
#pragma unmanaged class umTest { public: void Caller(void callback()) { callback(); }; }; #pragma managed using namespace System; using namespace System::Runtime::InteropServices; public delegate void callback(); ref class mTest { private: int a; int b; public: mTest(int a, int b) { this->a = a; this->b = b; callback ^d = gcnew callback(this,&mTest::callTest); IntPtr ptr = Marshal::GetFunctionPointerForDelegate(d); umTest *umT = new umTest(); umT->Caller(ptr.ToPointer()); } void callTest() { cout << "muh" <<endl; }; };
Das funktioniert so aber leider nicht, denn er gibt mir folgenden Fehler:
Fehler 2 error C2664: 'umTest::Caller': Konvertierung des Parameters 1 von 'void *' in 'void (__cdecl *)(void)' nicht möglich 36
Nach etwas googeln habe ich herausgefunden, dass __cdecl eine Aufrufkonvention hat und der andere offensichtlich __stdcall und das nicht einfach convertiert werden kann.
Nun habe ich ausserdem ergoogelt, das es das folgende Attribut gibt, mit dem ich bestimmen kann, welche Aufrufkonvention der Zeiger haben soll, den ich aus dem delegate hole:
[UnmanagedFunctionPointerAttribute(CallingConventi on.Cdecl)]
Nur wo muß ich das nun deklarieren? wenn ich das so mache (Wie man es in C# machen würde):
[UnmanagedFunctionPointerAttribute(CallingConventi on.Cdecl)] public delegate void callback();
Gibt er mir einen Fehler aus!
In meinem SDK ist ein Decoder der ein jpeg erzeugt. Ich möchte durch den Callback anzeigen lassen, wann das bild vollständig ist und gleichzeitig den Zeiger auf die byte-Daten und die länge mit dem Callback senden lassen. Im .NET angekommen will ich es dann in den managed speicher marshalen.
Glaubt ihr das würde gehen? Wo schreibe ich bei C++/CLI denn Attribute hin? Was mache ich falsch? Habt ihr evtl. eine elegantere Lösung?
Vielen Dank im Voraus,
David
-
Hab es selbst hinbekommen, falls jemand dasselbe Prob hat oder es jemanden interessiert hier die Lösung (Die Frage war evtl. wirklich etwas blöd *g+):
#include <iostream> #pragma unmanaged class umTest { private: unsigned char * data; public: void Caller(int size, void callback(int, unsigned char*)) { std::cout<<"UNMANAGED: Caller(int size="<<size<<", void callback(int, unsigned char*)="<<&callback<<") aufgerufen"<<std::endl; data = new unsigned char[size]; for(int i = 0; i < size; i++) data[i] = i; std::cout<<"UNMANAGED: Daten erzeugt"<<std::endl; callback(size,data); std::cout<<"UNMANAGED: Zurückgesprungen in Caller(...)"<<std::endl; if(data == NULL) std::cout<<"UNMANAGED: Zeiger zeigt auf NULL"<<std::endl; }; }; #pragma managed #pragma managed using namespace System; using namespace System::Runtime::InteropServices; public delegate void callback(int, unsigned char*); ref class mTest { private: array<unsigned char> ^destination; int size; public: mTest() { callback ^d = gcnew callback(this,&mTest::callTest); IntPtr ptr = Marshal::GetFunctionPointerForDelegate(d); umTest *umT = new umTest(); umT->Caller(5,static_cast<void (__cdecl *)(int, unsigned char*)>(ptr.ToPointer())); this->print(); } void callTest(int size, unsigned char* test) { this->size = size; Console::WriteLine("MANAGED: callTest(...) aufgerufen"); destination = gcnew array<unsigned char>(size); Marshal::Copy(IntPtr(test),destination,0,size); Console::WriteLine("MANAGED: Daten aus ungesichertem in gesicherten Speicher kopiert"); delete[](test); test = NULL; Console::WriteLine("MANAGED: Daten freigegeben"); this->print(); }; void print() { Console::WriteLine("Inhalt von destination[]:"); for(int i = 0; i<size; i++) Console::WriteLine("destination[{0}]={1}",i,destination[i]); }; };
-
So, ein neuer Fehler:
Wenn ich das ganze im Debug ausführe funktioniert alles wunderbar, sobald ich es aber im release-mode mache springt er nach beenden der Callback-Funktion erneut in die Funktion und scheitert dann beim 2. mal Marshalen....
Kann mir einer sagen warum? Bin für jede Hilfe dankbar.
Gruß David
-
Normalerweise verwendet man das gcroot-Template um Callbacks zu managed Code zu machen...
Siehe auch:
http://groups.google.de/group/microsoft.public.dotnet.languages.vc/msg/577d07f3522b5a25?hl=de&