C20、高级DLL

来源:互联网 发布:软件需求分析面试 编辑:程序博客网 时间:2024/06/05 04:52

一、显式加载DLL模块:

a)         HINSTANCE LoadLibrary(PCTSTR pszDllPathName);

b)        HINSTANCE LoadLibraryEx(

PCTSTR pszDllPathName,       //路径(包含文件全名)。

HANDLE hFile;       // NULL

DWORD dwFlags); // 0

dwFlags可以使用的其他标志:

u       DON’T_RESOLVE_DLL_REFERENCES:使系统不必调用DllMain函数;如果此DLL需要其他DLL,系统不自动加载。

u       LOAD_LIBRARY_AS_DATAFILE:系统不准备执行文件中的代码(就像是数据文件一样)。如果DLL只包含资源(不包含函数)或加载.exe文件而不启动新线程(可以访问其中的资源)需要设定此标志。

u       LOAD_WITH_ALTERED_SEARCH_PATH:用于改变搜索DLL的算法(常规是按C19中的顺序搜索),首先搜索pszDllPathName设定的目录。

二、显式御载DLL模块:

a)         BOOL FreeLibraryHINSTANCE hinstDll;

b)        VOID FreeLibraryAndExitThreadHINSTANCE hinstDll, DWORD dwExitCode; //如果对同一个DLL多次调用LoadLibrary(Ex),需要用此函数。

注:DLL加载时,系统启动一个线程。

u       测试DLL是否已经被映射到进程的地址空间:

HINSTANCE GetModuleHandle(PCTSTR pszModuleName)

u       查询DLL的全路径名:

DWORD GetModuleFileNamehinstModule,

pszPathName, // 缓存地址,TCHAR pszPathName[MAX_PATH]

DWORD cch);// 缓存的大小

三、显式链接到一个输出符号:

FARPROC GetProcAddress

       HINSTANCE hinstDll

       PCSTR pszSymbolName);// 可以使用符号名或序号:MAKEINTRESOURCE(2)

四、进入点函数(初始化使用):

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, PVOID fImpLoad);

应该避免

u       调用LoadLibrary(Ex), FreeLibrary

u       从其他DLL中输入的函数(User, Shell, ODBC, COM, RPC, 套接字函数)。

u       因为全局或静态C++对象也在调用DllMain时初始化,同样也应避免上述问题。

五、延迟加载DLL

使用目的:如果在WIN2000中设计的程序使用了WIN98没有的新函数,而要在98上运行,如果不应有延迟加载技术,将会终止进程。方法(两步):

1.         常规建立DLL和可执行模块

2.         链接可执行模块时,添加延迟加载开关

a)         /Lib: DelayImp.lib   // 需要DelayImp.lib库(

b)        /DelayLoad: MyDll.dll

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// 实践:把下面的代码添加给需要此功能的模块

u       #include <delayimp.h>   // 需要的头文件

u       #pragma comment(lib, "Delayimp.lib") //

u       #pragma comment(linker, "/DelayLoad:DelayLoadLib.dll") // 延迟加载的库

u       #pragma comment(linker, "/Delay:unload") // 可以手工卸载,可选

u       //#pragma comment(linker, "/Delay:nobind") // 不绑定,通常不要!

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

其他方面:

3.         卸载DLL时(如果不自己卸载,则不要这项修改!):

a)         开关:/delayunload  // 如果不用此开关,下面的函数将不执行操作!

b)        卸载DLL(不能使用FreeLibrary,否则函数地址不会被清除),使用:

BOOL __FUnloadDelayLoadedDLLPCSTR szDll);//不包含路径

4.         延迟加载使.exe文件变大。阻止变大,使用开关:/Delay:nobind(不建议用)。

5.         挂钩函数。接收__delayLoadHelper的进度通知和错误通知,用法(两步):

a)         自己编写挂钩函数(如DliHook);

b)        DelayImp.lib库中俩个PfnDliHook类型的变量:__pfnDliNotifyHook, __pfnDliFailureHook。将两个中的一个设置为挂钩函数的地址(PfnDliHook __pfnDliNotifyHook = DliHook

6.         细节:

u       程序运行时,对DLL中函数的调用,实际上是对DelayImp.lib库中的__delayLoadHelper函数的调用。__delayLoadHelper引用DLLDelay Import节(调用LoadLibraryGetProcAddres)调用函数。

u       如果有多个DLL需要延迟加载,则要为每一个DLL设定开关。

7.         异常处理:

u       如果加载失败,产生一个软件异常(ERROR_MOD_NOT_FOUND, ERROR_PROC_NOT_FOUND),(如果不用SHE处理,进程终止!),其信息包含在DelayLoadInfo结构中,szDllDLL名字,dlp为函数(DelayLoadProc结构),dwLastError—什么原因引发的错误;如果是成功加载,hmodCurDLL加载到的内存地址,pfnCur为函数地址。

六、函数转发(在DLL输出节中):

#pragma comment(linker, “/export:SomeFunc=DllWork.SomeOtherFunc”)

你在DLL输出的SomeFunc实际上位于DllWork.dll中的SomeOtherFunc

七、已知的DLL:操作系统提供的某些DLL可以在注册表(KnownDLLs)中找到的DLL(称为已知的DLL)。

u       如果使用LoadLibrary(“someLib.dll”);(使用了.dll扩展名),系统首先查找注册表。如果找到someLib值,而且如果注册表指示的DLL是在someOtherLib.dll,系统则在系统目录(%SystemRoot%/System32)查找someOtherLib.dll,如果找到了,系统直接加载someOtherLib.dll而不加载someLib.dll;如果找不到,LoadLibrary(Ex)返回NULLERRO_FILE_NOT_FOUND)。

u       我们是否也可以把自己的DLL注册、放入系统目录?加载更快?

八、DLL转移Windows98不支持):用于强制加载程序总是首先查找应用程序的目录。仅需在应用程序的目录中添加一个空文件,文件名必须是SuperApp.local。如:可执行文件名为myPrg.exe,添加一个(转移文件)myPrg.exe.local

九、改变模块的位置(如果你的程序带有多个DLL模块)

每个可执行模块和DLL模块都有一个首选基地址(分别是:0x004000000x10000000),用于标识模块映射到进程的理想地址空间。如果不能加载到首选基地址,将产生移位节

a)         可以创建不包含移位节的模块。

u       链接开关:/FIXED。可使模块变小,但不能被改变位置,如果不能加载到首选基地址,那么他就根本不能加载,“进程异常终止”!适用于仅包含资源的DLL

u       链接开关:/SUBSYSTEM:WINDOWS,5.0/SUBSYSTEM:CONSOLE,5.0

b)        如果有多个模块加载到单个地址空间,必须为每个模块设置不同的首选基地址。“工程设置”->Link->Output->Base Address=设置的首选基地址(分配粒度64K)。

c)        如果你的程序带有多个DLL模块,有必要改变首选基地址。建议使用实用程序:Rebase.exe(,详见MSDN)。(可以在链接前后执行)。

示例:rebase –b 0x61000000 myDll.dll

u       可以在改变前后,查看基地址:dumpbin /headers myDll.dll。在“OPTIONAL HEADER VALUES”的“image base”项。

十、绑定模块:(实用程序Bind.exe,查看MSDN)。使程序更快的初始化。在应用程序安装时执行

示例:bind –u myPrg.exe;

u       可以在绑定前后分别查看其输入节:dumpbin /imports myPrg.exe。如果在结尾(Summary)前有“Bound to KERNEL32.dll [……] ……”,标识绑定成功!([ ] 内的内容为“时间戳”,其后面接“可读时间戳”)

u       要把你的DLL模块(系统模块不必要)拷贝到与.exe同一个文件夹!

 

原创粉丝点击