Windows核心编程——动态库的加载与导出

1.隐示加载与显示加载

使用动态DLL有两种方法,一种是隐式链接,一种是显式链接,如果用loadlibrary就是显示链接,用lib就属于隐式链接。
两种方法对于你的程序调用动态库时没有任何区别,只是步骤是不一样的。显式调用麻烦了点,但可以没有相应的lib库;隐式调用,使用起来比较简单,有函数的声明就可以了,但必须有lib库。
隐式加载默认是加载到内存中的,始终占用内存。
显示加载,你加载时占用内存,释放了就不占用内存了。如果该DLL已经载入,loadlibrary只是会增加一个引用计数,相同,freelibrary也只是减少引用计数,如果引用计数为0时,DLL才从内存中移除。
显式和隐式只是对于代码编写时来说的,最后产生的可执行程序,不管是显式和隐式,都是用loadlibrary载入的。显式与隐式不是用在这些方面的,显式加载适合需要动态的选 用DLL的情况。

2.DLL与exe的依赖关系:

3.显示加载过程(动态加载)

调用API的方式来加载dll:

    //宏定义:
    typedef int (*PFN_ADD)(int, int);
    typedef int(*PFN_SUB)(int, int);
    typedef void(*PFN_TEST)();

 
  1) LoadLibrary - 将dll加载到进程的内存
    

    //加载dll到进程
    HMODULE hMod = LoadLibrary("dll.dll");


  2) GetProcAddress - 获取dll的导出函数的地址,通过函数指针的方式调用
    

    //获取出导出函数的地址
    PFN_ADD pfnAdd = (PFN_ADD)GetProcAddress(hMod, "Add");
    int nVal = pfnAdd(1, 5);

    //获取出导出变量的地址
    int* pnTest = (int*)GetProcAddress(hMod, "g_nTest");
    
    //通过序号获取函数地址
    PFN_SUB pfnSub = (PFN_SUB)GetProcAddress(hMod, (LPCTSTR)0x0006);
    nVal = pfnSub(1, 5);

 
  3)FreeLibrary - 卸载dll

    //卸载dll
    FreeLibrary(hMod);

 

4.GetProcAddres API是如何区分序号和字符串?

    低64K(0x10000)的地址是不可用的,如果是字符串的地址必高于0x10000,一定在用户区,所以小于0x10000的值是序号,大于的值是字符串地址。

5.如果一个函数没有导出,能不能调用?

    可以,但我们需要拿到函数的地址
    
     通过计算:模块基址 + 函数与模块基址的偏移 = 函数的地址

    //调用没有导出的函数
    PFN_TEST pfnTest = (PFN_TEST)((int)hMod + 0x11620);
    pfnTest();

6.def导出

添加 __declspec (dllexport)  测试导出函数,

 depend打开可以看到导出的函数:

 

 导出后是粉碎后的名称。

如果想知道完整明确的导出函数函数名,可以采用def导出 - 给其他编译器或语言使用
    指定函数导出的名字

用法:

添加.def 文件

 

DEF语法:
    =internalname:内部函数的名称
    @ordinal:序号
    NONAME:此函数不以名字导出,只以序号导出
    PRIVATE:只能显示加载,不能隐式加载
    DATA :导出的是数据。

 注意,使用 .def 文件从 DLL 中导出变量时,不需要在变量上指定 __declspec(dllexport)。但是,在任何使用 DLL 的文件中,仍必须在数据声明上使用 __declspec(dllimport)。

7.测试def导出:


 

可以看到函数已被导出:

 

原文地址:https://www.cnblogs.com/zhaoyixiang/p/12965868.html