2502,dll导出C++类

embedded/2025/2/2 23:08:23/

C++语言毕竟能和窗口DLL可和平共处.

介绍

自从窗口开始阶段,动态链接库(DLL)就是窗口平台的一个组成部分.动态链接库,允许在一个独立的模块中封装一系列的功能函数,然后以一个显式的C函数列表外部用户使用.

在上个世纪80年代,当窗口DLL面世时,对广大程序员而言只有C语言切实可行开发手段.所以,窗口DLL很自然地以C函数和数据向外暴露功能.

本质来说,一个DLL可由任意语言实现,但是为了使DLL其它语言和环境下使用,一个DLL接口必须回退C语言.

而由一个C++编译器产生的二进制代码,并不可以其它C++编译器兼容.再者,在同一个编译器不同版本二进制代码也是互不兼容的.

所有这些,导致从一个DLL中导出一个C++类简直就是一个冒险.

这里演示几种从一个DLL模块中导出C++类的方法.源码演示了导出虚构的Xyz对象的不同技巧.Xyz对象非常简单,只有一个函数:Foo.
下面是Xyz对象的图解:

Xyz
int Foo(int)

在一个DLL里实现Xyz对象,该DLL可按分布式系统大范围的客户使用.一个用户可按下面三个方式调用Xyz的功能:
1,使用纯C
2,使用一个普通的C++
3,使用一个抽象的C++接口
源码包含两个工程:

XyzLibrary,一个DLL工程
XyzExecutable,一个Win32使用"XyzLibrary.dll"控制台程序.
XyzLibrary工程使用下列方便的宏导出它的代码:

#if defined(XYZLIBRARY_EXPORT)//`DLL`内部
#   define XYZAPI   __declspec(dllexport)
#else//`DLL`外部
#   define XYZAPI   __declspec(dllimport)
#endif//`XYZLIBRARY_EXPORT`

仅在XyzLibrary工程定义XYZLIBRARY_EXPORT标识,因此在生成DLL时,按__declspec(dllexport)扩展XYZAPI宏而,而在生成客户程序时按__declspec(dllimport)扩展.

C语言方式

句柄

经典的C语言方式面向对象编程,就是使用晦涩的指针,比如句柄.一个用户可使用一个函数创建一个对象.实际上该函数返回的是该对象一个句柄.

接着用户可调用该对象相关的各种操作函数,只要该函数可按它的一个参数接受该句柄.一个很好的示例就是,在Win32窗口相关的API中句柄的习惯是,使用一个窗柄句柄来代表一个窗口.

Xyz对象,如下导出一个C接口:

typedef tagXYZHANDLE {} * XYZHANDLE;//创建一个`Xyz`对象实例的函数
XYZAPI XYZHANDLE APIENTRY GetXyz(VOID);//调用`Xyz.Foo`函数
XYZAPI INT APIENTRY XyzFoo(XYZHANDLE handle, INT n);//释放`Xyz`实例和消费的资源
XYZAPI VOID APIENTRY XyzRelease(XYZHANDLE handle);//在`WinDef.h`头文件中按`__stdcall`定义`APIENTRY`.

下面是一个客户调用的C代码:

#include "XyzLibrary.h"
...
/*创建`Xyz`实例*/
XYZHANDLE hXyz = GetXyz();
if(hXyz)
{/*调用`Xyz.Foo`函数*/XyzFoo(hXyz, 42);/*析构`Xyz`实例,并释放已取得的资源.*/XyzRelease(hXyz);hXyz = NULL;
}

这样,一个DLL必须有显式构建和删除对象的函数.

调用协议

对所有的导出函数,记住它们的调用协议是重要的.忘记添加调用协议非常普遍的错误.只要客户的调用协议DLL调用协议匹配,一切都能运行.

但是,一旦客户改变了它的调用协议,就会产生难以察觉运行时错误.XyzLibrary工程使用一个在"WinDef.h"该头文件里,按__stdcall定义的APIENTRY宏.

异常缺点

DLL的用户,取正确对象的合适的方法.比如下面代码片断,编译器不能捕捉其中的错误:

/*`void*GetSomeOtherObject(void)`是别的地方定义的一个函数*/
XYZHANDLE h = GetSomeOtherObject();
/*啊!错误:在错误的对象实例上调用`Xyz.Foo`函数*/
XyzFoo(h, 42);

很麻烦!

C++天然的方式:导出一个类

窗口平台上,几乎每一个现代的编译器都支持从一个DLL导出一个类.导出一个类导出一个C函数类似.

只要在类名前使用__declspec(dllexport/dllimport)关键字来指定导出整个类,或在函数声明前指定导出特定的成员函数,就可以.
如下:

 //导出包括它的函数和成员的整个`CXyz`类
class XYZAPI CXyz
{
public:int Foo(int n);
};//只导出`CXyz::Foo`函数
class CXyz
{
public:XYZAPI int Foo(int n);
};

导出整个类它们的方法时,不必显式指定一个调用协议.默认,C++编译器使用__thiscall作为类成员函数的调用协议.

然而,不同编译器不同命名混杂法则,导出的C++类只能来同一类型同一版本编译器.

只有MSVisualC++编译器可使用该DLL.DLL客户代码,只有在同一版本MSVC++编译器才能确保调用者被调的装饰名匹配.
如下客户使用Xyz对象的示例:

#include "XyzLibrary.h"
...//客户使用`Xyz`对象按一个规则`C++`类.
CXyz xyz;
xyz.Foo(42);

导出的C++类的用法和其它C++类的用法几乎是一样的…
客户代码DLL都必须和同一版本CRT动态连接在一起.为了可在模块间修复CRT资源的纪录,这一步是必需的.

C++成熟的方法:使用抽象接口

一个C++抽象接口,成功完成了两全其美:对对象而言,独立于编译器的规则接口方便面向对象方式调用函数.

要提供一个接口声明的头文件,同时实现一个可返回最新创建的对象实例工厂函数.只要用__declspec(dllexport/dllimport)指定该工厂函数就可以了.
不需要额外指定接口.

    //无需额外指定`Xyzobject`的`抽象接口`.
struct IXyz
{virtual int Foo(int n) = 0;virtual void Release() = 0;
};//创建`Xyz`对象实例的工厂函数
extern "C" XYZAPI IXyz* APIENTRY GetXyz();

上面代码片断中,按extern XYZAPI声明GetXyz工厂函数.用来避免装饰函数名.

这样,在外部按一个普通的C函数对待该函数.如下使用抽象接口:

#include "XyzLibrary.h"
...
IXyz* pXyz = ::GetXyz();
if(pXyz)
{pXyz->Foo(42);pXyz->Release();pXyz = NULL;
}

因为C++天生支持面向对象.微软很明显按产业COM重量级的工具开发它.作为COM技术的物主,微软已确保COM二进制标准和它们拥有的在VC++编译器实现的C++对象模型可以最小的成本实现匹配.


http://www.ppmy.cn/embedded/159033.html

相关文章

深度学习篇---数据存储类型

文章目录 前言第一部分:C语言中的数据存储类型1. char(通常是8位)优点缺点 2. short(通常是16位)优点缺点 3. int(通常是32位)优点缺点 4. long(通常是32位或64位)优点缺…

如何移植ftp服务器到arm板子?

很多厂家提供的sdk,一般都不自带ftp服务器功能, 需要要发人员自己移植ftp服务器程序。 本文手把手教大家如何移植ftp server到arm板子。 环境 sdk:复旦微 Buildroot 2018.02.31. 解压 $ mkdir ~/vsftpd $ cp vsftpd-3.0.2.tar.gz ~/vs…

苹果AR眼镜:产品规划与战略路线深度解析

随着增强现实(AR)技术的不断发展,苹果公司正逐步推进其AR智能眼镜项目。尽管Vision Pro作为一款高端混合现实设备已经面世,但苹果真正的目标是开发出一款轻便、全天候佩戴且能够取代智能手机功能的AR眼镜。本文将梳理苹果在AR领域的探索历程,并分析其当前的产品状态及未来…

【机器学习】自定义数据集 使用scikit-learn中svm的包实现svm分类

一、支持向量机(support vector machines. ,SVM)概念 1. SVM 绪论 支持向量机(SVM)的核心思想是找到一个最优的超平面,将不同类别的数据点分开。SVM 的关键特点包括: ① 分类与回归: SVM 可以用于分类&a…

[c语言日寄]越界访问:意外的死循环

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋:这是一个专注于C语言刷题的专栏,精选题目,搭配详细题解、拓展算法。从基础语法到复杂算法,题目涉及的知识点全面覆盖,助力你系统提升。无论你是初学者,还是…

概述、 BGP AS 、BGP 邻居、 BGP 更新源 、BGP TTL 、BGP路由表、 BGP 同步

BGP(Border Gatreway Protcol)边界网关路由协议 BGP基本配置 BGP实战拓扑 外部BGP基础配置 内部BGP邻居 EBGP基本配置 CCNP综合实验拓扑 文章目录 系列文件概述BGP ASBGP 邻居BGP 更新源BGP TTLBGP路由表BGP 同步 概述 当前所使用的计算机网络…

29.Word:公司本财年的年度报告【13】

目录 NO1.2.3.4 NO5.6.7​ NO8.9.10​ NO1.2.3.4 另存为F12:考生文件夹:Word.docx选中绿色标记的标题文本→样式对话框→单击右键→点击样式对话框→单击右键→修改→所有脚本→颜色/字体/名称→边框:0.5磅、黑色、单线条:点…

【web js逆向分析易盾滑块cb参数】逆向分析网易易盾滑块的cb参数,仅供学习交流

文章日期:2025.2.1 使用工具:Node.js 本章知识:分析易盾滑块的cb参数生成 version:2.28.0 文章难度:简单 文章全程已做去敏处理!!! 【需要做的可联系我】 AES解密处理(…