Möchte man in einer nativen C++ Anwendung Funktionen aus der Managed Welt benutzen, bietet es sich an die Funktionalität per C++/CLI zu kapseln. Die Wrapper-Funktionen werden dann in einer DLL als Exportiert gekennzeichnet und können von C++ Clients per Linker hinzugezogen werden.
In diesem Rahmen macht es Sinn die Abhängigkeiten zu reduzieren. Es ist wünschenswert die entsprechende C# Bibliothek in das gleiche C++/CLI Assembly zu packen.
Dies wird aus Visual Studio 2005 nicht direkt unterstützt, sondern muss durch die Kommandozeile erledigt werden. Anhand eines kleinen Beispiels zeige ich im folgenden wie sich ein solches Assembly erstellen lässt.
Nehmen wir an wir hätten folgende Klasse in C#
1: using System;
2: namespace Merging
3: {
4: public class SomeClass
5: {
6: public string HelloWorld()
7: {
8: return "Hello, world.";
9: }
10: }
11: }
Das Kompilat wird nun nicht in Visual Studio 2005 direkt erstellt sondern über die Kommandozeile. Der Grund hierfür ist das Visual Studio 2005 in den möglichen Outputs nur folgende Werte zulässt
Es fehlt leider der Ausgabetyp Modul. Module lassen sich nämlich zu einem Assembly zusammen linken. Kompiliert man von der Kommandozeile so lässt sich dort auch ein Modul als Erstellungsziel spezifizieren.
csc /t:module class1.cs Microsoft (R) Visual C# 2008 Compiler Beta 2 version 3.05.20706.1 for Microsoft (R) .NET Framework version 3.5 Copyright (C) Microsoft Corporation. All rights reserved.
Anmerkung: Ich benutze zwar den 2008 Beta 2 compiler, funktioniert aber auch einwandfrei mit dem 2005 compiler.
Als Ergebnis erhält man nun die Datei class1.netmodule. Jetzt kommt das Bindeglied: C++/CLI. Die Funktion HelloWorld() wird nun in einer C++ DLL durch eine Funktion NativeHelloWorld() zur Verfügung gestellt. Nach dem Erstellen eines Win32 DLL Projektes wird zuerst der Compiler Switch /CLR aktviert. Die Header Datei enthält folgende Definitionen:
1: #define NATIVELIB_API __declspec(dllexport)
2:
3: NATIVELIB_API void NativeHelloWorld();
Die Implementierung der Funktion in meinem Beispiel sieht nun folgendermaßen aus
1: NATIVELIB_API void NativeHelloWorld()
2: {
3: Merging::SomeClass ^cl = gcnew Merging::SomeClass();
4: System::IntPtr pointer =
5: System::Runtime::InteropServices::Marshal::StringToHGlobalAuto( cl->HelloWorld() );
6: const wchar_t *result = static_cast<const wchar_t*>(pointer.ToPointer());
7:
8: wprintf_s( L"%s\n", result );
Zwei wichtige Dinge gilt es zu tätigen damit dieser Code kompiliert. Zum einen muss die Deklaration von Merging::SomeClass dem Compiler bekannt gemacht werden. Hierzu wird das Modul per #using statement eingebunden.
#using "c:\Users\dparys\Documents\Proj\Merging\class1.netmodule"
Des Weiteren muss dem Linker gesagt werden das er dieses Modul als Library betrachten soll.
Jetzt lässt sich die C++ DLL kompilieren und enthält nun den C# Code und den C++/CLI Wrapper Code in einem Assembly wie hier in ILDASM zu sehen.
Per DumpBin Kommandozeilen Tool lässt sich auch der Export anzeigen.
c:\>users\dparys\proj\debug\dumpbin /exports NativeLib.dll Microsoft (R) COFF/PE Dumper Version 9.00.20706.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file NativeLib.dll File Type: DLL Section contains the following exports for NativeLib.dll 00000000 characteristics 46B17095 time date stamp Thu Aug 02 07:50:13 2007 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 00001050 ?NativeHelloWorld@@YAXXZ = ?NativeHelloWorld@@YAXXZ (void __cdecl NativeHelloWorld(void)) Summary 1000 .data 6000 .rdata 1000 .reloc 1000 .rsrc 3000 .text
Somit kann die Funktion nun in einem nativen C++ Client genutzt werden, wie der folgende Code zeigt.
1: #include "stdafx.h"
3: #define NATIVELIB_API __declspec(dllimport)
4: NATIVELIB_API void NativeHelloWorld();
5:
6:
7: int _tmain(int argc, _TCHAR* argv[])
8: {
9: NativeHelloWorld();
11:
Bleibt noch Anzumerken das sich der ganze Prozess in einem Build-Skript am besten integrieren lässt. Mein Beispiel ist trivial und einfach. Es soll nur demonstrieren das es möglich ist. Bei größeren Bibliotheken sind alleine die Abhängigkeiten von Referenzen und die Parameter der einzelnen Tools eine Herausforderung für sich.
Möchte man in einer nativen C++ Anwendung Funktionen aus der Managed Welt benutzen, bietet es sich an
I just produced a little screencast on the previous german post . This is just a test of the soapbox