第19课 动态链接库

1.DLL 简介,动态库,静态库。动态库节约磁盘空间,静态库体积大。可以用多种语言编写  DLL文件。动态库有两种加载方式:隐式调用和动态加裁!
Windows API 动态库: Kernel32.dll User32.dll GDI32.dll

使用动态链接库的好处:
  • 可以采用多种编程语言
  • 增强产品的功能(方便更新dll)
  • 提供二次开发的平台
  • 简化项目的管理
  • 可以节省磁盘空间和内存
  • 有助于资源共享
  • 有助于实现应用程序的本地化
动态链接库的加载方式:
  • 隐式链接
  • 显式加载
使用extern声明外部函数
使用_declspec(dllimport)声明外部函数

2. 新建一个 DLL1  dll工程,加入一源文件名为 dll1.cpp,加入add subtract两个函数,注意此时须在函数名前加 _declspec(dllexport),并且编译。用dumpbi -exports dll1.dll查看其导出的函数,发现函数名字已经被改成了 add@@YAHHH@Z, 这种现象叫做名字粉碎,是为了支持函数重载而做的。

3. 编写一个程序测试 DLL, 工程名为  DllTest,基于对话框的,放置两个按纽 addsubtract, 响应按纽消息,调用这个 Dlladd subtract函数。使用这两个函数前要先声明函数, //extern int add(int a,int b);
//extern int subtract(int a,int b);
还需要将  Dll1.libDll1.dll 拷贝到当前目录下!另外还需要在 Project->Setting->Link->Object/Library 中加入Dll1.lib,此种方式为隐式调用! OK!用Dumpbin -imports DllTest.exe查看它的输入信息,可以看到它加载了 dll1.dll。同时也可以用depends程序查看程序需要哪些 dll文件!除了用extern外,还可以用 //_declspec(dllimport) int add(int a,int b);
//_declspec(dllimport) int subtract(int a,int b);
告诉编译器,此函数是动态链接库中的函数,这样可以提高效率。

4. 通常写 Dll  时在dll1.h中声明函数,然后在 DllTest.h中包含这个头文件,另外会用一组宏来取代 _declspec(dllimport)
Dll1.h
#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllimport)
#endifDLL1_API int _stdcall add(int a,int b);
DLL1_API int _stdcall subtract(int a,int b);
Dll1.cpp 的代码:
#define DLL1_API extern "C" _declspec(dllexport)
#include "Dll1.h"
#include <Windows.h>
#include <stdio.h>int _stdcall add(int a,int b)
{
 return a+b;
}int _stdcall subtract(int a,int b)
{
 return a-b;
}

5. Dll1  中加入类 Point它有一个函数output(int a,intb),它的功能是在屏幕上输出 x,y值。须包含头文件windows.h stdio.h.然后在DllTest 中加入一个按纽来测试这个函数!此时我们可以 dumpbin来查看dll1.dll dllTest.exe的导出导入情况。注意,也可以只导出类的某个函数。

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

7. 解决之道是用模块定义文件。
  1. 新建 dll2.dll  工程;
  2. dll2.cpp  中写两个函数 addsubtract
  3. 在目录中新建 dll2.def 文件,增加到工程。
  4. dll2.def  中加入如下代码:
LIBRARY Dll2EXPORTS
add
subtract
   5.  编译后用 dumpbin查看函数名是否被改变?
   6.  测试,我们这次用动态加载的方法来调用 dll文件。以前是用隐式链接的方法,嘿嘿。动态加载的好处是需要时再加载,可以提高执行的效率。代码如下:
 HINSTANCE hInst;
 hInst=LoadLibrary("Dll3.dll");
 typedef int (/*_stdcall*/ *ADDPROC)(int a,int b);
 //ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"?add@@YAHHH@Z");
 ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));
 if(!Add)
 {
  MessageBox("  获取函数地址失败! ");
  return;
 }
 CString str;
 str.Format("5+3=%d",Add(5,3));
 MessageBox(str);
 FreeLibrary(hInst);
    7.  此时你改变调用约定,函数名不会被改变,但如果你加上 _stdcall定义函数,调用时也需要加入 _stdcall,否则会出错!

8.DllMain() Dll  的入口点,不过不是必须的。但在 DllMain中不要做复杂的调用。为什么?因为 DllMain加载时,某些核心Dll文件不一定已经被加载。

9. 创建一个基于 MFC  DLL工程,简介。

10. 当不使用 DLL  时,调用 FreeLibrary减少DLL 的使用计数,释放 DLL资源,减少系统负担。明白?

11. 上面总结:
     1).*.def 使函数名不改变;
     2).  定义时为 _stdcall,调用时也必须用_stdcall.

猜你喜欢

转载自blog.csdn.net/zhang_zxk/article/details/52401966