0x00前言
文章中的文字可能存在语法错误以及标点错误,请谅解;
如果在文章中发现代码错误或其它问题请告知,感谢!
0x01动态链接库(Dynamic Link Libraries, DLL)
动态链接库(DLL)为模块化应用程序提供一种方式,使得更新和重用应用程序更加方便。注意只有在其它模块调用动态链接库中的函数时,动态链接库才会发挥作用。
另外,动态链接库是代码重用的绝佳方式,我们可以不必在每个程序中都编写同一功能函数,只要对这个功能函数创建成DLL,然后再从需要该功能函数的程序调用它即可,这样一来减少了工作量。
还要注意的是,动态链接库不能够被直接使用或接收消息,但它是一个独立的文件,可以被可执行程序或其它DLL调用。
动态链接库包含两个文件:引入库文件(.lib)和DLL文件(.dll)。引入库文件(.lib)包含DLL文件中导出函数和API接口,DLL文件(.dll)包含实际的函数和数据。动态链接库在程序编译时并不会被插入可执行文件中,只有在可执行文件运行并需要动态库中的某个函数时,才会加载所需的动态链接库,将该库代码调入内存,这就是所谓的“动态链接”。
在发布产品时,除了发布可执行文件,还要同时发布该程序要调用的动态链接库(若有)。
0x02创建并生成动态链接库(DLL)
1.创建DLL项目
打开文件-》新建项目-》Win32 项目,新建工程“DLLDemo”,然后选择DLL,如下图所示:
此时查看左侧列表中的源文件中的dllmain.cpp,为vs2010自动生成,若在创建工程时第2步选择“空项目”则创建空工程,不会生成该例。该文件中的DllMain为动态链接库的入口点函数,windows系统在库的装载、卸载以及进程中创建和结束时调用该入口函数,该函数在vs2010中截图如下:
具体参数及分支选择释义如下:
BOOL APIENTRY DllMain( HMODULE hModule, //本模块调用句柄
DWORD ul_reason_for_call, //调用原因
LPVOID lpReserved //没有被使用
)
{
switch (ul_reason_for_call)
{
//动态链接库刚被映射到某个进程的地址空间
case DLL_PROCESS_ATTACH:
//应用程序创建了一个新线程
case DLL_THREAD_ATTACH:
//应用程序某个线程正常终止
case DLL_THREAD_DETACH:
//动态链接库将被卸载
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
动态链接库DLL可以定义两种函数:导出函数和内部函数。导出函数可以被其它模块调用,也可以被定义这个函数的模块调用,而内部函数只能被定义这个函数的模块调用。
动态链接库中的代码编写和通常的代码编写一样,可以使用头文件,资源以及类。
2.编写DLL实例
现在编写一个DLL实例,功能为在可执行文件运行时,会调用该DLL以弹出一个消息框,该消息框中显示的内容由可执行程序决定,标题栏内容由本DLL模块名决定。
(1)修改dllmain.cpp代码内容:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
HMODULE g_hModule;
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hModule = (HMODULE)hModule; //保存模块句柄,为DLL模块句柄实例
break;
}
return TRUE;
}
(2)在项目中新建.h文件
函数定义之后只能在本工程中使用,若要将函数导出供其它模块使用,需要新建头文件,在该头文件进行如下修改:
#include "stdafx.h"
__declspec(dllexport) void ExportFunc(LPCTSTR pszContent);
通常到这一步会发现错误列表中会有 “ “LPCTSTR” 类型的实参与 “LPCSTR” 类型的形参不兼容”报错:
打开项目-》属性-》配置属性-》常规,将字符集修改为“使用多字节字符集”解决:
(3)修改DLLDemo.cpp(由工程创建时的名称决定):
// DLLDemo.cpp : 定义 DLL 应用程序的导出函数。
#include "stdafx.h"
#include "DllDemo.h"
extern HMODULE g_hModule;
//自定义导出函数
void ExportFunc(LPCTSTR pszContent)
{
char sz[MAX_PATH];
::GetModuleFileNameA(g_hModule, sz, MAX_PATH);
::MessageBoxA(NULL, pszContent, strrchr(sz, '\\') + 1, MB_OK);
}
在可执行文件加载次DLL时,系统会调用DllMain函数,当ul_reason_for_call的值为DLL_PROCESS_ATTACH时,hModule会保存到全局变量g_hModule中。GetModuleFIleName函数用于取得指定的模块的文件名,strrchr函数会找到文件名最后的“\”字符串位置,即“strrchr(sz,’\’”) + 1”返回的值为本DLL模块不带目录的名称。
(4)代码完成后,便可生成DLL,选择生成-》生成DLLdemo,生成的.dll以及.lib存放在工程文件夹中的Deug文件夹中:
生成之后,.dll,.lib以及步骤(2)中新建的.h文件可以被其它工程使用。
0x03在程序中调用动态链接库
调入DLL中的导出函数有两种方式:
(1)装载期间动态链接。模块可以像调用本地函数一样可以从其它模块导出的函数,就好像API函数一样,装载期间必须使用DLL导入库(.lib文件),它为系统提供了加载这个DLL和定位DLL中导出函数所需要的信息。
(2)运行期间动态链接。模块可以使用LoadLibrary或者LoadLibrarayEx函数在运行期间加载这个DLL。DLL加载之后加载模块调用GetProAddress函数取得 DLL导出函数的地址然后通过 函数地址调用DLL中的函数。
使用第一种方法时,需要.dll,.lib以及上一小节步骤(2)中新建的.h,使用第2中方法时,只有.dll会用到。
1.装载期间动态链接
该方法在可执行文件运行时有载入器(加载可执行文件中的组件)载入.dll文件。载入器是通过PE文件中的.idata确定要载入的DLL。
新建一个DLLTest Win32控制台工程,将.dll,.lib以及上一小节步骤(2)中新建的.h赋值到该工程目录下。DLLTest调用导出函数ExportFunc方法如下:
#include"stdafx.h"
#include <windows.h>
#include"DllDemo.h"
// 指明要链接到DLLDemo.lib库
#pragma comment(lib, "DLLDemo")
void main()
{
// 调用DllDemo.dll库的导出函数
ExportFunc("调用成功!");
}
编译运行:
同样在编译的过程中要注意“ “LPCTSTR”: 未声明的标识符”问题,修改方法见上一小节。
注意 ,载入器加载DLL文件时,默认情况下时在应用程序目录下查找,若是找不到就回到系统盘“\windows\system32”文件夹下查找,如果还找不到就按照错误处理。
2.运行期间动态链接
运行期间动态链接库是在程序运行过程中显式加载DLL库,从中导出需要的函数,为了能在运行期间能够动态的导出函数,一般需要在DLLDemo工程中新建一个DEF文件来指定要导出的函数。
右键源文件-》添加-》新建项,选择新建def文件命名为DLLDemo,在其中添加:
EXPORTS
ExportFunc
这两行说明DLL库向外导入ExportFunc函数。重新编译该工程。
回到DLLTest工程下,修改程序DLLDemo.cpp(由工程创建时的名称决定):
#include"stdafx.h"
#include <windows.h>
// 声明函数原形
typedef void (*PFNEXPORTFUNC)(LPCTSTR);
int main(int argc, char* argv[])
{
// 加载DLL库
HMODULE hModule = ::LoadLibrary("E:\\下载\\01计算机学习\\windows程序设计学习\\自己照写Windows程序设计代码例子\\DLLDemo\\Debug\\DLLDemo.dll");
if(hModule != NULL)
{
// 取得ExportFunc函数的地址
PFNEXPORTFUNC mExportFunc = (PFNEXPORTFUNC)::GetProcAddress(hModule, "ExportFunc");
if(mExportFunc != NULL)
{
mExportFunc("调用成功!");
}
// 卸载DLL库
::FreeLibrary (hModule);
}
return 0;
}
编译运行,观察效果同上:
以上。
参考文档:
1.https://blog.csdn.net/u011642774/article/details/52440187
2.https://blog.csdn.net/u010335911/article/details/23474919?utm_source=blogxgwz3
3.https://blog.csdn.net/bloke_come/article/details/75336990
4.张铮,孙宝山,周立天.Windows程序设计(第3版)[M].北京;人民邮电出版社,2018.7.