创建并使用自己的DLL文件(动态链接库)

本文创建一个实现一些数学函数的DLL。然后,创建一个控制台应用程序,使用DLL中的函数。您还将获得一些在窗口动态链接库中使用的编程技术和约定的介绍。
本文任务:
1.在Visual Studio中创建一个DLL项目。
2. 将导出的函数和变量添加到动态链接库。
3. 在Visual Studio中创建控制台应用程序项目。
4. 在控制台应用程序中使用从DLL导入的函数和变量。
5. 运行完成的应用程序。

像静态链接库一样,DLL按名称导出变量、函数和资源。客户端应用程序导入名称以使用这些变量、函数和资源。
与静态链接库不同,Windows在加载时或运行时将应用程序中的导入 连接到DLL中的导出,而不是在链接时连接它们。Windows需要不属于标准C++编译模型的额外信息来建立这些连接。MSVC编译器实现了一些特定于微软的C++扩展来提供这些额外的信息。我们边走边解释这些扩展。
创建两个Visual Studio解决方案;一个构建DLL,一个构建客户端应用程序。DLL使用C调用约定。只要平台、调用约定和链接约定匹配,就可以从用其他编程语言编写的应用程序中调用它。客户端应用程序使用隐式链接,在加载时,窗口将应用程序链接到动态链接库。这种链接让应用程序调用DLL提供的函数,就像静态链接库中的函数一样。
本演练不涵盖一些常见情况。代码没有显示其他编程语言对c++ dll的使用。它没有展示如何创建一个只包含资源的动态链接库,或者如何使用显式链接在运行时而不是在加载时加载动态链接库。放心,这些都可以用MSVC和Visual Studio来做。

条件:

  1. 运行微软视窗7或更高版本的计算机。为了获得最佳的开发体验,推荐最新版本的Windows。
  2. Visual Studio的副本。有关如何下载和安装Visual Studio的信息,请参见安装Visual Studio。运行安装程序时,请确保检查了使用C++的桌面开发工作负载。如果您在安装Visual Studio时没有安装此工作负载,请不要担心。您可以再次运行安装程序并立即安装。
    在这里插入图片描述
  3. 了解使用Visual Studio集成开发环境的基础知识。如果你以前用过Windows桌面应用,你可能会跟上。有关介绍,请参见Visual Studio IDE功能教程。
  4. 对C++语言基础的理解。当然,我们不会做太复杂的事情。

总的流程概览:
在这里插入图片描述

在Visual Studio 2019中创建DLL项目

  1. 在菜单栏上,选择文件>新建>项目以打开创建新项目对话框。
  2. 在对话框顶部,将语言(language)设置为C++,将平台(Platform)设置为窗口(Windows),并将项目类型(Project type)设置为库(Library)。
  3. 从项目类型的筛选列表中,选择动态链接库,然后选择Next。
  4. 在“Configure your new project ”页面中,在“Project name box”框中输入“MathLibrary ”以指定项目的名称。保留默认的位置和解决方案名称值。设置解决方案以创建新的解决方案。取消选中将解决方案和项目放在同一目录中(如果已选中)。
  5. 创建解决方案时,您可以在Visual Studio的“解决方案资源管理器”窗口中看到生成的项目和源文件。在这里插入图片描述
    现在,这个动态链接库做的不多。接下来,您将创建一个头文件来声明DLL导出的函数,然后将函数定义添加到DLL中,使其更加有用。

向动态链接库添加头文件

  1. 要为您的功能创建头文件,请在菜单栏上选择“项目”>“添加新项目”。
  2. 在“添加新项”对话框的左窗格中,选择“Visual C++”。在中间窗格中,选择Header File (.h)。指定MathLibrary.h作为头文件的名称。
  3. 选择“添加”按钮生成一个空白头文件,该文件将显示在新的编辑器窗口中。用以下代码替换头文件的内容:
//MathLibrary.h -Contains decalrations of math functions
#paragma once 

#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif
//The Fibonacci recurrence relation describes a sequence F
//where F(n) is { n=0, a
//              { n= 1, b
//              {n>1, F(n-2)+F(n-1)
//for some initial integral values a and b.
//If the sequenc is initialized F(0) =1, F(1) = 1,
//then the realtion produces the well-known Fibonacci
//sequence :1,1,2,3,5,8,13,21,34,...
//Initialize  a Fibonacci relation sequence
//such that F(0)=a,F(1)=b.
//This function must be called before any by other function.
extern "C" MATHLIBRARY_API void fibonacci_init(const unsigned long long a ,const unsigned long long b);
//produce the next value in the sequence .
//return true on success and updates current value and index;
//false  on overflow, leaves current value and index unchanged.
extern "C" MATHLIBRARY_API bool fibonacci_next();
//get the current value in the sequence.
extern "C" MATHLIBRARY_API unsigned long long fibonacci_current();
//get the position of the curren value in the sequence.
extern "C" MATHLIBRARY_API unsigned fibonacci_index();

这个头文件声明了一些函数来产生一个广义的斐波那契序列,给定两个初始值。调用fibonacci_init(1,1)会生成熟悉的斐波那契数列。

注意,文件顶部的预处理语句。DLL项目的新项目模板将PROJECTNAME_EXPORTS添加到已定义的预处理器宏中。在本例中,Visual Studio在构建您的MathLibraryDLL项目时定义了MATHLIBRARY_EXPORTS。

当MATHLIBRARY_EXPORTS宏被定义时,MATHLIBRARY_API宏在函数声明中设置__declspec(dllexport)修饰符。这个修饰符告诉编译器和链接器从动态链接库导出一个函数或变量供其他应用程序使用。当MATHLIBRARY_EXPORTS未定义时,例如,当头文件被客户端应用程序包含时,MATHLIBRARY_API将__declspec(dllimport)修饰符应用于声明。此修饰符优化了应用程序中函数或变量的导入。

向动态链接库添加implementation

  1. 在解决方案资源管理器中,右键单击“ Source Files”节点,然后选择“添加”>“新建项目”。创建新的。名为MathLibrary.cpp的cpp文件,与您在上一步中添加新头文件的方式相同。
  2. 在编辑器窗口中,选择MathLibrary.cpp的选项卡(如果它已经打开)。如果没有,在解决方案Solution Explorer中,双击“ MathLibrary ”项目的“Source Files ”文件夹中的“ MathLibrary.cpp ”。
  3. 在编辑器中,用以下代码替换MathLibrary.cpp文件的内容:
//MathLibrary.cpp:Define the exported functions for the dll.
#include"pch.h"//use stdafx.h in visual studio 2017 and earlier 
#include<utility>
#include<limits.h>
#include "MathLibrary.h"

//Dll internal state variables:
static unsigned long long previous_;//previous value , if any
static unsigned long long curreut_;//current sequence value
static unsignedindex_;//current seq.position

//initialize a Fibonacci relation sequence 
//such that F(0) =a ,F(1)=b.
//this function must be called before any other function 
void fibonacci_init(const unsigned long long a, const unsigned long long b)
{
index_ = 0;
current_ =a;
previous_=b;//see special case when initialized
}
//ptoduce the next value in the sequence.
//return true on success,false on overflow.
bool fibonacci_next()
{
//check to see if we'd overflow result or position 
if ((ULLONG_MAX -previous_ < current_) ||(UINT_MAX ==index_)){return false;}
//special case when index==0,just return b value
if(index_>0)
{
//othwewise ,calculate next sequence value
previous_+=current_;
}
std::swap(current_, previous_);
++index_;
return true;
}

//get the current value in the sequence.
unsigned long long fibonacci_current(){return current_;}
//get the current indec position in the sequence.
unsigned fibonacci_index()
{return index_;}

要验证到目前为止一切正常,请编译动态链接库。若要编译,请选择菜单栏上的“构建”>“构建解决方案”。DLL和相关的编译器输出被放在一个名为Debug的文件夹中,该文件夹位于解决方案文件夹的正下方。如果您创建一个发布版本,输出将被放在一个名为“发布”的文件夹中。输出应该如下所示:

1>------ Build started: Project: MathLibrary, Configuration: Debug Win32 ------
1>pch.cpp
1>dllmain.cpp
1>MathLibrary.cpp
1>Generating Code...
1>   Creating library C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.lib and object C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.exp
1>MathLibrary.vcxproj -> C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.dll
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

以上已经使用Visual Studio创建了一个DLL!接下来,您将创建一个使用DLL导出的函数的客户端应用程序。(以上写了这么多,其实操作的话 复制粘贴就那么几步哈哈哈)

创建一个使用动态链接库的客户端应用程序
当你创建一个动态链接库时,想想客户端应用程序会如何使用它。要调用函数或访问由DLL导出的数据,客户端源代码必须具有编译时可用的声明。在链接时,链接器需要信息来解析函数调用或数据访问。动态链接库在导入库中提供这些信息,导入库是一个包含如何查找函数和数据的信息的文件,而不是实际的代码。在运行时,DLL必须在操作系统可以找到的位置对客户端可用。
无论是您自己的还是来自第三方的,您的客户端应用程序项目都需要几条信息来使用DLL。它需要找到声明DLL导出的头链接器的导入库以及DLL本身。一种解决方案是将所有这些文件复制到您的客户端项目中。对于在客户端开发过程中不太可能改变的第三方dll,这种方法可能是使用它们的最佳方式。但是,当您也构建DLL时,最好避免重复。如果您制作正在开发的DLL文件的本地副本,您可能会意外地在一个副本中更改头文件,而不是另一个副本,或者使用过期的库。

为了避免代码不同步,我们建议您将客户端项目中的包含路径设置为直接包含DLL项目中的DLL头文件。此外,在客户端项目中设置库路径,以包含来自DLL项目的DLL导入库。最后,将构建的DLL从DLL项目复制到您的客户端构建输出目录中。此步骤允许您的客户端应用程序使用您构建的相同DLL代码。
在Visual Studio中创建客户端应用程序

  • 在菜单栏上,选择文件>新建>项目以打开创建新项目对话框。
  • 在对话框顶部,将语言设置为C++,将平台设置为窗口,并将项目类型设置为控制台。
  • 从项目类型的筛选列表中,选择控制台应用程序,然后选择下一步。
  • 在“配置新项目”页面中,在“项目名称”框中输入“MathClient”以指定项目的名称。保留默认的位置和解决方案名称值。设置解决方案以创建新的解决方案。取消选中将解决方案和项目放在同一目录中(如果已选中)。
  • 选择“创建”按钮创建客户端项目。
    为您创建了一个最小的控制台应用程序项目。主源文件的名称与您之前输入的项目名称相同。在这个例子中,它被命名为MathClient.cpp。您可以构建它,但是它还没有使用您的DLL。

接下来,要在源代码中调用MathLibrary函数,您的项目必须包含MathLibrary.h文件。您可以将此头文件复制到您的客户端应用程序项目中,然后将其作为现有项目添加到项目中。这种方法对于第三方库来说是一个很好的选择。但是,如果您同时为您的DLL和客户端处理代码,头文件可能会不同步。要避免此问题,请在项目中设置“附加包含目录”路径,以包含原始标题的路径。

将动态链接库头添加到包含路径中

  • 右键单击解决方案资源管理器中的“MathClient”节点,打开“属性页(Property Pages)”对话框。
  • 在配置下拉框中,选择所有配置(如果尚未选择)。
  • 在左窗格中,选择配置属性> C/C++ >常规。
  • 在属性窗格中,选择“附加包括目录”编辑框旁边的下拉控件,然后选择“编辑”。
  • 在“附加包括目录”对话框的顶部窗格中双击以启用编辑控件。或者,选择文件夹图标来创建新条目。
  • 在编辑控件中,指定MathLibrary.h头文件位置的路径。您可以选择省略号(…)控件浏览到正确的文件夹。

您也可以输入从客户端源文件到包含DLL头文件的文件夹的相对路径。如果您按照指示将客户端项目放在一个独立于DLL的解决方案中,则相对路径应该如下所示:
…\MathLibrary\MathLibrary
如果您的DLL和客户端项目在同一个解决方案中,相对路径可能如下所示:
…\MathLibrary

当DLL和客户端项目在其他文件夹中时,调整相对路径以匹配。或者,使用省略号控件浏览文件夹。
在这里插入图片描述
在这里插入图片描述
在“附加包含目录”对话框中输入头文件的路径后,选择“确定”按钮。在“属性页”对话框中,选择“确定”按钮保存更改。

现在,您可以包含MathLibrary.h文件,并在客户端应用程序中使用它声明的函数。使用以下代码替换MathClient.cpp的内容:

//MathClient.cpp:Client app for MathLibrary DLL.
//#inclue"pch.h"Uncomment for Visual Studio 2017 and earlier
#include <iostream>
#include"MathLibrary.h"
int main()
{
//Initialize a Fibonacci relation sequence.
fibonacci_init(1,1);
//write out the sequence values untill overflow.
do{
std::cout <<fibonacci_index()<<":"<<fibonacci_current()<<std::endl;
}while (fibonacci_next());
//report count of values written before overflow.
std::cout<<fibonacci_index() +1<<"Fibonacci sequence values fit in an"<<"unsigned 64-bit inter."<<std::endl;
}

这段代码可以编译,但不能链接。如果您现在构建客户端应用程序,错误列表会显示几个LNK2019错误。这是因为您的项目缺少一些信息:您还没有指定您的项目依赖于MathLibrary.lib库。此外,您还没有告诉链接器如何找到MathLibrary.lib文件
要解决此问题,您可以将库文件直接复制到客户端应用程序项目中。链接器会自动找到并使用它。但是,如果库和客户端应用程序都在开发中,这可能会导致一个副本中的更改不会显示在另一个副本中。为了避免这个问题,您可以设置“附加依赖项”属性来告诉构建系统您的项目依赖于MathLibrary.lib。此外,您还可以在项目中设置一个附加库目录路径,以便在链接时包含原始库的路径。

将DLL导入库添加到项目中

  1. 右键单击解决方案资源管理器(Solution Explorer)中的“MathClient”节点,然后选择“属性”以打开“属性页”对话框。
  2. 在配置下拉框中,选择所有配置(如果尚未选择)。它确保任何属性更改都适用于调试和发布版本。
  3. 在左窗格中,选择配置属性>链接器>输入。在属性窗格中,选择“附加依赖项”编辑框旁边的下拉控件,然后选择“Edit”。
    在这里插入图片描述
  4. 在“附加依赖项”对话框中,将“MathLibrary.lib”添加到顶部编辑控件的列表中。

在这里插入图片描述

5.选择“确定”返回“属性页”对话框。
6.在左窗格中,选择配置属性>链接器>常规。在属性窗格中,选择“附加库目录”编辑框旁边的下拉控件,然后选择“编辑”。

7.在“附加库目录”对话框的顶部窗格中双击以启用编辑控件。在编辑控件中,指定MathLibrary.lib文件位置的路径。默认情况下,它位于DLL解决方案文件夹下名为调试的文件夹中。如果您创建了一个发布版本,该文件将被放在一个名为“发布”的文件夹中。您可以使用$(IntDir)宏,以便链接器可以找到您的DLL,无论您创建哪种版本。如果您按照指示将客户端项目放在一个独立于DLL项目的解决方案中,则相对路径应该如下所示:
…\MathLibrary$(IntDir)

如果您的DLL和客户端项目在其他位置,请调整相对路径以匹配。
8.在“附加库目录”对话框中输入库文件的路径后,选择“确定”按钮返回“属性页”对话框。选择“确定”保存属性更改。

您的客户端应用程序现在可以成功编译和链接,但它仍然没有运行所需的一切。当操作系统加载您的应用程序时,它会查找MathLibrary DLL。如果它在某些系统目录、环境路径或本地应用程序目录中找不到DLL,加载将失败。根据操作系统的不同,您会看到如下错误消息:
在这里插入图片描述
避免此问题的一种方法是将DLL复制到包含客户端可执行文件的目录中,作为构建过程的一部分。您可以向项目中添加一个后期生成事件,以添加一个将DLL复制到生成输出目录的命令。此处指定的命令仅在DLL丢失或已更改时才复制它。它根据您的生成配置,使用宏在调试或发布位置之间复制。

在后期生成事件中复制DLL(这步很重要,以后其他项目在添加dll文件的时候也可以借鉴,反正我是这样的,个人见解)

  1. 右键单击解决方案资源管理器中的“MathClient”节点,然后选择“属性”以打开“属性页”对话框。
  2. 在配置下拉框中,选择所有配置(如果尚未选择)。
  3. 在左窗格中,选择配置属性>生成事件>生成后事件。
  4. 在属性窗格中,选择命令行字段中的编辑控件。如果您按照说明将客户端项目放在与DLL项目不同的解决方案中,请输入以下命令:
    xcopy /y /d “…\MathLibrary$(IntDir)MathLibrary.dll” “$(OutDir)”

如果您的DLL和客户端项目在其他目录中,请更改DLL的相对路径以匹配。
在这里插入图片描述
5.选择“确定”按钮保存对项目属性的更改。

现在,您的客户端应用程序拥有了构建和运行所需的一切。通过选择菜单栏上的构建>构建解决方案来构建应用程序。根据您的Visual Studio版本,Visual Studio中的“输出”窗口应该类似于下面的示例:

1>------ Build started: Project: MathClient, Configuration: Debug Win32 ------
1>MathClient.cpp
1>MathClient.vcxproj -> C:\Users\username\Source\Repos\MathClient\Debug\MathClient.exe
1>1 File(s) copied
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

您已经创建了一个调用DLL中函数的应用程序。现在运行您的应用程序,看看它能做什么。在菜单栏上,选择调试>不调试启动。Visual Studio打开一个命令窗口,让程序在其中运行。输出的最后一部分应该如下所示:
在这里插入图片描述
按任意键关闭命令窗口。
现在您已经创建了一个动态链接库和一个客户端应用程序,您可以进行实验了。尝试在客户端应用程序的代码中设置断点,并在调试器中运行该应用程序。看看当你进入一个库调用时会发生什么。向库中添加其他函数,或者编写另一个使用您的DLL的客户端应用程序。

部署应用程序时,还必须部署它使用的dll。最简单的方法就是把你构建的或者你从第三方获得的动态链接库和你的应用放在同一个目录下。这就是所谓的应用本地部署。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值