DLL基础部分
通过对函数前加前缀:__declspec(dllexport) 或__declspec(dllimport),表明函数或类是导出到DLL,还是从DLL中导入。但在C++中,通过编译会改变函数名称,可以通过如下的宏进行更正,但过程比较繁琐,不适用于大批量改变函数名称的情况。
#pragma comment(linker, "export:Myfun=MyFun@8"
为了解决这个问题,可以通过另外的两种办法:
- 添加 extern "C"标识,如下文代码示例所示。
- 在.def文件中进行定义
void extern "C" __declspec(dllexport) funcName()
{
// Your code here
}
1. 相对虚拟地址
编译后生成的DLL,将函数放在了各自相对应的偏移地址上,包括变量等信息的地址。
2. 构建可执行模块
通过Dumpbin程序,可以查看到函数的导入情况:dumpbin -imports calc.exe
3. 运行可执行模块
需要运行可执行模块,需要查看模块所需要的DLL,但又是按照什么规则进行查找和加载的呢?规则如下:
- 可执行文件目录
- Windows系统目录——GetSystemDirectory可以获得
- Windows目录——GetWindowDirectory可以获得
- 进程当前目录(防止伪造的Windows DLL,故在windows目录后)
- PATH环境变量中的目录
这个只是微软预先配置好的搜索路径,若需要按照自定义的,需要修改注册表中对应的内容。
DLL载入
DLL载入方式有两种,一种是隐式,另一种为显式。
隐式:
#pragma comment(lib,"*.lib")
显式:
有以下两个函数LoadLibrary和LoadLibraryEx。其中带Ex可以附带属性控制,大致有如下几个功能:
1 | DONT_RESOLVE_DLL_REFERRENCES | 1. 不适用DllMain初始化 2. 不载入依赖 |
2 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | DLL是否独占方式打开 |
3 | LOAD_LIVRARY_AS_DATAFILE | 是否按照资源文件导入 |
4 | LOAD_WITH_ALTERED_SEARCH_PATH | 自定义DLL搜索路径等 |
5 | LOAD_LIBRARY_AS_IMAGE_RESOURCE | 虚拟地址修复 |
更详细的介绍可以见此地址:
HMODULE LoadLibraryA(
LPCSTR lpLibFileName
);
HMODULE LoadLibraryExA(
LPCSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags
);
显式载入后,通过获得的HMOUDLE句柄,可以显示链接到需要导出的符号,需要定义一个相对应的函数指针进行转换:
FARPROC GetProcAddress(
HMODULE hModule,
LPCSTR lpProcName
);
DLL释放
载入的DLL资源的回收:
BOOL FreeLibrary(
HMODULE hLibModule
);
小结:
通过DLL的方式,可以很方便的进行开发工作,将功能相近的封装到一起,在一定程度上可以方便软件规模的管理。同时,DLL加载方式决定了软件启动速度,通过只初始化必要的组件,来加快启动,并在后期需要调用一些功能的时候再进行选择性的加载。