博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++链接库
阅读量:6824 次
发布时间:2019-06-26

本文共 5988 字,大约阅读时间需要 19 分钟。

  静态链接库在程序编译链接过程中就导入lib文件并且包含在生成的exe文件里,而动态链接库DLL是在程序运行中由程序加载和卸载的,也就是说它是动态的,当然动态链接库DLL也可以静态加载当做静态来用;

  静态链接库使用方便直接,但程序内存占用大、使用不灵活,而动态链接库使用灵活但加载需要时间。

静态链接库

  本文使VC++6.0集成工具,新建一个Win32 Static Library工程,再新建一个如下cpp文件,完成构建之后可以得到一个lib文件,这就是之后的应用程序要用到的文件;

//--********* libTest.cpp **********--// //-----------  声 明 一 个 函 数,并 按 C 方 式 编 译 连 接 --------- extern "C" int getNumber(); int getNumber(){    return 23;}

  构建之后,静态连接库就算创建完成了,在lib文件中可以找到"_getNumber",这就是"C"编译方式的函数名;如果声明的函数名之前加上__stdcall",

即声明:int __stdcall getNumber();那么lib文件中可以见到"?getNumber@@YGHHH@Z",这就是C++编译方式的函数名(方便函数重载)。另外的以__cdecl(缺省)和__fastcall编译之后的命名方式也类似。

然后新建一个工程,把头文件libTest.h和库文件libTest.lib复制到新工程文件夹下,主程序代码如下:

#include 
#include "libTest.h"//--------- 把 要 用 到 的 外 部 链 接 文 件 和 本 地 obj 文 件 一 同 构 建 ------ //--------- 如 果 不 复 制 ,则 这 里 使 用 全 路 径 指 向 库 工 程 的 lib ---#pragma comment(lib,"libTest.lib")int main(int argc, char* argv[]){ printf("Number: %d\n", getNumber()); return 0;}

程序运行结果如下:

 

动态链接库

/*** 新建 一 个 dynamic~link Libbrary 工 程 dllTest ******* **** 添 加 一 个 dllTest.cpp 文 件 ***** */ //-------------- 声 明 并 导 出 函 数 ------- extern "C" __declspec(dllexport) int add(int x, int y); //__declspec(dllexport)int __stdcall add(int,int); int add(int x, int y){    return x + y;}

创建一个动态链接库时,需要把其中的方法导出,并生成dll文件,上述的__declspec(dllexport)是一种导出方式;

或者可以用def文件导出,新建一def文件,并添加文件到工程就行了,其全部的代码如下:

LIBRARY dllTestEXPORTSadd @ 1

其中add是函数名,1是序号;这样构建之后也可以得到一个dll文件。

新建一个应用工程,把生成的dll文件复制到工程文件夹下,主函数如下:

#include 
#include
//------------- 根 据 之 前 导 出 的 形 式 定 义 一 个 函 数 指 针 类 型 -----------typedef int (*lpAddFun)(int,int);//typedef int (__stdcall *lpAddFun)(int,int); int main(int argc, char* argv[]){
//-------------- 用 定 义 的 函 数 指 针 类 型 创 建 一 个 函 数 ------ lpAddFun add; //--------------- 加 载 动 态 链 接 库 --------- HINSTANCE hDll = LoadLibrary("dllTest.dll"); if(hDll) {
  //----------------- 可 以 用 函 数 名 导 入,也 可 以 用 序 号 来 导 入-------- add = (lpAddFun)GetProcAddress(hDll, "add");    //add = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1)); if(add) printf("5 + 7 = %d\n", add(5, 7));  //----------------- 卸 载 链 接 库 ----------- FreeLibrary(hDll); } return 0;}

程序运行结果如下: 

 

 dll的静态调用方式

   动态链接库的创建方式相同,但是使用时,需要复制dll文件、lib文件两个文件到应用工程文件夹下,主函数代码如下:

#include 
#pragma comment(lib, "dllTest.lib") //-------------- 以 "C" 方 式 导 出 的 函 数 ,这 里 就 要 以 "C" 的 方 式 导 入 ----------//-------------- 或 者 导 出 时 不 加 extern "C" ,这 里 也 不 加( 缺 省 __cdecl ) -------------//-------------- 也 就 是 说 导 入 和 导 出 的 方 式 必 须 一 致(注意!) ------------------extern "C" __declspec(dllimport) int add(int, int); //__declspec(dllimport)int __stdcall add(int,int); int main(){ printf("12 + 25 = %d\n", add(12, 25)); return 0;}

 两种调用方式的对比:

  (1) 静态调用的add(),是通过dllTest.lib文件索引到dllTest.dll文件中的add();

  (2) 动态调用方式是在程序运行时,直接加载dll文件,导出其中的add()方法并给函数指针赋值,通过函数指针调用函数。

 

函数DllMain

  Windows加载dll时需要一个入口函数DllMain(),当dll文件中没有DllMain()函数时,会调用一个缺省的无任何操作的DllMain(),它是由系统调用的,不能手动调用。

#include "dllTest.h"#include 
#include
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ switch(ul_reason_for_call) { case DLL_PROCESS_ATTACH: printf("dll process attach\n"); break; case DLL_THREAD_ATTACH: printf("dll thread attach\n"); break; case DLL_PROCESS_DETACH: printf("dll process detach\n"); break; case DLL_THREAD_DETACH: printf("dll process detach\n"); break; } return TRUE;}int add(int x, int y){ return x + y;}

  这是一个DllMain()使用实例,其中ul_reason_for_call代表dll的四种调用方式,包括两种加载和两种卸载;

用前面的应用程序实验之后,结果如下:

 

导出类class

class CPoint{public:    float x;    float y;    virtual void    add(float, float) = 0;    virtual float   getX() = 0;    virtual float   getY() = 0;};class CMyPoint : public CPoint{public:    CMyPoint(float, float);    virtual void    add(float, float);    virtual float   getX();    virtual float   getY();}; class __declspec(dllexport)CMyPoint;

 

  设计一对父类——子类,父类中的方法设计成纯虚函数,而它的子类就是我们要用的类了,在dll中工程中导出的也是子类CMyPoint,上面最后一句代码导出这个子类。

  这是子类的实现代码:

CMyPoint::CMyPoint(float x0, float y0){    var = 12;    x = x0;    y = y0;}void CMyPoint::add(float dx, float dy){    x = x + dx;    y = y + dy;}float CMyPoint::getX(){    return x;}float CMyPoint::getY(){    return y;}
CMyPoint.cpp

  类CMyPoint的实现还包含一个Create函数,是用来创建类对象的。这个方法也应该用C方式导出,否则使用起来有些麻烦。

extern "C" __declspec(dllexport)CMyPoint* CreatePoint(float,float);CMyPoint* CreatePoint(float x0, float y0){    return (new CMyPoint(x0, y0));}

 

 

动态加载方式

  新建应用工程,需要复制.h头文件、dll文件到新的工程文件夹下,程序先获取CreatePoint()方法,再通过它创建类CMyPoint并返回一个对象指针,接下来便可以操作这个类对象;

  注意:应用工程文件夹下的dllTest.h中不能有多余的导出或者导入语句,可以用宏屏蔽,也可以手动删除。

#include "dllTest.h" typedef CMyPoint*(*lpCreatePoint)(float,float);int main(int argc, char* argv[]){
lpCreatePoint CreatePoint; HINSTANCE hDll = LoadLibrary("dllTest.dll"); if(hDll) {
CreatePoint = (lpCreatePoint)GetProcAddress(hDll, "CreatePoint"); if(CreatePoint) { CMyPoint* p0 = CreatePoint(12, 14); printf("x = %.2f, y = %.2f\n", p0->getX(), p0->getY()); } FreeLibrary(hDll); } return 0;}

 

 

 

静态加载方式

  静态使用方式很简单,同样应用创建函数CreatePoint(),而且是导出后直接使用,当然类界面还是必须inlclude;

#include "dllTest.h" #pragma comment(lib, "E:\\VC6\\dllTest\\Debug\\dllTest.lib") extern "C" __declspec(dllimport)CreatePoint(float,float);int main(){        CMyPoint* p0 = CreatePoint(3, 4);    p0->add(2, 3);        printf("x = %.2f, y = %.2f\n", p0->getX(), p0->getY());    return 0;}

 

  静态导入方式构建生成的EXE文件可移植,lib文件和dll文件在构建时都固化在其中,所以这里生成的EXE无论到哪儿,只要系统还兼容,程序就可以运行;

  而之前的动态导入方式,dll文件需要随着EXE文件移动,不然程序运行时会找不到dll文件而出错,所以我们一般的见到的软件安装目录下都有一大堆dll文件。

 

导出变量

  在dll工程中,导出:int __declspec(dllexport)varName;

  在应用工程中,导入:int __declspec(dllimport)varName;

  其中,int是变量类型,varName是变量名,

 

posted on
2016-04-26 23:15 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/mwwf-blogs/p/5435712.html

你可能感兴趣的文章
线性插值针对位置量和角度量
查看>>
android4.0 在ubuntu10.04(64位)上的下载与编译
查看>>
记一次在 Linux 上创建 Django 应用的过程
查看>>
伍雨霏-懂游戏的云服务如何保驾护航
查看>>
MFC界面库BCGControlBar v25.1新版亮点四:网格控件等
查看>>
ssh 连接非22端口服务器的方法:
查看>>
Linux基础入门
查看>>
org.hibernate.hql.internal.ast.QuerySyntaxException: user is not mapped
查看>>
图解排序算法之快速排序-双端探测法
查看>>
mysql
查看>>
11月15日云栖精选夜读:分布式服务框架Dubbo疯狂更新!阿里开源要搞大事情?...
查看>>
Druid数据库连接池就这么简单
查看>>
Python最假的库:Faker
查看>>
IDE 插件新版本发布,开发效率 “biu” 起来了
查看>>
阿里云安全肖力:安全基础建设是企业数字化转型的基石
查看>>
Redis 基础、高级特性与性能调优
查看>>
BZT52C15S资料
查看>>
Laravel Telescope入门教程(上)
查看>>
Linux配置ip 及网络问题排查
查看>>
AndroidStudio用Cmake方式编译NDK代码(cmake配置.a库)
查看>>