转自:http://www.cnblogs.com/houkai/archive/2013/06/05/3119513.html


目录

1.dll的优点

代码复用是提高软件开发效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架,ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。“白盒复用”的缺点比较多,总结起来有4点。 暴露了源代码;多份拷贝,造成存储浪费; 容易与程序员的“普通”代码发生命名冲突; 更新功能模块比较困难,不利于问题的模块化实现; 实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。 说明:实现“黑盒复用”的途径不只dll一种,静态链接库甚至更高级的COM组件都是。

2.dll的创建

参考程序原文:http://msdn.microsoft.com/zh-cn/library/ms235636.aspx 新建“Win32项目”,选择应用程序类型为"DLL”,其他默认。添加头文件testdll.h

  1. //testdll.h
  2. #ifdef TESTDLL_EXPORTS
  3. #define TESTDLL_API __declspec(dllexport)
  4. #else
  5. #define TESTDLL_API __declspec(dllimport)
  6. #endif
  7. namespace MathFuncs
  8. {
  9. // This class is exported from the testdll.dll
  10. class MyMathFuncs
  11. {
  12. public:
  13. // Returns a + b
  14. static TESTDLL_API double Add(double a, double b);
  15.  
  16. // Returns a - b
  17. static TESTDLL_API double Subtract(double a, double b);
  18.  
  19. // Returns a * b
  20. static TESTDLL_API double Multiply(double a, double b);
  21.  
  22. // Returns a / b
  23. // Throws const std::invalid_argument& if b is 0
  24. static TESTDLL_API double Divide(double a, double b);
  25. };
  26. }

当定义了符号TESTDLL_EXPORTS,TESTDLL_API被设置为 __declspec(dllexport) 修饰符,This modifier enables the function to be exported by the DLL so that it can be used by other applications。若未定义则TESTDLL_API被设置为__declspec(dllimport),This modifier enables the compiler to optimize the importing of the function from the DLL for use in other applications。当DLL项目生成时,TESTDLL_EXPORTS默认是定义的,所以默认设置的是__declspec(dllexport) 修饰符。 添加cpp文件

  1. // testdll.cpp : 定义 DLL 应用程序的导出函数。
  2. #include "stdafx.h"
  3. #include "testdll.h"
  4. #include <stdexcept>
  5. using namespace std;
  6.  
  7. namespace MathFuncs
  8. {
  9. double MyMathFuncs::Add(double a, double b)
  10. {
  11. return a + b;
  12. }
  13.  
  14. double MyMathFuncs::Subtract(double a, double b)
  15. {
  16. return a - b;
  17. }
  18.  
  19. double MyMathFuncs::Multiply(double a, double b)
  20. {
  21. return a * b;
  22. }
  23.  
  24. double MyMathFuncs::Divide(double a, double b)
  25. {
  26. if (b == 0)
  27. {
  28. throw invalid_argument("b cannot be zero!");
  29. }
  30. return a / b;
  31. }
  32. }

编译就会生成对应的dll文件,同时也会生成对应的lib文件。 注意:a.DLL中导出函数的声明有两种方式:在函数声明中加上__declspec(dllexport);采用模块定义(.def)文件声明。详见:http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792099.html b.对于C文件创建dll时或者想使用C编译器创建dll时,建议使用 extern “C” 标志,参见extern "C"的简单解析

3.dll的调用

应用程序使用DLL可以采用两种方式:一种是隐式链接(调用),另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。VS在VC\bin目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件中的函数结构。两种的对比详见:http://blog.sina.com.cn/s/blog_53004b4901009h3b.html 隐式链接采用静态加载的方式,比较简单,需要.h、.lib、.dll三件套。新建“控制台应用程序”或“空项目”。配置如下: 项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件testdll.h所在的目录 项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件testdll.lib所在的目录 项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“testdll.lib”(若有多个 lib 则以空格隔开) 添加cpp文件

  1. //mydll.cpp
  2. #include <iostream>
  3. #include "testdll.h"
  4. using namespace std;
  5.  
  6. int main()
  7. {
  8. double a = 7.4;
  9. int b = 99;
  10.  
  11. cout << "a + b = " <<
  12. MathFuncs::MyMathFuncs::Add(a, b) << endl;
  13. cout << "a - b = " <<
  14. MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
  15. cout << "a * b = " <<
  16. MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
  17. cout << "a / b = " <<
  18. MathFuncs::MyMathFuncs::Divide(a, b) << endl;
  19.  
  20. try
  21. {
  22. cout << "a / 0 = " <<
  23. MathFuncs::MyMathFuncs::Divide(a, 0) << endl;
  24. }
  25. catch (const invalid_argument &e)
  26. {
  27. cout << "Caught exception: " << e.what() << endl;
  28. }
  29. return 0;
  30. }

现在可以编译通过了,但是程序运行就报错,还需要将testdll.dll复制到当前项目生成的可执行文件所在的目录。 显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。 新建项目,不需要特殊配置,添加cpp文件

  1. /*
  2. *作者:侯凯
  3. *说明:显式调用DLL
  4. *日期:2013-6-5
  5. */
  6. #include<Windows.h> //加载的头文件
  7. #include<iostream>
  8. using namespace std;
  9.  
  10. int main()
  11. {
  12. typedef double (*pAdd)(double a, double b);
  13. typedef double (*pSubtract)(double a, double b);
  14.  
  15. HMODULE hDLL = LoadLibrary("testdll.dll"); //加载dll文件
  16. if(hDLL != NULL)
  17. {
  18. pAdd fp1 = pAdd(GetProcAddress(hDLL, MAKEINTRESOURCE(1))); //得到dll中的第一个函数
  19. if(fp1 != NULL)
  20. {
  21. cout<<fp1(2.5, 5.5)<<endl;
  22. }
  23. else
  24. {
  25. cout<<"Cannot Find Function "<<"add"<<endl;
  26. }
  27. pSubtract fp2 = pSubtract(GetProcAddress(hDLL, "?Subtract@MyMathFuncs@MathFuncs@@SANNN@Z")); //得到dll中标示为"?..."的函数,C++编译器考虑了函数的参数
  28. if(fp2 != NULL)
  29. {
  30. cout<<fp2(5.5, 2.5)<<endl;
  31. }
  32. else
  33. {
  34. cout<<"Cannot Find Function "<<"Subtract"<<endl;
  35. }
  36. FreeLibrary(hDLL);
  37. }
  38. else
  39. {
  40. std::cout<<"Cannot Find "<<"testdll"<<std::endl;
  41. }
  42. return 1;
  43. }

显式调用的问题:在DLL文件中,dll工程中函数名称在编译生成DLL的过程中发生了变化(C++编译器),在DLL文件中称变化后的字符为“name标示”。GetProcAddress中第二个参数可以由DLL文件中函数的顺序获得,或者直接使用DLL文件中的”name标示”,这个标示可以通过Dumpbin.exe小程序查看。如果C++编译器下,想让函数名更规范(和原来工程中一样),具体方法详见:http://blog.csdn.net/btwsmile/article/details/6676802。 当然,为了让函数名更规范,最常用的方式是:创建dll过程中使用C编译器来编译函数,这样DLL文件中的函数名和原dll工程中的函数名就一致了。

4.更一般的显式调用

为了解决上部分最后的问题,可以使用 extern “C” 为dll工程中的函数建立C连接,简单的示例工程如下。 在DLL创建的工程中,添加cpp文件

  1. /*
  2. *作者:侯凯
  3. *说明:创建dll,使用C接口——C编译器生成的dll中函数的"name标示"仍为addfun
  4. *日期:2013-6-5
  5. */
  6. // cdll.cpp : 定义 DLL 应用程序的导出函数。
  7. //
  8. #include "stdafx.h"
  9.  
  10. #ifdef __cplusplus // if used by C++ code
  11. extern "C" { // we need to export the C interface
  12. #endif
  13.  
  14. __declspec(dllexport) int addfun(int a, int b)
  15. {
  16. return a+b;
  17. }
  18.  
  19. #ifdef __cplusplus
  20. }
  21. #endif

编译即可生成DLL文件。在dll调用工程中,添加cpp文件

  1. /*
  2. *作者:侯凯
  3. *说明:显式调用dll
  4. *日期:2013-6-5
  5. */
  6. #include <windows.h>
  7. #include <iostream>
  8. using namespace std;
  9.  
  10. void main()
  11. {
  12. typedef int(*FUNA)(int,int);
  13. HMODULE hMod = LoadLibrary("cdll.dll");//dll路径
  14. if (hMod)
  15. {
  16. FUNA addfun = (FUNA)GetProcAddress(hMod, TEXT("addfun"));//直接使用原工程函数名
  17. if (addfun != NULL)
  18. {
  19. cout<<addfun(5, 4)<<endl;
  20. }
  21. else
  22. {
  23. cout<<"ERROR on GetProcAddress"<<endl;
  24. }
  25. FreeLibrary(hMod);
  26. }
  27. else
  28. cout<<"ERROR on LoadLibrary"<<endl;
  29. }

运行,这样便可以调用dll的函数了。再进一步,上述dll文件如果通过隐式调用,利用.dll、.lib文件,调用函数应为

  1. //隐式链接
  2. #include <iostream>
  3. #pragma comment(lib,"cdll.lib")
  4. using namespace std;
  5.  
  6. extern "C" _declspec(dllimport) int addfun(int a,int b);
  7. //载入addfun函数,这里起到了.h文件的作用
  8. //dll中使用C编译器 故这里需要extern "C" 如果dll中无extern "C"
  9. //此处为:_declspec(dllimport) int addfun(int a,int b);
  10. void main()
  11. {
  12. cout<<addfun(5,4)<<endl;
  13. }

C++在VS下创建、调用dll的更多相关文章

  1. 在vs2013下手把手创建/调用dll

    body { font: 16px } 参考了大佬的文章 首先,体会一下静态编译: 创建Win32Project,选DLL,添加一个.h和.cpp文件 点击生成解决方案,然后去debug目录下拷贝.l ...

  2. delphi 基础之三 编写和调用dll文件

    delphi 编写和调用dll文件   Windows 的执行文件可以划分为两种形式程序和动态连接库 (DLLs).一般程序运行是用.EXE文件,但应用程序有时也可以调用存储在DLL的函数. 在如下几 ...

  3. vs2010下C++调用lib或dll文件

    注: DLL:表示链接库,包含dll,lib文件: dll: 表示my.dll文件 lib: 表示my.lib文件 C++ 调用.lib的方法: 一: 隐式的加载时链接,有三种方法 1  设置工程的 ...

  4. VC++创建、调用dll的方法步骤

    文章来源:http://www.cnblogs.com/houkai/archive/2013/06/05/3119513.html 代码复用是提高软件开发效率的重要途径.一般而言,只要某部分代码具有 ...

  5. [原创]使用GCC创建 Windows NT 下的内核DLL

    原文链接:使用GCC创建 Windows NT 下的内核DLL 在温习<<Windows 2000 Driving>>分层驱动程序一章的时候,看到了关于紧耦合驱动连接方式,这种 ...

  6. C++ Builder创建和调用dll中的资源

    程序开发中经常会用到一些图标.图片.光标.声音等,我们称它们为资源(Resource).当多个窗口用到同样的资源时,可以将这些公共的资源放到一个dll文件里调用,这样,由于定位资源比在磁盘中定位文件花 ...

  7. QT创建与调用Dll方法(包括类成员)--显式调用

    看网上的好多关于QT调用Dll的方法,大部分都是调用函数的,并没有调用C++类成员的情况,即使是有,比如说: 使用Qt编写模块化插件式应用程序 Qt 一步一步实现dll调用(附源码)---(这一篇里没 ...

  8. 在VC中创建并调用DLL

    转自:http://express.ruanko.com/ruanko-express_45/technologyexchange6.html 一.DLL简介 1.什么是DLL? 动态链接库英文为DL ...

  9. Windows下C语言调用dll动态链接库

    dll是windows下的动态链接库文件,下面记录一下在windows下如何调用C语言开发的dll动态链接库. 1.dll动态链接库的源代码 hello_dll.c #include "st ...

随机推荐

  1. Qt计算两个时间差

    QTime startTime = QTime::currentTime(); QThread::msleep(SLEEP_TIME_MILL); QTime stopTime = QTime::cu ...

  2. dubbo项目实战代码展示

    最近公司项目使用dubbo服务,于是就去网上搜索关于dubbo的相关资料,真的很多,但是对于很多人并不是很了解框架或者 不是太适合新手的片段代码,于是我就根据项目的相关内容把dubbo部分单独切出来, ...

  3. 解决 Unable to load native-hadoop library for your platform

    安装hadoop启动之后总有警告:Unable to load native-hadoop library for your platform... using builtin-java classe ...

  4. Linux安装好系统后优化

    author:headsen chen date: 2018-05-14 15:35:49 1.快速更改国内yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc ...

  5. 用httpclient做压力测试时Too many open files的解决办法

    在工作过程中,用httpclient去压测一个web api,发现压一小段时间就出现了Too many open files.实际上,HttpClient建立Socket时 ,post.release ...

  6. bootstrap datetimepicker 日期插件超详细使用方法

    日期时间选择器 目前,bootstrap有两种日历.datepicker和datetimepicker,后者是前者的拓展. Bootstrap日期和时间组件: 使用示例: 从左到右依次是十年视图.年视 ...

  7. IT资料常用网址汇总

    菜鸟联盟: http://www.runoob.com/ w3c school :https://www.w3cschool.cn/ http://www.w3school.com.cn/w3c/in ...

  8. 【题解】P5151 HKE与他的小朋友

    [题解]P5151 HKE与他的小朋友 实际上,位置的关系可以看做一组递推式,\(f(a_i)=f(a_j),f(a_j)=f(a_t),etc...\)那么我们可以压进一个矩阵里面. 考虑到这个矩阵 ...

  9. python脚本前两行

    1. 第一行指定解释器路径 推荐写法: #!/usr/bin/env python 详细说明: #!/usr/bin/python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python ...

  10. Unity系统自带函数生命周期以及相互关系

    官方参考手册:http://docs.unity3d.com/Manual/ExecutionOrder.html unity脚本从唤醒到销毁都有着一套比较完善的生命周期,添加任何脚本都要遵守生命周期 ...