孙鑫MFC 19 章 动态链接库编程

来源:互联网 发布:nvsip监控软件下载 编辑:程序博客网 时间:2024/06/06 17:12


Windows API中的所有函数都包含在DLL中。其中有三个最重要的DLL,Kernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。

静态库和动态库

静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.EXE文件);

在使用动态库的时候,往往提供两个文件:一个因入库和一个DLL。因入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行时候,再去加载DLL,访问DLL中导出的函数。

使用动态链接库的好处

可以采用多种编程语言来编写。

增强产品的功能。

提供二次开发的平台。

简化项目管理

可以节省磁盘空间和内存。

有助于资源的共享。

有助于实现应用程序的本地化

建立DLL文件代码如下:

Dll.h

 

int _declspec(dllimport) add(int a, int b);//必须带_declspec(dllexport)文件,以生成*.lib文件

 

dll.cpp

 

#include "dll.h"

 

int _stdcall add(int a, int b)

{

         returna + b;

}

 

链接的def 文件

 

LIBRARY dll

 

EXPORTS //自己的函数

add

如果要查找*dll中包含信息,比如说是否生成了函数调用,或者查看函数名字,可在命令行下进入Debug所在目录,输入以下命令

dumpbin -exports D:\Projects\C++\dll\Debug\dll.dll

发现函数名字已经被改成了?add@@YAHHH@Z,这种现象叫做名字粉碎,是为了支持函数重载而做的。

 

有些时候由于某种安装原因,dumpbin被认为是无效命令,接下来在

C:\Program Files\Microsoft Visual Studio\VC98\Bin\下找到VCVARS32.bat并在命令行运行,之后就能执行dumpbin命令了。

 

新建MFC程序,新建两个按钮,代码如下:

void CDllTestDlg::OnBtnAdd()

{

 // TODO: Add your controlnotification handler code here

 CString str;

 str.Format("3+5=&d",Add(3,5));

 MessageBox(str);

}

 

void CDllTestDlg::OnBtnSubtract()

{

 // TODO: Add your controlnotification handler code here

 CString str;

 str.Format("5-3=%d",Subtract(5,3));

 MessageBox(str);

}

为使编译器认识Add,Subtract,必须在之前使用两个声明:

extern int Add(int x,int y);

extern int Subtract(int x,int y);

可以使用标示符表示这两个函数是从动态链接库的.lib文件引用的,以生成效率更高的代码

_declspec(dllimport) int Add(int x,int y);

_declspec(dllimport) int Subtract(int x,int y);

这两段代码我们也可以在DLL中新建一个头文件放进去,并在MFC程序中添加头文件

 

如果要查看DllTest.exe文件信息,使用命令行dumpbin-imports dlltest.exe

 

修改动态链接库Dll.h

#ifdef DLL_API

#else

#define DLL_API _declspec(dllimport)

#endif

 

DLL_API int Add(int x,int y);

DLL_API int Subtract(int x,int y);

 

修改Dll.cpp文件

#define DLL_API _declspec(dllexport)

#include "Dll.h"

 

int Add(int x,int y)

{

 return x+y;

}

int Subtract(int x,int y)

{

 return x-y;

}

这样做是为了方便外部程序调用同时方便内部程序使用,因为动态链接库中只有导出的函数才可以被使用,没有导出的函数在外部是看不到的,是不能被访问的

 

我们希望导出的函数名不被改变,加extern "C"大写的C!即可,#define DLL1_API extern "C" _declspec(dllexport),但它只能导出全局函数,不能导出类的成员函数,并且如果调用约定被改成了别的方式,此时函数名也被改变。所以这种方式不太好。

 

解决之道是用模块定义文件。

  a.新建dll2.dll工程;

  b.加dll2.cpp中写两个函数add和subtract

  c.在目录中新建dll2.def文件,增加到工程。

  d.在dll2.def中加入如下代码:

LIBRARY Dll2EXPORTS

add

subtract

   e.编译后用dumpbin查看函数名是否被改变?

此时你改变调用约定,函数名不会被改变,但如果你加上_stdcall定义函数,调用时也需要加入_stdcall,否则会出错!

 静态加载的方法: 

 1、添加头文件#include "Dll.h" 

2、引入Lib库  #pragma comment(lib,"Dll.lib") 

3、这样就可以直接使用Dll.h中导入的函数 

动态加载的方法:

         HINSTANCEhandle = LoadLibrary("dll.dll");

         typedefint(*addproc)(int, int);       //typedefint(_stdcall *addproc)(int, int);

 

         if(handle)

         {

                   addprocmyadd = (addproc)GetProcAddress(handle,"?Add@@YAHHH@Z");  //这里的名字随着变化

                   if(myadd)

                   {

                            cout<< myadd(1, 2) << endl;

                   }

         }

 

XX.def文件可以很好的解决 编译时,函数名字变化的问题

动态连接,如果要调用很多的dll文件时,这样可以节省内存

不需要连接lib对象,也不需要头文件,只需要一个dll文件即可。

 

 

0 0
原创粉丝点击