Migration und Nutzung von vorhandenem Code Interoperability.

38
Migration und Nutzung Migration und Nutzung von vorhandenem Code von vorhandenem Code Interoperability Interoperability

Transcript of Migration und Nutzung von vorhandenem Code Interoperability.

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

Questions and AnswersQuestions and Answers

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

Ihr Potenzial. Unser Antrieb.