ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

利用__declspec(dllexport)和__declspec(dllimport)在Windows平台编写和使用DLL的小例子

2020-02-05 15:41:54  阅读:262  来源: 互联网

标签:__ 文件 lib Windows declspec DLL GenDLL


文章目录

前言

关于 __declspec(dllexport)__declspec(dllimport) 这两个关键字在上大学期间就没见过几次面,直到毕业后在公司项目的代码中又遇到过几次,每次也是绕着走,生怕和它产生什么联系,只知道它和动态链接库 DLL 有关,但是当前这个项目中几乎没有用到自己写的动态链接库,所以我也就心安理得的躲了它这么久。

最近看一些开源项目的源码时又发现了这两个关键字,此时凭借自己掌握的知识和学习方法再来看这两个关键字,发现也没有什么值得害怕的地方,其实简单来说就是 __declspec(dllexport) 是用来说明指定类和函数需要从 DLL 中导出的,而 __declspec(dllimport) 是用来说明指定的类和函数是从DLL中导入的。

说明

  • __declspec(dllexport)__declspec(dllimport) 只在 Windows 平台才有,用来说明类或函数的导出和导入。
  • 在 Linux 平台上源文件中的所有函数都有一个的visibility属性,默认导出。如果要隐藏所有函数导出,则需要在GCC编译指令中加入 -fvisibility=hidden 参数。
  • 生成 dll 的同时还会生成对应的 lib 文件,一般是一些索引信息,记录了 dll 中函数的入口和位置,这在之前还真的不知道,原来一直以为 lib 只是静态库文件呢。

疑问

  1. 为什么要导入导出,直接把代码拿过来一起编译不好吗?

想要一起编译前提是你得有源代码,如果人家就给你一个动态库或者静态库,你想把源代码放到一起编译的愿望根本实现不了。

  1. 为什么要分为静态库和动态库?搞这么麻烦,还要导入导出。

这具体的就要查查他们两者的优缺点了,每种事务的产生必要有其产生的原因,比如静态库,很可能就是一个程序员今天在A工程写了一个读取文件的类,过一段时间又在B工程写了一个读取文件的类,代码都差不多,不久又在C工程中直接把代码复制过来改一改又写了一份,这时想到干脆了写个“静态库”这种东西吧,相同的代码直接封装到库中,哪个工程需要就直接拿过来编译,也不需要再复制代码了。

又比如动态库,前面的静态库解决了代码重复开发和维护的问题,但是读取文件的静态库中的代码在A、B、C三个工程中都存在一份,导致每个可执行程序都很大,可不可以共用一份呢?结果又发明了动态库,在编译时只指定函数的入口地址,运行时才加载动态库,这样就使得可执行程序体积大大缩小。

以上内容纯粹我个人想像的,真正发明静态库和动态库是由于什么原因,大家可以自行去了解…

  1. 动态库要比静态库好吗?

个人感觉合适的才是最好的,不存在动态库要比静态库好的说法,最起码不是全都好,动态库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小,但是运行时要去加载库会花费一定的时间,执行速度相对会慢一些,总的来说静态库是牺牲了空间换时间,而动态库是牺牲了时间换空间。

  1. .h(头文件) .lib(库文件) .dll(动态链接库文件) 之间的联系和区别

.h 文件是编译时需要的, .lib 是链接时需要的, .dll 是运行时需要的。如果有 .dll 文件,那么 .lib 一般是一些索引信息,记录了 .dll 中函数的入口和位置,.dll 中是函数的具体的执行内容。如果只有 .lib 文件,那么这个 .lib 文件是静态编译出来的,索引和实现都在文件中。

DLL的编写与使用

前面说了这么多,其实就是想带大家先了解一下动态链接库 DLL ,接下来开始编写一个DLL并在另一个工程中使用它,前提是你已经会使用开发工具VS,如果不会先查查教程。

测试环境

  • VS2013随意版(个人感觉这个版本启动能快一点)
  • Win10畅想版(我也不知道啥版本)

编写DLL

编写 DLL 的方法不知一种,这里只简单介绍一种,对于直接写 .def 文件的方法这里不会展开,尽量依靠开发工具一步步向下执行就好,其实当你理解了开发工具的是怎样工作的,一切就没有那么神秘了,有些步骤直接修改配置文件也是可以实现的,只不过开发工具给我们提供了界面,操作起来更加方便了而已,下面我们开始编写:

  1. 打开VS新建项目,选择Win32项目,项目名称GenDLL,解决方案名称DLLExample,点击确定:

dll_1

  1. 直接下一步,应用程序类型选择DLL,点击完成:

dll_2

  1. 项目会自动创建一个GenDLL.cpp文件,我们在手动创建一个GenDLL.h文件,两个文件中编写如下代码:
// GenDLL.h

#ifdef GENDLL_EXPORTS
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif

TEST_API int add(int a, int b);
// GenDLL.cpp : 定义 DLL 应用程序的导出函数。

#include "stdafx.h"
#include "GenDLL.h"

TEST_API int add(int a, int b)
{
    return a + b;
}

这段代码中有一个 TEST_API 是我在头文件中自定义的,当存在GENDLL_EXPORTS宏时, TEST_API 代表 __declspec(dllexport) 也就是导出函数,当不存在GENDLL_EXPORTS宏时, TEST_API 代表 __declspec(dllimport) 表示导入函数,而 GENDLL_EXPORTS 这个宏是与项目名相关的,自动生成的宏,在 DLL 项目中存在格式为 “大写项目名_EXPORTS”。

也就是说同一个头文件中计算加法的函数 addGenDLL 这个生成 DLL 的项目中表示导出函数,在其他使用这个 DLL 的项目中表示导入函数。

  1. 编译看输出发现有GenDLL.lib和GenDLL.dll两个文件:
1>------ 已启动生成:  项目: GenDLL, 配置: Debug Win32 ------
1>  stdafx.cpp
1>  dllmain.cpp
1>  GenDLL.cpp
1>     正在创建库 c:\users\administrator\documents\visual studio 2013\Projects\DLLExample\Debug\GenDLL.lib
    和对象 c:\users\administrator\documents\visual studio 2013\Projects\DLLExample\Debug\GenDLL.exp
1>  GenDLL.vcxproj -> c:\users\administrator\documents\visual studio 2013\Projects\DLLExample\Debug\GenDLL.dll
========== 生成:  成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========

使用DLL

  1. 在DLLExample这个解决方案下添加一个新项目,命名为UseDLL,然后点击确定:

dll_3

  1. 直接下一步,应用程序类型选择“控制台应用程序”,点击完成:

dll_4

  1. 在文件UseDLL.cpp文件中引用之前GenDLL项目的头文件,编写使用 add 函数的代码:
// UseDLL.cpp : 定义控制台应用程序的入口点。

#include "stdafx.h"
#include <iostream>
#include "../GenDLL/GenDLL.h"

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << "100+1=" << add(100, 1) << std::endl;
    system("pause");
    return 0;
}
  1. 编译代码发现报错,提示有一个无法解析的外部命令:
1>------ 已启动生成:  项目: UseDLL, 配置: Debug Win32 ------
1>  UseDLL.cpp
1>  stdafx.cpp
1>  正在生成代码...
1>UseDLL.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport)
    int __cdecl add(int,int)" (__imp_?add@@YAHHH@Z),该符号在函数 _wmain 中被引用
1>c:\users\administrator\documents\visual studio 2013\Projects\DLLExample\Debug\UseDLL.exe :
    fatal error LNK1120: 1 个无法解析的外部命令
========== 生成:  成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

提示这个错误本意就是说链接没有找到函数实现,链接需要什么文件,前面提到需要lib文件,那么我们设置一下,让UseDLL工程能够找到GenDLL.lib文件。

  1. 打开UseDLL工程的属性,在“配置属性->链接器->输入->附加依赖项”中添加GenDLL.lib:

dll_5

  1. 然后在“配置属性->链接器->常规->附加库目录”中添加GenDLL.lib所在路径“…/Debug”即可成功编译:

dll_6

  1. 直接运行就可以看到调用DLL的结果,因为这两个工程在同一解决方案下,所以最终UseDLL.exe和GenDLL.dll在同一目录下,这样不会报找不到DLL的错误

dll_7

  1. 如果是不同的目录就会像下图那样,提示找不到GenDLL.dll,只要把GenDLL.dll复制到和UseDLL.exe相同目录即可:

dll_8

加载DLL

上面提到当运行程序找不到 DLL时可以把 DLL 放到可执行程序程序的目录,有时运行大型软件找不到 DLL 时,我们也会下载一个放到System32目录,其实程序在加载 DLL 的时候是会按照一定顺序的,这些目录包括:包含exe文件的目录、进程的当前工作目录、Windows系统目录、Windows目录、Path环境变量中的一系列目录等等,这些目录的搜索顺序还会受到安全 DLL 搜索模式是否启用的影响。

所以说如果不是对DLL 放置的位置有特殊要求,那么直接放在exe文件所在的目录就好了,一般也是会优先搜索的。

总结

  • Windows上才有 __declspec(dllexport)__declspec(dllimport)
  • .h 文件是编译时需要的, .lib 是链接时需要的, .dll 是运行时需要的
  • 程序运行时加载 DLL 一般优先从exe文件的所在目录优先加载
AlbertS 博客专家 发布了148 篇原创文章 · 获赞 254 · 访问量 52万+ 他的留言板 关注

标签:__,文件,lib,Windows,declspec,DLL,GenDLL
来源: https://blog.csdn.net/shihengzhen101/article/details/104183333

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有