Kernel32.dll 包含管理内存、进程和线程的函数
User32.dll 包含用于执行用户界面任务的函数,如窗口的创建和消息的传送
GDI32.dll 包含用于画图和显示文本的函数
静态库:函数和数据被编译成一个二进制文件(.lib),链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe),发布时不需要发布被使用的静态库。
动态库:提供两个文件:引入库(.lib),DLL(.dll)。引入库和静态库文件有本质区别。引入库文件包含该DLL导出的函数和变量的符号名,.dll文件包含该DLL实际的函数和数据。编译链接时只需要链接该DLL的引入库文件。发布时还要发布程序将要调用的动态链接库。
dumpbin 命令:应用程序如果想要访问某个DLL中的函数,那么该函数必须是已经被导出的函数。dumpbin -export *.dll 可以显示DLL提供的导出函数。
为了让DLL导出函数,需要在每个将要被导出的函数前加标识符:_declspec(dllexport)。
dumpbin -imports *.exe 显示程序从哪个DLL引函数。
depends 工具也可以显示程序依赖的动态链接库。
隐式链接:
在程序中 extern ...声明外部函数,将*.dll,*.lib 文件放到程序目录
或者 _declspec(dllimport) ... 此种方式VC++生成更高效率的代码,推荐使用。
为给使用此DLL的其他人使用,应该在DLL文件中增加头文件.h ,其中使用 _declspec(dllimport) ...说明函数是外部函数。
exp:
dlltest.h
#ifdef DLLTEST_API
#else
#define DLLTEST_API _declspec(dllimport)
#endif
DLLTEST_API int add(int a, int b);
DLLTEST_API int sub(int a, int b);
dlltest.cpp
#define DLLTEST_API _declspec(dllexport)
#include "dlltest.h"
int add(int a, int b)
{
return a+b;
}
int sub(int a, int b)
{
return a-b;
}
DLL导出C++类:
在类名前加上 _declspec(dllexport) 即可。或者要导出类中某个函数,只要在函数前加上导出标识符。
名字改编问题:
利用 extern "C" _declspec(dllimport) 可以解决C和C++的相互调用问题。但不能用于导出一个类的成员函数,只能用于导出全局函数。但如果声明函数时采用了标准调用约定_stdcall,则extern"C" 也不能解决名字改编问题。最好的解决方法是定义一个 .def 文件,格式如下:
LIBRARY DLLTEST
EXPORTS
add
sub
LIBRARY 用来指定动态链接库的内部名称,该名称与生成的动态链接库的名称一定要匹配。这句不是必须的。
EXPORTS 表明DLL将要导出的函数以及为这些导出函数指定的符号名。如果将要导出的符号名和源文件中定义的函数名不一样,可以用下面的语法:
entryname(导出的符号名)=internalname(DLL中将要导出的函数名)
显示加载DLL:
使用函数HMODULE LoadLibrary( LPCTSTR lpFileName) ,该函数作用是将
指定的可执行模块映射到调用进程的地址空间。可以加载DLL和EXE文件。
不需要包含导出函数声明的头文件和引入库文件,只需要.dll文件即可。
HMODULE和HINSTANCE通用。
得到HMODULE后,使用FARPROC GetProcAddress(HMODULE hModule,
LPCSTR lpProcName) 取得导出函数的地址,lpProcName 为导出函数的名
字或序号:如果是序号,序号必须在低位字中,高位必须是0。
当调用完毕,应该用FreeLibrary(HMODULE hModule)释放以减少引用。
采用隐式链接时,在程序启动的时候已经加载所有DLL,而动态链接是在程
序使用的时候才加载,因此动态加载可以加快程序启动时间。实际上隐式
链接在启动也是通过LoadLibrary函数来加载的。使用动态加载时,在客户
端程序中将不能看到调用该DLL的输入信息。
当DLL中导出函数采用标准调用约定时,访问该DLL的客户端程序也应该采
用该约定类型来访问相应的导出函数。
根据序号访问DLL中的导出函数:
GetProcAddress函数的第二个参数是LPCSTR,把INT类型转换为LPCSTR
可以用MAKEINTRESOURCE宏,该宏会把指定的函数序号转换为相应的函数名
字字符串。
DllMain函数:dll入口函数
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason,
LPVOID lpvReserved );
该函数是可选的,如果在DLL中提供了该函数,当加载该DLL时,就会调用
。
hinstDLL:该DLL的句柄。如果需要用到,可以将其保存到一全局变量中。
fdwReason: 标记值,用来指示调用该DLL入口函数的原因。其值如下:
DLL_PROCESS_ATTACH:进程第一次加载DLL并调用DllMain()
DLL_THREAD_ATTACH:当前进程正创建一个线程
DLL_THREAD_DETACH:线程结束
DLL_PROCESS_DETACH:进程结束
可以用switch/case进行判断。
lpvReserved:保留参数。但可以检测,如果为NULL,是动态加载;非NULL
为静态加载。
如果提供了dllMian函数,不应进行太复杂调用。因为可能此DLL会比系统
核心的DLL先加载,由此造成失败。
MFC DLL:
分三种:
1、常规静态DLL:发布只需提供该DLL
2、常规动态DLL:发布时如果用户机器上没有MFCDLL,则调用会失败。
3、扩展动态MFCDLL:与前者区别是:前者可以导出MFC类,后者只能导
出自己写的C++类。