web-dev-qa-db-fra.com

Comment appeler C ++ / CLI depuis C #?

J'ai une classe implémentée en C++ qui est responsable du calcul arithmétique du programme, et une interface utilisant WPF. Je traite l'entrée avec C # mais comment puis-je utiliser ma classe C++?

J'ai vu quelques commentaires sur la création d'une classe wrapper C++ managé pour interagir avec elle, mais je ne sais pas par où commencer. Je ne sais pas non plus comment je pourrais le compiler avec tous les autres codes. Je ne trouve pas vraiment de tutoriel à ce sujet, et les choses que Google montre sur le C++ managé ne semblent pas vraiment utiles.

Quelque chose pour m'aider? Cela ne me semble pas déraisonnable.

EDIT Solution m3rLinEz essayée mais ça me donne une BadImageFormatException, je pense que c'est parce que le DLL n'est pas généré. J'ai fait tout comme dit, ne sais pas ce qui s'est passé. Des idées?

64
master chief

Avez-vous jeté un œil à C++/CLI?

Permettez-moi de donner un très court exemple. Voici le fichier source d'un projet Visual C++ -> CLR -> Bibliothèque de classes. Il obtient essentiellement le nom d'utilisateur Windows et le renvoie.

Veuillez noter que pour obtenir cette compilation, vous devez entrer dans les paramètres du projet et marquer "Autres dépendances" comme "Hériter du parent" car nous utilisons ces bibliothèques Windows (kernel32.lib, user32.lib, ..)

// CSCPP.h

#pragma once

#include "windows.h"

using namespace System;

namespace CSCPP {

    public ref class Class1
    {
        // TODO: Add your methods for this class here.
    public:
        String^ GetText(){
            WCHAR acUserName[100];
            DWORD nUserName = sizeof(acUserName);
            if (GetUserName(acUserName, &nUserName)) {
                String^ name = gcnew String(acUserName);
                return String::Format("Hello {0} !", name);
            }else{
                return gcnew String("Error!");
            }
        }
    };
}

Maintenant, créez un nouveau projet C # et ajoutez une référence à notre premier projet de bibliothèque de classes C++/CLI. Et puis appelez la méthode d'instance.

namespace CSTester
{
    class Program
    {
        static void Main(string[] args)
        {
            CSCPP.Class1 instance = new CSCPP.Class1();
            Console.WriteLine(instance.GetText());
        }
    }
}

Cela a donné le résultat suivant sur ma machine:

Bonjour m3rlinez!

C++/CLI est fondamentalement une extension gérée par rapport à la norme C++. Il vous permet d'utiliser des classes CLR et des types de données dans votre projet C++/CLI et également de l'exposer au langage managé. Vous pouvez créer un wrapper managé pour votre ancienne bibliothèque C++ à l'aide de ceci. Il existe des syntaxes étranges telles que String^ pour définir le type de référence à la chaîne CLR. Je trouve "Quick C++/CLI - Apprenez C++/CLI en moins de 10 minutes" pour être utile ici.

56
Gant

Il existe au moins trois façons d'appeler du code non managé depuis managé dans le même processus:

  1. C++/CLI
  2. Appel de la plateforme
  3. Enveloppez votre C++ dans un objet COM

Au travail, nous utilisons C++/CLI pour cela, cela semble fonctionner.

9
Arve

Je créerais une bibliothèque de liens dynamiques standard (non COM/Managed) comme décrite ici puis j'utiliserais attribut DllImport (appel de plateforme) dans le code c # pour accéder aux fonctions exportées .

Le point clé de cet article:

Notez le modificateur __declspec (dllexport) dans les déclarations de méthode de ce code. Ces modificateurs permettent d'exporter la méthode par la DLL afin qu'elle puisse être utilisée par d'autres applications. Pour plus d'informations, voir dllexport, dllimport.

Il s'agit d'une alternative plus légère à un wrapper d'interopérabilité COM réel et évite les problèmes tels que l'enregistrement, etc. (le DLL peut simplement être placé dans le répertoire de l'application).

Une autre alternative est Ça marche juste (IJW). C'est probablement un meilleur choix si vous avez géré du code C++ et devez y accéder à partir d'autres langages .NET. Mais ce n'est qu'une option si vous êtes capable/heureux de convertir votre C++ non managé en C++ managé.

4
Ash

Je resterais loin de P/Invoke car c'est assez lent par rapport à IJW (It Just Works). Ce dernier vous permet d'entrelacer de manière transparente le c ++ géré et non managé. Tout ce que vous avez à faire est de créer un assembly c ++ managé, d'écrire une classe managée visible depuis c # et d'appeler le code non managé à partir de cela.

Uhm ... OK. J'avais l'impression que les appels P/Invoke étaient plus lents, ce qui n'est pas le cas par nature. Cependant, en ayant un contrôle explicite sur le marshaling, vous pouvez améliorer la performance de votre version C++/CLI dans la plupart des cas.

Voici l'article de Microsoft sur les deux mécanismes:

http://msdn.Microsoft.com/en-us/library/ms235282.aspx

Avantages de l'IJW

  • Il n'est pas nécessaire d'écrire des déclarations d'attribut DLLImport pour les API non managées que le programme utilise. Il vous suffit d'inclure le fichier d'en-tête et de créer un lien avec la bibliothèque d'importation.
  • Le mécanisme IJW est légèrement plus rapide (par exemple, les stubs IJW n'ont pas besoin de vérifier la nécessité d'épingler ou de copier des éléments de données car cela est fait explicitement par le développeur).
  • Il illustre clairement les problèmes de performances. Dans ce cas, le fait que vous traduisez d'une chaîne Unicode vers une chaîne ANSI et que vous disposez d'une allocation de mémoire et d'une désallocation. Dans ce cas, un développeur écrivant le code à l'aide d'IJW se rendrait compte qu'appeler _putws et utiliser PtrToStringChars seraient meilleurs pour les performances.
  • Si vous appelez de nombreuses API non managées à l'aide des mêmes données, le marshaling une fois et la transmission de la copie marshalée est beaucoup plus efficace que le marshaling à chaque fois.

Il y a aussi des avantages esthétiques:

  • Le code C # ressemble au code C # sans aucune bizarrerie interop'y.
  • Vous n'avez pas à définir l'attribut DLLImport, vous n'avez à définir aucune des structures de données (également avec des attributs spécifiques p/invoke) qui pourraient ressembler à ceci:

    [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct DevMode {[MarshalAs (UnmanagedType.ByValTStr, SizeConst = 32)] chaîne publique dmDeviceName; }

  • Vous n'avez pas besoin de convertir tous les types primitifs de paramètres en leurs homologues .NET (il y a un tableau sur cette page qui répertorie la façon dont les types gérés sont mappés en types non gérés).
  • Vous obtenez de travailler avec C++/CLI qui est vraiment amusant à apprendre et est vraiment poli. Il a parcouru un long chemin depuis VS 2003 et est maintenant un langage .NET complet. La documentation Microsoft est assez bonne, tout comme toutes les informations IJW.
  • Faire de l'interopérabilité C++ en C++/CLI semble très naturel par opposition à C #. C'est complètement subjectif, mais je préférerais de loin faire du marshaling de chaînes en C++ qui fait Marshal.PtrToString(ptr).
  • Si vous exposez une API, vous voudrez probablement envelopper toutes les choses P/Invoke dans une autre couche, vous n'avez donc pas à vous occuper de la laideur P/Invoke. De cette façon, vous avez la surcharge de tout le marshaling ET la couche C # autour d'elle. Avec C++/CLI, le marshaling et l'abstraction d'interopérabilité sont au même endroit, et vous pouvez choisir la quantité de marshalling dont vous avez besoin.

À mon humble avis, si vous appelez une fonction étrange dans le SDK Windows, allez avec P/Invoke. Si vous exposez une API C++ modérément complexe au monde géré, certainement C++/CLI.

3
Igor Zevaka