zoukankan      html  css  js  c++  java
  • 【windows核心编程】DLL相关(2)

     

    关于DLL的延迟加载

     

    延迟加载DLL,使用的是隐式加载方式,当为exe使用的DLL指定为延迟加载的时候,连接器会将exe的【导入段】中去除该DLL的相关信息,同时在exe中嵌入一个新的【延迟加载段】表示要从该DLL中导入哪些函数。

    通过让对延迟加载函数的调用跳转到delayimp.lib中的__delayLoadHelper2函数,来完成对延迟加载的DLL的解析。

    当exe中第一次调用了一个延迟加载的DLL中的某个导出函数时,加载器才会将该DLL加载到进程地址空间中。需要注意的是:虽然此时已经加载了DLL,但是未调用到的其他函数,还是需要在调用的时候才能被修复(修复即不再通过__delayLoadHelper2函数来解析)。

    延迟加载的DLL也可以卸载,当再次调用该DLL中的函数时,再次加载DLL到进程地址空间。

     

    延迟加载 及支持卸载DLL的步骤,以ADll.dll为例

    ①项目属性--配置属性--链接器--输入--延迟加载的DLL

     输入DLL的名字,注意这里:卸载的时候使用的DLL的名字不能包含路径 并且 大小写必须和这里设置的一样。

    /DELAYLAOD:[DLLName]

     

     

    ②项目属性--配置属性--链接器--高级--延迟加载的DLL

      选择【支持卸载(/DELAY:UNLOAD)】

      

     

     

     ③加载DelayImp.lib静态库

      __delayLoadHelper2函数和__FUnloadDelayLoadedDLL2函数都在这个静态库中。

     

    #pragma comment(lib, "DelayImp.lib") 

    #include <delayimp.h>

     

    ④添加DLL的头文件 或者 添加DLL导出函数声明


    extern "C" __declspec(dllimport) int __stdcall Add(int a, int b);

     

     

     demo

    void CUseADll2Dlg::OnBnClickedButton1()
    {
        // TODO: 在此添加控件通知处理程序代码
    
        __try
        { 
            int sum = Add(10, 20); 
        }
        __except(1)
        {
            AfxMessageBox(_T("异常"));
        }
    }
    
    void CUseADll2Dlg::OnBnClickedButton2()
    {  
        //检测该DLL是否加载了
        HINSTANCE hInst = GetModuleHandle(TEXT("ADll.dll"));
    }
    
    void CUseADll2Dlg::OnBnClickedButton3()
    { 
        //参数DLL的名字不能包含路径,并且 大小写要和/DELAYLOAD设置的一致
        BOOL bRet = __FUnloadDelayLoadedDLL2("ADll.dll");
    }

     

     

     函数转发器

    函数转发器是DLL【输出段】中的一个条目,用来将一个函数调用【转发】到另一个DLL中的另一个函数。

    可用【dumpbin  -exports  XX.dll】查看

     

    在DLL中设置函数转发器:

    #pragma comment(linker, "/export:本DLL导出函数=其他DLL.函数名")

     

    e.g.

    #pragma comment(linker, "/export:Add=SecondDll.Sum")

    将本DLL中的导出函数Add【转发】到另一个名为SecondDll的DLL中函数Sum。

     

    经过实验,发现这里需要特别注意:#pragma来表示转发的函数名一定是经过name manling改变后的! 

    如下:

    //DLL1中的导出函数,Add
    #pragma comment(linker, "/export:_Add@8=Dll2._Sum@8")
    
    extern "C" __declspec(dllexport) int __stdcall Add(int a, int b);

     

     

    //DLL2中的导出函数Sum
    extern "C" __declspec(dllexport)  int __stdcall Sum(int a, int b)
    {
        return a + b;
    }

     

     

    一开始,当我试图这样写时:#pragma  comment(lib, "exports:Add=Dll2.Sum)

    看起来好像没有问题,可实际上,GetProcAddress总是失败的,经过测试,发现必须使用经过编译器name manling改变过后的名字,从depends查看如下

     

     

     

     

     

    所以必须使用改变过后的名字才可以,这一点也同样体现在GetProcAddress中,如下

    HINSTANCE hInst = LoadLibrary(TEXT("Dll1.dll"));
        if (NULL == hInst)
        {
            AfxMessageBox(_T("LoadLibrary 失败"));
        }
    
        typedef  int (__stdcall *  PUNC)(int, int);  //必须加上__stdcall
    
        PUNC pfnAdd = (PUNC)GetProcAddress(hInst, "_Add@8"); //这里的名字也必须是改编后的
        if (NULL == pfnAdd)
        {
            AfxMessageBox(_T("GetProcAddress失败")); return;
        }
    
        int sum = pfnAdd(100, 400);
        char chBuffer[100] = {0};
        sprintf_s(chBuffer, "result is %d", sum);
        AfxMessageBox(CString(chBuffer));

     

     

    另外,如果想要使用未经改变的名字,那么可以在VS编译器下使用

    extern "C"  __declspec(dllexport)   int  __cdecl  Add(int a,  int b);

    使用extern  "C" 和  __cdecl调用约定组合方式,在VS下的名字就是Add。

     

    结论:

    ①DLL函数转发时,转发到的函数也必须是导出函数,两者的调用约定以及名字改编方式最好一致

    ②在设置转发时,必须使用经过改编后的名字

    ③在定义导出函数的函数指针时,要加上调用约定,并且GetProcAddress()函数也要用改编后的名字

     

     

     

     

    已知DLL

    在注册表HKLMSYSTEMCurrentControlSetControlSession ManagerKnownDlls中列出的那些

     

    在使用LoadLibrary或LoadLibraryEx时,参数中的DLL是否带.dll后缀导致的不同情况:

    ①带.dll后缀

    首先去掉后缀,比如LoadLibrary("xx.dll"),首先去掉后缀,然后去上述注册表路径下查找【名称】为"xx"的项目,然后找到其对应的【数据】,即带.dll后缀的dll名字(这个名字可能和LoadLibrary时使用的不一样),如果在注册表中找不到该【名称】为"xx"的项目,则再去搜索路径依次查找"xx.dll", 如果在注册表中找到了该DLL名字,则去%systemroot%sytstem32目录中去查找该xx.dll,系统会并且只会该目录下查找该xx.dll,如果找到了则加载进进程地址空间,如果找不到则失败返回。

     

     ②不带.dll后缀

    按照正常搜索路径搜索DLL

     

     

     

      

     

  • 相关阅读:
    Jmeter命令行运行实例讲解
    ab压力测试工具
    F12找到页面某一元素所绑定的点击事件
    F12修改html进行本地js操作页面元素
    JMeter中利用Parameters 和Body Data传递参数有什么不同
    centos7 VNC安装
    centos7安装python3
    linux du命令的疑惑
    centos cgroup配置
    linux下postgres未能正常启动的解决过程
  • 原文地址:https://www.cnblogs.com/cuish/p/3756267.html
Copyright © 2011-2022 走看看