静态链接库
WIN32 Static Library
//lib.h #ifndef __LIB_H_ #define __LIB_H_ extern "C" int _declspec(dllexport)add(int x, int y); #endif
//lib.cpp #include "stdafx.h" #include "lib.h" int add(int x, int y) { return x + y; }
调用libtest.lib:
//libCall.cpp #include <iostream> #include "lib.h" using namespace std; #pragma comment(lib, "LibTest.lib") int main(int argc, char *argv[]) { cout<< "2 + 3 = " << add(2,3) <<endl; return 1; }
动态DLL
WIN32 DYNAMIC-LINK LIBRARY
代码跟LIBREST的生成是一样的
动态调用:
//dllcall.cpp #include <iostream> #include <windows.h> using namespace std; typedef int(*lpFunAdd)(int, int); int main(int argc, char *argv[]) { HINSTANCE hDll; lpFunAdd addFun; hDll = LoadLibrary("TestDll.dll"); if(hDll != NULL) { addFun = (lpFunAdd)GetProcAddress(hDll, "add"); if(addFun != NULL) { int result = addFun(2, 3); cout<< "2 + 3 = " << result <<endl; } } FreeLibrary(hDll); return 0; }
下面我们来看看静态调用的例子(单击此处下载本工程附件),将编译dllTest 工程所生
成的.lib 和.dll 文件拷入dllCall 工程所在的路径,dllCall 执行下列代码:
#include <iostream> //#include <windows.h> using namespace std; #pragma comment(lib, "TestDll.lib") extern "C" int _declspec(dllexport)add(int x, int y); int main(int argc, char *argv[]) { cout<< "2 + 3 = " << add(2, 3) <<endl; return 0; }
DLL 中导出函数的声明有两种方式:一种为在函数声明中加上__declspec(dllexport),这里不再举例说明;另外一种方式是采用模块定义(.def)文件声明,.def 文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。
下面的代码演示了怎样同.def 文件将函数add 声明为DLL 导出函数(需在dllTest 工程
中添加lib.def 文件):
; lib.def : 导出DLL 函数
LIBRARY dllTest
EXPORTS
add @ 1
.def 文件的规则为:
(1)LIBRARY 语句说明.def 文件相应的DLL;
(2)EXPORTS 语句后列出要导出函数的名称。可以在.def 文件中的导出函数名后加@n,
表示要导出函数的序号为n (在进行函数调用时,这个序号将发挥其作用);
(3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一
行。
由此可以看出,例子中lib.def 文件的含义为生成名为“dllTest”的动态链接库,导出其
中的add 函数,并指定add 函数的序号为1。
带DllMain的DLL:
Windows 在加载DLL 的时候,需要一个入口函数,就如同控制台或DOS 程序需要main
函数、WIN32 程序需要WinMain 函数一样。在前面的例子中,DLL 并没有提供DllMain 函
数,应用工程也能成功引用DLL,这是因为Windows 在找不到DllMain 的时候,系统会从
其它运行库中引入一个不做任何操作的缺省DllMain 函数版本,并不意味着DLL 可以放弃
DllMain 函数。
根据编写规范,Windows 必须查找并执行DLL 里的DllMain 函数作为加载DLL 的依据,
它使得DLL 得以保留在内存里。这个函数并不属于导出函数,而是DLL 的内部函数。这意
味着不能直接在应用工程中引用DllMain 函数,DllMain 是自动被调用的。
//dllmaindll.h #ifndef __DLLMAINDLL_H_ #define __DLLMAINDLL_H_ extern "C" int _declspec(dllexport)add(int x, int y); #endif
//dllmaindll.cpp #include "dllmaindll.h" int add(int x, int y) { return x + y; }
//dllmain.cpp #include <iostream> #include <windows.h> using namespace std; BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch(ul_reason_for_call) { case DLL_PROCESS_ATTACH: cout<< "DLL_PROCESS_ATTACH" <<endl; break; case DLL_THREAD_ATTACH: cout<< "DLL_THREAD_ATTACH" <<endl; break; case DLL_THREAD_DETACH: cout<< "DLL_THREAD_DETACH" <<endl; break; case DLL_PROCESS_DETACH: cout<< "DLL_PROCESS_DETACH" <<endl; break; } return true; }
调用就跟其他DLL调用是一样的!
如果通过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
调用),弹出错误对话框。
对于MFC规则库和MFC扩展库都一样啦,只是在Regular DLL using shared MFC DLL 序最后一行代码"CDLLApp theApp;"后面输入函数,并在函数中输入AFX_MANAGE_STATE(AfxGetStaticModuleState());