想【C++ 】高手发起挑战,请教一个【动态链接库 dll 和 类成员函数 显式链接】问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了想【C++ 】高手发起挑战,请教一个【动态链接库 dll 和 类成员函数 显式链接】问题相关的知识,希望对你有一定的参考价值。

参考:
如果你想导出并显式链接一组C++成员函数又该怎么办呢?这里有两个问题。第一是C++成员函数名是经过修饰的(即使指定extern "C"标记也是这样);第二是C++不允许将指向成员函数的指针转换成其它类型。这两个问题限制了C++类的显式链接。下面介绍两种方法来解决这个问题:①用虚函数表的方法,这也是COM使用的方法;②用GetProcAddress直接调用。我将以下面这个类为例进行讲解:

class A



private:

int m_nNum;

public:

A();

A(int n);

virtual ~A();

void SetNum(int n);

int GetNum();

;

一.用虚函数表进行显式链接

这个方法是COM的基础。当我们定义一组虚函数的时候,编译器会创建一个虚函数表,将各虚函数的地址按声明的顺序放入其中。当一个类对象被创建时,它的前四个字节是一个指针,指向这个虚函数表。如果我们将A的定义修改成这样:

class A



private:

int m_nNum;

public:

A();

A(int n);

virtual ~A();

virtual void SetNum(int n);

virtual int GetNum();

;

那么一个虚函数表将被编译器创建出来,其中包含三个函数的地址:析构函数,SetNum和GetNum。现在类对象要在dll中创建。既然我们要显式链接,就需要一些全局导出函数来调用operator new以创建对象。因为A有两种构造函数,所以我们定义两个函数CreateObjectofA()和CreateObjectofA1(int)并将其导出。客户可以这样来使用类对象:

typedef A* (*PFNCreateA1)();PFNCreateA1 pfnCreateA1 = (PFNCreateA1)GetProcAddress(hMod, TEXT("CreateObjectofA1"));A* a = (pfnCreateA1)();a-SetNum(1); _tprintf(TEXT("Value of m_nNum in a is %d\n"),a-GetNum());delete a;要注意的是CreateObjectofA必须使用operator new来创建对象这样客户端才可以安全地调用operator delete来销毁对象:

extern "C" __declspec(dllexport) A* CreateObjectofA1()



return new A();


这个方法的使用得用户可以很容易地为你的程序制作插件。它的缺点是创建对象的内存必须在dll中分配

上面东西有些不明白啥意思,请高手赐教,不甚感激!解决问题再给分,最好做个例子f发过来:mailto:350860482@qq.com

简单的说,就是导出一个辅助函数创建类而不是直接导出整个类。
两个helper function:CreateObject和ReleaseObject。
这两个函数通过extern "C"去掉C++函数名修饰还有__declspec(dllexport)来导出函数。
两个Helper的存在就可以避免导出整个类。client调用这两个函数就可以间接的构造、销毁类了。
虚函数应该不用解释吧,这是C++的基础。插件DLL可以通过虚函数构造自己的代码。
参考资料里有个例子。

参考资料:https://github.com/skies457/Nova-3D/blob/master/Nova%203D/src/renderer/RenderDevice.h

参考技术A 孤独鳏寡

C#调用C++语言编写的动态链接库

引言

    上一篇文章我们讲了,这篇文章我们就讲讲关于C#怎么样调用C++动态链接库。


C++动态链接库

    我们先新建一个C++动态链接库项目,注意笔者开发环境为WIndows 10 x64 + Visual Studio Enterprise 2017。

新建cplusplus.h和cplusplus.cpp文件,cplusplus.h文件输入如下内容:

#ifndef CPLUSPLUS_H#define CPLUSPLUS_H
#ifdef CPLUSPLUS_PORTS#define CPLUSPLUS_API __declspec(dllexport)#else #define CPLUSPLUS_API __declspec(dllimport)#endif // !CPLUSPLUS_PORTS

class CPLUSPLUS_API CCPlus{public: CCPlus(); ~CCPlus();
public: int add(int a, int b); int sub(int a, int b);};
#endif // !CPLUSPLUS_H

在cplusplus.cpp文件中输入以下内容:

#include "cplusplus.h"#include <iostream>using namespace std;
CCPlus::CCPlus(){ cout << "CCPlus" << endl;}
CCPlus::~CCPlus(){ cout << "~CCPlus" << endl;}
int CCPlus::add(int a, int b){ cout << "add" << endl; return a + b;}
int CCPlus::sub(int a, int b){ cout << "sub" << endl; return a - b;}

至此,代码我们已经写完了,接下来需要设置cplusplus工程属性,选中“cplusplus”工程->鼠标右键->属性->配置属性->常规->配置类型 更改为“动态库(.dll)”;还要设置预处理宏,在C/C++->预处理器->预处理器定义,在末尾输入";CPLUSPLUS_PORTS",注意双引号不要输入。设置完毕如下图所示:

C#调用C++语言编写的动态链接库

C#调用C++语言编写的动态链接库

设置完毕,Ctrl+Shift+B编译一下,结果如下所示:

C#调用C++语言编写的动态链接库

至此,C++的项目就已经写好了。

C#调用

    接下来我们试一下创建一个C#项目调用我们编写的C++动态库; 选中解决方案->鼠标右键->添加->新建项目,在弹出的窗口左侧找到Visual C#,中间选中“控制台应用(.NET Framework)”,在下方输入名称“CShape”,点击确定按钮完成项目的创建,如下所示:

C#调用C++语言编写的动态链接库

我们先以上篇文章的方法测试一下,打开Program.cs文件,输入如下代码:

using System;using System.Runtime.InteropServices;
namespace CShape{
public class CPlus { [DllImport("cplusplus.dll", EntryPoint = "add", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int add(int a, int b); }
class Program { static void Main(string[] args) { CPlus.add(1, 2);
Console.ReadKey(); } }}

将CShape项目的输出路径改为“..Debug”(不含双引号,详见可参见上一篇文章),并将CShape设为启动项目。

按Ctrl+Shift+B编译一下,没有错误的话按F5快捷键运行,会发现运行不起来,报错了,如下图所示:

C#调用C++语言编写的动态链接库

找不到名为“add”的入口点?可是我们明明在cplusplus项目中有add函数啊。这里所涉及的知识就太多了,本文不对其进行讲解,有兴趣的朋友可以网上查找相关资料。虽然不能像调用C那样调用C++,但是我们可以通过CLR将C++中转成C#可以调用的。


CLR

    首先我们先新建一个CLR工程,选中“解决方法”->添加->新建项目,如下所示:

C#调用C++语言编写的动态链接库

然后我们在CLR自动生成的CLR.h文件中写入如下内容:

#ifndef CLR_H#define CLR_H
#include "../cplusplus/cplusplus.h"#pragma comment(lib, "../Debug/cplusplus.lib")using namespace System;
namespace CLR { public ref class ClassCLR { public: ClassCLR(); ~ClassCLR();
int add(int a, int b); int sub(int a, int b);
private: CCPlus *m_pPlus; };}
#endif // !CLR_H

在CLR.cpp文件中写入如下内容:

#include "pch.h"#include "CLR.h"
CLR::ClassCLR::ClassCLR() :m_pPlus(new CCPlus){}
CLR::ClassCLR::~ClassCLR(){ delete m_pPlus;}
int CLR::ClassCLR::add(int a, int b){ return m_pPlus->add(a, b);}
int CLR::ClassCLR::sub(int a, int b){ return m_pPlus->sub(a, b);}

确认无误后按Ctrl+Shift+B编译。至此C++和CLR项目都编写完了,接下来我们在C#调用CLR来达到调用C++动态链接库。

打开C#项目CShape,选中“引用”,鼠标右键->添加引用,选中刚刚创建的CLR项目,如下图所示:

在CShape的Program.cs文件中输入如下内容:

using System;using System.Runtime.InteropServices;
namespace CShape{ class Program { static void Main(string[] args) { CLR.ClassCLR clr = new CLR.ClassCLR(); Console.WriteLine(clr.add(1, 2)); Console.WriteLine(clr.sub(10, 20));
Console.ReadKey(); } }}

OK,确认无误全部编译后启动,效果如下:

至此成功调用了C++编写的类类型的动态链接库。

思考一下,如果C++动态链接库类成员函数接受的是std或指针类型的参数,C#该怎么处理?

以上是关于想【C++ 】高手发起挑战,请教一个【动态链接库 dll 和 类成员函数 显式链接】问题的主要内容,如果未能解决你的问题,请参考以下文章

请教python调用dll动态库的传参问题

请教一个C++ 问题

如何(可移植地)使用 C++ 类层次结构和动态链接库

C语言怎么使用动态链接库,如何创建?(高手进)

C语言怎么使用动态链接库,如何创建?(高手进)

C#调用C++语言编写的动态链接库