C++ CLI
« | 27 Feb 2021 | »Vor 10 Jahren gehörte neben Win32
und POSIX
noch eine dritte quasi-Plattform zum ersten Entwurf des damalig GATE Projektes.
Und das war dotNet, genau
genommen C++/CLI, also jene
Erweiterung von C++, mit der man
seit Studio 2005
verwalteten dotNet Code mit C++ kreuzen konnte.
Tja … gebraucht hat das keiner und am wenigsten ich selbst. … aber zumindest war es eine nette Erfahrung.
Folgende Info zu Visual Studio 2017 erregte neulich meine Aufmerksamkeit:
If your code needs to be safe or verifiable, then we recommend that you port it to C#.
Das steht bei der Info zum alten pure
und safe
Mode für C++/CLI.
in der MS Doku.
Es ist kein besonderes Kunststück mit C++ CLI eine dotNet Anwendung zu bauen.
Im sogenannten mixed
Mode kann man freie Pointer und native APIs gemeinsam
mit dotNet Assemblies nutzen … zumindest theoretisch.
Denn wenn die dotNet Security Features zur Geltung kommen, wird eine solche
“Mixed Assembly” schnell als Risiko eingestuft und nicht ausgeführt.
Für Bibliotheken ist das ein sprichwörtlicher Killer.
Doch genau dafür gab es den pure
Mode. Damit konnte man jeglichen C++ Code
dotNet-like kompilieren, jedoch nicht gegen native Bibliotheken linken.
Heißt also:
- Algorithmen, Datenformate und In-Memory-Operationen: gut
WINAPI
, DLLs, oder nicht-pure statische Libs: schlecht
Genau das faszinierte mich damals, denn so könnte man die Codes von ZLIB und Co als “pure” dotNet Assembly kompilieren und von C# oder VB.NET aus nutzen.
Aber auch der umgekehrte Weg war interessant: Ein C++ Framework, das optional als “echte” dotNet Assembly kompiliert werden kann.
Ein Problem dabei war das Aufbewahren von dotNet-Referenz Objekten in nicht-verwalteten C++ Objekten.
Pointer-Äquivalent für dotNet Objekt
Man kann zwar native Pointer in dotNet ref
-Klassen aufnehmen, aber nicht
umgekehrt.
Aber: Man kann eine ref
-Instanz in ein
GCHandle
kapseln und dieses als IntPtr
speichern und umgekehrt aus dem IntPtr
wieder ein GCHandle
machen. Und reine Integer kann man in ein natives
Objekt bekanntlich problemlos aufnehmen.
Dafür hatte ich damals ein kleines Template geschrieben, das aus einem
irgendwo gespeicherten intptr
eine nutzbare dotNet Referenz machen konnte:
1template<class T> Class CliHandle 2{ 3protected: 4 intptr_t& refaddr; // external intptr to store object 5 6public: 7 // bind to external `intptr` Instance 8 CliHandle(intptr_t& h = 0) : refaddr(h) {} 9 CliHandle(void*& ptr) 10 : refaddr(reinterpret_cast<intptr_t&>(ptr)) 11 { } 12 ~CliHandle() {} 13 14 // release object for intptr 15 void release() 16 { 17 if(this->refaddr) 18 { 19 System::Runtime::InteropServices::GCHandle gch( 20 System::Runtime::InteropServices::GCHandle::FromIntPtr( 21 System::IntPtr(this->refaddr))); 22 gch.Free(); 23 this->refaddr = 0; 24 } 25 } 26 27 // embed object in intptr 28 void set(T t) 29 { 30 this->release(); 31 System::Runtime::InteropServices::GCHandle gch( 32 System::Runtime::InteropServices::GCHandle::Alloc(t)); 33 this->refaddr = (intptr_t) 34 System::Runtime::InteropServices::GCHandle::ToIntPtr(gch); 35 } 36 37 // access object by intptr 38 T% get() 39 { 40 System::Runtime::InteropServices::GCHandle gch( 41 System::Runtime::InteropServices::GCHandle::FromIntPtr( 42 System::IntPtr(this->refaddr))); 43 return safe_cast<T%>(gch.Target); 44 } 45 46 T% operator*() 47 { 48 return this->get(); 49 } 50 51 T% operator->() 52 { 53 return this->get(); 54 } 55};
So bettet man also dotNet Objekte in native Strukturen als intptr_t
ein.
Und in nativen Callbacks kann man über diesen Trick aus dem intptr
wieder
eine dotNet Instanz machen und darauf dann Methoden ausführen.
Theoretisch lassen sich so dotNet Codes auch durch reine C Schichten durchschleifen … aber so weit habe ich es damals nicht getrieben.
Fazit
Naja, wie gesagt, gebraucht habe ich das nie und so flog die Idee aus dem GATE Konzept und ist heute auch nicht mehr vorhanden.
Trotzdem finde ich es Schade, das Microsoft diesen Pfad nun zugeschüttet hat, schließlich war er ja schon “ausgetrampelt” und gehbereit. Aber vermutlich war das so eine Nischenlösung, dass sie wirklich fast niemand gebraucht hat.
Wie auch immer … ich hatte zumindest kurzfristig etwas Spaß damit, diesen Weg zu entdecken.