Migration und Nutzung Migration und Nutzung von vorhandenem Codevon vorhandenem Code
InteroperabilityInteroperability
Migration und Nutzung Migration und Nutzung von vorhandenem Codevon vorhandenem CodeInteroperabilityInteroperability
Bernd MarquardtBernd MarquardtSoftware & ConsultingSoftware & [email protected]@go-sky.de
AgendaAgenda Das Problem des „alten“ CodesDas Problem des „alten“ Codes EinführungEinführung Interop-MöglichkeitenInterop-Möglichkeiten
DLL‘sDLL‘s COMCOM ActiveXActiveX Wrapper-KlassenWrapper-Klassen Unmanaged Code ruft managed Unmanaged Code ruft managed
Code aufCode auf Migration Migration ZusammenfassungZusammenfassung
EinführungEinführung
Grundsätzlich gibt es im Moment Grundsätzlich gibt es im Moment in der Windows-Welt zwei Arten in der Windows-Welt zwei Arten von Code:von Code: Unmanaged Code (Die „alte“ Welt)Unmanaged Code (Die „alte“ Welt)
„„Alter“ x86-MaschinencodeAlter“ x86-Maschinencode Wird sofort ausgeführtWird sofort ausgeführt
Managed Code (Die .NET-Welt)Managed Code (Die .NET-Welt) „„Neuer“, maschinenunabhängiger Neuer“, maschinenunabhängiger
IL-CodeIL-Code Wird zur Laufzeit in Wird zur Laufzeit in
Maschinencode übersetzt und Maschinencode übersetzt und ausgeführtausgeführt
EinführungEinführung
In beiden Welten gibt es In beiden Welten gibt es unterschiedliche „Codesorten“:unterschiedliche „Codesorten“: Unmanaged: Clients (EXE), DLL‘s Unmanaged: Clients (EXE), DLL‘s
mit API‘s, COM-Komponenten (als mit API‘s, COM-Komponenten (als DLL und als EXE), ActiveX-DLL und als EXE), ActiveX-Komponenten, „normale“ Komponenten, „normale“ KlassenbibliothekenKlassenbibliotheken
Managed: Clients (EXE), .NET-Managed: Clients (EXE), .NET-KomponentenKomponenten
EinführungEinführung
In den Code der „alten“ Welt In den Code der „alten“ Welt wurde sehr viel Geld investiertwurde sehr viel Geld investiert
Es ist unmöglich, in kurzen Zeit Es ist unmöglich, in kurzen Zeit alles wieder neu für .NET zu alles wieder neu für .NET zu programmierenprogrammieren
Welche Möglichkeiten gibt es Welche Möglichkeiten gibt es in .NET, mit „altem“ Code in .NET, mit „altem“ Code weiterzuarbeiten?weiterzuarbeiten?
EinführungEinführung
Was macht die gemeinsame Was macht die gemeinsame Nutzung von .NET und unman. Nutzung von .NET und unman. Code Code schwerschwer?? Monolithischer Code („eine EXE“)Monolithischer Code („eine EXE“)
Was macht die gemeinsame Was macht die gemeinsame Nutzung von .NET und unman. Nutzung von .NET und unman. Code Code leichtleicht?? Aufteilung des gesamten Projektes Aufteilung des gesamten Projektes
in kleine Einheiten (Client, DLL‘s, in kleine Einheiten (Client, DLL‘s, COM- und ActiveX-Komponenten) COM- und ActiveX-Komponenten)
DLL (API) aus man. CodeDLL (API) aus man. Code
Unmanaged DLL‘s mit Attribut Unmanaged DLL‘s mit Attribut „DllImport“„DllImport“[DllImport („Kernel32.Dll“)]public extern static void Sleep(uint uiMSec);
[DllImport („Kernel32.Dll“, EntryPoint = „Sleep“)]public extern static void Schlaf(uint uiMSec);
[DllImport („User32.Dll“, CharSet = CharSet::Ansi)]public extern static void MessageBox(int hWnd, string strMsg, string strTitel, uint uiFlags);
Beispiel für Standard-Beispiel für Standard-MarshallerMarshaller
int SomeFunc(int sel, void* buf, int size);
[DllImport("some.dll")]static extern int SomeFunc(int sel, [MarshalAs(UnmanagedType.LPArray)] byte[] buf,
int size);
[DllImport("some.dll", EntryPoint="SomeFunc")]static extern int SomeFunc_String(int sel,
StringBuilder sb, int size);
[DllImport("some.dll", EntryPoint="SomeFunc")]static extern int SomeFunc_Int(int sel,
ref int i, int size);
Methode in einer Standard-DLL Methode in einer Standard-DLL
Einfache Deklaration erfordert u.U. Einfache Deklaration erfordert u.U. spezielle Dekodierungspezielle Dekodierung
““Maßgeschneiderte” Versionen Maßgeschneiderte” Versionen möglichmöglich
Array als ReferenzparameterArray als Referenzparameter// Originaldeklaration der Library-Funktion// Originaldeklaration der Library-Funktion// int f(int** ppArray, int* pSize);// int f(int** ppArray, int* pSize);
[DllImport("PInvokeLib.dll")][DllImport("PInvokeLib.dll")]public static extern int f(ref IntPtr array, ref int size);public static extern int f(ref IntPtr array, ref int size);
// Unmanaged Speicherbereich anlegen// Unmanaged Speicherbereich anlegenIntPtr buffer = Marshal.AllocCoTaskMem( IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf(size) * array2.Length); Marshal.SizeOf(size) * array2.Length);
// Daten in Speicherbereich kopieren// Daten in Speicherbereich kopierenMarshal.Copy(array2, 0, buffer, array2.Length); Marshal.Copy(array2, 0, buffer, array2.Length);
int res = f(ref buffer, ref size); int res = f(ref buffer, ref size);
int[] arrayRes = new int[size];int[] arrayRes = new int[size];
// Daten zurückkopieren// Daten zurückkopierenMarshal.Copy(buffer, arrayRes, 0, size);Marshal.Copy(buffer, arrayRes, 0, size);
// Unmanaged Speicherbereich freigeben// Unmanaged Speicherbereich freigebenMarshal.FreeCoTaskMem(buffer); Marshal.FreeCoTaskMem(buffer);
DLL (API) aus man. CodeDLL (API) aus man. Code System-DLL‘s und eigene DLL‘s System-DLL‘s und eigene DLL‘s
können aufgerufen werdenkönnen aufgerufen werden Typ-Konvertierungen der Typ-Konvertierungen der
Standardtypen werden automatisch Standardtypen werden automatisch gemachtgemacht
Aufruf ist etwas langsamer, als aus Aufruf ist etwas langsamer, als aus unmanaged Codeunmanaged Code
Interfaces als Parameter werden Interfaces als Parameter werden unterstütztunterstützt Können nicht als Ausgabeparameter Können nicht als Ausgabeparameter
dienendienen Arrays von Basistypen können benutzt Arrays von Basistypen können benutzt
werdenwerden Attribute steuern KonvertierungAttribute steuern Konvertierung
DllImport – wann nicht?DllImport – wann nicht?
Beispiel: char* DoSomething(...)Beispiel: char* DoSomething(...)
[DllImport („TheSuperLib“)][DllImport („TheSuperLib“)]extern „C“ String* DoSomething(...);extern „C“ String* DoSomething(...);
Problem hier:Problem hier: Der Speicher, auf den der Zeiger Der Speicher, auf den der Zeiger
zeigt, kann nicht freigegeben zeigt, kann nicht freigegeben werdenwerden
DLL (API) aus man. CodeDLL (API) aus man. Code
Zweite Methode: IJW = „It just Zweite Methode: IJW = „It just works“works“
Geht nur mit managed C++ Geht nur mit managed C++ ExtensionsExtensions
Benötigte C/C++-Header-Datei Benötigte C/C++-Header-Datei muss eingefügt werdenmuss eingefügt werden Z.B.: #include <stdio.h>Z.B.: #include <stdio.h> Aufruf der API-Funktionen ist jetzt Aufruf der API-Funktionen ist jetzt
möglichmöglich
DLL (API) aus man. CodeDLL (API) aus man. Code
Vorteile von „IJW“:Vorteile von „IJW“: Kein „DllImport“ erforderlichKein „DllImport“ erforderlich Geringfügig schnellerGeringfügig schneller Bei Aufruf mehrerer unmanaged Bei Aufruf mehrerer unmanaged
API‘s: Die Daten müssen nur einmal API‘s: Die Daten müssen nur einmal „gemarshalled“ werden„gemarshalled“ werden
COM aus man. CodeCOM aus man. Code Erzeugung eines Runtime-Callable-Erzeugung eines Runtime-Callable-
Wrappers (RCW) durch .NETWrappers (RCW) durch .NET
Unmanaged CodeUnmanaged Code
COM-ObjektCOM-Objekt
Managed CodeManaged Code
RuntimeRuntimeCallableCallableWrapperWrapper
Managed ClientManaged Client
COM aus man. CodeCOM aus man. Code
Wrapper von Hand erzeugen mit:Wrapper von Hand erzeugen mit: TblImp.EXETblImp.EXE
Wrapper automatisch erzeugen Wrapper automatisch erzeugen mit:mit: Visual Studio .NETVisual Studio .NET Befehl „Add Reference“Befehl „Add Reference“
Ist in vielen Fällen ausreichendIst in vielen Fällen ausreichend Datenkonvertierung findet stattDatenkonvertierung findet statt
Z.B.: String nach BSTRZ.B.: String nach BSTR Geht nicht bei Custom MarshalingGeht nicht bei Custom Marshaling
COM aus man. CodeCOM aus man. Code Problem bei der Benutzung der Problem bei der Benutzung der
COM-Komponenten:COM-Komponenten: Durch den Garbage Collector von .NET Durch den Garbage Collector von .NET
wird die COM-Komponente nicht wird die COM-Komponente nicht „sofort“ freigegeben„sofort“ freigegeben
Das kann zu unerwartetem Verhalten Das kann zu unerwartetem Verhalten führenführen
Lösungen:Lösungen: Aufruf von System.GC.Collect (nicht Aufruf von System.GC.Collect (nicht
gut!)gut!) Aufruf von Aufruf von
System.Runtime.InteropServices.MarSystem.Runtime.InteropServices.Marshal. ReleaseComObject shal. ReleaseComObject
Rückgabewerte und Rückgabewerte und ExceptionsExceptions COM meldet Fehler via Result-COM meldet Fehler via Result-
Code zurückCode zurück .NET-Methoden werfen Exceptions.NET-Methoden werfen Exceptions Runtime setzt Konzepte ineinander Runtime setzt Konzepte ineinander
umum HRESULT gibt Fehlerklasse anHRESULT gibt Fehlerklasse an Zusätzliche Informationen Zusätzliche Informationen
werden via IErrorInfo erhaltenwerden via IErrorInfo erhalten Ein COM-Objekt sollte dieses Ein COM-Objekt sollte dieses
Interface implementierenInterface implementieren
Early und Late BindingEarly und Late Binding
Early Binding: Objekterzeugung Early Binding: Objekterzeugung mit „new“mit „new“
Late Binding: (IDispatch)Late Binding: (IDispatch) Benutzung von Benutzung von
Type.GetTypeFromProgID, Type.GetTypeFromProgID, Activator.CreateInstance und Activator.CreateInstance und Type.InvokeMemberType.InvokeMember
Parameterübergabe in einem ArrayParameterübergabe in einem Array
Aufruf eines COM-Servers Aufruf eines COM-Servers (late)(late)namespace namespace LateBoundClientLateBoundClient
{{
using System.Reflection;using System.Reflection; ...... Type typ;Type typ; Object obj;Object obj; Object[] prms = new Object[2];Object[] prms = new Object[2]; int r;int r;
typ = Type.GetTypeFromProgID(„MyLib.MyServer");typ = Type.GetTypeFromProgID(„MyLib.MyServer"); obj = Activator.CreateInstance(typ); obj = Activator.CreateInstance(typ); prms[0] = 10; prms[0] = 10; prms[1] = 150; prms[1] = 150;
r = (int)typ.InvokeMember(„aMethod", r = (int)typ.InvokeMember(„aMethod", BindingFlags.InvokeMethod, null, obj, BindingFlags.InvokeMethod, null, obj,
prms);prms); ... ...
}}
Wrapper-KlassenWrapper-Klassen
Managed Wrapper-Klassen Managed Wrapper-Klassen umhüllen eine normale umhüllen eine normale unmanaged C++-Klasseunmanaged C++-Klasse
Die Hüllklasse funktioniert wie ein Die Hüllklasse funktioniert wie ein ProxyProxy
Die Hüllklasse hat die gleiche Die Hüllklasse hat die gleiche Funktionalität wie die C++-KlasseFunktionalität wie die C++-Klasse
Es wird immer ein „Klassenpaar“ Es wird immer ein „Klassenpaar“ erzeugt, bzw. zerstörterzeugt, bzw. zerstört
Wrapper-KlassenWrapper-Klassenclass CppClass { public: // Konstruktor CppClass() { …} // Destruktor ~CppClass() { …} // Methoden void native_f() { …} };
__gc class ManClass { public: // Konstruktor ManClass() { m_pC = new CppClass(); } // Freigabe ~ManClass() { delete m_pC; } // Methoden void managed_f() { m_pC->native_f(); } private: CppClass * m_pC; };
Wrapper-KlassenWrapper-Klassen Erzeugung von Wrapper-KlassenErzeugung von Wrapper-Klassen
Erzeugung einer man. C++-KlasseErzeugung einer man. C++-Klasse Data member: Zeiger vom Typ Data member: Zeiger vom Typ
der C++-Klasseder C++-Klasse In der managed Klasse müssen die In der managed Klasse müssen die
Konstruktoren nachgebildet werdenKonstruktoren nachgebildet werden In der Dispose-Methode der In der Dispose-Methode der
managed Klasse die Instanz der managed Klasse die Instanz der unmanaged Klasse zerstörenunmanaged Klasse zerstören
Alle Methoden in der managed Alle Methoden in der managed Klasse implementieren Klasse implementieren
Wrapper-KlassenWrapper-Klassen
DestruktorenDestruktoren Sowohl den Destruktor als auch die Sowohl den Destruktor als auch die
Dispose-Methode implementierenDispose-Methode implementieren Wrapper-Klasse von IDisposable Wrapper-Klasse von IDisposable
ableitenableiten „„Selbst-gesteuertes“ Zerstören des Selbst-gesteuertes“ Zerstören des
Objektes durch Dispose-AufrufObjektes durch Dispose-Aufruf Dispose vergessen: Garbage Dispose vergessen: Garbage
Collector schlägt mit dem Collector schlägt mit dem Destruktor zuDestruktor zu
System.GC.SupressFinalize in der System.GC.SupressFinalize in der Dispose-Methode nicht vergessenDispose-Methode nicht vergessen
Probleme mit WrappernProbleme mit Wrappern
Variable ParameteranzahlVariable Parameteranzahl Daten-MarshalingDaten-Marshaling Default ArgumenteDefault Argumente Destruktoren (Garbage Collector)Destruktoren (Garbage Collector) PropertiesProperties Vergleichsoperatoren mit Vergleichsoperatoren mit
OverloadingOverloading
.NET-Code aus unman. .NET-Code aus unman. CodeCode Man kann .NET-Komponenten aus Man kann .NET-Komponenten aus
altem Code aufrufenaltem Code aufrufen CCW = COM Callable WrapperCCW = COM Callable Wrapper using using
System.Runtime.InteropServices;System.Runtime.InteropServices; Attribut Attribut
[ClassInterface(ClassInterfaceType.AutoDual[ClassInterface(ClassInterfaceType.AutoDual)])] vor der Klasse definieren vor der Klasse definieren
Assembly in das Verzeichnis des Assembly in das Verzeichnis des Clients kopierenClients kopieren
TypeLib mit TLBEXP erzeugenTypeLib mit TLBEXP erzeugen Registrieren mit REGASMRegistrieren mit REGASM
Migration: Grundsätzliche Migration: Grundsätzliche ÜberlegungenÜberlegungen Es gibt verschiedene Arten von Es gibt verschiedene Arten von
CodeCode AlgorithmenAlgorithmen API-intensiver CodeAPI-intensiver Code MFC-intensiver Code (UI-Code)MFC-intensiver Code (UI-Code) ATL-intensiver Code (COM/DCOM)ATL-intensiver Code (COM/DCOM)
Alle Codearten erfordern einen Alle Codearten erfordern einen anderen Migrationsaufwandanderen Migrationsaufwand
Grundsätzliche Grundsätzliche ÜberlegungenÜberlegungen Was erschwert eine Migration?Was erschwert eine Migration?
Monolithischer CodeMonolithischer Code Die Verwendung von Klassen-Die Verwendung von Klassen-
BibliothekenBibliotheken Besonders dann, wenn die Besonders dann, wenn die
Klassen als Code in das Projekt Klassen als Code in das Projekt aufgenommen wurden aufgenommen wurden Später: Später: Wrapper-KlassenWrapper-Klassen
Viel UI-CodeViel UI-Code Templates (Generische Datentypen)Templates (Generische Datentypen) Multiple InheritanceMultiple Inheritance
Grundsätzliche Grundsätzliche ÜberlegungenÜberlegungen Was erschwert eine Migration?Was erschwert eine Migration?
Klassen, welche im Destruktor die Klassen, welche im Destruktor die Resourcenverwaltung implementiert Resourcenverwaltung implementiert haben (Thema: Garbage Colletion)haben (Thema: Garbage Colletion)
Code, der intensiv mit Zeigern Code, der intensiv mit Zeigern arbeitetarbeitet
Code mit vielen API-AufrufenCode mit vielen API-Aufrufen
Grundsätzliche Grundsätzliche ÜberlegungenÜberlegungen Was erleichtert die Migration?Was erleichtert die Migration?
Komponenten-orientierter CodeKomponenten-orientierter Code Mehrschichtiges Applikations-Mehrschichtiges Applikations-
DesignDesign Wichtig ist eine gute Trennung Wichtig ist eine gute Trennung
von UI und Business-Logikvon UI und Business-Logik Code in COM-Komponenten ist Code in COM-Komponenten ist
nützlichnützlich Code in DLL‘s ist nützlichCode in DLL‘s ist nützlich
Grundsätzliche Grundsätzliche ÜberlegungenÜberlegungen Eine Migration sollte nach Eine Migration sollte nach
Möglichkeit in mehreren kleinen Möglichkeit in mehreren kleinen Schritten durchgeführt werdenSchritten durchgeführt werden
Was kann zunächst ohne Was kann zunächst ohne Migration wiederverwendet Migration wiederverwendet werden?werden? COM-KomponentenCOM-Komponenten Windows-DLL‘sWindows-DLL‘s Der erste Schritt soll kurz sein!Der erste Schritt soll kurz sein! COM-Komponenten und DLL‘s sind COM-Komponenten und DLL‘s sind
aber auch am leichtesten zu aber auch am leichtesten zu portierenportieren
Grundsätzliche Grundsätzliche ÜberlegungenÜberlegungen Komplexe Codeteile zunächst Komplexe Codeteile zunächst
einmal als unmanaged Code einmal als unmanaged Code übernehmenübernehmen
„„Unsaubere“ Codeteile in Unsaubere“ Codeteile in managed Code portierenmanaged Code portieren
Resourcen-intensive in managed Resourcen-intensive in managed Code portieren (Ausnutzung des Code portieren (Ausnutzung des Garbage Collectors)Garbage Collectors)
Grundsätzliche Grundsätzliche ÜberlegungenÜberlegungen Vor der Migration:Vor der Migration: Test- Szenarien mit der „alten“ Test- Szenarien mit der „alten“
Applikation generierenApplikation generieren Während der Migration kann immer Während der Migration kann immer
wieder vergleichend getestet wieder vergleichend getestet werdenwerden
Migrieren Sie keine Projekte, die Migrieren Sie keine Projekte, die im Moment in der im Moment in der Entwicklungsphase sindEntwicklungsphase sind
ZusammenfassungZusammenfassung
Microsoft hat viel für die Microsoft hat viel für die Interoperabilität von “altem” Interoperabilität von “altem” und .NET-Code getanund .NET-Code getan
Die Investitionen werden Die Investitionen werden weitgehend geschütztweitgehend geschützt
Eine Applikation kann Schritt für Eine Applikation kann Schritt für Schritt migriert werdenSchritt migriert werden
Wichtig: Komponenten müssen Wichtig: Komponenten müssen vorhanden seinvorhanden sein
Further InformationFurther Information Microsoft Visual C++ .NET Compiler Migration GuideMicrosoft Visual C++ .NET Compiler Migration Guide
http://www.gotdotnet.com/team/cplusplus/articles/compmig.dohttp://www.gotdotnet.com/team/cplusplus/articles/compmig.docc
Managed Extensions for C++ Migration GuideManaged Extensions for C++ Migration Guidehttp://www.gotdotnet.com/team/cplusplus/articles/mcmigrate.http://www.gotdotnet.com/team/cplusplus/articles/mcmigrate.docdoc
GlossaryGlossary Managed Code:Managed Code: Code, der unter der Kontrolle der .NET- Code, der unter der Kontrolle der .NET-
Common Language Runtime läuft.Common Language Runtime läuft. Unmanaged Code:Unmanaged Code: Prozessor-abhängiger Prozessor-abhängiger
Maschinencode.Maschinencode. CLRCLR: Common Language Runtime; gemeinsame : Common Language Runtime; gemeinsame
Runtime für alle .NET-Sprachen.Runtime für alle .NET-Sprachen. GCGC: Garbage Collector; verwaltet den Speicher : Garbage Collector; verwaltet den Speicher
bei .NET-Anwendungen; zerstört bei Bedarf die nicht bei .NET-Anwendungen; zerstört bei Bedarf die nicht mehr referenzierten Objekte im Speicher.mehr referenzierten Objekte im Speicher.
(MS-) IL-Code(MS-) IL-Code: Microsoft Intermediate Language; : Microsoft Intermediate Language; Platform-unabhängiger Zwischencode, der von Platform-unabhängiger Zwischencode, der von den .NET-Sprach-Compilern erzeugt wird; wird vom den .NET-Sprach-Compilern erzeugt wird; wird vom JIT-Compiler zur Laufzeit in nativen Maschinencode JIT-Compiler zur Laufzeit in nativen Maschinencode übersetzt.übersetzt.
JIT-CompilerJIT-Compiler: Just In Time-Compiler; übersetzt zur : Just In Time-Compiler; übersetzt zur Laufzeit den MS-IL-Code in native Maschinensprache.Laufzeit den MS-IL-Code in native Maschinensprache.
ILDASMILDASM: Tool zum Anschauen des MS-IL-Codes in einer : Tool zum Anschauen des MS-IL-Codes in einer .NET-EXE-Datei..NET-EXE-Datei.
RCW:RCW: Runtime Callable Wrapper; Wrapper-Klasse, die Runtime Callable Wrapper; Wrapper-Klasse, die den Aufruf von „unmanaged“ COM-Komponenten aus den Aufruf von „unmanaged“ COM-Komponenten aus „managed“ Code ermöglicht„managed“ Code ermöglicht