最初的网页链接已经挂了, 在此贴一个中间的转载链接

https://blog.csdn.net/zhazhiqiang/article/details/51577523

一 概要

  vs中导出 dll的方法有两种: 一种是使用__declspec(dllexport), 另一种就是使用.def文件方式,

  这两种方式的最主要区别是在导出函数的名字上, 其次还有一些操作的灵活性上以及功能的强弱

二 一个具体的例子

1 __declspec(dllexport) 方式
首先对C和C++编译(extern "C")与调用约定(__cdecl、__stdcall、__fastcall)进行组合测试:
<1> C++编译
__declspec(dllexport) int add(int, int);

__declspec(dllexport) int __cdecl add(int, int);

__declspec(dllexport) int __stdcall add(int, int);

__declspec(dllexport) int __fastcall add(int, int);

对于C++编译器的函数名修饰规则:不管__cdecl, __fastcall还是__stdcall调用方式,函数修饰名都是以"?"开始,后面是函数在名字,再后面是函数返回类型和参数类型按照代号拼出的参数表。对于__stdcall方式,参数表的开始标示是"@@YG”,对于__cdecl方式则是"@@YA”,对于__fastcall方式则是"@@YI”.
参数表后以"@Z”标示整个名字的结束,如果该函数无参数,则以"Z”标识结束。

<2> C编译
extern "C" __declspec(dllexport) int add(int, int);

extern "C" __declspec(dllexport) int __cdecl add(int, int);

extern "C" __declspec(dllexport) int __stdcall add(int, int);

extern "C" __declspec(dllexport) int __fastcall add(int, int);

<3> 备注
__declspec(dllexport)的位置:
To export functions, the __declspec(dllexport) keyword must appear to the left of the calling-convention keyword, if a keyword is specified.

For example:
__declspec(dllexport) void __cdecl Function1(void);

To export all of the public data members and member functions in a class, the keyword must appear to the left of the class name as follows:
class __declspec(dllexport) CExampleExport : public CObject

{  class definition  };

2 def文件导出方式(项目中添加一个def后缀的文件, 然后在项目属性->连接器->所有选项->模块定义文件)

具体到测试实例,我们的def文件内容如下:
LIBRARY "win"

EXPORTS
add @1

其中LIBRARY指定dll的模块名称,即dll名字,EXPORTS后的每一行指定一个导出函数名字,这个名字和头文件中的声明一致,后面可以跟@序号指定该函数的序号(这个是可选的,后面按序号导入函数的时候再详细说)。

然后再测试一下__stdcall和__fastcall是否会对导出函数改名,测试结果如下,均未改名:

extern "C" int __stdcall add();

extern "C" int __fastcall add();

另外一种方案是在代码中给链接器指定导出函数名字:
extern "C" __declspec(dllexport) int __fastcall add(int a, int b);
#pragma comment(linker, "/export:add=@add@8")
这里告诉链接器,导出一个函数名为add的函数,函数入口点和@add@8相同

这样,我们既可以使用add,也可以使用@add@8了。
__stdcall方式和这类似,为add=_add@8。

【按序号而不是按名称从dll导出函数】
http://msdn.microsoft.com/zh-cn/library/e7tsx612%28VS.80%29.aspx
def文件定义如下:
LIBRARY "win"

EXPORTS
add @1 NONAME

函数名称和Hint都不见了。
这样也可以用来隐藏dll中一些重要函数。

隐藏了函数名称,在应用程序中使用序号来导入函数:
#include <windows.h>
#include <stdio.h>

int main()
{
  typedef int (* AddFunc)(int, int);
  HMODULE hModule = LoadLibrary("dll.dll");
  AddFunc add = (AddFunc)GetProcAddress(hModule, MAKEINTRESOURCE(1)); //注意这里序号的指定方式
  printf("%d\n", add(1, 2));
  return 0;
}

三 总结:

1 __declspec(dllexport)

在 32 位编译器版本中,可以使用 __declspec(dllexport) 关键字从 DLL 导出数据、函数、类或类成员函数。__declspec(dllexport) 在link时会将导出指令添加到obj文件中,因此不需要使用 .def 文件。当然,即使用了__declspec(dllexport)依然可以使用*.def文件,因为不同编译器对于类的成员函数的name mangling规则不同,可以定义.def文件通过序号调用。为每个dll写def显得很繁杂,目前def使用已经比较少了,更多的是使用__declspec(dllexport)在源代码中定义dll的输出函数。

若要输出类的所有成员:数据or函数,__declspec(dllexport)要放在类名左边声明:
class __declspec(dllexport) Class1{}
如果类没有数据成员,__declspec(dllexport)放在class关键字前声明就会被编译器忽略,就没有lib生成,如下:
__declspec(dllexport) class Class1{}

使用 __declspec(dllexport) 的优缺点(zz)
使用 __declspec(dllexport) 非常方便,因为不必考虑维护 .def 文件和获取导出函数的修饰名。例如,如果您设计的 DLL 供自己控制的应用程序使用,则此方法很适用。如果通过新的导出函数重新生成 DLL,还必须重新生成应用程序,因为如果使用不同版本的编译器进行重新编译,则导出的 C++ 函数的修饰名可能会发生变化。
 
2 def文件
其实def文件的功能相当于extern “C” __declspec(dllexport)
def文件中PRIVTATE的作用

The optional keyword PRIVATE prevents entryname from being placed in the import library generated by LINK. It has no
effect on the export in the image also generated by LINK.用了PRIVATE,生成的lib里没有对应方法或者数据的entryname因此不能被客户隐式调用。

使用 .DEF 文件的优缺点(zz)
在 .def 文件中导出函数使您得以控制导出序号。当将附加的导出函数添加到 DLL 时,可以给它们分配更高的序号值(高于任何其他导出函数)。当您进行此操作时,使用隐式链接的应用程序不必与包含新函数的新导入库重新链接。这非常重要,例如,在设计将由许多应用程序使用的第三方DLL 时。可以通过添加附加功能不断地增强 DLL,同时确保现有应用程序继续正常使用新的 DLL。MFC DLL 是使用 .def 文件生成的。

使用 .def 文件的另一个优点是:可以使用 NONAME 属性导出函数,该属性仅将序号放到 DLL 的导出表中。对具有大量导出函数的 DLL,使用NONAME 属性可以减小 DLL 文件的大小。有关编写模块定义语句的信息,请参见模块定义语句的规则。有关序号导出的更多信息,请参见按序号而不是按名称从 DLL 导出函数。
使用 .def 文件的主要缺点是:在 C++ 文件中导出函数时,必须将修饰名放到 .def 文件中,或者通过使用外部“C”用标准 C 链接定义导出函数,以避免编译器进行名称修饰。如果需要将修饰名放到 .def 文件中,则可以通过使用 DUMPBIN 工具或 /MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特定的。如果将 Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到 DLL 的应用程序必须也是用相同版本的 Visual C++ 生成的,这样调用应用程序中的修饰名才能与
DLL 的 .def 文件中的导出名相匹配。 

dll导出函数的两种方式的比较的更多相关文章

  1. (转)DLL中导出函数的两种方式(dllexport与.def文件)

    DLL中导出函数的两种方式(dllexport与.def文件)http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792 ...

  2. 【转】DLL中导出函数的两种方式(dllexport与.def文件)

    DLL中导出函数的两种方式(dllexport与.def文件) DLL中导出函数的声明有两种方式: 一种方式是:在函数声明中加上__declspec(dllexport):另外一种方式是:采用模块定义 ...

  3. DLL中导出函数的两种方式(dllexport与.def文件)

    DLL中导出函数的声明有两种方式: 一种方式是:在函数声明中加上__declspec(dllexport): 另外一种方式是:采用模块定义(.def)文件声明,(.def)文件为链接器提供了有关被链接 ...

  4. DLL声明导出函数的两种方式

    DLL中导出函数的声明有两种方式:一种为在函数声明中加上__declspec(dllexport):另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出.属 ...

  5. C++ DLL导出函数的两种方法(导出序号那种方法,别人看不到函数名)

    第一种就直接导出函数名如下代码: #ifdef__cplusplus #define TEXPORT extern "c" _declspec(dllexport) #dlse # ...

  6. C#调用C++ DLL动态库的两种方式

    第一种方式:C++导出函数, c#dllimport 的方式 在很多地方都看到过,如[dllimport "user32.dll"]这种代码,调用windows API,就是通过这 ...

  7. vue项目中导出PDF的两种方式

    参考大家导出的方式,基本上是如下两种: 1.使用 html2Canvas + jsPDF 导出PDF, 这种方式什么都好,就是下载的pdf太模糊了.对要求好的pdf这种方式真是不行啊! 2.调用浏览器 ...

  8. JS定义函数的两种方式:函数声明和函数表达式

    函数声明 关于函数声明的方式,它的一个重要的特性就是函数声明提升(function declaration hoisting),意思是在执行代码之前会先读取函数声明.这就意味着可以把函数声明放在调用它 ...

  9. JS调用函数的两种方式

    <script type="text/javascript"> window.onload = init; //onload 表示页面全部加载完毕后,再调用init() ...

随机推荐

  1. iOS代码块block使用

    代码块的本质是和其他的变量类似,不同的是,代码块存储的数据是一个函数体.使用代码块,你可以像调用其他标准函数一样的调用,可以传入参数,并得到返回值.     脱字符是代码块的语法标记.下图表示代码块的 ...

  2. 转 Postman访问Webapi的Get/Post/Put/Delte请求

    Postman访问Webapi的Get/Post/Put/Delte请求 2018年07月26日 15:04:46 DoNotWorkOvertime 阅读数:348 标签: WebApiPostma ...

  3. RxJS核心概念之Subjet在angular2+上的应用

    Subject,在RxJS中是一类特殊的Observable(可观察对象),它可像多个Observer(观察者)推送值.每一个Subject也可以作为Observer(观察者) Subject同样也是 ...

  4. python excel 读写

    python操作Excel读写--使用xlrd xlwt python中使用xlrd.xlwt操作excel表格详解

  5. xterm配置

    最近转到i3wm桌面下, 发现调用xfce4-terminal有些慢,索性卸载掉一切所谓高级的终端,使用xterm,其实这个才是更牛的家伙. 安装 apt install xterm in i3-wm ...

  6. jquery的相关用法

    选择器基本选择器1.id选择器$('#id1')找到id为id1 的标签2.class选择器$('.class1')找到class中有class1这个类的标签3.标签选择器$('tag') 找到tag ...

  7. 聚类--K均值算法:自主实现与sklearn.cluster.KMeans调用

    1.用python实现K均值算法 import numpy as np x = np.random.randint(1,100,20)#产生的20个一到一百的随机整数 y = np.zeros(20) ...

  8. javascript 4.2

    element.value="......"也可以为属性设置新的值 setAttribute()方法是“第一级DOM”的组成部分之一,DOM是适用于多种环境和多种程序设计语言的通用 ...

  9. js处理数字加后缀w

    num > 9999 ? (Math.floor(num/1000)/10) + 'w' : num

  10. java删除文件支持通配符

    原文:https://blog.csdn.net/wdzayyt/article/details/7209530 感谢原作者 package com.covics.zfh; import java.i ...