1.静态链接库
lib.h extern "C" int add(int x,int y);
lib.c int add(int x,int y){ ...}
main.cpp调用如下:
#include "..\lib.h"
#pragma comment(lib,"..\\debug\\libTest.lib") //指定与静态库一起连接
printf("2 + 3 = %d",add(2,3));
2、动态链接库下的动态调用
lib.h extern "C" int __declspec(dllexport) add(int x,int y); //###这里是dllexport
lib.c int add(int x,int y){ ...}
main.cpp调用如下:
#include "windows.h"
typedef int ( * lpAddFun)(int,int);
int main(int argc, char* argv[])
{
HINSTANCE hDll; //DLL句柄
lpAddFun addFun; //函数指针
hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll,"add");
if(addFun!=NULL)
{
int result = addFun(2,3);
printf("%d",result);
}
FreeLibrary(hDll);
}
return 0;
}
3、动态链接库下的静态调用
lib.h extern "C" int add(int x,int y);
lib.c int add(int x,int y){ ...}
main.cpp调用如下:
#include "windows.h"
#pragma comment(lib,"dllTest.lib")
//在link时,应链接dllTest.lib文件
//Lib库中仅仅是关于其对应DLL文件中函数的重定位信息
extern "C" _declspec(dllimport) add(int x,int y); //###这里是dllimport
int main(int argc, char* argv[])
{
int result = add(2,3);
printf("%d",result);
return 0;
}
4、动态链接库下,也有一个类似于main函数的函数,叫DllMain。(用户没有写的话,系统会自动给出一个隐式的DllMain)
lib.h extern "C" int add(int x,int y);
lib.cpp: start----------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "windows.h"
#include "stdio.h"
// lib.cpp : DLLMain函数的例子
// 作者 : 宋宝华 21cnbao@21cn.com
//如果程序员没有为DLL模块编写一个DLLMain函数,系统会从其它运行库中引入一个不做任何操作的
//缺省DLLMain函数版本。在单个线程启动和终止时,DLLMain函数也被调用。正如由dwReason参数所
//表明的那样。
BOOL APIENTRY DllMain( HANDLE hModule,
/*
进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识进程自己还有一个HINSTANCE句柄。
所有这些模块句柄都只有在特定的进程内部有效,它们代表了DLL或EXE模块在进程虚拟空间中的起始
地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用。进程模块句柄几乎
总是等于0x400000,而DLL模块的加载地址的缺省句柄是0x10000000。如果程序同时使用了几个DLL模
块,每一个都会有不同的HINSTANCE值。这是因为在创建DLL文件时指定了不同的基地址,或者是因为
加载程序对DLL代码进行了重定位。
*/
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
printf("\nprocess attach of dll");
break;
case DLL_THREAD_ATTACH:
printf("\nthread attach of dll");
break;
case DLL_THREAD_DETACH:
printf("\nthread detach of dll");
break;
case DLL_PROCESS_DETACH:
printf("\nprocess detach of dll");
break;
}
return TRUE;
}
int add(int x,int y)
{
return x + y;
}
lib.cpp: end----------------------------------------------------------------------------------------------------------------------------------------------------------------
main.cpp调用如下:
#include "windows.h"
typedef int ( * lpAddFun)(int,int);
int main(int argc, char* argv[])
{
HINSTANCE hDll; //DLL句柄
lpAddFun addFun; //函数指针
hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll,"add");
if(addFun!=NULL)
{
int result = addFun(2,3);
printf("%d",result);
}
FreeLibrary(hDll);
}
return 0;
}
运行输出如下;
process attach of dll
call add in dll:5
process detach of dll
5、__stdcall约定
如果通过 VC++编写的 DLL 欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall 方式, WINAPI 都采用这种方式,而
C/C++缺省的调用方式却为__cdecl。
__stdcall 方式与__cdecl 对函数名最终生成符号的方式不同。若采用 C 编译方式(在 C++中需将函数声明为 extern "C"),
__stdcall 调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而__cdecl
调用约定仅在输出函数名前面加下划线,形如_functionname。
Windows 编程中常见的几种函数类型声明宏都是与__stdcall 和__cdecl 有关的(节选自 windef.h):
#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 调用),弹出如图 7 所示的对话框。
6、 DLL 导出变量
lib.h extern int dllGlobalVar;
extern "C" int GetGlobalVar();
-----------------------------------------------------------------------------------------
lib.cpp
#include "lib.h"
#include <windows.h>
int dllGlobalVar;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
dllGlobalVar = 100;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
int GetGlobalVar()
{
return dllGlobalVar;
}
--------------------------------------------------------------------------------
lib.def //如果不加这个文件,生成Dll的时候,lib不会生成
;在DLL中导出变量
LIBRARY "dllTest"
EXPORTS
dllGlobalVar CONSTANT ;或dllGlobalVar DATA
GetGlobalVar
--------------------------------------------------------------------------------
(1)main.cpp 中使用extern int dllGlobalVar;
#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")
extern "C" int _declspec(dllimport) GetGlobalVar();
extern int dllGlobalVar;
int main(int argc, char* argv[])
{
printf("%d ",GetGlobalVar());
*(int *)dllGlobalVar = 1;
//特别要注意的是这种方法导出的并不是变量本身,而是DLL中导出变量的指针,应用程序必
//须通过强制指针转换来使用
printf("%d ",GetGlobalVar());
return 0;
}
(2)main.cpp 中使用extern int dllGlobalVar;
#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")
extern "C" int _declspec(dllimport) GetGlobalVar();
extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入
//通过_declspec(dllimport)方式导入的就是 DLL 中全局变量本身而不再是其地址了
int main(int argc, char* argv[])
{
printf("%d ",GetGlobalVar());
dllGlobalVar = 1;
//这里就可以直接使用, 无须进行强制指针转换
printf("%d ",GetGlobalVar());
return 0;
}