zoukankan      html  css  js  c++  java
  • DLL编程的导入导出,__declspec(dllimport),__declspec(dllexport)

    在Windows DLL编程时,可使用__declspec(dllimport)关键字导入函数或者变量。

     
    __declspec(dllimport),函数的导入
     
    当你需要使用DLL中的函数时,往往不需要显示地导入函数,编译器可自动完成。但如果你显示地导入函数,编译器会产生质量更好的代码。由于编译器确切地知道了一个函数是否在一个DLL中,它就可以产生更好的代码,不再需要间接的调用转接。
     
    Win32的PE格式(Portable Executable Format)把所有导入地址放在一个导入地址表中。下面用一个具体实例说明使用__declspec(dllimport)导入函数和不使用的区别:
     
    假设func是一个DLL中的函数,现在在要生成的.exe的main函数中调用func函数,并且不显示地导入func函数(即没有:__declspec(dllimport)),代码示例如下:
    int main()
    {
        func();
    }
     
    编译器将产生类似这样的调用代码:
    call func
     
    然后,链接器把该调用翻译为类似这样的代码:
    call 0x40000001       ; ox40000001是"func"的地址
     
    并且,链接器将产生一个Thunk,形如:
    0x40000001: jmp DWORD PTR __imp_func
     
    这里的imp_func是func函数在.exe的导入地址表中的函数槽的地址。然后,加载器只需要在加载时更新.exe的导入地址表即可。
     
    而如果使用了__declspec(dllimport)显示地导入函数,那么链接器就不会产生Thunk(如果不被要求的话),而直接产生一个间接调用。因此,下面的代码:
    __declspec(dllimport) void func1(void);
    void main(void) 
    {
         func1();
    }
     
    将调用如下调用指令:
    call DWORD PTR __imp_func1
     
    因此,显示地导入函数能有效减少目标代码(因为不产生Thunk)。另外,在DLL中使用DLL外的函数也可以这样做,从而提高空间和时间效率。
     

    __declspec(dllimport),变量的导入
     
    与函数不同的是,在使用DLL中的变量时,需要显示地导入变量。使用__declspec(dllimport)关键字导入变量。若在DLL中使用.def导出变量,则应使用DATA修饰变量,而不是使用已经被遗弃的CONSTANT。因为CONSTANT可能需要使用指针间接访问变量,不确定什么时候会出问题。

    DLL定义的全局变量可以被调用进程访问,DLL也可以访问调用进程的全局数据。

    /* 文件名:lib.h */

    #ifndef LIB_H
    #define LIB_H
    extern int dllGlobalVar;
    #endif

    /* 文件名:lib.cpp */

    #include "lib.h"
    #include <windows.h>
    int dllGlobalVar;


    BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)

    {
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    dllGlobalVar = 100; //在dll被加载时,赋全局变量为100
    break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
    break;
    }

    return TRUE;
    }


    ;文件名:lib.def
    ;在DLL中导出变量
    LIBRARY "dllTest"
    EXPORTS
    dllGlobalVar CONSTANT
    ;或dllGlobalVar DATA
    GetGlobalVar


    从lib.h和lib.cpp中可以看出,全局变量在DLL中的定义和使用方法与一般的程序设计是一样的。若要导出某全局变量,我们需要在.def文件的EXPORTS后添加:

    变量名 CONSTANT   //过时的方法

    变量名 DATA     //VC++提示的新方法

    在主函数中引用DLL中定义的全局变量:

    #include <stdio.h>
    #pragma comment(lib,"dllTest.lib")
    extern int dllGlobalVar;
    int main(int argc, char *argv[])
    {
    printf("%d ", *(int*)dllGlobalVar);
    *(int*)dllGlobalVar = 1;
    printf("%d ", *(int*)dllGlobalVar);

    return 0;
    }


    特别要注意的是用extern int dllGlobalVar声明所导入的并不是DLL中全局变量本身,而是其地址,应用程序必须通过强制指针转换来使用DLL中的全局变量。这一点,从*(int*)dllGlobalVar可以看出。因此在采用这种方式引用DLL全局变量时,千万不要进行这样的赋值操作:

    dllGlobalVar = 1;

    其结果是dllGlobalVar指针的内容发生变化,程序中以后再也引用不到DLL中的全局变量了。

    在应用工程中引用DLL中全局变量的一个更好方法是:

    #include <stdio.h>
    #pragma comment(lib,"dllTest.lib")
    extern int __declspec(dllimport) dllGlobalVar; //用__declspec(dllimport)导入
    int main(int argc, char *argv[])
    {
    printf("%d ", dllGlobalVar);
    dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换
    printf("%d ", dllGlobalVar);
    return 0;
    }

    通过__declspec(dllimport)方式导入的就是DLL中全局变量本身而不再是其地址了,笔者建议在一切可能的情况下都使用这种方式。

    (通过程序测试,只用__declspec(dllexport)导出也能以本身形式使用全局变量)


    __declspec(dllexport),导出

    先看代码:以下是在dev-c++里建立自已的dll时的dll.h里面的代码,这里面有一个:_declspec(dllexport)

    #ifndef _DLL_H_
    #define _DLL_H_//防重复定义

    #if BUILDING_DLL
    #define DLLIMPORT __declspec (dllexport)
    #else /* Not BUILDING_DLL */
    #define DLLIMPORT __declspec (dllimport)
    #endif /* Not BUILDING_DLL */


    DLLIMPORT void HelloWorld (void);


    #endif /* _DLL_H_ */

     

    上面代码里面的_delcspce(dllexport)被定义为宏,这样可以提高程序的可读性!这个的作是是将函数定义为导出函数,也就是说这个函数要被包含这个函数的程序之外的程序调用!本语句中就是:void Helloword(void):

    摘自msdn:在 32 位编译器版本中,可以使用 __declspec(dllexport) 关键字从 DLL 导出数据、函数、类或类成员函数。__declspec(dllexport) 将导出指令添加到对象文件。


    若要导出函数,__declspec(dllexport) 关键字必须出现在调用约定关键字的左边(如果指定了关键字)。例如:

    __declspec(dllexport) void __cdecl Function1(void);


    若要导出类中的所有公共数据成员和成员函数,关键字必须出现在类名的左边,如下所示:

    class __declspec(dllexport) CExampleExport : public CObject
    { ... class definition ... };


    生成 DLL 时,通常创建一个包含正在导出的函数原型和/或类的头文件,并将 __declspec(dllexport) 添加到头文件中的声明。若要提高代码的可读性,请为 __declspec(dllexport) 定义一个宏并对正在导出的每个符号使用该宏:

    #define DllExport   __declspec( dllexport ) 

    __declspec(dllexport) 将函数名存储在 DLL 的导出表中。


    转载自:http://blog.csdn.net/stone_kingnet/article/details/3862504

  • 相关阅读:
    安装mysql apache php smb
    解决IE8placeholder属性问题
    将博客搬至CSDN
    shell脚本获取配置文件中的内容
    shell sed指令全解
    salt一键部署hive
    salt一键部署habse
    salt一键部署kafka
    salt一键部署elasticsearch
    salt一键部署mysql
  • 原文地址:https://www.cnblogs.com/inory/p/5468697.html
Copyright © 2011-2022 走看看