总结一下__declspec(dllimport)的作用

作者:朱金灿
来源:http://blog.youkuaiyun.com/clever101/


是时候总结一下__declspec(dllimport)的作用了。可能有人会问:__declspec(dllimport)和__declspec(dllexport)是一对的,在动态链接库中__declspec(dllexport)管导出,__declspec(dllimport)管导出,就像一个国家一样,有出口也有进口,有什么难理解的呢?这是一种很自然的思路,开始我也是这样理解。


但是在两年前的一个项目中,我发现不用__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中的__declspec(dllexport) void HelloWorld();改为__declspec(dllimport) void HelloWorld();才能提供给外部调用,实际上这并不需要,这个我已经测试过。从那时我就产生一个疑问:照这样,像类似下面的:


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

是不是就只剩下一种作用:让外部调用者看得更自然些,知道哪些接口是自己工程需要导入的?__declspec(dllimport)是不是一点实际作用都没有呢?这个疑问一直盘旋在我的脑海。直到最近,我在优快云论坛上发了一个帖子:


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

总结了各位大虾的发言,特得出如下结论:

1. 在导入动态链接库中的全局变量方面起作用:
使用类似

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


可以更好地导出dll中的全局变量,比如按照的宏,可以在dll中这样导出全局变量:


API_DECLSPEC CBtt g_Btt;


然后在调用程序这样导入:


API_DECLSPEC CBtt g_Btt;


当然也可以使用extern关键字,比如在dll中这样导出全局变量:


CBtt g_Btt;


然后在调用程序这样导入:


extern CBtt g_Btt;


但据说使用__declspec(dllimport)更有效。


2. __declspec(dllimport)的作用主要体现在导出类的静态成员方面,
比如在动态链接库中定义这样一个导出类:


class __declspec(dllexport) CBtt { public: CBtt(void); ~CBtt(void); public: CString m_str; static int GetValue() { return m_nValue; } private: static int m_nValue; };


照上面这样声明,外部虽然可以使用CBtt类,但不能使用CBtt类的GetValue函数,一使用就会出现无法解析的外部符号 "public: static int CBtt::m_nValue" (?m_nValue@CBtt@@2HA)。只有如下声明才能使用CBtt类的GetValue函数:


#ifdef _EXPORTING #define API_DECLSPEC __declspec(dllexport) #else #define API_DECLSPEC __declspec(dllimport) #endif class API_DECLSPEC CBtt { public: CBtt(void); ~CBtt(void); public: CString m_str; static int GetValue() { return m_nValue; } private: static int m_nValue; };



3. 使用隐式使用dll时,不加__declspec(dllimport)完全可以,使用上没什么区别,只是在生成的二进制代码上稍微有点效率损失。


a、 不加__declspec(dllimport)时,在使用dll中的函数时,编译器并不能区别这是个普通函数,还是从其它dll里导入的函数,所以其生 成的代码如下:

call 地址1

地址1:
jmp 实际函数地址


b、有 __declspec(dllimport)时,编译器知道这是要从外部dll导入的函数,从而在生成的exe的输入表里留有该项,以便在运行 exe,PE载入器加载exe时对输入地址表IAT进行填写,这样生成的代码如下:

call dword ptr[输入表里哪项对应的内存地址] (注意:现在就不需要jmp stub了)。这里
有兴趣的朋友可以参看《编译原理》和 PE文件格式。


4.使用__declspec(dllimport)体现了语言的一种对称美,比如虽然!true就是表示false,但是我们还是需要false这个关键字,这里体现了一种对称美。

在此特别感谢优快云的众位大侠:superdiablo、wltg2001、ccpaishi、jszj、WizardK、hurryboylqs、jingzhongrong、jameshooo、glacier3d、winnuke、starnight1981、laiyiling、yang79tao、ForestDB、zhouzhipen、lxlsymbome、Beyond_cn。


参考文献:


1. __declspec(dllimport) 到底有什么用?


































<think>好的,我现在要解决用户的问题:“C1_DLL_IMPORT __declspec(dllimport)使用场景”。首先,我需要理解用户的问题到底是什么。看起来用户是在问在C/C++中使用__declspec(dllimport)的场景,以及它和C1_DLL_IMPORT的关系。 首先,我应该回忆一下DLL(动态链接库)的基本知识。在Windows平台上,DLL允许代码在多个程序之间共享。当创建一个DLL时,通常会使用__declspec(dllexport)来导出函数或类,这样其他程序才能使用它们。而__declspec(dllimport)则是在使用DLL时,告诉编译器这些符号是从DLL中导入的,而不是在当前模块中定义的。 接下来,用户提到的C1_DLL_IMPORT可能是一个宏定义。通常,在编写跨平台或可移植的代码时,开发者会使用宏来简化导入导出的处理。例如,在DLL的实现项目中,可能会定义C1_DLL_EXPORT为__declspec(dllexport),而在使用该DLL的项目中,C1_DLL_IMPORT可能被定义为__declspec(dllimport)。这样做的好处是方便切换,比如在同一个源文件中,根据是否定义了DLL_EXPORTS宏,自动选择导出或导入。 现在,用户的问题是关于使用场景,所以我需要详细说明在哪些情况下需要使用__declspec(dllimport)。例如,当开发一个DLL,并希望其他程序调用其中的函数或类时,需要在DLL的头文件中使用__declspec(dllexport)来标记导出的符号。而当另一个项目引用这个DLL时,必须使用__declspec(dllimport)来声明这些符号是从DLL中导入的。否则,链接器可能无法正确解析符号,导致链接错误。 另外,需要注意的是,对于类和变量,使用__declspec(dllimport)时有一些特殊要求。比如,导入类时,必须确保类的定义在DLL内部和外部是一致的,否则可能导致类型不匹配的问题。此外,对于静态成员变量,可能需要显式地在DLL中进行定义,否则在导入时可能会遇到未解析的符号错误。 可能用户还想知道如何实际应用这个宏。比如,在DLL的项目设置中定义一个宏,比如DLL_EXPORTS,然后在头文件中使用: #ifdef DLL_EXPORTS #define C1_DLL_IMPORT __declspec(dllexport) #else #define C1_DLL_IMPORT __declspec(dllimport) #endif 这样,当编译DLL时,使用dllexport导出符号,而其他项目在包含头文件时,会使用dllimport导入。 还需要提到的是,虽然__declspec(dllimport)主要用于Windows平台,但在其他平台上可能有不同的机制,比如使用__attribute__((visibility("default")))或者动态加载库的方式。不过用户的问题集中在Windows上的使用,所以重点应放在那里。 最后,总结一下使用场景:当需要从DLL导入函数、类或变量时,使用__declspec(dllimport)来声明这些符号,确保编译器生成正确的导入代码,并且链接器能够正确解析符号。同时,结合宏定义可以更方便地管理导入导出的符号,提高代码的可维护性。</think>在Windows平台开发动态链接库(DLL)时,`__declspec(dllimport)` 用于声明从DLL中导入的符号(如函数、类或变量),其典型使用场景如下: ### 使用场景说明 1. **DLL使用者** 当你的项目需要引用其他DLL提供的功能时,需在头文件中通过 `__declspec(dllimport)` 声明这些符号,告知编译器它们的定义不在当前模块中。 2. **宏定义简化** 通常与宏(如 `C1_DLL_IMPORT`)结合使用,通过条件编译区分导出/导入逻辑。例如: ```cpp #ifdef C1_DLL_EXPORTS // 定义在DLL项目中 #define C1_DLL_IMPORT __declspec(dllexport) #else // 定义在使用DLL的项目中 #define C1_DLL_IMPORT __declspec(dllimport) #endif ``` 3. **类/结构体导入** 若DLL导出类或结构体,需在头文件中使用 `__declspec(dllimport)` 修饰类定义,确保成员函数和静态变量的正确解析。 4. **避免手动维护导入库** 相比传统的 `.lib` 导入库文件,`__declspec(dllimport)` 可直接在代码中控制符号导入,减少对 `.def` 文件的依赖。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值