Dariusz quatscht

Web Development on Microsoft's Platform
Navigation

Mischen von C# und C++ Code in einem Assembly

Mischen von C# und C++ Code in einem Assembly

  • Comments 2

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

shot1

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 );
   9: }

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.

shot2

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.

shot3

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"
   2:  
   3: #define NATIVELIB_API __declspec(dllimport)
   4: NATIVELIB_API void NativeHelloWorld();
   5:  
   6:  
   7: int _tmain(int argc, _TCHAR* argv[])
   8: {
   9:     NativeHelloWorld();
  10: }
  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

Page 1 of 1 (2 items)