1.Windows API动态链接库:Kernel32.dll User32.dll GDI32.dll
2.静态库:扩展名为.lib
动态库: 包括:(1) 引入库:扩展名为.lib,引入库包含被DLL导出的函数和变量的符号名
(2) 动态链接库:扩展名为.dll,DLL包含实际的函数和数据,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载DLL访问DLL中导出的函数
3.使用动态链接库的好处:
(1)可以采用多种编程语言来编写
(2)增强产品的功能
(3)提供二次开发的平台
(4)简化项目管理
(5)可以节省磁盘空间的内存
(6)有助于资源的共享
(7)有助于实现应用程序的本地化
4.动态链接库的加载的两种方式:
(1)隐式链接
(2)显示加载
5.隐式链接:
(1)查看导出函数:
E:\>cd E:\TDDOWNLOAD\孙鑫c++教程\c++练习\DLL\myDLL\Debug
E:\TDDOWNLOAD\孙鑫c++教程\c++练习\DLL\myDLL\Debug>dumpbin
提示:如果找不到dumpbin 找到D:\软件\VC98\Bin下的批处理文件VCVARS32.BAT 直接拖到到dos运行即可
E:\TDDOWNLOAD\孙鑫c++教程\c++练习\DLL\myDLL\Debug>dumpbin -exports myDLL.dll
注:
基本dos操作:cd.. cd\ cd
(2)编写DLL文件:
/***two kinds of ways to import DLL***/
//extern int add(int a,int b);
//extern int subtract(int a,int b);
//_declspec(dllimport)int add(int a,int b);
//_declspec(dllimport)int subtract(int a,int b);
头文件:myDLL.h
#ifdef MYDLL_API
#else
#define MYDLL_API _declspec(dllimport)
#endif
MYDLL_API int add(int a,int b);
MYDLL_API int subtract(int a,int b);
源文件:myDLL.cpp
#define MYDLL_API _declspec(dllexport)
#include "myDLL.h"
int add(int a,int b)
{
return a+b;
}
int subtract(int a,int b)
{
return a-b;
}
注:编译时,头文件不参与编译,源文件单独编译
(3)编写测试工程TextmyDLL
#include "myDLL.h"
将 myDLL.h myDLL.lib myDLL.dll 拷贝到测试工程中
还需在project-settings下的Link添加myDLL.lib 或者增加#pragma comment(lib,"myDLL.h")
注:查看导入函数:dumpbin -imports TestmyDLL.exe
E:\TDDOWNLOAD\孙鑫c++教程\c++练习\DLL\TestmyDLL\Debug>dumpbin -imports TestmyDll.exe
(4)导出类
头文件:myDLL.h
#ifdef MYDLL_API
#else
#define MYDLL_API _declspec(dllimport)
#endif
MYDLL_API int add(int a,int b);
MYDLL_API int subtract(int a,int b);
class MYDLL_API Point
{
public:
void output(int x,int y);
};
源文件:myDLL.cpp
#define MYDLL_API _declspec(dllexport)
#include "myDLL.h"
#include<windows.h>
#include<stdio.h>
int add(int a,int b)
{
return a+b;
}
int subtract(int a,int b)
{
return a-b;
}
void Point::output(int x,int y)
{
HWND hwnd=GetForegroundWindow();
HDC hdc=GetDC(hwnd);
char buf[20];
memset(buf,0,20);
sprintf(buf,"x=%d,y=%d",x,y);
TextOut(hdc,0,0,buf,strlen(buf));
ReleaseDC(hwnd,hdc);
}
注:如果只想导出类中的一个函数,则在myDLL.h中改为:
class Point
{
public:
MYDLL_API void output(int x,int y);
void test();//test函数没有被导出
};
测试工程文件中:
void CTestmyDLLDlg::OnAdd()
{
CString str;
str.Format("5+3=%d",add(5,3));
MessageBox(str);
}
void CTestmyDLLDlg::OnCsubtract()
{
CString str;
str.Format("5-3=%d",subtract(5,3));
MessageBox(str);
}
void CTestmyDLLDlg::OnOutput()
{
Point pt;
pt.output(5,3);
}
注意:如果DLL改变 myDLL.h myDLL.lib myDLL.dll 必须重新拷贝到测试工程中
(5)防止c++名字改编:
头文件:myDLL.h
#ifdef MYDLL_API
#else
#define MYDLL_API extern "C" _declspec(dllimport)//加入extern "C"防止导出名字改编
#endif
MYDLL_API int add(int a,int b);
MYDLL_API int subtract(int a,int b);
源文件:myDLL.cpp
#define MYDLL_API extern "C" _declspec(dllexport) //"C"为大写
#include "myDLL.h"
int add(int a,int b)
{
return a+b;
}
int subtract(int a,int b)
{
return a-b;
}
注:加入 extern "C"不能导出一个类的成员函数,只能导出全局函数,让全局函数不发生名字改编,但如果调用编写改变,即使加入了extern "C",导出名字仍然会改编
(6)调用约定
_stdcall 标准调用约定,就是winAPI调用约定,也是pascal调用约定
没有加_stdcall,为c调用约定
头文件:myDLL.h
#ifdef MYDLL_API
#else
#define MYDLL_API extern "C" _declspec(dllimport)//加入extern "C"防止导出名字改编
#endif
MYDLL_API int _stdcall add(int a,int b);
MYDLL_API int _stdcall subtract(int a,int b);
源文件:myDLL.cpp
#define MYDLL_API extern "C" _declspec(dllexport) //"C"为大写
#include "myDLL.h"
int _stdcall add(int a,int b)
{
return a+b;
}
int _stdcall subtract(int a,int b)
{
return a-b;
}
(7)模块定义文件:防止调用约定名字改编
模块定义文件 myDLL.def
LIBRARY myDLL
EXPORTS
add
subtract
注:如果要导出改编的名字:
模块定义文件 myDLL.def
LIBRARY myDLL
EXPORTS
myadd=add //导出为myadd
mysubtract=subtract //导出为mysubtract
6.动态加载
去掉 project-settings下的Link添加的myDLL.lib
只需拷贝myDLL.dll到测试工程,不需要myDLL.h和myDLL.lib
测试工程文件中:
void CTestmyDLLDlg::OnAdd()
{
HINSTANCE hInst;
hInst=LoadLibrary("myDLL.dll");
typedef int (/*_stdcall*/ *ADDPROC)(int a,int b);
ADDPROC Add=(ADDPROC)GetProcAddress(hInst,"add");
//ADDPROC Add=(ADDPROC)GetProcAddress (hInst,MAKEINTERSOURCE(1));//利用序号访问
if(!Add)
{
MessageBox("获取函数地址失败!");
}
CString str;
str.Format("5+3=%d",Add(5,3));
MessageBox(str);
FreeLibrary(hInst);//释放DLL
}