__declspec(dllimport)的理解

2023-11-03

__declspec(dllimport)

MSDN中说明:

不使用 __declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

那我就来试验一下,假定,你在DLL里只导出一个简单的类,注意,我假定你已经在项目属性中定义了 SIMPLEDLL_EXPORT
SimpleDLLClass.h

#ifdef SIMPLEDLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif

class DLL_EXPORT SimpleDLLClass
{
public:
 SimpleDLLClass();
 virtual ~SimpleDLLClass();

 virtual getValue() { return m_nValue;};
private:
 int m_nValue;
};

SimpleDLLClass.cpp

#include "SimpleDLLClass.h"

SimpleDLLClass::SimpleDLLClass()
{
 m_nValue=0;
}

SimpleDLLClass::~SimpleDLLClass()
{
}

使用这个DLL类,在你的APP中include SimpleDLLClass.h时,你的APP的项目不用定义 SIMPLEDLL_EXPORT 所以,DLL_EXPORT 就不会存在了,这个时候,你在APP中,不会遇到问题。这正好对应MSDN上说的__declspec(dllimport)定义与否都可以正常使用。但我们也没有遇到变量不能正常使用呀。 那好,我们改一下SimpleDLLClass,把它的m_nValue改成static,然后在cpp文件中加一行

int SimpleDLLClass::m_nValue=0;

改完之后,再去LINK一下,你的APP,看结果如何, 结果是LINK告诉你找不到这个m_nValue。明明已经定义了,为什么又没有了?? 肯定是因为我把m_nValue定义为static的原因。但如果我一定要使用Singleton的Design Pattern的话,那这个类肯定是要有一个静态成员,每次LINK都没有,那不是完了? 如果你有Platform SDK,用里面的Depend程序看一下,DLL中又的确是有这个m_nValue导出的呀。
再回去看看我引用MSDN的那段话的最后一句。 那我们再改一下SimpleDLLClass.h,把那段改成下面的样子:

#ifdef SIMPLEDLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif

再LINK,一切正常。原来dllimport是为了更好的处理类中的静态成员变量的,如果没有静态成员变量,那么这个__declspec(dllimport)无所谓。

总结一下__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声明一个函数:

[cpp]  view plain copy
  1. __declspec(dllexportvoid HelloWorld();  


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


[cpp]  view plain copy
  1. void HelloWorld()  
  2. {  
  3.     AfxMessageBox(_T("HelloWorld"));  
  4. }  


        这样外部的应用程序或dll就能调用HelloWorld函数。这里要特别提醒的是:有些网友说要把DllApi.h中的__declspec(dllexport) void HelloWorld();改为__declspec(dllimport) void HelloWorld();才能提供给外部调用,实际上这并不需要,这个我已经测试过。从那时我就产生一个疑问:照这样,像类似下面的:


[cpp]  view plain copy
  1. #ifdef _EXPORTING  
  2. #define API_DECLSPEC    __declspec(dllexport)  
  3. #else  
  4. #define API_DECLSPEC    __declspec(dllimport)  
  5. #endif  

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


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

总结了各位大虾的发言,特得出如下结论: 
1. 在导入动态链接库中的全局变量方面起作用:
使用类似

[cpp]  view plain copy
  1. #ifdef _EXPORTING  
  2. #define API_DECLSPEC    __declspec(dllexport)  
  3. #else  
  4. #define API_DECLSPEC    __declspec(dllimport)  
  5. #endif  


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


[cpp]  view plain copy
  1. API_DECLSPEC CBtt g_Btt;  


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


[cpp]  view plain copy
  1. API_DECLSPEC CBtt g_Btt;  


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


[cpp]  view plain copy
  1. CBtt g_Btt;  


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


[cpp]  view plain copy
  1. extern CBtt g_Btt;  


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


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


[cpp]  view plain copy
  1. class __declspec(dllexport) CBtt  
  2. {  
  3. public:  
  4.     CBtt(void);  
  5.     ~CBtt(void);  
  6. public:  
  7.     CString m_str;  
  8.     static int GetValue()  
  9.     {  
  10.         return m_nValue;  
  11.     }  
  12. private:  
  13.     static int m_nValue;  
  14. };  


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


[cpp]  view plain copy
  1. #ifdef _EXPORTING  
  2. #define API_DECLSPEC    __declspec(dllexport)  
  3. #else  
  4. #define API_DECLSPEC    __declspec(dllimport)  
  5. #endif  
  6. class API_DECLSPEC CBtt  
  7. {  
  8. public:  
  9.     CBtt(void);  
  10.     ~CBtt(void);  
  11. public:  
  12.     CString m_str;  
  13.     static int GetValue()  
  14.     {  
  15.         return m_nValue;  
  16.     }  
  17. private:  
  18.     static int m_nValue;  
  19. };  



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这个关键字,这里体现了一种对称美。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

__declspec(dllimport)的理解 的相关文章

  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • 随着时间的推移,添加到 List 变得非常慢

    我正在解析一个大约有 1000 行的 html 表 我从一个字符串中添加 10 个字符串 td 每行到一个list td
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 如何连接重叠的圆圈?

    我想在视觉上连接两个重叠的圆圈 以便 becomes 我已经有部分圆的方法 但现在我需要知道每个圆的重叠角度有多大 但我不知道该怎么做 有人有主意吗 Phi ArcTan Sqrt 4 R 2 d 2 d HTH Edit 对于两个不同的半
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • C# 中最小化字符串长度

    我想减少字符串的长度 喜欢 这串 string foo Lorem ipsum dolor sit amet consectetur adipiscing elit Aenean in vehicula nulla Phasellus li
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • 在OpenGL中,我可以在坐标(5, 5)处精确地绘制一个像素吗?

    我所说的 5 5 正是指第五行第五列 我发现使用屏幕坐标来绘制东西非常困难 OpenGL 中的所有坐标都是相对的 通常范围从 1 0 到 1 0 为什么阻止程序员使用屏幕坐标 窗口坐标如此严重 最简单的方法可能是通过以下方式设置投影以匹配渲
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐

  • python_error

    inspection info This inspection detects code which can not be normally reached 检验信息 本次检验检测到正常情况下无法达到的代码 inspection info
  • Spring、SpringMVC、SpringBoot三者的区别

    目录 Spring是什么 SpringMVC是什么 SpringBoot是什么 Spring SpringMVC SpringBoot三者之间的关系 Spring是什么 Spring是一个开源的应用程序框架 它提供了一种简易的开发方式 通过
  • 《MATLAB 神经网络43个案例分析》:第40章 动态神经网络时间序列预测研究——基于MATLAB的NARX实现

    MATLAB 神经网络43个案例分析 第40章 动态神经网络时间序列预测研究 基于MATLAB的NARX实现 1 前言 2 MATLAB 仿真示例 3 小结 1 前言 MATLAB 神经网络43个案例分析 是MATLAB技术论坛 www m
  • 一篇文章读懂少儿机器人编程课程学什么?

    少儿机器人编程课程都学习什么 可以说在少儿成长阶段 孩子想接触的各式各样的主题和对生活的感性认知 通过机器人编程课程都可以学习到 以贝尔机器人编程课程为例 在锻炼孩子们逻辑思维能力的同时 会结合生活观察 启发思考 精细动作等训练 拓展生物
  • 服务器root权限安全策略配置

    服务器root权限安全策略配置 linux系统中 root具有至高无上的权限 为了安全起见 项目生产环境一般会要求禁止root用户直接ssh登录 使用普通用户登录 有特殊需求时 特定组用户可以使用su切换到root用户 或sudo切换到ro
  • 关于wait/notify以及顺序打印

    1 等待和唤醒是同一个对象 2 notify只会唤醒一个线程 使用角度 1 Object wait 会抛出 InterruptedException 2 调用object wait 必须首先对object monitor lock 进行加锁
  • 2020.12.6-参加中国电子学会的青少年软件编程等级考试C语言5级(良好通过)

    Jensen今天参加了中国电子学会的青少年软件编程等级考试C语言的5级考试 目前C语言的5级是最高级别 需要将基本算法都学完的孩子来参加 基本算法包括递推 递归 贪心 分治 搜索 动态规划等 Jensen 两道题AC 其他两道题没有AC 但
  • 机器学习:马尔可夫模型

    后续遇到合适的案例会再补充 1 马尔可夫模型 马尔可夫模型 Markov Model MM 是一种统计模型 广泛应用在自然语言处理等领域中 1 1 数学定义 考虑一组随机变量序列 X X 0
  • Android获取手机联系人的基本信息(如姓名、电话、邮箱、备注)

    在做项目的过程中 需要获取我们手机通讯录联系人的基本信息 如姓名 电话 邮箱 备注 昵称 公司 职位 家庭电话等等信息 下面就是我总结的一些具体方法 1 首先读取联系人需要添加读取权限 6 0以上需要动态获取权限 1 1AndroidMan
  • go context用法详解

    转发自 作者kingeasternsun https studygolang com articles 10155 fr sidebar 本文主要基于官方文档Go Concurrency Patterns Context以及视频Advanc
  • SpringBoot+Vue校园学习成绩管理系统

    简介 本项目采用了基本的springboot vue设计的校园学习成绩管理系统 详情请看截图 经测试 本项目正常运行 本项目适用于Java毕业设计 课程设计学习参考等用途 项目描述 项目名称 SpringBoot Vue校园学习成绩管理系统
  • Hyperledger Fabric 带入门 Python 课程

    Hyperledger Fabric 带入门 Python 课程 Hyperledger Fabric 是一个开源的区块链框架 被广泛应用于企业级联盟链场景 Python 是一种高级编程语言 易于学习和使用 是区块链领域的热门语言之一 本文
  • 数字表达_金字塔神秘数字之谜!142857这组神奇的数字表达着什么含义

    在埃及金字塔内 发现一组看似平凡 但很神奇的数字 142857 这组数字背后隐藏着人类无法解释的谜团 有人说这组数字和汶川大地震的时间偶合 这组数字隐藏了灾难发生的预言之秘 这到底是有心人无端的揣测 还是严谨的科学推论 142857这组数字
  • c++ list 容器splice函数

    list 的splice函数主要是用来合并两个list splice是list中特有的拼接方法 splice实现了不需要拷贝的list合并 即可以在常数时间内从list的一个区域拼接到另一个list的一个区域 也就是说splice是一个常数
  • ParallelLoopState.Break与ParallelLoopState.Stop区别

    ParallelLoopState Break与ParallelLoopState Stop区别 Break和Stop都是为了停止循环的一种手段 不同之处在于 Break不立马结束循环 而是要等所有小于ParallelLoopState L
  • shell脚本中调用java程序,并传递参数的方法

    为了方便运行多个java程序 选择写了脚本进行统一管理 简单介绍下传输参数的方法 假设要向file java传输参数 step1 需要先生成 class文件 javac Test java step2 在脚本中定义参数 int a 5 do
  • 拉格朗日插值多项式

    1 拉格朗日插值多项式 首先给出 n次插值基函数定义 若 n 次 多 项 式 l j
  • windows驱动开发8:虚拟摄像头方案

    一 摄像头框架 在业务场景中 有许多是需要应用能够通过摄像头的方式来访问相关的音视频数据 比如美颜 摄像头多路复用 IP摄像头接入视频会议等 这些功能通过虚拟摄像头的方式来实现 是一个比较通用的解决方案 那么如何及选用哪种技术方案来开发虚拟
  • SAR成像系列:【12】层析合成孔径雷达(层析SAR,Tomographic SAR,TomoSAR)

    自1995年Knaell为解决曲线SAR成像结果中的强旁瓣问题 将二维计算机断层 Computed Tomography 成像技术扩展到三维空间 并通过投影切片理论和后向投影算法获得了雷达成像三维空间的点响应函数 从而为分析三维成像分辨率和
  • __declspec(dllimport)的理解

    declspec dllimport MSDN中说明 不使用 declspec dllimport 也能正确编译代码 但使用 declspec dllimport 使编译器可以生成更好的代码 编译器之所以能够生成更好的代码 是因为它可以确定