Ist es möglich, eine C-Funktion von C # .Net aus aufzurufen?

70

Ich habe eine C lib und möchte die Funktion in dieser Bibliothek aus der C # -Anwendung aufrufen. Ich habe versucht, einen C ++ / CLI-Wrapper für die C lib zu erstellen, indem ich die C lib-Datei als Linker-Eingabe und die Quelldateien als zusätzliche Abhängigkeiten hinzugefügt habe.

Gibt es einen besseren Weg, um dies zu erreichen, da ich nicht sicher bin, wie ich der C # -Anwendung eine C-Ausgabe hinzufügen soll?

Mein C-Code -

__declspec(dllexport) unsigned long ConnectSession(unsigned long handle,
                            unsigned char * publicKey,
                            unsigned char   publicKeyLen);

Mein CPP Wrapper -

long MyClass::ConnectSessionWrapper(unsigned long handle,
                                unsigned char * publicKey,
                                unsigned char   publicKeyLen)
    {
        return ConnectSession(handle, publicKey, publicKeyLen);
    }
Chinjoo
quelle
2
Ja, es ist möglich, und es gibt viele Beispiele, wenn Sie es googeln, aber keine Ahnung, was Ihr Problem von den begrenzten Informationen ist
Keith Nicholas
1
Wenn es bei X weh tut, hören Sie auf, X zu tun. Benennen Sie Ihre C ++ - Wrapper-Methode um, um dies als Ursache des Problems auszuschließen. Und poste deinen Code :-)
Eric J.
Ich habe eine AC-Bibliothek von einem Anbieter bereitgestellt, der Funktionen wie ConnectToMachine (unsigned char * psig, char apt) hat. Ich möchte diese Funktion aus meiner c # -Klasse aufrufen.
Chinjoo
2
Schauen Sie in PInvoke
Mark Hall
3
F: Ist es möglich, eine C-Funktion von C # .Net aus aufzurufen? A: Ja natürlich! F: Ist ein C ++ / CLI-Wrapper der richtige Weg? Persönlich halte ich C ++ / CLI für einen Gräuel. Aber die Leute benutzen es in solchen Szenarien. Ich würde es vorziehen, Interop direkt zu verwenden (dh PInvoke) - alles C #, bis hin zum nativen, nicht verwalteten C / C ++. Es gibt viele Tutorials und es ist nicht schwierig. Sicher nicht schwieriger als C / C ++ von VB6
aufzurufen

Antworten:

99

Das Beispiel lautet für Linux :

1) Erstellen Sie eine CDatei libtest.cmit folgendem Inhalt:

#include <stdio.h>

void print(const char *message)
{
  printf("%s\\n", message);
}

Das ist ein einfacher Pseudo-Wrapper für printf. Stellt jedoch jede CFunktion in der Bibliothek dar, die Sie aufrufen möchten. Wenn Sie eine C++Funktion haben, vergessen Sie nicht, extern zu setzenC zu , um eine Verfälschung des Namens zu vermeiden.

2) Erstellen Sie die C#Datei

using System;

using System.Runtime.InteropServices;

public class Tester
{
        [DllImport("libtest.so", EntryPoint="print")]

        static extern void print(string message);

        public static void Main(string[] args)
        {

                print("Hello World C# => C++");
        }
}

3) Wenn Sie die Bibliothek libtest.so nicht in einem Standardbibliothekspfad wie "/ usr / lib" haben, wird wahrscheinlich eine System.DllNotFoundException angezeigt. Um dies zu beheben, können Sie Ihre libtest.so nach / usr / lib oder verschieben Besser noch, fügen Sie einfach Ihr CWD zum Bibliothekspfad hinzu: export LD_LIBRARY_PATH=pwd

Credits von hier

BEARBEITEN

Für Windows ist das nicht viel anders. Wenn Sie ein Beispiel von hier nehmen , haben Sie nur *.cppIhre Methode in Ihre Datei mit extern "C" Etwas wie eingeschlossen

extern "C"
{
//Note: must use __declspec(dllexport) to make (export) methods as 'public'
      __declspec(dllexport) void DoSomethingInC(unsigned short int ExampleParam, unsigned char AnotherExampleParam)
      {
            printf("You called method DoSomethingInC(), You passed in %d and %c\n\r", ExampleParam, AnotherExampleParam);
      }
}//End 'extern "C"' to prevent name mangling

Dann kompilieren und in Ihrer C # -Datei tun

[DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]

public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);

und dann benutze es einfach:

using System;

    using System.Runtime.InteropServices;

    public class Tester
    {
            [DllImport("C_DLL_with_Csharp.dll", EntryPoint="DoSomethingInC")]

    public static extern void DoSomethingInC(ushort ExampleParam, char AnotherExampleParam);

            public static void Main(string[] args)
            {
                    ushort var1 = 2;
                    char var2 = '';  
                    DoSomethingInC(var1, var2);
            }
    }
Gonzalo.-
quelle
7
Gutes Beispiel! Dies ist ein sehr gutes Beispiel für die Verwendung von Interop. Es ist offensichtlich Linux (nicht Windows), aber das Prinzip ist das gleiche. Dies ist genau der Ansatz, den ich dem OP empfehlen würde.
Pauls4
Könnte jemand die Antwort bearbeiten? Ich habe Probleme mit dem Include, zeigt nicht stdio.h
Gonzalo.-
Ich denke, das sollte funktionieren. Aber ich weiß nicht, wie ich die Ausgabe als .so von vs2010 erhalten soll. Es gibt nur .lib und .dll, wieder .dll, wenn sie als dllimport festgelegt sind, da das Format schlecht ist
Chinjoo
wie Sie hier sehen können en.csharp-online.net/… Sie könnten das gleiche mit einer .dll tun
Gonzalo.-
1
@Chinjoo Betrachten Sie dieses Codeprojekt.com/Articles/6912/… . Haben Sie Ihren C-Code geändert und _declspec (dllexport) hinzugefügt? Wenn dieses Beispiel für Sie funktioniert, werde ich es der Antwort hinzufügen
Gonzalo.-
36

UPDATE - 22. Februar 2019: Da diese Antwort einige positive Stimmen erhalten hat, habe ich beschlossen, sie mit einer besseren Methode zum Aufrufen der C-Methode zu aktualisieren. Früher hatte ich vorgeschlagen, unsafeCode zu verwenden, aber der sichere und korrekte Weg besteht darin, ein MarshalAsAttribut zum Konvertieren eines .NET stringin ein .NET zu verwendenchar* . Außerdem gibt es in VS2017 kein Win32-Projekt mehr. Wahrscheinlich müssen Sie eine Visual C ++ - DLL oder ein leeres Projekt erstellen und diese ändern. Vielen Dank!

Sie können C-Funktionen direkt von C # aus mit P / Invoke aufrufen.
Hier finden Sie eine kurze Anleitung zum Erstellen einer C # -Bibliothek, die eine C-DLL umschließt.

  1. Erstellen Sie ein neues C # Library-Projekt (ich nenne es "Wrapper")
  2. Fügen Sie der Lösung ein Win32-Projekt hinzu und setzen Sie den Anwendungstyp auf: DLL (ich nenne es "CLibrary")

    • Sie können alle anderen cpp / h-Dateien entfernen, da wir sie nicht benötigen
    • Benennen Sie die Datei CLibrary.cpp in CLibrary.c um
    • Fügen Sie eine CLibrary.h-Headerdatei hinzu
  3. Jetzt müssen wir das CLibrary-Projekt konfigurieren, mit der rechten Maustaste darauf klicken und zu den Eigenschaften gehen und Konfiguration auswählen: "Alle Konfigurationen".

    • Setzen Sie unter Konfigurationseigenschaften> C / C ++> Vorkompilierte Header vorkompilierte Header auf: "Vorkompilierte Header nicht verwenden"
    • Gehen Sie im selben C / C ++ - Zweig zu Erweitert und ändern Sie Kompilieren in: "Kompilieren als C-Code (/ TC)"
    • Gehen Sie nun im Linker-Zweig zu Allgemein und ändern Sie die Ausgabedatei in: "$ (SolutionDir) Wrapper \ $ (ProjectName) .dll". Dadurch wird die erstellte C-DLL in das C # -Projektstammverzeichnis kopiert.

CLibrary.h

__declspec(dllexport) unsigned long ConnectSession(unsigned long   handle,
                                                   unsigned char * publicKey,
                                                   unsigned char   publicKeyLen);

CLibrary.c

#include "CLibrary.h"

unsigned long ConnectSession(unsigned long   handle,
                             unsigned char * publicKey,
                             unsigned char   publicKeyLen)
{
    return 42;
}
  • Klicken Sie mit der rechten Maustaste auf CLibrary-Projekt, erstellen Sie es, damit die DLL im C # -Projektverzeichnis angezeigt wird
  • Klicken Sie mit der rechten Maustaste auf das C # Wrapper-Projekt, fügen Sie ein vorhandenes Element hinzu und fügen Sie CLibrary.dll hinzu
  • Klicken Sie auf CLibrary.dll, gehen Sie zum Eigenschaftenbereich und setzen Sie "In Ausgabeverzeichnis kopieren" auf "Immer kopieren".

Es ist eine gute Idee, das Wrapper-Projekt von CLibrary abhängig zu machen, damit CLibrary zuerst erstellt wird. Klicken Sie dazu mit der rechten Maustaste auf das Wrapper-Projekt, gehen Sie zu "Projektabhängigkeiten" und aktivieren Sie "CLibrary". Nun zum eigentlichen Wrapper-Code:

ConnectSessionWrapper.cs

using System.Runtime.InteropServices;

namespace Wrapper
{
    public class ConnectSessionWrapper
    {
        [DllImport("CLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern uint ConnectSession(uint handle,
            [MarshalAs(UnmanagedType.LPStr)] string publicKey,
            char publicKeyLen);

        public uint GetConnectSession(uint handle, 
                                      string publicKey,
                                      char publicKeyLen)
        {
            return ConnectSession(handle, publicKey, publicKeyLen);
        }
    }
}

Rufen GetConnectSessionSie jetzt einfach an und es sollte zurückkehren 42.

Ergebnis:
Testen der Wrapper-Bibliothek in einer Konsolenanwendung

Shahin Dohan
quelle
Das ist für mich gearbeitet, aber ich hatte auch zu meinem .NET - Projekt ändern Properties->Build->Platform Targetzu x64.
Joseph
4

Okay, öffnen Sie VS 2010, Springen Sie zu Datei -> Neu -> Projekt -> Visual C ++ -> Win32 -> Win32-Projekt und geben Sie ihm einen Namen (in meinem Fall HelloWorldDll). Wählen Sie dann im folgenden Fenster unter Anwendungstyp 'DLL' ' und unter Zusätzliche Optionen wählen Sie ' Projekt leeren ' .

Gehen Sie nun zu Ihrer Registerkarte " Projektmappen- Explorer", normalerweise rechts im VS-Fenster, klicken Sie mit der rechten Maustaste auf "Quelldateien" -> "Element hinzufügen" -> "C ++" -Datei (.cpp) und geben Sie ihr einen Namen (in meinem Fall HelloWorld ).

Fügen Sie dann in der neuen Klasse diesen Code ein:

#include <stdio.h>

extern "C"
{
  __declspec(dllexport) void DisplayHelloFromDLL()
  {
    printf ("Hello from DLL !\n");
  }
}

Jetzt bauen das Projekt, nach dem Navigieren Sie zu Ihren Projekten DEBUG Ordner und dort sollten Sie finden: HelloWorldDll.dll .

Jetzt erstellen wir unsere C # -App, die auf die DLL zugreift. Gehe zu Datei -> Neu -> Projekt -> Visual C # -> Konsolenanwendung und gib ihr einen Namen (CallDllCSharp). Kopiere diesen Code und füge ihn in dein Hauptmenü ein:

using System;
using System.Runtime.InteropServices;
...
        static void Main(string[] args)
        {
            Console.WriteLine("This is C# program");
            DisplayHelloFromDLL();
            Console.ReadKey();
        }

und erstellen Sie das Programm, jetzt, da wir beide Apps erstellt haben, können Sie sie verwenden, Ihre * .dll und Ihre .exe (bin / debug / .exe) im selben Verzeichnis abrufen und die Anwendungsausgabe ausführen

Dies ist ein C # -Programm

Hallo von DLL!

Ich hoffe, das klärt einige Ihrer Probleme.

Referenzen :

David Kroukamp
quelle
Ich konnte das C # -Programm nicht kompilieren, da mein Projekt die DLL-Datei nicht finden kann. Also habe ich den vollständigen Pfad in der DllImport-Anweisung verwendet:[DllImport("C:\\Users\\...full path...\\HelloWorldDll\\Debug\\HelloWorldDll.dll")]
Quazi Irfan
Immer wieder 'Es wurde versucht, ein Programm mit einem falschen Format zu laden. (Ausnahme von HRESULT: 0x8007000B) '. Hat noch jemand dieses Problem?
Max
0

HINWEIS: Der folgende Code gilt für mehrere Methoden aus der DLL.

[DllImport("MyLibc.so")] public static extern bool MyLib_GetName();
[DllImport("MyLibc.so")] public static extern bool MyLib_SetName(string name);
[DllImport("MyLibc.so")] public static extern bool MyLib_DisplayName(string name);

public static void Main(string[] args)
{
string name = MyLib_GetName();
MyLib_SetName(name);
MyLib_DisplayName(name);
}
HaseeB Mir
quelle
0

Die P / Invoke-Methode wurde bisher ausführlich und wiederholt beschrieben. Was mir hier fehlt, ist, dass die C ++ / CLI-Methode einen großen Vorteil hat: Sicherheit aufrufen. Im Gegensatz zu P / Invoke, bei dem der Aufruf der C-Funktion dem blinden Schießen in den Himmel gleicht (wenn dieser Vergleich zulässig ist), überprüft niemand die Funktionsargumente beim Aufrufen der C-Funktion. Wenn Sie in diesem Fall C ++ / CLI verwenden, fügen Sie normalerweise eine Header-Datei mit den Funktionsprototypen hinzu, die Sie verwenden möchten. Wenn Sie die C-Funktion mit falschen / zu vielen / zu wenigen Argumenten aufrufen, werden Sie vom Compiler darüber informiert.

Ich glaube nicht, dass man allgemein sagen kann, welche Methode die bessere ist, ehrlich gesagt mag ich keine von beiden.

Thomas Schmid
quelle