在C#中包装C++类DLL

Wrap C++ class DLL in C#

本文关键字:DLL C++ 包装      更新时间:2023-10-16

我必须在一个新的C#项目中使用C++现有代码。此C++代码是包含在DLL中的非托管代码。我的一位同事已经用"C"代码做到了这一点,但在我的情况下,我的DLL包含一个"类",我不知道是否可以做到这一点。

在我的C#应用程序中使用这个C++类的正确方法是什么?

更新:

谢谢你的回答。我试着用一个简单的类来完成它,方法是按照这篇文章在NET应用程序中使用非托管的C库DLL

我的初始类是在Borland C++Builder 6:上编码的

测试.cpp:

#include <basepch.h>
#pragma hdrstop
#include "Test.h"
#pragma package(smart_init)
__fastcall TTest::TTest() {
        //rien
}
__fastcall TTest::~TTest() {
        //rien
}
void __fastcall TTest::setNombre(int nbr) {
        nombre = nbr;
}
int __fastcall TTest::getNombre() {
        return nombre;
}

测试.h:

#ifndef TestH
#define TestH
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <string.h>
#include <stdio.h>
#include <StrUtils.hpp>
#include <time.h>
class PACKAGE TTest
{
private:
       int nombre;
protected:
public:
        __fastcall TTest();
        __fastcall ~TTest();
        void __fastcall setNombre(int nbr);
        int __fastcall getNombre();
};
extern PACKAGE TTest *Test;
#endif

这个类的编译是可以的:D

然后我尝试像文章中那样创建非托管类。但是我在C++Builder中创建这个类时遇到了一个问题。

未管理.cpp:

#pragma hdrstop
#include "Unmanaged.h"
#pragma package(smart_init)
struct UnmanagedClasseTest
{
        int nombre;
        [DllImport("ClasseTest.dll", EntryPoint="@TTest@$bctr$qqrv", CallingConvention=CallingConvention::ThisCall)]
        static void ctor(UnmanagedClasseTest* c);
        [DllImport("ClasseTest.dll", EntryPoint="@TTest@$bdtr$qqrv", CallingConvention=CallingConvention::ThisCall)]
        static void dtor(UnmanagedClasseTest* c);
        [DllImport("ClasseTest.dll", EntryPoint="@TTest@setNombre$qqri", CallingConvention=CallingConvention::ThisCall)]
        static void setNombre(UnmanagedClasseTest* c, int nbr*);
        [DllImport("ClasseTest.dll", EntryPoint="@TTest@getNombre$qqrv", CallingConvention=CallingConvention::ThisCall)]
        static int getNombre(UnmanagedClasseTest* c);
        static void Uctor(UnmanagedClasseTest* c) {
                ctor(c);
        }
        static void Udtor(UnmanagedClasseTest* c) {
                dtor(c);
        }
        static void UsetNombre(UnmanagedClasseTest* c, int i) {
                nombre = setNombre(c);
        }
        static int UgetNombre(UnmanagedClasseTest* c) {
                return getNombre(c);
        }
};

未管理。h:

#ifndef UnmanagedH
#define UnmanagedH
static void ctor(UnmanagedClasseTest* c);
static void dtor(UnmanagedClasseTest* c);
static void setNombre(UnmanagedClasseTest* c, int nbr*);
static int getNombre(UnmanagedClasseTest* c);
static void Uctor(UnmanagedClasseTest* c);
static void Udtor(UnmanagedClasseTest* c);
static void UsetNombre(UnmanagedClasseTest* c, int i);
static int UgetNombre(UnmanagedClasseTest* c);
#endif

当我想编译这个非托管类时,我会出现以下错误://

[C++ Erreur] Unmanaged.h(6): E2147 'UnmanagedClasseTest' ne peut pas démarrer une déclaration de parameter
[C++ Erreur] Unmanaged.h(7): E2147 'UnmanagedClasseTest' ne peut pas démarrer une déclaration de parameter
[C++ Erreur] Unmanaged.h(8): E2451 Symbole 'UnmanagedClasseTest' non define
[C++ Erreur] Unmanaged.h(9): E2147 'UnmanagedClasseTest' ne peut pas démarrer une déclaration de parameter
[C++ Erreur] Unmanaged.h(10): E2147 'UnmanagedClasseTest' ne peut pas démarrer une déclaration de parameter
[C++ Erreur] Unmanaged.h(11): E2147 'UnmanagedClasseTest' ne peut pas démarrer une déclaration de parameter
[C++ Erreur] Unmanaged.h(12): E2147 'UnmanagedClasseTest' ne peut pas démarrer une déclaration de parameter
[C++ Erreur] Unmanaged.h(13): E2147 'UnmanagedClasseTest' ne peut pas démarrer une déclaration de parameter
[C++ Erreur] Unmanaged.cpp(17): E2040 Déclaration terminée incorrectement
[C++ Erreur] Unmanaged.cpp(20): E2040 Déclaration terminée incorrectement
[C++ Erreur] Unmanaged.cpp(23): E2040 Déclaration terminée incorrectement
[C++ Erreur] Unmanaged.cpp(26): E2040 Déclaration terminée incorrectement
[C++ Erreur] Unmanaged.cpp(30): E2034 Impossible de convertir 'UnmanagedClasseTest *' en 'int *'
[C++ Erreur] Unmanaged.cpp(30): E2342 Mauvaise correspondance de type dans le paramètre 'c' ('int *' désiré, 'UnmanagedClasseTest *' obtenu)
[C++ Erreur] Unmanaged.cpp(33): E2034 Impossible de convertir 'UnmanagedClasseTest *' en 'int *'
[C++ Erreur] Unmanaged.cpp(33): E2342 Mauvaise correspondance de type dans le paramètre 'c' ('int *' désiré, 'UnmanagedClasseTest *' obtenu)
[C++ Erreur] Unmanaged.cpp(36): E2268 Appel à une fonction non définie 'setNombre'
[C++ Erreur] Unmanaged.cpp(36): E2231 Le membre UnmanagedClasseTest::nombre ne peut pas être utilisé sans un objet
[C++ Erreur] Unmanaged.cpp(39): E2034 Impossible de convertir 'UnmanagedClasseTest *' en 'int *'
[C++ Erreur] Unmanaged.cpp(39): E2342 Mauvaise correspondance de type dans le paramètre 'c' ('int *' désiré, 'UnmanagedClasseTest *' obtenu)

我可以提出两个解决方案。(1) 通过标记为__declspec(dllexport)导出整个C++。它的所有方法都将从DLL中导出,并带有一些损坏的名称。找到这些名称(例如,通过Depends实用程序)并在C#代码中编写DllImport包装器。

(2) 在类中实现基本的COM功能并使用COM互操作。最小动作集:

a) 实现IUnknown和IMarshal方法:

class YourClass: public IMarshal
{
  // override AddRef, Release, QueryInterface and Marshal here
  ...
  int __declspec(stdcall) foo(int x);
}

b) 然后ComImport你的类在C#:

[ComImport, Guid("5BADB572-FE70-4602-8854-E4B461FC5DAE")] 
class YourClass
{ 
  [PreserveSig] int foo(int x);
}

c) 编写一个创建YourClass实例的c++函数,将其从DLL导出并为其编写DllImport包装。

完成任务有几个先决条件。

  1. 您必须确认文件所在的位置
  2. 您必须为要调用的方法输入签名

假设您的dll(mycpp.dll)位于位置"c:\mycpp.dll",并且有一个名为"Sum"的方法,它分别接受两个int参数a和b并返回int。您可以使用以下代码:

public class Program
{
    [DllImport(@"c:mycpp.dll")]
    private static extern int Sum(int a, int b);
    static void Main(string[] args)
    {
        Console.WriteLine(Sum(3,5));
    }
}

p.S:您可以在以下位置找到原始代码:C#中的动态调用C++DLL函数