使用C#代码中的C ++ dll中定义的类


73

我有一个用C ++编写的dll,我需要在我的C#代码中使用此dll。搜索之后,我发现使用P / Invoke可以访问所需的函数,但是这些函数是在类中定义的,并且使用非静态私有成员变量。因此,我需要能够创建此类的实例以正确使用这些功能。如何获得对此类的访问权限,以便可以创建实例?我一直无法找到一种方法来做到这一点。

我想我应该注意,C ++ dll不是我的代码。

Answers:


95

无法在C#代码中直接使用C ++类。您可以间接使用PInvoke来访问您的类型。

基本模式是为Foo类中的每个成员函数创建一个关联的非成员函数,以调用该成员函数。

class Foo {
public:
  int Bar();
};
extern "C" Foo* Foo_Create() { return new Foo(); }
extern "C" int Foo_Bar(Foo* pFoo) { return pFoo->Bar(); }
extern "C" void Foo_Delete(Foo* pFoo) { delete pFoo; }

现在只需将这些方法调用到您的C#代码中即可

[DllImport("Foo.dll")]
public static extern IntPtr Foo_Create();

[DllImport("Foo.dll")]
public static extern int Foo_Bar(IntPtr value);

[DllImport("Foo.dll")]
public static extern void Foo_Delete(IntPtr value);

缺点是您将要传递一个笨拙的IntPtr,但是围绕此指针创建C#包装器类以创建更可用的模型是一件简单的事情。

即使您不拥有此代码,也可以创建另一个DLL,该DLL包装原始DLL并提供一个小的PInvoke层。


2
“没有办法在C#代码中直接使用C ++类”的说法是不正确的。静态C ++函数的工作方式与C样式函数相同,并且可以通过添加指向实例本身的ThisObject作为第一个参数来将实例函数声明为ThisCall调用约定。有关详细信息,您可能需要阅读我的博客。您可能还需要尝试使用我们的工具。(我是作者)
xInterop 2014年

3
尽管在当时的罚款答案,也可以说是更好地使用C ++ / CLI,以避免代理功能的手动创建-这可能成为相当乏味相当快
MickyD

2
C ++代理功能应该被包围extern "C" { .... }吗?或者纯粹在C ++中做就可以了?
2015年

2
@jaredpar我应该在哪里复制C ++ dll?在C#exe旁边?我得到System.EntryPointNotFoundException:'无法在DLL中找到名为'...'的入口点
Hamed_gibago19年

28

元帅C ++类并使用PInvoke

C ++代码,ClassName.h

class __declspec(dllexport) CClassName
{
 public:
    CClassName();
    ~CClassName();
    void function();

};

C ++代码ClassName.cpp

CClassName::CClassName()
{
}

CClassName::~CClassName()
{
}

void CClassName::function()
{
    std::cout << "Bla bla bla" << std::endl;

}

C ++代码,用于调用者函数的ClassNameCaller.h文件

#include "ClassName.h"      

#ifdef __cplusplus
extern "C" {
#endif

extern __declspec(dllexport) CClassName* CreateClassName();

extern __declspec(dllexport) void DisposeClassName(CClassName* a_pObject);

extern __declspec(dllexport) void function(CClassName* a_pObject);


#ifdef __cplusplus
}
#endif

C ++代码,用于调用者函数的ClassNameCaller.cpp文件

#include "ClassNameCaller.h"


CClassName* CreateClassName()
{
    return new CClassName();
}

void DisposeClassName(CClassName* a_pObject)
{
    if(a_pObject!= NULL)
    {
        delete a_pObject;
        a_pObject= NULL;
    }
}

void function(CClassName* a_pObject)
{
    if(a_pObject!= NULL)
    {
        a_pObject->function();
    }
}

C#代码

[DllImport("ClassNameDll.dll")]
static public extern IntPtr CreateClassName();

[DllImport("ClassNameDll.dll")]
static public extern void DisposeClassName(IntPtr pClassNameObject);

[DllImport("ClassNameDll.dll")]
static public extern void CallFunction(IntPtr pClassNameObject);

//use the functions
IntPtr pClassName = CreateClassName();

CallFunction(pClassName);

DisposeClassName(pClassName);

pClassName = IntPtr.Zero; 

2
在第一个文件ClassName.h中是否应该有一个“ dllexport”而不是“ dllimport”?
PW

1
是的,它用于将此类导出到.Net项目,因此我们可以使用CClassName类
Amir Touitou

2
这个例子确实很有帮助,非常感谢:-)
Ashish Rana

你好 不知道这是否为时已晚,但是可以显示您对代码的修改,以便我可以通过该函数传递字符串,对其进行修改然后将其返回给c#代码吗?我一直在尝试,但是不允许我在a_pObject-> function(std :: string args1)中添加类型名称;
CausticLasagne

1
附带说明,如果您收到“ BadImageFormatException”,则可能必须更改平台。我像往常一样将我的C ++代码编译为x64,但是我为C#端创建的默认项目设置为“ Any CPU”。这是行不通的,但是一旦切换到x64,它就可以了。确保匹配。
Clocktown


3

您可能需要编写一个中间DLL(也许是C ++)来为您处理此问题并公开所需的接口。您的DLL将负责加载第三方DLL,创建此C ++对象的实例,并根据需要通过您设计的任何API公开其成员函数。然后,您将使用P / Invoke获取您的API并干净地操作该对象。

注意:对于DLL的API,请尝试将数据类型限制为原语(long,int,char *等),以防止模块边界问题。


3

我这样做的方法是围绕我的非托管C ++ DLL创建一个瘦的托管C ++包装器。托管包装器包含“代理”类,这些类包装了非托管代码,从而暴露了.NET应用程序所需的接口。这有点双重工作,但是它允许在正常环境下进行非常无缝的操作。在某些情况下(例如ASP.NET),依赖的确会变得更加棘手,但您可能不会遇到这种情况。


2

我同意JaredPar。在托管代码中创建非托管类的实例应该是不可能的。

另一件事是-如果您可以在托管C ++中重新编译DLL或使用其中的COM组件,这将变得更加容易/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.