Zusammenspiel

Dass die .NET-Entwicklungsumgebung mit unterschiedlichen Programmiersprachen klarkommt, hat sich inzwischen sicher rumgesprochen. Der folgende Praxistipp zeigt, wie Programmierer diese Interoperabilität in einem Projekt nutzen können.

vorlesen Druckansicht 26 Kommentare lesen
Lesezeit: 7 Min.
Von
  • Dr. Holger Schwichtenberg
Inhaltsverzeichnis

Die .NET-Laufzeitumgebung (Common Language Runtime (CLR)) ermöglicht eine von der Implementierungssprache unabhängige Nutzung von Komponenten. Auch die Vererbung von Schnittstellen und Klassen ist sprachübergreifend möglich. Die aus dem Component Object Model (COM) bekannten Interoperabilitätsprobleme bedingt durch zwei Interoperabilitätsmechanismen (IUnknown und IDispatch) findet man in .NET nicht mehr.

Das folgende Beispiel (‘InterOpBeispiel’, Download auf dem FTP-Server der iX) zeigt das Zusammenspiel zwischen verschiedenen Sprachen im .NET-Umfeld. Die einfache Anwendung gibt den Namen des angemeldeten Benutzers einschließlich der Domain aus, wobei die Aufgabe bewusst komplizierter gelöst ist als notwendig, da nicht nur drei .NET-Sprachen (VB.NET, C# und JScript.NET beteiligt sind), sondern zudem noch eine COM-Komponente und die C-DLL-basierten Win32-API (siehe Abbildung 1 ‘Beziehungen zwischen den Komponenten’). Hier wird also im wahrsten Sinne des Wortes mit Kanonen auf Spatzen geschossen.

Nutzungsbeziehungen zwischen den Komponenten aus dem InterOpBeispiel (Abb. 1).

Zur Sicherung der Abwärtskompatibilität mit ‘Unmanaged Code’ unterstützt die CLR weiterhin den Aufruf von COM-Komponenten über den Runtime Callable Wrapper (RCW) und klassischen Win32-API-Funktionen über einen Mechanismus mit Namen Platform Invocation Service (PInvoke oder P/Invoke abgekürzt). Benutzername und Domainname ermittelt die Anwendung mit Hilfe der Komponente Windows Scripting Runtime Library (Interner Name: IWshRuntimeLibrary), die sich mit dem Windows Script Host (WSH) installiert.

Um das Einlesen der Daten kümmert sich die C#-Klasse CSKlasse (Listing 2). Deren Methode GetUserInfo() speichert die Daten in der C#-Klasse UserInfo. Klasse UserInfo erbt von einer Visual-Basic-Klasse DomainInfo das String-Attribut Domain (Listing 1). Im Gegensatz zum Component Object Model (COM) ermöglicht das .NET Framework die sprachübergreifende Schnittstellen- und Implementierungsvererbung. Sogar von ‘visuellen’ Klassen wie Fenster und Steuerelementen kann geerbt werden.

Mehr Infos

Listing 1

Die VB-Klasse, die der C#-Klasse UserInfo als Oberklasse dient.

' VB-Komponente
' Holger Schwichtenberg
Namespace VBKomponente
Public Class DomainInfo
Public Domain As String
End Class
End Namespace
Mehr Infos

Listing 2

Die C#-Komponente, die die COM-Komponente aufruft.

// C#-Komponente
// Holger Schwichtenberg
using System;
namespace CSKomponente
{
public class UserInfo : VBKomponente.DomainInfo
{
public String Name;
}
public class CSKlasse
{
public UserInfo GetUserInfo()
{
IWshRuntimeLibrary.WshNetworkClass wshnet = new IWshRuntimeLibrary.WshNetworkClass();

UserInfo u = new UserInfo();
u.Name = wshnet.UserName;
u.Domain = wshnet.UserDomain;
return(u);
}
}
}

Der Kern des Projektes ist eine VB.NET-Anwendung. Die in der Klasse VBKlasse implementierten Startroutine main() ruft die Methode GetUserInfo() aus der CSKlasse auf und erhält als Antwort eine Instanz der C#-Klasse UserInfo (Listing 3).

Mehr Infos

Listing 3

Der Kern der Anwendung in Visual Basic.NET.

' Visual Basic.NET-Anwendung
' Holger Schwichtenberg
' --- Wrapper-Klasse
Class Win32
Declare Auto Function MessageBox Lib "user32.dll"
(ByVal hWnd As Integer, ByVal txt As String,
ByVal caption As String, ByVal Typ As Integer)
As Integer
End Class
' --- Client
Class VBKlasse
Public Shared Sub Main()
Dim cs As New CSKomponente.CSKlasse()
Dim ui As CSKomponente.UserInfo
Dim js As New JSKomponente.JSKlasse()
Dim Ausgabe As String
' Aufruf der CSharp-Klasse
ui = cs.GetUserInfo
' Generierung der Ausgabe
Ausgabe = "Angemeldeter Benutzer" & Chr(13) & _
js.domuser(ui)
' Ausgabe per Win32-API
Win32.MessageBox(0, Ausgabe, "iX-Beispiel", 0)
End Sub
End Class

Diese C#-Klasse übergibt VB.NET dann an die in JScript.NET (JS.NET) implementierte Klasse JSKlasse, die die Anmeldedaten in eine Zeichenkette der Form Domainname\Benutzername zusammensetzt (Listing 4). Damit noch nicht genug: VB.NET ruft zur Ausgabe dieser Zeichenkette nicht die .NET-Klassenbibliothek, sondern das Win32-API direkt auf (Funktion MessageBox() aus der User.dll).

Mehr Infos

Listing 4

Die JScript.NET-Komponente, die die beiden Zeichenketten zusammenfügt.

// JScript.NET-Komponente
// Holger Schwichtenberg
import System
import CSKomponente
package JSKomponente
{
public class JSKlasse
{
function domuser(ui : UserInfo) : String
{
var trenner : String;
trenner = "\\";
return ui.Domain.concat(trenner.concat(ui.Name));
}
}
}

Derzeit kann im .NET-Framework eine ausführbare Datei immer nur in einer einzigen Programmiersprache entwickelt werden, das heißt man kann nicht die C#-Klasse UserInfo und die VB-Klasse DomainInfo zusammen in eine Datei kompilieren. Bei .NET stehen aber nicht mehr einzelne Dateien im Mittelpunkt; eine .NET-Komponente (genannt Assembly) kann aus mehreren Modulen bestehen. Man hat die Wahl zwischen einer Single-File-Assembly (eine Assembly aus einem Modul) oder eine Multi-File-Assembly. Da letzteres nur von den .NET-Kommandozeilencompilern, nicht aber von Visual Studio.NET unterstützt wird, erzeugen wir in diesem Beispiel für jede Sprache eine eigene Assembly. So entstehen vier Assemblies: VBKomponente.dll, CSKomponente.dll, JSKomponente.dll und VBClient.exe.

Um dieses Beispiel aufzubauen, sind einige Schritte notwendig. Die VB.NET-Anwendung Projekte und die C#-Komponente kann man bequem mit VS.NET anlegen und in einer einzigen Solution zusammenfassen (siehe [[bild_url3] Titelbild]). Bei der Kompilierung setzt VS.NET jedes einzelne Projekt in genau eine Assembly (eine .dll oder .exe-Datei) um. Die Erzeugung von Multi-File-Assemblys erlauben die mit dem Framework ausgelieferten Kommandozeilencompiler, nicht aber die VS-Entwicklungsumgebung. Ein einzelnes Projekt darf immer nur aus Quelldateien in genau einer Programmiersprache bestehen.

Den VB-Client legt man als Console-Anwendung oder Windows-Anwendung an, sodass eine startbare .exe-Datei entsteht. Die Projekte für die C#- und die VB-Komponente müssen jeweils eine Class Library (.dll) erzeugen. Von der C#-Komponente aus muss man einen Verweise auf die WSH Runtime Library und die VB-Komponente setzen. Ebenso ist ein Verweis von der VB.NET-Anwendung auf das C#-Projekt und die JS-Komponente notwendig.

Außerdem benötigt der VB-Client einen Verweis auf die von Microsoft mit dem Framework ausgelieferte Microsoft.JScript.dll und die VB-Komponente. Letzteres ist merkwürdig, denn der VB-Client sollte alle Informationen über die Klasse UserInfo aus der C#-Komponente beziehen können. Beim Übersetzen meckert der VB.NET-Compiler aber, dass er auch einen Verweis auf die Implementierung der Oberklasse DomainInfo brauche. Dabei reicht ihm nicht mal wie sonst in Visual Studio.NET üblich ein Verweis auf die Projektdatei: Vielmehr besteht er darauf, direkt den Standort der kompilierten DLL zu wissen.

Da es für JScript.NET keine Unterstützung innerhalb der VS-Entwicklungsumgebung gibt, setzt man hier den Kommandozeilencompiler ein. Wichtig sind der Parameter /t:library, damit der Compiler eine .dll und keine .exe erstellt und der Parameter /r:, mit dem Namen und Pfad zur C#-Komponente an den Compiler übergeben werden.

<I>jsc /t:library /r:PFAD\\CSkomponente.dll
jskomponente.js<I>

Diese Referenz ist notwendig, da die JS-Komponente die Klasse UserInfo aus der C#-Komponente nutzt. Im Gegensatz zu seinem wissenshungrigen Kollegen, dem VB.NET-Compiler, gibt sich der JScript.NET-Compiler damit zufrieden und will nicht auch noch einem Verweis auf die Implementierung der Oberklasse.

Einbindung einer .NET- oder COM-Komponente in ein Visual-Studio-Projekt (Abb. 3).

Die JS-Komponente gilt es zuerst zu übersetzen, da die Entwicklungsumgebung nur Verweise auf kompilierte Komponenten oder andere VS.NET-Projekte akzeptiert. Durch die Festlegung des VB-Clients als ‘Startup-Projekt’ (siehe Kontextmenü des Projekts) und die Abhängigkeitsbeziehungen ‘weiß’ die Entwicklungsumgebung dann, das zuerst die VB-Komponente, dann die C#-Komponente und zum Schluss erst der VB-Client des C#-Projekts zu übersetzen sind. Wenn die Entwicklungsumgebung das eventuell nicht von sich aus erkennt, kann der Entwickler die Kompilierungsreihenfolge auch über den Menüeintrag ‘Project Build Order’ im Kontextmenü der VS.NET-Solution ändern.

Damit ist die Aufgabe erfüllt und der Spatz nach dem Start der Anwendung getroffen.

Holger Schwichtenberg
ist wissenschaftlicher Mitarbeiter am Lehrstuhl für Betriebliche Kommunikationssysteme an der Universität Essen und Autor mehrerer Bücher, die bei Addison-Wesley und beim Interest-Verlag erschienen sind.

[1] Holger Schwichtenberg; Softwareentwicklung; Ausgepackt; .NET Framework und Visual Studio.NET Final; iX 3/2002, S. 46

[2] Frank Eller, Holger Schwichtenberg; Programmieren mit der .NET-Klassenbibliothek; Addison-Wesley 2002 (wm)