dll动态链接库文件编写

dll动态链接库文件编写

转载 2017年12月02日 20:50:52

1.动态链接库(dll)概述

没接触dll之前觉得它很神秘,就像是一个黑盒子,既不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其他dll调用来完成某项工作的函数,只有在其他模块调用dll中的函数时,dll才发挥作用。 
在实际编程中,我们可以把完成某项功能的函数放在一个动态链接库里,然后提供给其他程序调用。像Windows API中所有的函数都包含在dll中,如Kernel32.dll, User32.dll, GDI32.dll等。那么dll究竟有什么好处呢?

1.1 静态库和动态库

  • 静态库:函数和数据被编译进一个二进制文件(扩展名通常为.lib),在使用静态库的情况下,在编译链接可执行文件时,链接器从静态库中复制这些函数和数据,并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe)。当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。
  • 动态库:在使用动态库时,往往提供两个文件:一个引入库(.lib,非必须)和一个.dll文件。这里的引入库和静态库文件虽然扩展名都是.lib,但是有着本质上的区别,对于一个动态链接库来说,其引入库文件包含该动态库导出的函数和变量的符号名,而.dll文件包含该动态库实际的函数和数据。

1.2 使用动态链接库的好处

  1. 可以使用多种编程语言编写:比如我们可以用VC++编写dll,然后在VB编写的程序中调用它。
  2. 增强产品功能:可以通过开发新的dll取代产品原有的dll,达到增强产品性能的目的。比如我们看到很多产品踢动了界面插件功能,允许用户动态地更换程序的界面,这就可以通过更换界面dll来实现。
  3. 提供二次开发的平台:用户可以单独利用dll调用其中实现的功能,来完成其他应用,实现二次开发。
  4. 节省内存:如果多个应用程序使用同一个dll,该dll的页面只需要存入内存一次,所有的应用程序都可以共享它的页面,从而节省内存。

2. dll的创建

dll的创建主要有两种方法:一是使用 __declspec(dllexport) 创建dll,二是使用模块定义(.def)文件创建dll。

2.1 使用 __declspec(dllexport) 创建dll

首先在VS中的Visual C++中创建一个Win32 Project,取名为Dll1。在Application Type中选择DLL,在Additional options中选择Empty project,即创建一个空的动态链接库工程。 
这里写图片描述 
然后为工程添加一个C++源文件:Dll1.cpp,假设我要实现的是加法和减法运算,则代码如下:

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

__declspec(dllexport) int subtract(int a, int b){
    return a - b;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

为了让dll导出函数,需要在每一个需要被导出的函数前面加上标识符:__declspec(dllexport)。 
利用Build命令生成Dll1动态链接库,这时在Dll1/Debug目录下就会生成.dll文件和.lib文件,这两个文件即为所需的动态链接库的文件。 
这里写图片描述 
既然已经有了这个dll文件,是不是就可以在其他程序中访问该dll中的add和subtract函数了呢?必须注意的一点是:应用程序如果想要访问某个dll中的函数,那么这个函数必须是已经被导出的函数。 
为了查看一个dll中有哪些导出函数,Visual Studio提供了一个命令行工具:Dumpbin。

2.2 使用Dumpbin命令确认dll的导出函数

首先在命令行中进入到VS的安装目录下,运行一个名为VCVARS32.bat的批处理程序(对于VS2013来说,该bat文件位于\VC\bin目录下),该文件的作用是用来创建VC++使用的环境信息。(注意,当在命令行界面执行VCVARS32.bat文件后,该文件设置的环境信息只在当前命令行窗口生效。) 
然后输入dumpbin命令,即可列出该命令的使用方法: 
这里写图片描述 
那么想要查看一个dll提供的导出函数,在Dll1.dll文件所在目录下,在命令行中输入下述命令:

dumpbin -exports Dll1.dll

这里写图片描述

在上图中可以看到我们导出了两个函数,但是导出函数的名称长得很奇怪,add导出函数的名称是“?add@@YAHHH@Z”,subtract导出函数的名称是“?subtrct@@YAHHH@Z”。这是因为在编译链接时,C++会按照自己的规则篡改函数的名称,这一过程称为“名字改编”。这会导致不同的编译器、不同的语言下调用dll发生问题。因此我们希望动态链接库文件在编译时,导出函数的名称不要发生变化。 
为了实现这一目的,可以再定义导出函数时加上限定符:extern “C”,如

extern "C" __declspec(dllexport) int add(int a, int b){
//...
}
  • 1
  • 2
  • 3

但是这种方式只能解决C++和C语言之间相互调用时函数命名的问题。为了彻底解决这个问题,可以通过模块定义(.def)文件实现。

2.3 使用模块定义(.def)文件创建dll

使用def文件创建dll的话就不再需要__declspec(dllexport),因此将代码写成最原始的样子:

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

int subtract(int a, int b){
    return a - b;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

同时为工程创建一个后缀名为.def的文件,并添加进工程,编辑其内容为:

LIBRARY Dll1

EXPORTS
add
subtract
  • 1
  • 2
  • 3
  • 4
  • 5

其中LIBRARY语句用于指定动态链接库的名称,该名称与生成的动态链接库名称一定要匹配。EXPORTS语句用于表明dll将要导出的函数,以及为这些导出函数指定的符号名。 
将该模块定义文件链接到工程中,方法为工程属性页面>链接器>输入>模块定义文件中写入“Dll1.def”。 
这里写图片描述 
然后重新Build Solution,并用dumpbin工具查看现在dll导出的函数,可以发现函数的名字改编问题得到了解决! 
这里写图片描述


以上就是创建dll的两种方法,个人比较提倡使用模块定义(.def)文件创建dll,代码简洁的同时还没有名字改编的问题。 
接下来我们来看看如何使用创建好的dll。

3. dll的使用

dll的使用也有两种方法,一是隐式链接的方式加载dll,二是显示加载方式加载dll。

3.1 隐式链接方式加载dll

为了更好地展示dll的使用,我首先创建了一个基于MFC的对话框程序,然后为其添加两个按钮。 
这里写图片描述 
将生成好的Dll1.dll和Dll1.lib复制到对话框程序所在的文件夹,然后在CXXXDlg.h中注册动态链接库的引入库文件。因为.lib文件包含了Dll1.dll中导出函数的符号名,相当于告诉对话框程序相关函数应该去dll中调用。

#pragma comment(lib,"Dll1.lib")
  • 1

然后在CXXXDlg.cpp中声明外部函数:

_declspec(dllimport) int add(int a, int b);
_declspec(dllimport) int subtract(int a, int b);
  • 1
  • 2

这样我们就可以使用这两个函数了。为两个按钮添加事件响应程序,并添加如下代码:

void CXXXDlg::OnBtnAdd()
{
    // TODO: Add your control notification handler code here
    CString str;
    str.Format(_T("5 + 3 = %d"), add(5, 3));
    MessageBox(str);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

运行程序发现可以通过dll的导出函数来实现加法功能了。说明dll可以使用。 
这里写图片描述

3.2 显示加载方式加载dll

另一种是通过LoadLiabrary函数显示加载dll。代码如下。需要注意的是这时候我们不再需要注册.lib文件,也不需要声明外部函数。只要在需要使用的地方调用dll文件即可。

void CXXXDlg::OnBtnSubtract()
{
    // TODO: Add your control notification handler code here
    HINSTANCE hInst;
    hInst = LoadLibrary(L"Dll1.dll");
    typedef int(*SUBPROC)(int a, int b);
    SUBPROC Sub = (SUBPROC)GetProcAddress(hInst, "subtract");
    CString str;
    str.Format(_T("5-3=%d"), Sub(5, 3));
    FreeLibrary(hInst);       //LoadLibrary后要记得FreeLibrary
    MessageBox(str);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这里写图片描述

3.3 两种加载方式对比

通过以上的例子,可以看到隐式链接和动态加载两种加载dll的方式各有优点。

  • 隐式链接方式实现简单,一开始就把dll加载进来,在需要调用的时候直接调用即可。但是如果程序要访问十多个dll,如果都采用隐式链接方式加载他们的话,在该程序启动时,这些dll都需要被加载到内存中,并映射到调用进程的地址空间,这样将加大程序的启动时间。而且一般来说,在程序运行过程中只是在某个条件满足的情况下才需要访问某个dll中的函数,如在上述例子中,我只有在点击按钮时才需要访问dll,其他情况下并不需要访问。这样如果所有dll都被加载到内存中,资源浪费是比较严重的。
  • 显示加载的方法则可以解决上述问题,dll只有在需要用到的时候才会被加载到内存中。另外,其实采用隐式链接方式访问dll时,在程序启动时也是通过调用LoadLibrary函数加载该进程需要的动态链接库的。

本文将创建一个简单的动态链接库,并编写一个控制应用程序使用该动态链接库,该动态链接库为“JAVA调用动态链接库DLL之JNative学习”中使用的DLL,只是项目及文件名称不同。

创建动态链接库项目:
1、打开Microsoft Visual Studio 2010,选择文件->新建->项目。


2、在新建项目窗口中选择其他语言->Visual C++->Win32。


3、选择Win32 项目,设置名称:simpleDLL,设置解决方案名:simpleDLL
4、单击确定,在出现的Win32 应用程序向导的概述对话框中点击下一步。


5、在应用程序设置中,选择应用程序类型下的DLL。


6、勾选附加选项下的空项目。
7、单击完成创建项目。
向动态链接库添加类:
1、添加新类头文件。右键单击simpleDLL项目,添加->新建项,选择头文件(.h),设置名称为simpleDLL,单击添加。



2、添加新类源文件。右键单击simpleDLL项目,添加->新建项,选择C++ 文件(.cpp),设置名称为simpleDLL,单击添加。



3、为新类添加内容。内容如下:

头文件simpleDLL.h:

[cpp]  view plain  copy




  1. //—————— SimpleDLL.h —————-  

  2.   

  3. #pragma once;  

  4.   

  5. //该宏完成在dll项目内部使用__declspec(dllexport)导出  

  6. //在dll项目外部使用时,用__declspec(dllimport)导入  

  7. //宏DLL_IMPLEMENT在SimpleDLL.cpp中定义  

  8. #ifdef DLL_IMPLEMENT  

  9. #define DLL_API __declspec(dllexport)  

  10. #else  

  11. #define DLL_API __declspec(dllimport)  

  12. #endif  

  13. DLL_API int add(int x, int y); //简单方法  

  14. DLL_API const wchar_t getPlayUrl(const wchar_t mgrIp, long mgrPort, long materialId);  

  15. DLL_API const char getUrl(const char mgrIp, long mgrPort, long materialId);  

源文件simpleDLL.cpp:

[cpp]  view plain  copy
  1. //—————— SimpleDLL.cpp —————-  
  2.   
  3. //注意此处的宏定义需要写在#include “SimpleDLL.h”之前  
  4. //以完成在dll项目内部使用__declspec(dllexport)导出  
  5. //在dll项目外部使用时,用__declspec(dllimport)导入  
  6. #define DLL_IMPLEMENT  
  7.   
  8. #include “SimpleDLL.h”  
  9. #include<Windows.h>  
  10. #include <intrin.h>  
  11. #include <stdlib.h>  
  12. #include <string.h>  
  13.   
  14. int DLL_API add(int x, int y)  
  15. {  
  16.     return x+y;  
  17. }  
  18.   
  19. DLL_API const wchar_t* getPlayUrl(const wchar_t* mgrIp, long mgrPort, long materialId)  
  20. {  
  21.     static wchar_t url[260] = { 0 };  
  22.     wcscpy_s(url, L”http://中文”);  
  23.     wcscat_s(url, mgrIp);  
  24.     wcscat_s(url, L”:”);  
  25.     wchar_t szPort[20] = { 0 };  
  26.     _ltow_s(mgrPort, szPort, 10);  
  27.     wcscat_s(url, szPort);  
  28.     return url;  
  29. }  
  30.   
  31. DLL_API const char* getUrl(const char* mgrIp, long mgrPort, long materialId)  
  32. {  
  33.     static char url[260] = { 0 };  
  34.     strcpy_s(url, ”http://中文”);  
  35.     strcat_s(url, mgrIp);  
  36.     strcat_s(url, ”:”);  
  37.     char szPort[20] = { 0 };  
  38.     _ltoa_s(mgrPort, szPort, 10);  
  39.     strcat_s(url, szPort);  
  40.     return url;  
  41. }  

创建引用动态链接库的应用程序:
1、在解决方案上单击鼠标右键->添加->新建项目。


2、在添加新项目中选择其它语言->Visual C++->Win32。


3、选择Win32 控制台应用程序,设置名称:simpleDLLTest。
4、单击确定,在出现的Win32 应用程序向导的概述对话框中点击下一步。


5、在应用程序设置中,选择应用程序类型下的控制台应用程序。


6、单击完成创建项目
在控制台应用程序中使用类库的功能:
1、为SimpleDLLTest.cpp添加内容。如下所示:

[cpp]  view plain  copy
  1. // SimpleDLLTest.cpp : 定义控制台应用程序的入口点。  
  2.   
  3. #include “stdafx.h”  
  4. #include ”../SimpleDLL/SimpleDLL.h” //添加头文件引用  
  5. #pragma comment(lib, ”..\\..\\SimpleDLL\\Release\\SimpleDLL.lib”) //添加lib文件引用   
  6. #include <process.h>  
  7. #include <locale.h>  
  8.   
  9. int _tmain(int argc, _TCHAR* argv[])  
  10. {  
  11.     setlocale(LC_ALL, ”chs”); //配置地域化信息为简体中文,否则打印出来的中文是乱码  
  12.     wprintf(L”getPlayUrl: %s\r\n”, getPlayUrl(L“127.0.0.1”, 10087, 1));  
  13.   
  14.     printf(”getUrl: %s\r\n”, getUrl(“127.0.0.1”, 10087, 1));  
  15.     system(”pause”);  
  16.     return 0;  
  17. }  
2、引用simpleDLL项目。右键单击 SimpleDLLTest 项目,选择项目依赖项。


3、依赖于窗口中勾选SimpleDLL,单击确定。


4、设置SimpleDLLTest项目为活动项目。右键单击SimpleDLLTest项目,选择设为启动项目。


6、生成解决方案。Debug运行结果如下:


注意:现在创建的DLL只能由c++调用,C语言等其它语言是调用不了的!

我们来用工具看一下,在解决方案上单击鼠标右键,在Windows资源管理器中打开文件夹


我编译的是Release版本,所以打开Release文件夹,找到SimpleDLL.dll文件,用Depends工具打开它


目前编译的版本对MSVCR100.DLL还有依赖,这样复制到其它没有安装VS2010的电脑上是用不了的;

在项目上单击鼠标右键,属性:


在左侧的配置属性中选择常规,在右侧找到MFC的使用,选择在静态库中使用MFC


这里是修改MFC的使用,还有另一种方法:vs2010发布时去除msvcp100.dll和msvcr100.dll图解说明

再看函数,带有一些特殊字符,这样c++之外其它语言是调用不了的;我们需要在头文件的函数声明中添加extern “C”标志,代码如下:

[cpp]  view plain  copy
  1. //—————— SimpleDLL.h —————-  
  2.   
  3. #pragma once;  
  4.   
  5. //该宏完成在dll项目内部使用__declspec(dllexport)导出  
  6. //在dll项目外部使用时,用__declspec(dllimport)导入  
  7. //宏DLL_IMPLEMENT在SimpleDLL.cpp中定义  
  8. #ifdef DLL_IMPLEMENT  
  9. #define DLL_API __declspec(dllexport)  
  10. #else  
  11. #define DLL_API __declspec(dllimport)  
  12. #endif  
  13. extern “C” DLL_API int add(int x, int y); //简单方法  
  14. extern “C” DLL_API const wchar_t* getPlayUrl(const wchar_t* mgrIp, long mgrPort, long materialId);  
  15. extern “C” DLL_API const char* getUrl(const char* mgrIp, long mgrPort, long materialId);  
重新编译,再 Depends工具打开它,可以发现依赖项已经没有了,函数名称也正常了:


参考:演练:创建和使用动态链接库 (C++)

解决方案源码下载:http://download.csdn.net/detail/testcs_dn/7411383






$(".MathJax").remove();

1.动态链接库(dll)概述

没接触dll之前觉得它很神秘,就像是一个黑盒子,既不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其他dll调用来完成某项工作的函数,只有在其他模块调用dll中的函数时,dll才发挥作用。 
在实际编程中,我们可以把完成某项功能的函数放在一个动态链接库里,然后提供给其他程序调用。像Windows API中所有的函数都包含在dll中,如Kernel32.dll, User32.dll, GDI32.dll等。那么dll究竟有什么好处呢?

1.1 静态库和动态库

  • 静态库:函数和数据被编译进一个二进制文件(扩展名通常为.lib),在使用静态库的情况下,在编译链接可执行文件时,链接器从静态库中复制这些函数和数据,并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe)。当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。
  • 动态库:在使用动态库时,往往提供两个文件:一个引入库(.lib,非必须)和一个.dll文件。这里的引入库和静态库文件虽然扩展名都是.lib,但是有着本质上的区别,对于一个动态链接库来说,其引入库文件包含该动态库导出的函数和变量的符号名,而.dll文件包含该动态库实际的函数和数据。

1.2 使用动态链接库的好处

  1. 可以使用多种编程语言编写:比如我们可以用VC++编写dll,然后在VB编写的程序中调用它。
  2. 增强产品功能:可以通过开发新的dll取代产品原有的dll,达到增强产品性能的目的。比如我们看到很多产品踢动了界面插件功能,允许用户动态地更换程序的界面,这就可以通过更换界面dll来实现。
  3. 提供二次开发的平台:用户可以单独利用dll调用其中实现的功能,来完成其他应用,实现二次开发。
  4. 节省内存:如果多个应用程序使用同一个dll,该dll的页面只需要存入内存一次,所有的应用程序都可以共享它的页面,从而节省内存。

2. dll的创建

dll的创建主要有两种方法:一是使用 __declspec(dllexport) 创建dll,二是使用模块定义(.def)文件创建dll。

2.1 使用 __declspec(dllexport) 创建dll

首先在VS中的Visual C++中创建一个Win32 Project,取名为Dll1。在Application Type中选择DLL,在Additional options中选择Empty project,即创建一个空的动态链接库工程。 
这里写图片描述 
然后为工程添加一个C++源文件:Dll1.cpp,假设我要实现的是加法和减法运算,则代码如下:

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

__declspec(dllexport) int subtract(int a, int b){
    return a - b;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

为了让dll导出函数,需要在每一个需要被导出的函数前面加上标识符:__declspec(dllexport)。 
利用Build命令生成Dll1动态链接库,这时在Dll1/Debug目录下就会生成.dll文件和.lib文件,这两个文件即为所需的动态链接库的文件。 
这里写图片描述 
既然已经有了这个dll文件,是不是就可以在其他程序中访问该dll中的add和subtract函数了呢?必须注意的一点是:应用程序如果想要访问某个dll中的函数,那么这个函数必须是已经被导出的函数。 
为了查看一个dll中有哪些导出函数,Visual Studio提供了一个命令行工具:Dumpbin。

2.2 使用Dumpbin命令确认dll的导出函数

首先在命令行中进入到VS的安装目录下,运行一个名为VCVARS32.bat的批处理程序(对于VS2013来说,该bat文件位于\VC\bin目录下),该文件的作用是用来创建VC++使用的环境信息。(注意,当在命令行界面执行VCVARS32.bat文件后,该文件设置的环境信息只在当前命令行窗口生效。) 
然后输入dumpbin命令,即可列出该命令的使用方法: 
这里写图片描述 
那么想要查看一个dll提供的导出函数,在Dll1.dll文件所在目录下,在命令行中输入下述命令:

dumpbin -exports Dll1.dll

这里写图片描述

在上图中可以看到我们导出了两个函数,但是导出函数的名称长得很奇怪,add导出函数的名称是“?add@@YAHHH@Z”,subtract导出函数的名称是“?subtrct@@YAHHH@Z”。这是因为在编译链接时,C++会按照自己的规则篡改函数的名称,这一过程称为“名字改编”。这会导致不同的编译器、不同的语言下调用dll发生问题。因此我们希望动态链接库文件在编译时,导出函数的名称不要发生变化。 
为了实现这一目的,可以再定义导出函数时加上限定符:extern “C”,如

extern "C" __declspec(dllexport) int add(int a, int b){
//...
}
  • 1
  • 2
  • 3

但是这种方式只能解决C++和C语言之间相互调用时函数命名的问题。为了彻底解决这个问题,可以通过模块定义(.def)文件实现。

2.3 使用模块定义(.def)文件创建dll

使用def文件创建dll的话就不再需要__declspec(dllexport),因此将代码写成最原始的样子:

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

int subtract(int a, int b){
    return a - b;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

同时为工程创建一个后缀名为.def的文件,并添加进工程,编辑其内容为:

LIBRARY Dll1

EXPORTS
add
subtract
  • 1
  • 2
  • 3
  • 4
  • 5

其中LIBRARY语句用于指定动态链接库的名称,该名称与生成的动态链接库名称一定要匹配。EXPORTS语句用于表明dll将要导出的函数,以及为这些导出函数指定的符号名。 
将该模块定义文件链接到工程中,方法为工程属性页面>链接器>输入>模块定义文件中写入“Dll1.def”。 
这里写图片描述 
然后重新Build Solution,并用dumpbin工具查看现在dll导出的函数,可以发现函数的名字改编问题得到了解决! 
这里写图片描述


以上就是创建dll的两种方法,个人比较提倡使用模块定义(.def)文件创建dll,代码简洁的同时还没有名字改编的问题。 
接下来我们来看看如何使用创建好的dll。

3. dll的使用

dll的使用也有两种方法,一是隐式链接的方式加载dll,二是显示加载方式加载dll。

3.1 隐式链接方式加载dll

为了更好地展示dll的使用,我首先创建了一个基于MFC的对话框程序,然后为其添加两个按钮。 
这里写图片描述 
将生成好的Dll1.dll和Dll1.lib复制到对话框程序所在的文件夹,然后在CXXXDlg.h中注册动态链接库的引入库文件。因为.lib文件包含了Dll1.dll中导出函数的符号名,相当于告诉对话框程序相关函数应该去dll中调用。

#pragma comment(lib,"Dll1.lib")
  • 1

然后在CXXXDlg.cpp中声明外部函数:

_declspec(dllimport) int add(int a, int b);
_declspec(dllimport) int subtract(int a, int b);
  • 1
  • 2

这样我们就可以使用这两个函数了。为两个按钮添加事件响应程序,并添加如下代码:

void CXXXDlg::OnBtnAdd()
{
    // TODO: Add your control notification handler code here
    CString str;
    str.Format(_T("5 + 3 = %d"), add(5, 3));
    MessageBox(str);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

运行程序发现可以通过dll的导出函数来实现加法功能了。说明dll可以使用。 
这里写图片描述

3.2 显示加载方式加载dll

另一种是通过LoadLiabrary函数显示加载dll。代码如下。需要注意的是这时候我们不再需要注册.lib文件,也不需要声明外部函数。只要在需要使用的地方调用dll文件即可。

void CXXXDlg::OnBtnSubtract()
{
    // TODO: Add your control notification handler code here
    HINSTANCE hInst;
    hInst = LoadLibrary(L"Dll1.dll");
    typedef int(*SUBPROC)(int a, int b);
    SUBPROC Sub = (SUBPROC)GetProcAddress(hInst, "subtract");
    CString str;
    str.Format(_T("5-3=%d"), Sub(5, 3));
    FreeLibrary(hInst);       //LoadLibrary后要记得FreeLibrary
    MessageBox(str);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这里写图片描述

3.3 两种加载方式对比

通过以上的例子,可以看到隐式链接和动态加载两种加载dll的方式各有优点。

  • 隐式链接方式实现简单,一开始就把dll加载进来,在需要调用的时候直接调用即可。但是如果程序要访问十多个dll,如果都采用隐式链接方式加载他们的话,在该程序启动时,这些dll都需要被加载到内存中,并映射到调用进程的地址空间,这样将加大程序的启动时间。而且一般来说,在程序运行过程中只是在某个条件满足的情况下才需要访问某个dll中的函数,如在上述例子中,我只有在点击按钮时才需要访问dll,其他情况下并不需要访问。这样如果所有dll都被加载到内存中,资源浪费是比较严重的。
  • 显示加载的方法则可以解决上述问题,dll只有在需要用到的时候才会被加载到内存中。另外,其实采用隐式链接方式访问dll时,在程序启动时也是通过调用LoadLibrary函数加载该进程需要的动态链接库的。

本文将创建一个简单的动态链接库,并编写一个控制应用程序使用该动态链接库,该动态链接库为“JAVA调用动态链接库DLL之JNative学习”中使用的DLL,只是项目及文件名称不同。

创建动态链接库项目:
1、打开Microsoft Visual Studio 2010,选择文件->新建->项目。


2、在新建项目窗口中选择其他语言->Visual C++->Win32。


3、选择Win32 项目,设置名称:simpleDLL,设置解决方案名:simpleDLL
4、单击确定,在出现的Win32 应用程序向导的概述对话框中点击下一步。


5、在应用程序设置中,选择应用程序类型下的DLL。


6、勾选附加选项下的空项目。
7、单击完成创建项目。
向动态链接库添加类:
1、添加新类头文件。右键单击simpleDLL项目,添加->新建项,选择头文件(.h),设置名称为simpleDLL,单击添加。



2、添加新类源文件。右键单击simpleDLL项目,添加->新建项,选择C++ 文件(.cpp),设置名称为simpleDLL,单击添加。



3、为新类添加内容。内容如下:

头文件simpleDLL.h:

[cpp]  view plain  copy




  1. //—————— SimpleDLL.h —————-  

  2.   

  3. #pragma once;  

  4.   

  5. //该宏完成在dll项目内部使用__declspec(dllexport)导出  

  6. //在dll项目外部使用时,用__declspec(dllimport)导入  

  7. //宏DLL_IMPLEMENT在SimpleDLL.cpp中定义  

  8. #ifdef DLL_IMPLEMENT  

  9. #define DLL_API __declspec(dllexport)  

  10. #else  

  11. #define DLL_API __declspec(dllimport)  

  12. #endif  

  13. DLL_API int add(int x, int y); //简单方法  

  14. DLL_API const wchar_t getPlayUrl(const wchar_t mgrIp, long mgrPort, long materialId);  

  15. DLL_API const char getUrl(const char mgrIp, long mgrPort, long materialId);  

源文件simpleDLL.cpp:

[cpp]  view plain  copy
  1. //—————— SimpleDLL.cpp —————-  
  2.   
  3. //注意此处的宏定义需要写在#include “SimpleDLL.h”之前  
  4. //以完成在dll项目内部使用__declspec(dllexport)导出  
  5. //在dll项目外部使用时,用__declspec(dllimport)导入  
  6. #define DLL_IMPLEMENT  
  7.   
  8. #include “SimpleDLL.h”  
  9. #include<Windows.h>  
  10. #include <intrin.h>  
  11. #include <stdlib.h>  
  12. #include <string.h>  
  13.   
  14. int DLL_API add(int x, int y)  
  15. {  
  16.     return x+y;  
  17. }  
  18.   
  19. DLL_API const wchar_t* getPlayUrl(const wchar_t* mgrIp, long mgrPort, long materialId)  
  20. {  
  21.     static wchar_t url[260] = { 0 };  
  22.     wcscpy_s(url, L”http://中文”);  
  23.     wcscat_s(url, mgrIp);  
  24.     wcscat_s(url, L”:”);  
  25.     wchar_t szPort[20] = { 0 };  
  26.     _ltow_s(mgrPort, szPort, 10);  
  27.     wcscat_s(url, szPort);  
  28.     return url;  
  29. }  
  30.   
  31. DLL_API const char* getUrl(const char* mgrIp, long mgrPort, long materialId)  
  32. {  
  33.     static char url[260] = { 0 };  
  34.     strcpy_s(url, ”http://中文”);  
  35.     strcat_s(url, mgrIp);  
  36.     strcat_s(url, ”:”);  
  37.     char szPort[20] = { 0 };  
  38.     _ltoa_s(mgrPort, szPort, 10);  
  39.     strcat_s(url, szPort);  
  40.     return url;  
  41. }  

创建引用动态链接库的应用程序:
1、在解决方案上单击鼠标右键->添加->新建项目。


2、在添加新项目中选择其它语言->Visual C++->Win32。


3、选择Win32 控制台应用程序,设置名称:simpleDLLTest。
4、单击确定,在出现的Win32 应用程序向导的概述对话框中点击下一步。


5、在应用程序设置中,选择应用程序类型下的控制台应用程序。


6、单击完成创建项目
在控制台应用程序中使用类库的功能:
1、为SimpleDLLTest.cpp添加内容。如下所示:

[cpp]  view plain  copy
  1. // SimpleDLLTest.cpp : 定义控制台应用程序的入口点。  
  2.   
  3. #include “stdafx.h”  
  4. #include ”../SimpleDLL/SimpleDLL.h” //添加头文件引用  
  5. #pragma comment(lib, ”..\\..\\SimpleDLL\\Release\\SimpleDLL.lib”) //添加lib文件引用   
  6. #include <process.h>  
  7. #include <locale.h>  
  8.   
  9. int _tmain(int argc, _TCHAR* argv[])  
  10. {  
  11.     setlocale(LC_ALL, ”chs”); //配置地域化信息为简体中文,否则打印出来的中文是乱码  
  12.     wprintf(L”getPlayUrl: %s\r\n”, getPlayUrl(L“127.0.0.1”, 10087, 1));  
  13.   
  14.     printf(”getUrl: %s\r\n”, getUrl(“127.0.0.1”, 10087, 1));  
  15.     system(”pause”);  
  16.     return 0;  
  17. }  
2、引用simpleDLL项目。右键单击 SimpleDLLTest 项目,选择项目依赖项。


3、依赖于窗口中勾选SimpleDLL,单击确定。


4、设置SimpleDLLTest项目为活动项目。右键单击SimpleDLLTest项目,选择设为启动项目。


6、生成解决方案。Debug运行结果如下:


注意:现在创建的DLL只能由c++调用,C语言等其它语言是调用不了的!

我们来用工具看一下,在解决方案上单击鼠标右键,在Windows资源管理器中打开文件夹


我编译的是Release版本,所以打开Release文件夹,找到SimpleDLL.dll文件,用Depends工具打开它


目前编译的版本对MSVCR100.DLL还有依赖,这样复制到其它没有安装VS2010的电脑上是用不了的;

在项目上单击鼠标右键,属性:


在左侧的配置属性中选择常规,在右侧找到MFC的使用,选择在静态库中使用MFC


这里是修改MFC的使用,还有另一种方法:vs2010发布时去除msvcp100.dll和msvcr100.dll图解说明

再看函数,带有一些特殊字符,这样c++之外其它语言是调用不了的;我们需要在头文件的函数声明中添加extern “C”标志,代码如下:

[cpp]  view plain  copy
  1. //—————— SimpleDLL.h —————-  
  2.   
  3. #pragma once;  
  4.   
  5. //该宏完成在dll项目内部使用__declspec(dllexport)导出  
  6. //在dll项目外部使用时,用__declspec(dllimport)导入  
  7. //宏DLL_IMPLEMENT在SimpleDLL.cpp中定义  
  8. #ifdef DLL_IMPLEMENT  
  9. #define DLL_API __declspec(dllexport)  
  10. #else  
  11. #define DLL_API __declspec(dllimport)  
  12. #endif  
  13. extern “C” DLL_API int add(int x, int y); //简单方法  
  14. extern “C” DLL_API const wchar_t* getPlayUrl(const wchar_t* mgrIp, long mgrPort, long materialId);  
  15. extern “C” DLL_API const char* getUrl(const char* mgrIp, long mgrPort, long materialId);  
重新编译,再 Depends工具打开它,可以发现依赖项已经没有了,函数名称也正常了:


参考:演练:创建和使用动态链接库 (C++)

解决方案源码下载:http://download.csdn.net/detail/testcs_dn/7411383




猜你喜欢

转载自blog.csdn.net/dong1528313271/article/details/79917923