首页 > 其他 > 详细

第20章 DLL高级技术(2)

时间:2015-12-03 00:28:13      阅读:320      评论:0      收藏:0      [点我收藏+]

20.3 延迟载入DLL

(1)延迟载入的目的

  ①如果应用程序使用了多个DLL,那么它的初始化可能比慢,因为加载程序要将所有必需的DLL映射到进程的地址空间。→利用延迟加载可将载入过程延伸到执行过程时

  ②如果我们的代码调用的操作系统的一个新函数,但程序又试图在老版本的操作系统运行。这时程序会被终止。这时可以有两种解决方法:一种是判断利用GetVersionEx操作系统,在老系统中使用旧函数而不使用新函数。另一种是通过延迟载入,通过SEH来捕获异常。

(2)延迟载入技术

  ①延迟载入是针对隐式链接DLL的

  ②一个导出了字段(如即全局变量)的DLL是无法延迟载入的

  ③Kernel32.dll模块是无法延迟载入的,因为必须载入该模块才能调用LoadLibrary和GetProcAddress。

  ④不应在DllMain入口函数中调用一个延迟载入的函数,这可能导致程序崩溃

(3)使用方法及相关说明

  ①使用方法

  A常规建立DLL和可执行模块

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

    Ⅰ、为了延迟加载Dll,还需要在解决方案的该项目“属性”->“配置属性”->“链接器”->“输入”->“延迟加载的Dll”中输入MyDll.dll(注意/DelayLoad:MyDll.dll这个开关不    能用#pragma comment(linker, "/DelayLoad:MyDll.dll")来设置。

    Ⅱ、增加/Lib:DelayImp.lib开关:这可以用#include <delayimp.h>和#pragma comment(lib, "Delayimp.lib")。这个开关告诉链接器将delayimp            中的__delayLoadHelper2函数嵌入到我们的可执行文件中。

    Ⅲ、如果需要手动卸载Dll,则需在可选“链接器”→“高级”中指定“卸载延迟加载的DLL”中输入“MyDll.dll”。但要注意两点:一是卸载时只能        调用__FUnloadDelayLoadedDll2(PCSTR szDll)函数,而不能调用FreeLibrary。二是该卸载操作是可选的,不是必需的,只有在需要手动卸载Dll时才设置。

  ②/Lib:DelayImp.lib此时链接器将执行下列的事项

  A、将MyDll.dll从.exe的导入段去除,这样操作系统就不会隐式载入该DLL

  B、在.exe中嵌 入一个新的延迟载入段(Delay Import Section,称为.didata)表示要从MyDll.dll中导入哪些函数。

  C、对延迟载入函数的调用会跳转到__delayLoadHelper2函数,来完成对延迟载入函数的解析。

  ③其他说明

  A、应用程序对延迟载入函数的调用实际上会调用__delayLoadHelper2函数,该函数会引用那个特殊的延迟载入段,并用LoadLibrary和GetProcAddress得到延迟载入函数的地址,然后修改对该函数的调用,这样以后将直接调用该延迟载入函数。

  B、同一个DLL中的其他函数仍然必须在第一次被调用的时修复,即其他函数第1次调用时仍然会LoadLibrary+GetProcAddess并修复函数地址。

 【Export/ImportDelay程序】演示延迟载入Dll

技术分享

注意图中第1次列出模块中没有20_ExportDelay.dll,而第2次有且执行了其DllMain函数

//Dll源文件

/************************************************************************
Module: ExportDelay.h
************************************************************************/
#pragma  once

#ifdef DELAYLIB_EXPORT
//MYLIB_EXPORT必须在Dll源文件包含该头件前被定义
#define DELAYAPI extern "C" __declspec(dllexport)
//本例中所有的函数和变量都会被导出
#else
#define DELAYAPI extern "C" __declspec(dllimport)
#endif

//定义要导出的函数的原型
DELAYAPI int Func_A(int iVal);
DELAYAPI int Func_B(int iVal1, int iVal2);
DELAYAPI int Func_C(int iVal1, int iVal2,int iVal3);
#include <windows.h>
#include <tchar.h>
#include <locale.h>

//在这个DLL源文件定义要导出的函数和变量
#define DELAYLIB_EXPORT   //这个源文件中须定义这个宏,以告诉编译器函数要
//__declspect(dllexport),这个宏须在包含MyLib.h
//之前被定义

#include "ExportDelay.h"

BOOL APIENTRY DllMain(HMODULE hDllHandle, DWORD dwReason, LPVOID lpreserved)
{
    static TCHAR pModuleName[MAX_PATH] = {};

    switch (dwReason)
    {
    case DLL_PROCESS_ATTACH:
        _tsetlocale(LC_ALL, _T("chs"));
        GetModuleFileName(hDllHandle, pModuleName, MAX_PATH);
        _tprintf(_T("进程[%u]调用线程[0x%X]加载DLL[0x%08X]:%s\n"),
                 GetCurrentProcessId(),GetCurrentThreadId(),hDllHandle,pModuleName);
        break;

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;

    case DLL_PROCESS_DETACH:
        //GetModuleFileName(hDllHandle, pModuleName, MAX_PATH);
        _tprintf(_T("进程[%u]调用线程[0x%X]卸载DLL[0x%08X]:%s\n"),
                 GetCurrentProcessId(), GetCurrentThreadId(), hDllHandle, pModuleName);
        break;
    }
    return TRUE;
}

int Func_A(int iVal)
{
    return iVal;
}

int Func_B(int iVal1, int iVal2)
{
    return iVal1 + iVal2;
}

int Func_C(int iVal1, int iVal2, int iVal3)
{
    return iVal1 + iVal2 + iVal3;
}

//exe源文件

#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <locale.h>
#include <Psapi.h> //For EnumProcessModules函数
#include "../../Chap20/20_ExportDelay/ExportDelay.h"

//#include <delayimp.h>  
//#pragma comment(lib, "Delayimp.lib")  //Vs2013这两行可要可不要了。

#pragma comment(lib,"psapi")
#pragma comment(lib,"../../Debug/20_ExportDelay.lib")
//1、延迟加载是针对Dll的隐式链接的
//2、为了延迟加载Dll,还需要在解决方案的该项目“属性”->“配置属性”->
//“链接器”->“输入”->“延迟加载的Dll”中输入20_ExportDelay.dll

void PrintModules(DWORD dwProcessID){
    HMODULE* phMods = NULL;
    HANDLE hProcess = NULL;
    DWORD dwNeeded = 0;
    TCHAR szModName[MAX_PATH] = {};

    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
                           FALSE, dwProcessID);
    if (NULL == hProcess){
        _tprintf(_T("不能打开进程[ID:0x%X],错误码:[%u]\n"), dwProcessID,
                 GetLastError());
        return;
    }

    EnumProcessModules(hProcess, NULL, 0, &dwNeeded);
    phMods = (HMODULE*)malloc(dwNeeded);

    if (EnumProcessModules(hProcess,phMods,dwNeeded,&dwNeeded)){
        for (DWORD i = 0; i < (dwNeeded / sizeof(HMODULE));i++){
            ZeroMemory(szModName, MAX_PATH*sizeof(TCHAR));
            if (GetModuleFileNameEx(hProcess,phMods[i],szModName,MAX_PATH)){
                _tprintf(_T("\t(0x%08X)\t%s\n"), phMods[i], szModName);            
            }
        }
    }

    free(phMods);
    CloseHandle(hProcess);
}

int _tmain(){

    _tsetlocale(LC_ALL, _T("chs"));
    //显示进程中己加载的模块(此时不含(ExportDelay.dll)
    PrintModules(GetCurrentProcessId());
    _tsystem(_T("PAUSE"));

    int iVal1 = 10;
    int iVal2 = 20;
    int iVal3 = 30;
    _tprintf(_T("Func_A(%d)=%d\n"),iVal1,Func_A(iVal1));
    _tprintf(_T("Func_B(%d,%d)=%d\n"), iVal1,iVal2,Func_B(iVal1,iVal2));
    _tprintf(_T("Func_C(%d,%d,%d)=%d\n"), iVal1,iVal2,iVal3, Func_C(iVal1,iVal2,iVal3));

    PrintModules(GetCurrentProcessId());
    _tsystem(_T("PAUSE"));

    return 0;
}

 

第20章 DLL高级技术(2)

原文:http://www.cnblogs.com/5iedu/p/5014798.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!