静态库和动态库
静态库:函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.EXE文件);
动态库: 在使用动态库的时候,往往提供两个文件:一个因入库和一个DLL。因入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行时候,再去加载DLL,访问DLL中导出的函数.
工具的使用
dumpbin:位于VC98/BIN目录下
使用示例:
dumpbin -exports Mydll.dll 可以查看dll文件中导出的函数
dumpbin -imports dlltest.exe 查看exe文件中所需的dll文件
Depends:VC自带的工具 可视化的用于查看dll导出的函数
隐式链接加载DLL
_declspec(dllexport)表明导出函数
_declspec(dllimport)引入库函数
首先必须将 .dll和.lib文件复制到 对话框的工程目录中,然后在对话框的工程-设置当中-Link什么的 加上 xxx.lib.
然后进行示例.
示例:
生成DLL的源文件
_declspec(dllexport) int add(int a,int b) //导出库函数
{return a+b;}
MFC的对话框文件
_declspec(dllimport) int add(int,int); //引入声明库函数
//然后之后的代码 你就可以调用DLL中的add函数了
导出一个类:
示例:
//DLL.h///
//.h文件应打包 给要借助DLL链接库搞开发的人
#ifdef DLL1_API //如果定义了DLL1_API则不定义 也就是导出
#else
#define DLL1_API _declspec(dllimport) //否则定义DLL1_API 让其引入.
#endif
DLL1_API int _stdcall add(int a,int b);
DLL1_API int _stdcall subtract(int a,int b);
class DLL1_API Point
{
public:
void OutPut(int x,int y);
};
//DLL.cpp/
#define DLL1_API _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;
}
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);
}
改编名字的问题
因为编译器生成的DLL文件 在导出函数的时候 因为编译器什么的原因可能导出来的函数名会变.
extern "C":此方法只适用于c和c++ 只能导出全局函数 不能导出类
示例:
extern "C" _declspec(dllexport) int add(int a,int b)
{ a+b }
如果想要Delphi之类的语言访问所导出的函数 应该_stdcall 即:标准调用约定 WINAPI调用约定. 用此约定导出的函数 可能在函数名前有个_函数名后有个@8 @后面的数字是其字节.
示例:_declspec(dllexport)int _stdcall add(int a,int b);
最好的一个方法:弄一个***.def的文件 并且将其加入工程中
文件内容示例:
LIBRARY DLL1.lib
EXPORTS
add
uwanttoknow=subtract
其中,LIBRARY 语句用来指定动态链接库的内部名称,该名称与生成的动态链接库的名
称一定要匹配
EXPORTS 的作用是表明 DLL 将要导出的函数,以及为这些导出函数指定的符号名.
如果要将导出的符号名和源文件中定义的函数名不一样,则可以按照一下语法指定导出
的函数:
entryname=internalname
其中, entryname 项是导出的符号名, internalname 项是 DLL 中将要导出的函数名
字
显示加载DLL
使用动态方式加载动态链接库时,需要用到 LoadLibrary 函数.该函数的作用是将指定的
可执行模块映射到调用进程的地址空间.原型如下:
HMODULELoadLibrary(LPCTSTR lpFileName );
该函数返回类型是 HMODULE,HMOUDULE 类型和 HINSTANCE 类型可以通用.
当获取到动态链接库模块的句柄后,接着需要获取该动态链接库中导出函数的地址,这可
以通过调用 GetProcAddress 函数实现,原型如下:
FARPORC GetProcAddress (HMODULE hModule, LPCSTR lpProcName);
示例:
void CDllT estDlg::OnBtnAdd()
{
HINST ANCE hInst;
hInst=LoadLibrary("Dll3.dll");
typedef int(/*_stdcall*/ *ADDPROC)(inta,intb); //如果导出函数是_stdcall标准调用约定的那么你在这 不能把_stdcall注释掉
//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);
}
这里 ADDPROC 是一个函数指针类型
动态加载 DLL 时,客户端程序不再需要包含导出函数声明的头文件和引入库文件,只需要带 dll 文件即可.
关于DllMain
DllMain 函数
Dll 文件的入口函数:DllMain.该函数是可选的.原型如下:
BOOL WINAPI DllMain( HINSTANCE hinstDLL,DWORDf dwReason,LPVOID lpvReserved);
注意:如果提供了 DllMain 函数,那么在此函数中不要进行太复杂的调用,因为在加载该动态
链接库时,可能还有一些核心动态链接库没有加载,比如 user32.dll 和 GDI32.dll 等,由于我
们自己的 dll 会比较靠前的加载,而某些核心没被加载的话,就可能调用失败.
当我们的动态链接库不再使用时 尤其是动态加载的 可以调用 FreeLibrary 使动态链接库使用计数减 1,当使用计数为零时,系统会收回模块资源.原型如下:
BOOL FreeLibrary( HMODULE hModule);
MfcDLL
MFC Extension(Using Shared mfc dll):创建此类型的DLL可以导出MFC的类 常规的MFC DLL不行
mfc的DLL编写与win32 dll编写类型 mfc的DLL对MFC有很好的支持