【转载】extern "C" __declspec(dllexport) __declspec(dllimport) 和 def
转自:http://www.cppblog.com/FateNo13/archive/2009/08/03/92052.html
前面的extern "C" __declspec(dllexport) __declspec(dllimport)都是用于函数或者变量,甚至类的声明的(可以把extern "C"放在class的前面,但是编译器会忽略掉,最后产生的还是C++修饰符,而不是C修饰符)这样的用法有个好处就是下面的代码可以在混有类的函数和变量上使用下面的宏,虽然对类不起作用:
#ifdef __cplusplus
extern "C"
{
//函数声明
//变量声明,变量一般前面都有extern
//类声明,这个不起作用,编译器直接忽略掉class前面的extern “C”
#ifdef __cplusplus
}
#endifC 和C++ 对应不同的调用约定,产生的修饰符也各不相同,如下:
| 调用约定 | extern "C" 或 .c 文件 | .cpp、.cxx 或 /TP |
|---|---|---|
|
C 命名约定 (__cdecl) |
_test |
?test@@ZAXXZ |
|
Fastcall 命名约定 (__fastcall) |
?test@@YIXXZ |
|
|
标准调用命名约定 (__stdcall) |
_test@0 |
?test@@YGXXZ |
__declspec(dllexport) __declspec(dllimport)一般也是使用宏的形式:
#ifdef ONEDLL_EXPORTS
#define ONEDLL_API __declspec(dllexport)
#else
#define ONEDLL_API __declspec(dllimport)
#endif这样在DLL代码本身就是__declspec(dllexport) ,在使用DLL的程序中就变成了__declspec(dllimport),这两标志分别用来指明当前的函数将被导出,和当前函数是被导入的。
上面的两个宏结合一下就是下面这样的了:
// 下列 ifdef 块是创建使从 DLL 导出更简单的
// 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 ONEDLL_EXPORTS
// 符号编译的。在使用此 DLL 的
// 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
// ONEDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
// 符号视为是被导出的。
#ifdef ONEDLL_EXPORTS
#define ONEDLL_API __declspec(dllexport)
#else
#define ONEDLL_API __declspec(dllimport)
#endif
// 此类是从 OneDll.dll 导出的
#ifdef __cplusplus
extern "C"
{
#endif
class ONEDLL_API COneDll {
public:
COneDll(void);
~COneDll(void);
// TODO: 在此添加您的方法。
int m_a;
int m_b;
int *m_p;
int m_n;
void AddValue();
};
extern ONEDLL_API int nOneDll;
ONEDLL_API int fnOneDll(void);
#ifdef __cplusplus
}
#endif 
如果调用模块和被调用模块都是C++(而且是同一种编成环境,如VC,甚至需要同一版本的VC),那么就不需要extern “C”了,因为这个标志的作用就是用在函数和变量声明前,无论是调用模块,还是被调用模块,都将生成C修饰符,调用模块将需要C修饰符的函数,而被调用模块将产生C修饰符的函数,所以这个标志在两者都是C++的时候使用并不受影响,不使用这个标志,也不受影响。
但是如果C模块要调用C++ 模块,那么C++模块就需要使用extern “C”,当然C不用,由于是在头文件的声明中使用,所以使用下面的宏能够使得这个头文件也在C中顺利使用:
#ifdef __cplusplus
extern "C"
{
//函数声明
//变量声明,变量一般前面都有extern
//类声明,这个不起作用,编译器直接忽略掉class前面的extern “C”
#ifdef __cplusplus
}
#endif如果C++模块要调用C模块,那么C++模块还是需要extern “C”,当然C不用,由于是在头文件的声明中使用,所以使用上面的宏同样能够使得这个头文件也在C中顺利使用。
总结一下就是加上extern “C”在什么情况下都没错,但是要注意函数重载的问题。
def文件是一种比较麻烦的方法,下面是MSDN中的部分内容:
模块定义 (.def) 文件是包含一个或多个描述 DLL 各种属性的 Module 语句的文本文件。如果不使用 __declspec(dllexport) 关键字导出 DLL 的函数,则 DLL 需要 .def 文件。
.def 文件必须至少包含下列模块定义语句:
1.文件中的第一个语句必须是 LIBRARY 语句。此语句将 .def 文件标识为属于 DLL。LIBRARY 语句的后面是 DLL 的名称。链接器将此名称放到 DLL 的导入库中。
2.EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。
例如,包含实现二进制搜索树的代码的 DLL 看上去可能像下面这样:
| 提示: |
|---|
|
如果希望优化 DLL 文件的大小,请对每个导出函数使用 NONAME 属性。使用 NONAME 属性时,序号存储在 DLL 的导出表中而非函数名中。如果导出许多函数,这样做可以节省相当多的空间。 |
其实 __declspec(dllexport)的作用就是让编译器按照某种预定的方式(前面大致解释了这种方式的规则)来输出导出函数及变量的符号,而def文件则是自己为每一个函数和变量指定导出符号,所以def是一个非自动化,手工很强的方式,不是特殊情况的话,实在没有必要浪费这些时间。
还有一个问题,就是使用def会把调用方式和 __declspec(dllexport)的作用全部覆盖掉,所以还需要自己处理调用方式不同产生的错误。
一般使用def文件的情况是你需要使用运行时加载,并且需要使用GetProcAddress函数获得函数地址,这个函数需要直接指明函数产生的导出符号,而可以自己指定导出符号的方式就是使用def。
def文件的具体语法可以看看msdn。
【转载】extern "C" __declspec(dllexport) __declspec(dllimport) 和 def的更多相关文章
- extern "C" __declspec(dllexport) __declspec(dllimport) 和 def
原文:extern "C" __declspec(dllexport) __declspec(dllimport) 和 def 前面的extern "C" _ ...
- __declspec(dllexport) & __declspec(dllimport)
__declspec(dllexport) 声明一个导出函数,是说这个函数要从本DLL导出.我要给别人用.一般用于dll中 省掉在DEF文件中手工定义导出哪些函数的一个方法.当然,如果你的DLL里全是 ...
- 【转载】 __declspec(dllexport) 和__declspec(dllimport)
转自:http://www.cppblog.com/Dutyboy/archive/2010/11/15/133699.html __declspec(dllexport) __declspec( ...
- __declspec(dllexport) 和 __declspec(dllimport)的作用
operatordll.h #include <iostream> #ifndef _WIN32 #define DLL_EXPORT#else #ifdef OPERATORDLL_EX ...
- __declspec(dllexport)
__declspec(dllexport) (2010-06-17 10:04:28) 转载▼ 标签: __declspec dllexport 导出 it 分类: C 先看代码:以下是在dev-c+ ...
- __declspec(dllexport)和__declspec(dllimport) (——declspec方法创建dll的方法已验证ok)
转载:https://www.cnblogs.com/chengbing2011/p/4084125.html __declspec(dllimport)和__declspec(dllexport)经 ...
- 浅谈__declspec(dllexport)和__declspec(dllimport)
__declspec(dllimport)和__declspec(dllexport)经常是成对的,在动态链接库中__declspec(dllexport)导出dll中的成员,__declspec(d ...
- DLL模块例2:使用__declspec(dllexport)导出函数,extern "C"规范修饰名称,隐式连接调用dll中函数
以下内容,我看了多篇文章,整合在一起,写的一个例子,关于dll工程的创建,请参考博客里另一篇文章:http://www.cnblogs.com/pingge/articles/3153571.html ...
- DLL进一步讲解:extern "C" __declspec(dllexport)
一.__declspec(dllexport): 将一个函数声名为导出函数,就是说这个函数要被其他程序调用,即作为DLL的一个对外函数接口. 通常它和extern "C" ...
随机推荐
- Bazinga HDU - 5510【技巧暴力+字符串】
题目:https://vjudge.net/problem/HDU-5510 $2015ACM/ICPC$ 亚洲区沈阳站 题目大意: 输入$t$(表示样例个数) 如何每个样例一个 $n$,表示字符串的 ...
- P1525 关押罪犯【二分+二分图】
输入输出样例 输入 #1 复制 4 6 1 4 2534 2 3 3512 1 2 28351 1 3 6618 2 4 1805 3 4 12884 思路 对于要求最大值最小的问题,不难想到二分. ...
- LeetCode 13、罗马数字转整数
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值I 1V 5X 10L 50C 100D 500M 1000例如, 罗马数字 2 写做 II ,即为两个并列的 1.12 ...
- yamlapi接口测试框架
1.思路: yamlapi支持unittest与pytest两种运行模式, yamlapi即为yaml文件+api测试的缩写, 可以看作是一个脚手架工具, 可以快速生成项目的各个目录与文件, 只需维护 ...
- CF1093E Intersection of Permutations [分块 +bitset]
大家好, 我非常喜欢暴力数据结构, 于是就用分块A了此题 分块题,考虑前缀和 \(b_i\) 表示 bitset 即 \(0\) ~ $i $ 出现过的数字,然后考虑直接暴力复制块然后前缀和,修改也很 ...
- 《C++面向对象程序设计》第6章课后编程题2拓展
设计一个程序,其中有3个类CBank.BBank.GBank,分别为中国银行类,工商银行类和农业银行类.每个类都包含一个私有数据成员balance用于存放储户在该行的存款数,另有一个友元函数Total ...
- 记录 Docker 的学习过程 (自建私有仓库)
私有仓库的创建 node1#wget http://harbor.orientsoft.cn/harbor-v1.4.0/harbor-offline-installer-v1.4.0.tgz nod ...
- python3练习100题——044
41-43讲关于python的变量范围,没有什么别的内容,我删除了. 原题链接:http://www.runoob.com/python/python-exercise-example44.html ...
- 2019-08-23 纪中NOIP模拟A组
T1 [JZOJ2908] 矩阵乘法 题目描述 给你一个 N*N 的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第 K 小数. 数据范围 对于 $20\%$ 的数据,$N \leq 100$,$Q ...
- 杭电oj 1087——super jump!jump!jump(java实现)
question:Super Jumping! Jumping! Jumping! 意思就是找一串数字中的和最大子串 思路:创建另一个数组,每一项是路径数组对应项之前最大子串的和,然后遍历此数组找出最 ...