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,基于对话框的,放置两个按纽 add和subtract, 响应按纽消息,调用这个 Dll的add 和subtract函数。使用这两个函数前要先声明函数, //extern int add(int a,int b);
//extern int subtract(int a,int b);
还需要将
Dll1.lib和Dll1.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
中写两个函数 add和subtract
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.