(0) exportieren Drucken
Alle erweitern
Erweitern Minimieren

So arbeitet .NET mit Win32 und COM zusammen

Veröffentlicht: 18. Nov 2001 | Aktualisiert: 06. Dez 2004

Von Don Box

Die P/Invoke und Interop-Funktionen von .NET erlauben den wechselseitigen Aufruf von Funktionen in DLL-, COM- und .NET-Komponenten. Dies erfordert relativ wenige Anpassungen des vorhandenen Codes, so dass bestehende Anwendungen mit .NET-Anwendungen leicht gemischt werden können.

Auf dieser Seite

P/Invoke
Typisierte Übergänge
RCW und CCW
TLBIMP und TLBEXP
REGASM
Strategien
Fazit

Diesen Artikel können Sie hier lesen dank freundlicher Unterstützung der Zeitschrift:

sys1


Trotz aller Hoffnungen des Managements, der Aktionäre und der eifrigen Entwickler ist es einfach nicht möglich, jedes Stückchen Software, das jemals geschrieben wurde, auf der allen Sprachen gemeinsamen Laufzeitschicht CLR (common language runtime) so laufen zu lassen, dass es vom Microsoft .NET verwaltet wird, also als "managed code" läuft. Angesichts des Mangels an fähigen Programmierern ist es sehr unwahrscheinlich, dass viele Firmen 100 Prozent ihres Bestands an Win32-Anwendungen umstellen, wie leistungsfähig die neue Laufzeitschicht auch immer sein mag. Das bedeutet, dass die Welt des C, C++, Visual Basic und COM-Codes, wie sie vor der .NET-Zeit entstanden ist, lange Zeit friedlich neben den neuen verwalteten Komponenten weiterbestehen muss, die in einer .NET-Sprache entwickelt werden. Zum Glück ist die CLR in der Lage, beide Welten ohne großen Aufwand unter einen Hut zu bringen.

Damit der normale Maschinencode und der verwaltete Code (managed code) nebeneinander bestehen können, wird ein Mechanismus gebraucht, der dem verwalteten Code, der ja unter der Aufsicht der CLR (MSCOREE.DLL) läuft, die Zusammenarbeit und Integration mit dem außerhalb der MSCOREE.DLL laufenden traditionellen Code erlaubt. Das bedeutet, dass verwaltete Programme lange genug in einer Art "unverwalteten Modus" laufen müssen, um klassischen COM- oder Win32-Code ausführen zu können. Außerdem müssen unverwaltete Threads in der Lage sein, lange genug die MSCOREE.DLL benutzen zu können, um eine Methode eines verwalteten Typs aufrufen zu können.

Zum Glück bietet die CLR beide Arten von Übergängen an, so dass verwaltete und traditionelle DLLs im selben Prozess nebeneinander bestehen können und Aufrufe von einer DLL in die andere unabhängig von der Frage erfolgen können, welcher der beteiligten Partner von der CLR behütet wird oder nicht. Bild B1 zeigt ein typisches Beispiel für einen Prozess, wie er sich im Jahre 2001 im .NET gestaltet. Ein Teil des Codes läuft unter der Aufsicht der MSCOREE.DLL, ein anderer Teil aber nicht. Solange nicht das gesamte Betriebssystem auf den verwalteten Code umgestellt wird, wird dies der Stand der Dinge sein. Die eigentliche Plattform, nämlich Windows, wird ja in Form von traditionellen DLLs implementiert, also nicht mit verwalteten DLLs.

09Migration011

B1 Die Integration von verwaltetem und unverwaltetem Code.

P/Invoke

Der Aufruf einer traditionellen Win32-DLL von der CLR aus ist mit dem P/Invoke-Mechanismus relativ einfach (P steht für Plattform).
P/Invoke ist ein Mechanismus, mit dem man eine statische Methodendeklaration auf einen PE/COFF-Eintrittspunkt abbilden kann, der sich mit LoadLibrary/GetProcAddress auflösen lässt. Wie zuvor schon die JNI (Java Native Interface) und J/Direct benutzt P/Invoke zur Beschreibung des Stapelrahmens eine verwaltete Methodendeklaration, geht dann aber davon aus, dass der Körper der Methode von einer externen traditionellen DLL bereitgestellt wird. Im Gegensatz zur JNI lassen sich mit P/Invoke die Altbestände an DLLs importieren, die noch ohne jeden Gedanken an die CLR entwickelt wurden.

Zur Verdeutlichung, dass eine Methode in einer externen traditionellen DLL implementiert wird, kennzeichnen Sie die statische Methode als extern und geben ihr das Methodenattribut System.Runtime.InteropServices.DllImport. Das DllImport-Attribut weist die CLR darauf hin, welche Argumente beim Aufruf der Methode an LoadLibrary und GetProcAddress zu übergeben sind. Das in C# eingebaute DllImport-Attribut ist einfach ein Zweitname für System.Runtime.InteropServices.DllImport.

L1 Das DllImport-Attribut

namespace System.Runtime.InteropServices { 
 public class DllImportAttribute : Attribute { 
  public DllImportAttribute(String dllname); 
    // EntryPoint überschreibt den Methodennamen  
    // für GetProcAddress 
  public String            EntryPoint; 
    // ExactSpelling verbietet die  
    // W/A _@-Namenskodierung 
  public bool              ExactSpelling; 
    // cdecl, stdcall und so weiter 
  public CallingConvention CallingConvention;  
    // unicode/ansi/auto 
  public CharSet           CharSet; 
    // Funktion ruft SetLastError auf 
  public bool              SetLastError; 
    // Das Ergebnis der aufgerufenen Methode wird 
    // als HRESULT betrachtet, wie [retval] 
  public bool              TransformSig; 
 } 
}

Das DllImport-Attribut hat eine ganze Reihe von Parametern. Wie in Listing L1 gezeigt, verlangt das DllImport-Attribut zumindest die Angabe eines Dateinamens. Dieser Dateiname wird von der Laufzeitschicht dazu benutzt, vor dem Methodenaufruf die entsprechende DLL mit LoadLibrary zu laden. Als String für GetProcAddress dient der symbolische Name der Methode, sofern kein EntryPoint-Parameter für DllImport angegeben wird. Listing L2 zeigt zwei verschiedene Aufrufe der Sleep-Methode in der kernel32.dll. Das erste Beispiel geht davon aus, dass der Name der C#-Funktion mit dem Namen des Symbols in der DLL übereinstimmt. Das zweite Beispiel legt den gewünschten Eintrittspunkt in der DLL dagegen mit dem EntryPoint-Parameter fest.

L2 So wird das DllImport-Attribut eingesetzt

using System.Runtime.InteropServices; 
public class K32Wrapper { 
  [ DllImport("kernel32.dll") ] 
  public extern static void Sleep(uint msec); 
  [ DllImport("kernel32.dll", EntryPoint = "Sleep") ] 
  public extern static void Doze(uint msec); 
  [ DllImport("user32.dll") ] 
  public extern static uint MessageBox(int hwnd, String m,  
                                       String c, uint flags); 
  [ 
    DllImport("user32.dll", EntryPoint="MessageBoxW", 
              ExactSpelling=true, CharSet=CharSet.Unicode) 
  ] 
  public extern static uint UniBox(int hwnd, String m,  
                                   String c, uint flags); 
}

Vor dem Aufruf von Methoden, die Strings als Parameter haben, müssen Sie sich noch für Unicode oder ANSI entscheiden, und zwar entweder in der Methode selbst oder im dazugehörigen Typ. Davon hängt es ab, wie die Strings für den unverwalteten Code übersetzt werden. Mit dem CharSet-Parameter von DllImport können Sie festlegen, ob immer Unicode (CharSet.Unicode) oder ANSI (CharSet.Ansi) benutzt werden soll oder ob mit CharSet.Auto die zugrundeliegende Plattform entscheiden soll - Windows NT oder 2000 versus Windows 9x oder Millennium Edition (Me). Der Griff zu CharSet.Auto ähnelt der Entwicklung von Win32-Code in C mit dem Datentyp TCHAR, nur dass der Zeichentyp nicht beim Kompilieren festgelegt wird, sondern beim Laden. Daher kann dieselbe Binärdatei auf allen Versionen von Windows sauber und effizient arbeiten.

Unter Windows gibt es eine Reihe von speziellen Namenskodierungen, aus denen die Aufrufkonvention und die Zeichensätze hervorgehen. Wird CharSet auf CharSet.Auto gesetzt, hat der symbolische Name automatisch das Suffix W oder A, je nachdem, ob zur Laufzeit Unicode oder ANSI benutzt wird. Lässt sich das reine Symbol nicht finden, versucht die Laufzeitschicht es mit der Symbolform für die stdcall-Aufrufkonvention (so wird aus Sleep zum Beispiel _Sleep@4). Diese zusätzliche Kodierung von Informationen in den Namen lässt sich mit dem ExactSpelling-Parameter vom DllImport-Attribut unterdrücken.

L3 Hier wird DllImport mit TransformSig benutzt

using System.Runtime.InteropServices; 
public class OLE32Wrapper { 
  [  
    DllImport("ole32.dll",  
              EntryPoint="CoImpersonateClient") 
  ] 
  public extern static int CoImpersonateClient1(); 
  [  
    DllImport("ole32.dll",  
              EntryPoint="CoImpersonateClient",  
              TransformSig=true)  
  ] 
  public extern static void CoImpersonateClient2(); 
}

Beim Aufruf von Win32-Funktionen schließlich, die nach COM-Art mit HRESULTs arbeiten, haben Sie zwei Optionen. Normalerweise behandelt P/Invoke den HRESULT als einfachen 32-Bit-Wert, der von der Funktion zurückgegeben wird. Der Programmierer muss dann selbst für den Fehlertest sorgen. Etwas bequemer verläuft der Aufruf solch einer Funktion, wenn man den Parameter TransformSig=true für das DllImport-Attribut angibt. Dadurch wird die P/Invoke-Schicht angewiesen, die 32-Bit-Integer als COM-HRESULT zu betrachten und bei einem Fehler eine entsprechende COMException zu melden (Der Parameter TransformSig aus der Beta 1 wird in der Beta 2 PreserveSig heißen und die entgegengesetzte Bedeutung erhalten.)

Mit den beiden Deklarationen aus Listing L3 macht es der Aufruf von OLE32Wrapper.CoImpersonateClient1 erforderlich, das Ergebnis von Hand zu überprüfen und explizit auf die Fehlercodes in den HRESULTs zu reagieren. Da bei der nächsten Deklaration der Parameter TransformSig benutzt wurde, ist die CLR beim Aufruf von OLE32Wrapper.CoImpersonateClient2 angewiesen, die Fehlercodes in den HResults implizit auf COMExceptions umzusetzen, ohne dass der Programmierer explizit in diesen Prozess einzugreifen braucht. Im Falle der OLE32Wrapper.CoImpersonateClient2 hat die Methode aber den Ergebniswert void, weil die zugrundeliegende Funktion ebenfalls keinen Wert an den Aufrufer zurückgibt. Wäre die Methode mit einem Rückgabewert deklariert worden (zum Beispiel double), würde die P/Invoke davon ausgehen, dass die zugrundeliegende Funktion einen zusätzlichen Parameter hat, der sich als Referenz benutzen lässt (nach der Art des COM-Atributs [retval]). Diese Abbildung findet statt, wenn der TransformSig-Parameter auf true steht.

09Migration022

B2 Die Parameter auf dem Aufrufstapel.

Typisierte Übergänge

Beim Aufruf aus der Laufzeitschicht heraus oder in sie hinein werden die Parameter immer auf dem Stapel übergeben, wie in Bild B2 gezeigt. Diese Parameter sind sowohl für die Laufzeitschicht als auch für die Außenwelt Instanzen von Typen. Der Schlüssel zur Funktionsweise der Interoperabilitätsschicht liegt darin, dass jeder gegebene "Wert" zwei Typen hat, nämlich einen verwalteten Typ und einen unverwalteten Typ. Wichtiger noch, einige verwaltete Typen sind mit einem unverwalteten Typ isomorph. Das bedeutet, dass keine Konvertierung erforderlich ist, falls eine Instanz von solch einem Typ aus der Laufzeitschicht heraus übergeben werden muss. Allerdings sind viele Typen nicht isomorph und machen eine gewisse Umwandlung erforderlich, damit die Außenwelt eine Form des betreffenden Werts erhält, die sie versteht.

T1 Isomorphe und nicht isomorphe Typen

Typname

Typ

Beschreibung

Single

isomorph

Wird als Grundtyp übergeben (native type)

Double

isomorph

Wird als Grundtyp übergeben

SByte

isomorph

Wird als Grundtyp übergeben

Byte

isomorph

Wird als Grundtyp übergeben

Int16

isomorph

Wird als Grundtyp übergeben

Uint16

isomorph

Wird als Grundtyp übergeben

Int32

isomorph

Wird als Grundtyp übergeben

Uint32

isomorph

Wird als Grundtyp übergeben

Int64

isomorph

Wird als Grundtyp übergeben

Uint64

isomorph

Wird als Grundtyp übergeben

Eindimensionale Arrays mit isomorphen Typen

isomorph

Wird als Grundtyp übergeben

Alle anderen Arrays

nicht isomorph

Werden als Schnittstelle oder Safearray übergeben

Boolean

nicht isomorph

VARIANT_BOOL oder Win32-BOOL

Char

nicht isomorph

Win32-WCHAR oder CHAR

String

nicht isomorph

Win32-LPWSTR/LPSTR oder BSTR

Objekt

nicht isomorph

VARIANT (nur COM-Interoperabilitätsschicht)

Tabelle T1 zeigt eine Liste mit wichtigen isomorphen und nicht isomorphen Typen. Beim Aufruf einer externen Funktion, die als Argumente nur isomorphe Typen erwartet, ist keine Konvertierung erforderlich und der Aufrufer sowie die aufgerufene Funktion können sich trotz des Umstands, dass einer der beiden im nicht verwalteten Modus läuft, einen Stapelrahmen teilen. Handelt es sich bei mindestens einem Parameter um einen nicht isomorphen Typ, muss der Stapelrahmen in einem passenden Format an die Außenwelt von MSCOREE weitergeleitet werden. Die Konvertierung kann auch auf dem umgekehrten Wege erforderlich werden, wenn die aufgerufene Funktion Werte an den Aufrufer übergibt. Zum Glück (für den Programmierer) weiß der C#-Compiler schon anhand der Schlüsselwörter ref und out, in welche Richtung die Werte übertragen werden. Bild B3 skizziert diesen Effekt.

09Migration033

B3 Nicht isomorphe Parameter.

Mit dem Attribut MarshalAs können Sie steuern, wie ein gegebener Parameter (oder Feld in einer Struktur) übermittelt wird. Dieses Attribut zeigt an, welcher unverwaltete Typ der Welt außerhalb von MSCOREE präsentiert werden soll. Zumindest können Sie mit dem Attribut MarshalAs angeben, welcher Grundtyp zu einem gegebenen Parameter oder Feld passt. Für viele Typen wird die CLR passende Gegenstücke auswählen. Allerdings können Sie diese Vorgaben mit dem Attribut MarshalAs überschreiben. So zeigt der Code in Listing L4 zum Beispiel, wie man den CLR-Typ System.String mit dem Attribut MarshalAs auf eines der vier gängigen Win32-Formate abbilden kann. Mit Ausnahme des UnmanagedType.LPWStr-Parameters wird für alle Parameter eine Kopie des Strings erstellt, die zum zugrundeliegenden Format passt.

L4 MarshalAs mit Parametern

using System.Runtime.InteropServices; 
public class FooBarWrapper { 
// Diese Methode umhüllt eine externe Funktion, die 
// folgendermaß;en deklariert wird: 
// void _stdcall DoIt(LPCWSTR s1, LPCSTR s2, 
//                    LPTSTR  s3, BSTR s4); 
  [ DllImport("foobar.dll") ] 
  public static extern void DoIt( 
    [MarshalAs(UnmanagedType.LPWStr)] String s1, 
    [MarshalAs(UnmanagedType.LPStr)] String s2, 
    [MarshalAs(UnmanagedType.LPTStr)] String s3, 
    [MarshalAs(UnmanagedType.BStr)] String s4 
  ); 
}

Sie können nicht nur mit dem Attribut MarshalAs Feld für Feld oder Parameter für Parameter die Abbildung der Typen steuern, sondern darüber hinaus auch die zugrundeliegende Darstellung der Strukturen und Klassen. Insbesondere ist es mit den Attributen StructLayout und FieldOffset möglich, genau den Aufbau der Klassen und Strukturen im Speicher zu steuern. Das wird wichtig, sobald zum Beispiel Strukturen in die Außenwelt übergeben werden sollen. Der folgende Code zeigt eine entsprechend kommentierte C#-Struktur:

using System.Runtime.InteropServices; 
[ StructLayout(LayoutKind.Sequential) ] 
public struct PERSON_REP { 
  [ MarshalAs(UnmanagedType.BStr) ]  
  public String name; 
  public double age; 
  [ MarshalAs(UnmanagedType.VariantBool) ] 
  bool dead; 
}

Dieser Code entspricht der folgenden IDL-Definition für COM:

struct PERSON_REP { 
    BSTR name; 
    public double age; 
    VARIANT_BOOL dead; 
  };

RCW und CCW

Werden andere Objektreferenzen als System.String oder System.Object übergeben, so werden CLR-Objektreferenzen und COM-Objektreferenzen im Zuge der Weiterleitung der Parameter ineinander umgewandelt. Wenn eine Referenz auf ein CLR-Objekt über die MSCOREE-Grenze hinweg weitergegeben wird, wie in Bild B4, so wird eine von COM aufrufbare Hülle generiert, die als Stellvertreter für das CLR-Objekt dient (CCW, COM-callable wrapper). Wird andererseits eine Referenz auf ein COM-Objekt durch die MSCOREE-Grenze weitergeleitet, so wird eine von der Laufzeitschicht aufrufbare Hülle generiert, die als Stellvertreter für das COM-Objekt dient (RCW, runtime-callable wrapper). In beiden Fällen implementiert der Stellvertreter (der Proxy) alle Schnittstellen des zugrundeliegenden Objekts. Außerdem versucht der Stellvertreter, COM- und CLR-Mechanismen wie IDispatch, Objektpersistenz und Ereignisse (Events) auf das entsprechende Konstrukt der jeweils anderen Seite umzusetzen.

09Migration044

B4 RCWs und CCWs - Stellvertreter der besonderen Art.

Bei den Schnittstellen, die via RCWs oder CCWs die MSCOREE-Grenze überschreiten, ist die CLR auf die Anmerkungen angewiesen, die in der Definition der verwalteten Schnittstelle gemacht werden, um der zugrundeliegenden Weiterleitungsschicht (marshaling layer) entsprechende Hinweise für die Übersetzung der Typen geben zu können. Diese Hinweise stellen eine Obermenge der Hinweise dar, die ich gerade für P/Invoke beschrieben habe. Zu den zusätzlichen Aspekten, die definiert werden müssen, gehören UUIDs, vtable versus Dispatch versus duale Schnittstelle sowie die Fragen, wie mit IDispatch umgegangen werden soll und wie Arrays übersetzt werden. Diese Aspekte werden mit entsprechenden Attributen aus dem Namensraum System.Runtime.InteropServices zur Definition der verwalteten Schnittstelle hinzugefügt. Falls diese Attribute fehlen, stellt die CLR konservative Vermutungen darüber an, wie die Standardvorgaben für eine gegebene Schnittstelle und Methode sein sollten. Bei neuen verwalteten Schnittstellen, die von Grund auf neu definiert werden, ist es durchaus sinnvoll, diese Attribute explizit zu vergeben, falls die Schnittstellen außerhalb der gemeinsamen Laufzeitschicht vom .NET benutzt werden sollen.

09Migration054

B5 TLBIMP und TLBEXP.

TLBIMP und TLBEXP

Die Übersetzung von COM-Typdefinitionen (wie structs, Schnittstellen und so weiter) auf CLR-Verhältnisse lässt sich von Hand erledigen. In manchen Fällen ist dies sogar notwendig, wenn zum Beispiel keine genaue TLB verfügbar ist. Die Übersetzung der Typdefinitionen in die andere Richtung ist angesichts der allgegenwärtigen Reflexion in der CLR einfacher. Aber auch in diesem Fall ist man mit einem passenden Werkzeug besser beraten. Die CLR wird mit Code ausgeliefert, der eine recht ordentliche Übersetzung für Sie vornimmt, sofern die COM-TLBs hinreichend genau sind.

System.Runtime.InteropServices.TypeLibConverter kann zwischen TLBs und CLR-Baugruppen übersetzen. Die Methode ConvertAssemblyToTypeLib liest eine CLR-Baugruppe ein und wirft eine TLB mit den entsprechenden COM-Typdefinitionen aus. Alle Hinweise für diesen Übersetzungsvorgang (wie zum Beispiel MarshalAs) müssen in den Quelltypen als anwenderdefinierte Attribute an den Schnittstellen, Methoden, Feldern und Parametern gegeben werden. Die Methode ConvertTyeLibToAssembly liest eine COM-TLB ein und wirft eine CLR-Baugruppe aus, in der die entsprechenden CLR-Typbeschreibungen zu finden sind. Im SDK gibt es zwei Hilfsprogramme mit den Namen TLBIMP.EXE und TLBEXP.EXE, die diese Methoden aufrufen, auf der Kommandozeile aufrufbar sind und sich auch zusammen mit NMAKE einsetzen lassen. Bild B5 skizziert die Beziehung zwischen diesen beiden Programmen.

L5 C# als "bessere IDL"

using System; 
using System.Runtime.CompilerServices; 
using System.Runtime.InteropServices; 
[assembly: Guid("4c5025ef-3ae4-4128-ba7b-db4fb6e0c532") ] 
[assembly: AssemblyVersion("2.1") ] 
namespace AcmeCorp.MathTypes { 
  [  
    Guid("ddc244a4-c8b3-4c20-8416-1e7d0398462a"), 
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown) 
  ] 
  public interface ICalculator 
  { 
    double CurrentValue { get; } 
    void   Clear(); 
    void Add(double x); 
    void Subtract(double x); 
    void Multiply(double x); 
    void Divide(double x); 
  } 
}

Im Allgemeinen ist es einfacher, die Typen zuerst in einer CLR-fähigen Sprache zu definieren und dann die TLB zu generieren. Sehen Sie sich zum Beispiel den C#-Code aus Listing L5 an. Betrachtet man diesen Code als Pseudo-IDL-Datei, so kompiliert man ihn mit dem csc.exe und generiert dann mit dem tlbexp.exe eine TLB, die funktional mit der TLB identisch ist, wie sie der "echte" IDL-Code aus Listing L6 liefert. Der Vorteil der C#-Lösung besteht darin, dass die Typdefinitionen erweiterbar und leicht lesbar sind. Was man weder von der TLB noch von der IDL-Datei behaupten kann.

L6 Dieser IDL-Code liefert die gleiche TLB

[ 
  uuid(4C5025EF-3AE4-4128-BA7B-DB4FB6E0C532), 
  version(2.1) 
] 
library AcmeCorp_MathTypes 
{ 
  importlib("stdole2.tlb"); 
  [ 
    object, 
    uuid(DDC244A4-C8B3-4C20-8416-1E7D0398462A), 
    oleautomation, 
    custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9},  
           "AcmeCorp.MathTypes.ICalculator")     
  ] 
  interface ICalculator : IUnknown { 
    [propget]  
    HRESULT CurrentValue([out, retval] double* pRetVal); 
    HRESULT Clear(); 
    HRESULT Add([in] double x); 
    HRESULT Subtract([in] double x); 
    HRESULT Multiply([in] double x); 
    HRESULT Divide([in] double x); 
  } 
}

REGASM

Manche Entwickler möchten ihre CLR-Klassen für COM zugänglich machen, via CoCreateInstance. Um das zu ermöglichen, müssen einige Einträge in der Registrierdatenbank vorgenommen werden, die eine COM-CLSID an Ihre Baugruppe und Typ binden. Dafür gibt es das Programm REGASM.EXE. REGASM.EXE erwartet den Namen einer Baugruppe als Argument und schreibt für jede öffentliche Klasse die entsprechenden Registriereinträge.
Der InprocServer32-Eintrag verweist auf die MSCORE.DLL, die als COM-Fassade für Ihre Objekte auf CLR-Basis dient. Übrigens ist das REGASM.EXE nicht unbedingt erforderlich, denn man kann die CLR relativ einfach vom traditionellen Code aus starten und mit der Standard-AppDomain Typen laden und von jeder Klasse neue Instanzen anlegen. Listing L7 zeigt ein Beispiel dafür, und zwar anhand der Klasse System.Collections.Stack unter Visual Basic 6.0. Das ließe sich mit einem Moniker weiter automatisieren. Wenn Sie im Internet auf http://discuss.develop.com/archives/wa.exe?A2=ind0008&L=DOTNET&P=R63059 gehen, finden Sie eine Beschreibung des "NET Moniker", der es Ihnen erlaubt, mit den klassischen COM-Methoden CoGetObject oder GetObject unter Visual Basic 6.0 von jedem CLR-Typ Instanzen anzulegen.

L7 Die CLR lässt sich auch von Visual Basic aus benutzen

' Der Code benutzt mscorlib.tlb und mscoree.tlb 
Dim rt As mscoree.CorRuntimeHost 
Dim unk As IUnknown 
Dim ad As ComRuntimeLibrary.AppDomain 
Dim s As ComRuntimeLibrary.Stack 
Private Sub Form_Load() 
  Set rt = New mscoree.CorRuntimeHost 
  rt.Start 
  rt.GetDefaultDomain unk 
  Set ad = unk 
  Set s = ad.CreateInstance("mscorlib", _ 
                            "System.Collections.Stack").Unwrap 
  s.Push "Hello" 
  s.Push "Goodbye" 
  s.Push 42 
  MsgBox s.Pop() 
  MsgBox s.Pop() 
  MsgBox s.Pop() 
End Sub

09Migration066

B6 Umstieg auf das .NET mit partieller Portierung.

Strategien

Für die Umstellung des traditionellen Codes auf die neue gemeinsame Laufzeitschicht gibt es drei Basisstrategien. Bild B6 zeigt den Lösungsansatz mit der partiellen Portierung, in dem der Code von einer Sprache aus der Zeit vor der CLR (zum Beispiel Visual Basic 6.0) mechanisch in eine CLR-fähige Sprache übersetzt wird (zum Beispiel Visual Basic.NET oder C#). Diese Lösung stützt sich auf den Umstand, dass sich alle traditionellen Bibliotheken mit den Interop-Diensten in die CLR importieren lassen.

09Migration07

B7 Umstellung auf das .NET mit vollständiger Portierung des Codes.

Bild B7 zeigt den Lösungsansatz mit der vollständigen Portierung, bei dem der gesamte Quelltext in einer CLR-fähigen Sprache vollständig neu geschrieben wird und sich daher nicht mehr auf traditionelle Komponenten stützt. Diese Lösung ist davon abhängig, dass es von allen benutzten Bibliotheken verwaltete Versionen gibt. Außerdem ist sie davon abhängig, dass man dazu bereit ist, den Quelltext an alle stilistischen Unterschiede anzupassen, die vielleicht von den verwalteten Bibliotheken verlangt werden. Selbstverständlich ist dies auch die arbeitsintensivste Lösung. Sie hat aber den Vorteil, mit wesentlich weniger Interop-Übergängen auszukommen und auf (normalerweise) umfangreichere und besser konzipierte Bibliotheken zurückgreifen zu können.

09Migration088

B8 Der Sprung ins kalte Wasser: .NET via Binärimport.

Bild B8 skizziert sozusagen den "Sprung ins kalte Wasser". Dabei wird nichts am Quelltext geändert. Der Code wird mit Hilfe der Interop-Dienste in die CLR importiert. Dieser Lösungsansatz kommt ohne die Verfügbarkeit von verwalteten Versionen der benutzten Bibliotheken aus. Außerdem braucht der Entwickler keine nennenswerten Änderungen am Quelltext vorzunehmen. Dieser Lösungsansatz erfordert zudem den geringsten Arbeitsaufwand und mag auch den Vorteil einer geringen Anzahl von Interop-Übergängen haben, da die Anzahl der Aufrufe von Bibliotheksfunktionen (mit Interop-Übergang) pro Methode vermutlich geringer ist.

Fazit

Während sie stets auf etablierte Verfahren wie XML oder SOAP zurückgreifen können, bietet die CLR den Entwicklern für den Übergang von der alten Win32- und COM-Welt in die neue Welt von C# und den Webdiensten eine Reihe von Alternativen.

Microsoft führt eine Onlineumfrage durch, um Ihre Meinung zur MSDN-Website zu erfahren. Wenn Sie sich zur Teilnahme entscheiden, wird Ihnen die Onlineumfrage angezeigt, sobald Sie die MSDN-Website verlassen.

Möchten Sie an der Umfrage teilnehmen?
Anzeigen:
© 2014 Microsoft