Win32 Dll 的创建和调用:
1. 创建:
在VC中创建project: Win32 Dynamic-Link Library: win32Dll
*.cpp 的内容与上节中的静态链接库一样,不同的是头文件的实现:
动态链接库:extern "C" int __declspec(dllexport)add(int x, int y);//声明add为dll的导出函数。
静态链接库:extern "C" int add(int x, int y);
编译生成 win32Dll.lib 和 win32Dll.dll
2. 调用:
动态调用步骤如下:
(1) 定义函数指针:
typedef int (* FunPtr)(int,int);
FunPtr addFun;
(2) 加载动态链接库:
Handle handle =LoadLibrary("win32Dll.dll");
(3) 获取函数指针调用函数:
addFun =(FunPtr)GetProcAddress(handle ,"Add");
int res = addFun(2,3); // 2+3;
(4) 释放动态链接库:
FreeLibrary(handle); // 释放载入的动态库
静态调用:
#pragma comment(lib,"win32Dll.lib") //告诉编译器lib文件所在的路径。
extern "C" __declspec(dllimport) add(int x,int y);
win32Dll.lib 文件中包含了DLL 导出函数的符号名及序号(并不含有实际的代码),经过编译将这些信息与动态链接库的文件名一起编译到exe文件中。
当执行exe时,可以根据这些信息加载dll 并调用dll中的函数。
这样,EXE将能直接通过函数名调用DLL的输出函数,就象调用程序内部的其他函数一样。
3. dll 导出函数的声明:
采用__declspec 进行声明;
采用模块定义(.def) 文件声明:
必须在工程中加入lib.def文件:
; lib.def : 导出DLL函数
LIBRARY dllTest
EXPORTS
add @ 1
分号表示注释,dllTest 为dll名称;EXPORTS后列出要导出函数的名称,可以用@1表示导出函数的序号为1.
因此也可以这样调用库函数:
addFun =(FunPtr)GetProcAddress(handle , MAKEINTRESOURCE ( 1 ));
4. DllMain 函数:
windows 程序在加载dll库的时候,需要一个入口函数 DllMain, 默认调用缺省的DllMain 函数。
DllMain是Dll的内部函数,是被系统自动调用的,外部程序无法直接调用DllMain。
- ul_reason_for_call指明了被调用的原因。原因共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和THREAD_DETACH。
- APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;
- 进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了DLL模块在进程虚拟空间中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数hModule的来历。
5. __stdcall 和__cdecl
如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却__cdecl。
__stdcall方式与__cdecl对函数名最终生成符号的方式不同。
若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;
而__cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
#define CALLBACK __stdcall //这就是传说中的回调函数
#define WINAPI __stdcall //这就是传说中的WINAPI
#define WINAPIV __cdecl
#define APIENTRY WINAPI //DllMain的入口就在这里
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
在lib.h中,应这样声明add函数:
int __stdcall add(int x, int y);
在应用工程中函数指针类型应定义为:
typedef int(__stdcall *lpAddFun)(int, int);
若在lib.h中将函数声明为__stdcall调用,而应用工程中仍使用typedef int (* lpAddFun)(int,int),运行时将发生错误(因为类型不匹配,在应用工程中仍然是缺省的__cdecl调用)
导出函数
/
#ifndef LIB_H
#define LIB_H
extern "C" int __declspec(dllexport)add(int x, int y);
#endif
#include "stdafx.h"
#include "lib.h"
int add(int x, int y)
{
return x + y;
}
【注】windows and linux 动态/静态链接库 学习整理系列
参考资料:http://pcedu.pconline.com.cn/empolder/gj/vc/