web-dev-qa-db-fra.com

Est-il possible d'appeler une fonction C à partir de C # .Net

J'ai une bibliothèque C et je veux appeler une fonction dans cette bibliothèque à partir de l'application C #. J'ai essayé de créer un wrapper C++/CLI sur la bibliothèque C en ajoutant le fichier C lib comme entrée de l'éditeur de liens et en ajoutant les fichiers source en tant que dépendances supplémentaires.

Existe-t-il un meilleur moyen d'y parvenir car je ne sais pas comment ajouter la sortie C à l'application c #.

Mon code C -

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

Mon emballage CPP -

long MyClass::ConnectSessionWrapper(unsigned long handle,
                                unsigned char * publicKey,
                                unsigned char   publicKeyLen)
    {
        return ConnectSession(handle, publicKey, publicKeyLen);
    }
54
Chinjoo

L'exemple sera, pour Linux:

1) Créez un fichier C, libtest.c avec ce contenu:

#include <stdio.h>

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

C’est un simple pseudo-wrapper pour printf. Mais représente n'importe quelle fonction C de la bibliothèque que vous souhaitez appeler. Si tu as un C++ fonction n'oubliez pas de mettre extern C pour éviter de modifier le nom.

2) créez le C# fichier

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) Sauf si vous avez la bibliothèque libtest.so dans un chemin de bibliothèque standard comme "/ usr/lib", vous verrez probablement une System.DllNotFoundException, pour résoudre ce problème, vous pouvez déplacer votre libtest.so vers/usr/lib, ou mieux encore, ajoutez simplement votre CWD au chemin de la bibliothèque: export LD_LIBRARY_PATH=pwd

crédits de ici

[~ # ~] modifier [~ # ~]

Pour Windows, ce n'est pas très différent. Prenant un exemple de ici , vous n'avez qu'à enfermer dans votre *.cpp fichier votre méthode avec extern "C" Quelque chose comme

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

puis, compilez et dans votre fichier C # faites

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

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

puis utilisez-le:

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);
            }
    }
80
Gonzalo.-

MISE À JOUR - 22 février 2019: Puisque cette réponse a reçu pas mal de votes positifs, j'ai décidé de la mettre à jour avec une meilleure façon d'appeler la méthode C. Auparavant, j'avais suggéré d'utiliser le code unsafe, mais la manière sûre et correcte consiste à utiliser l'attribut MarshalAs pour convertir un .NET string en char*. De plus, dans VS2017, il n'y a plus de projet Win32, vous devrez probablement créer une DLL Visual C++ ou un projet vide et le modifier. Merci!

Vous pouvez appeler directement les fonctions C à partir de C # à l'aide de P/Invoke.
Voici un petit guide pratique sur la création d'une bibliothèque C # qui s'enroule autour d'une DLL C.

  1. Créez un nouveau projet de bibliothèque C # (je l'appellerai "Wrapper")
  2. Ajoutez un projet Win32 à la solution, définissez le type d'application sur: DLL (je l'appellerai "CLibrary")

    • Vous pouvez supprimer tous les autres fichiers cpp/h car nous n'en aurons pas besoin
    • Renommez le fichier CLibrary.cpp en CLibrary.c
    • Ajouter un fichier d'en-tête CLibrary.h
  3. Maintenant, nous devons configurer le projet CLibrary, cliquer dessus avec le bouton droit et aller dans les propriétés, puis sélectionner Configuration: "Toutes les configurations"

    • Dans Propriétés de configuration> C/C++> En-têtes précompilés, définissez En-têtes précompilés sur: "Ne pas utiliser d'en-têtes précompilés"
    • Dans la même branche C/C++, accédez à Avancé, changez Compiler en: "Compiler en code C (/ TC)"
    • Maintenant, dans la branche Linker, accédez à Général et modifiez le fichier de sortie en: "$ (SolutionDir) Wrapper\$ (ProjectName) .dll", cela copiera le C DLL vers le C #) racine du projet.

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;
}
  • Faites un clic droit sur le projet CLibrary, construisez-le, donc nous obtenons le DLL dans le répertoire du projet C #
  • Cliquez avec le bouton droit sur le projet Wrapper C #, ajoutez un élément existant, ajoutez CLibrary.dll
  • Cliquez sur CLibrary.dll, allez dans le volet des propriétés, définissez "Copier dans le répertoire de sortie" sur "Copier toujours"

C'est une bonne idée de rendre le projet Wrapper dépendant de CLibrary pour que CLibrary soit construit en premier, vous pouvez le faire en cliquant avec le bouton droit sur le projet Wrapper, en allant dans "Project Dependencies" et en cochant "CLibrary". Maintenant, pour le code de wrapper réel:

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

Maintenant, appelez simplement GetConnectSession, et il devrait retourner 42.

Résultat:
Testing wrapper library in a console application

26
Shahin Dohan

D'accord, ouvrez VS 2010, Goto Fichier -> Nouveau -> Projet -> Visual C++ -> Win32 -> Projet Win32 et donnez-lui un nom (HelloWorldDll dans mon cas), puis dans le fenêtre qui suit sous Type d'application choisissez 'DLL' et sous Options supplémentaires choisissez 'Projet vide' .

Maintenant, allez dans votre Explorateur de solutions onglet généralement à droite de la fenêtre VS, faites un clic droit Fichiers source -> Ajouter un élément -> Fichier C++ (.cpp) et donnez un nom (HelloWorld dans mon cas)

Ensuite, dans la nouvelle classe, collez ce code:

#include <stdio.h>

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

Maintenant Build le projet, après avoir accédé à vos projets [~ # ~] déboguer le dossier [~ # ~] et là vous devriez trouver: HelloWorldDll.dll .

Maintenant, créons notre application C # qui accédera à la dll, Goto Fichier -> Nouveau -> Projet -> Visual C # -> Application console et donnez-lui un nom (CallDllCSharp), maintenant copiez et collez ce code dans votre main:

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

et construisez le programme, maintenant que nos deux applications sont construites, utilisons-les, récupérez votre * .dll et votre . exe (bin/debug /. exe) dans le même répertoire, et exécutez la sortie de l'application doit être

Ceci est le programme C #

Bonjour de DLL!

J'espère que cela clarifie certains de vos problèmes.

Références :

3
David Kroukamp