none
__declspec(dllimport)的作用到底在哪里呢? RRS feed

  • 问题

  •   我一直搞不明白__declspec(dllimport)的作用在哪里。比如现在我新建一个使用共享MFC DLL的规则DLL工程:DllDlg。然后我新建两个文件:DllApi.h和DllApi.cpp。DllApi.h作为接口文件,DllApi.cpp作为实现文件。

    接着在DllApi.h声明一个函数:

    __declspec(dllexport) void HelloWorld();
    

     

    在DllApi.cpp写这个函数的实现:

    void HelloWorld()
    {
        AfxMessageBox(_T("HelloWorld"));
    
    }
    

     

    这样外部的应用程序或dll就能调用HelloWorld函数。但是我见到更多似乎是脱裤子放屁式的做法,首先自定义导出宏,然后在添加预处理器。以上例为例:
    首先是在DllApi.h声明:

    #ifdef _EXPORTING
    #define API_DECLSPEC    __declspec(dllexport)
    #else
    #define API_DECLSPEC    __declspec(dllimport)
    #endif
    
    
    API_DECLSPEC void HelloWorld();
    

     

    在DllApi.cpp写这个函数的实现:

    void HelloWorld() { AfxMessageBox(_T("HelloWorld")); }

    最后在DllDlg工程里添加预处理器:_EXPORTING。这等于告诉大家:定义了这个预处理器的就是把函数导出,不定义这个预处理器的就是把函数导入。

    但是比较这两种做法,效果有区别吗?说实话我看不出有任何区别。那为什么我们还是热衷于定义一个导出宏,然后添加预处理器的做法吗?__declspec(dllimport)的作用到底在哪里?

    附msdn 2005对__declspec(dllimport)的解释:

    如果一个程序使用 DLL 定义的公共符号,就说该程序是在导入公共符号。为使用 DLL 生成的应用程序创建头文件时,在公共符号的声明上使用 __declspec(dllimport)。不论是用 .def 文件导出还是用 __declspec(dllexport) 关键字导出,__declspec(dllimport) 关键字均有效。

    若要提高代码的可读性,请为 __declspec(dllimport) 定义一个宏,然后使用此宏声明每个导入的符号:

    复制代码
    #define DllImport __declspec( dllimport )

    DllImport int j;
    DllImport void func();


    在函数声明上使用 __declspec(dllimport) 是可选操作,但如果使用此关键字,编译器将生成更有效的代码。但是,为使导入的可执行文件能够访问 DLL 的公共数据符号和对象,必须使用 __declspec(dllimport)。请注意,DLL 的用户仍然需要与导入库链接。

    对 DLL 和客户端应用程序可以使用相同的头文件。为此,请使用特殊的预处理器符号来指示是生成 DLL 还是生成客户端应用程序。例如:

    复制代码
    #ifdef _EXPORTING
    #define CLASS_DECLSPEC __declspec(dllexport)
    #else
    #define CLASS_DECLSPEC __declspec(dllimport)
    #endif

    class CLASS_DECLSPEC CExampleA : public CObject
    { ... class definition ... };

     

     

     


    前无古人,后无来者
    2010年3月21日 16:12

答案

  • 导入全局、静态或者类成员变量需要__declspec(dllimport)。

    #define DllImport   __declspec(dllimport) 

    DllImport int  j;

    __declspec(dllexport)是用于避免需要自己写DEF文件的。编译器会为被__declspec(dllexport)修饰的函数自动添加一个导出函数入口。如果你在其他模块中包含__declspec(dllexport)的头文件,这些项目的导出表中也会生成一个同名导出函数。

     



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    2010年3月23日 2:11
    版主

全部回复

  • 想想在一个DLL中导入另一个DLL的函数怎么办。

    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    2010年3月21日 23:52
    版主
  • dllexport:把那个类写在符号表上

    dllimport:把那个类跟dll的符号表联系起来,调用的函数会转到dll里面去

    2010年3月22日 4:49
  • 想想在一个DLL中导入另一个DLL的函数怎么办。

    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP


        老大,遵照你的意思。今晚我测试过,把DllDlg工程的DllApi.h、DllDlg.lib和DllDlg.dll拷贝到其它解决方案作为一个第三方库,供另外一个dll调用。结果调用成功。具体是:

    我又新建了一个使用共享MFC DLL的规则DLL工程:SecDll。然后我新建两个文件:SecApi.h和SecApi.cpp。SecApi.h作为接口文 件,SecApi.cpp作为实现文件。

    接着在SecApi.h声明一个函数:

    C/C++ code

    __declspec(dllexport)
    void SecondCall();



    在SecApi.cpp写这个函数的实现:

    C/C++ code

    #include
    " StdAfx.h "
    #include
    " ..\thirdpartylib\include\DllApi.h "
    #include
    " SecApi.h "


    void SecondCall()
    {
    HelloWorld();
    }



    然后在一个对话框程序里调用SecondCall函数,依然可以调用成功。

     

     


    前无古人,后无来者
    2010年3月22日 15:48
  •        问题是我不用dllimport,我还不是照样调用成功。
    前无古人,后无来者
    2010年3月22日 15:49
  • 我不知道MFC的共享规则是什么规则,我创建dll都是用最普通的win32 application,然后

    dllimport/export是给连接器用的。链接器在链接你的代码的时候,看到一个函数声明,如果没有dll关联,就会去自己的obj文件找。如果有,就去lib文件找。找不到就会有链接错误。

    2010年3月22日 16:35
  • 每个函数在被声明模块中需要声明为导出,在使用的模块中需要被声明为导入。MFC有个AFX_EXT_CLASS可以做到自动检测DLL和EXE,这样可以避免为每个函数写一个#ifdef,或者为每个编译条件写一个头文件。但是一个DLL导入另一个DLL的时候AFX_EXT_CLASS判断不了,所以要参考http://support.microsoft.com/kb/128199的做法。

     



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    2010年3月22日 17:15
    版主
  • 每个函数在被声明模块中需要声明为导出,在使用的模块中需要被声明为导入。MFC有个AFX_EXT_CLASS可以做到自动检测DLL和EXE,这样可以避免为每个函数写一个#ifdef,或者为每个编译条件写一个头文件。但是一个DLL导入另一个DLL的时候AFX_EXT_CLASS判断不了,所以要参考http://support.microsoft.com/kb/128199 的做法。

     



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP


          大侠,我既没有把函数被声明为导入,也没有使用AFX_EXT_CLASS。但是我的dll依然可以被外部的exe和dll正常调用啊,这是为什么呢?能否举一个例子否定我的第一种做法呢?

     


    前无古人,后无来者
    2010年3月23日 0:57
  • 导入全局、静态或者类成员变量需要__declspec(dllimport)。

    #define DllImport   __declspec(dllimport) 

    DllImport int  j;

    __declspec(dllexport)是用于避免需要自己写DEF文件的。编译器会为被__declspec(dllexport)修饰的函数自动添加一个导出函数入口。如果你在其他模块中包含__declspec(dllexport)的头文件,这些项目的导出表中也会生成一个同名导出函数。

     



    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    2010年3月23日 2:11
    版主
  • 不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。
    2011年3月9日 2:47
  • dllimport是为了更好的处理类中的静态成员变量的,如果没有静态成员变量,那么这个__declspec(dllimport)无所谓。
    2011年3月9日 3:18
  • 用下面的方式定义输出函数并不是脱裤子放屁,是有作用的

    #ifdef _EXPORTING
    #define CLASS_DECLSPEC __declspec(dllexport)
    #else
    #define CLASS_DECLSPEC __declspec(dllimport)
    #endif
    首先,对于动态库本身,不用说,需要在preprocessor定义 _EXPORTING使得所有使用了CLASS_DECLSPEC 的类或者函数接口被指定为输出类或函数,而对于调用动态库一方来说,因为无需定义_EXPORTING,所以在包含动态库头文件的时候,所有动态库输出函数或者类在调用方全部是被声明为__declspec(dllimport)的了,这样动态库函数调用的时候生成的目标代码更加有效。举例来说,动态库输出了函数 api1,那么在调用api1的时候,如果没用__declspec(dllimport)修饰,
    编译器将产生类似这样的调用代码:call api1,然后,链接器把该调用翻译为类似这样的代码:call 0x40000001 ; 0x40000001是"api1"的地址,并且链接器将产生一个Thunk,形如:0x40000001:jmp DWORD PTR __imp_api1 ,而如果使用了__declspec(dllimport)显示地导入函数,那么链接器就不会产生Thunk,而直接产生一个间接调用,形如:call DWORD PTR __imp_api,可见,显示地导入函数能有效减少目标代码(因为不产生Thunk)。所以一般动态库的输出函数都是用上面的方式先行定一个宏,用这个宏来描述输出函数,目的就在于此。

     

    2012年1月12日 7:21