1>------ Build started: Project: MathLibrary, Configuration: Debug Win32 ------
1>MathLibrary.cpp
1>dllmain.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
1>MathLibrary.vcxproj -> C:\Users\username\Source\Repos\MathLibrary\Debug\MathLibrary.pdb (Partial PDB)
========== 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 导入库。 最后,将生成的 DLL 从 DLL 项目复制到客户端生成输出目录中。 此步骤允许客户端应用使用生成的同一 DLL 代码。
在 Visual Studio 中创建客户端应用
在菜单栏上,选择“文件”>“新建”>“项目”,打开“创建新项目”对话框。
在对话框顶部,将“语言”设置为“C++”,将“平台”设置为“Windows”,并将“项目类型”设置为“控制台”。
从筛选的项目类型列表中,选择“控制台应用”,然后选择“下一步” 。
在“配置新项目”页面,在“项目名称”框中输入“MathClient”,以指定项目的名称 。 保留默认“位置”和“解决方案名称”值 。 将“解决方案”设置为“创建新解决方案” 。 如果“将解决方案和项目放在同一目录中”已选中,则取消选中 。
选择“创建” 按钮创建客户端项目。
将为你创建一个最小的控制台应用程序项目。 主源文件的名称与你之前输入的项目名称相同。 在本例中,命名为 MathClient.cpp 。 可以生成它,但它还不会使用你的 DLL。
在 Visual Studio 2017 中创建客户端应用
若要创建使用创建的 DLL 的 C++ 应用,在菜单栏上,选择“文件”>“新建”>“项目”。
在“新建项目” 对话框的左窗格中,选择“已安装” >“Visual C++” 下的“Windows 桌面” 。 在中间窗格中,选择“Windows 控制台应用程序” 。 在“名称”编辑框中,为项目指定名称 MathClient 。 保留默认“位置”和“解决方案名称”值 。 将“解决方案”设置为“创建新解决方案” 。 选中“创建解决方案的目录”(如果未选中) 。
选择“确定”以创建客户端应用项目 。
将为你创建一个最小的控制台应用程序项目。 主源文件的名称与你之前输入的项目名称相同。 在本例中,命名为 MathClient.cpp 。 可以生成它,但它还不会使用你的 DLL。
在 Visual Studio 2015 中创建客户端应用
若要创建使用创建的 DLL 的 C++ 应用,在菜单栏上,选择“文件”>“新建”>“项目”。
在“新建项目” 对话框的左窗格中,选择“已安装” >“模板” >“Visual C++” 下的“Win32” 。 在中间窗格中,选择 “Win32 控制台应用程序” 。 在“名称”编辑框中,为项目指定名称 MathClient 。 保留默认“位置”和“解决方案名称”值 。 将“解决方案”设置为“创建新解决方案” 。 选中“创建解决方案的目录”(如果未选中) 。
选择“确定” 按钮以关闭“新建项目” 对话框并启动“Win32 应用程序向导” 。 在 “Win32 应用程序向导” 对话框的 “概述” 页上,选择 “下一步” 按钮。
在“应用程序设置” 页的“应用程序类型” 下,选择“控制台应用程序” (如果尚未选择)。
选择 “完成” 按钮创建项目。
向导完成后,将为你创建一个最小的控制台应用程序项目。 主源文件的名称与你之前输入的项目名称相同。 在本例中,命名为 MathClient.cpp 。 可以生成它,但它还不会使用你的 DLL。
接下来,要在源代码中调用 MathLibrary 函数,你的项目必须包括 MathLibrary.h 文件 。 可以将此头文件复制到客户端应用项目中,然后将其作为现有项添加到项目中。 对于第三方库,此方法可能是一个不错的选择。 但是,如果同时处理 DLL 的代码和客户端的代码,则头文件可能会变为不同步。要避免此问题,请设置项目中的“附加包含目录” 路径,使其包含指向原始标头的路径。
右键单击“解决方案资源管理器” 中的“MathClient” 节点以打开“属性页” 对话框。
在“配置”下拉框中,选择“所有配置”(如果尚未选择) 。
在左窗格中,选择“配置属性”>“C/C++”>“常规” 。
在属性窗格中,选择“附加包含目录” 编辑框旁的下拉控件,然后选择“编辑” 。
在“附加包含目录” 对话框的顶部窗格中双击以启用编辑控件。 或者,选择文件夹图标以创建新条目。
在编辑控件中,指定指向 MathLibrary.h 头文件的位置的路径。 可选择省略号 (...) 控件浏览到正确的文件夹 。
还可将客户端源文件中的相对路径输入到包含 DLL 头文件的文件夹。 如果已按照指示将客户端项目置于 DLL 的单独解决方案中,则相对路径应如下所示:
..\..\MathLibrary\MathLibrary
如果 DLL 和客户端项目位于同一解决方案中,则相对路径可能如下所示:
..\MathLibrary
如果 DLL 和客户端项目位于其他文件夹中,请调整相对路径以进行匹配。 或者,使用省略号控件浏览文件夹。
在“附加包含项目”对话框中输入标头文件的路径后,选择“确定”按钮 。 在“属性页”对话框中,选择“确定”按钮以保存更改 。
现在可以包括 MathLibrary.h 文件,并使用它在客户端应用程序中声明的函数。 使用以下代码替换 MathClient.cpp 的内容:
// MathClient.cpp : Client app for MathLibrary DLL.
// #include "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 until overflow.
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 integer." << std::endl;
此代码可进行编译,但不能链接。 如果现在生成客户端应用,则错误列表会显示几个 LNK2019 错误。 这是因为项目丢失了一些信息:你尚未指定项目在 MathLibrary.lib 库上有依赖项 。 而且,你尚未告诉链接器如何查找 MathLibrary.lib 文件 。
要解决此问题,可以直接将库文件复制到客户端应用项目中。 链接器将自动查找并使用它。 但是,如果库和客户端应用都处于开发过程中,则可能会导致一个副本中的更改未在另一个副本中显示。 要避免此问题,可以设置“附加依赖项”属性,告诉生成系统项目依赖于 MathLibrary.lib 。 此外,还可设置项目中的“附加库目录” 路径,使其在链接时包含指向原始库的路径。
将 DLL 导入库添加到项目中
右键单击“解决方案资源管理器”中的“MathClient”节点,然后选择“属性”以打开“属性页”对话框 。
在“配置”下拉框中,选择“所有配置”(如果尚未选择) 。 它可确保任何属性更改同时应用于调试和发布版本。
在左窗格中,选择“配置属性”>“链接器”>“输入” 。 在属性窗格中,选择“附加依赖项” 编辑框旁的下拉控件,然后选择“编辑” 。
在“附加依赖项”对话框中,将 MathLibrary.lib 添加到顶部编辑控件的列表中 。
选择“确定” 返回到“属性页” 对话框。
在左窗格中,选择“配置属性”>“链接器”>“常规” 。 在属性窗格中,选择“附加库目录” 编辑框旁的下拉控件,然后选择“编辑” 。
在“附加库目录” 对话框的顶部窗格中双击以启用编辑控件。 在编辑控件中,指定指向 MathLibrary.lib 文件位置的路径。 默认情况下,它位于 DLL 解决方案文件夹下的“Debug”文件夹中 。 如果创建发布版本,该文件会放置在“Release”文件夹中 。 可以使用 $(IntDir)
宏,这样无论创建的是哪种版本,链接器都可找到 DLL。 如果已按照指示将客户端项目置于 DLL 项目的单独解决方案中,则相对路径应如下所示:
..\..\MathLibrary\$(IntDir)
如果 DLL 和客户端项目位于其他位置,请调整相对路径以进行匹配。
在“附加库目录”对话框中输入指向库文件的路径后,选择“确定”按钮返回到“属性页”对话框 。 选择“确定”以保存属性更改 。
客户端应用现在可以成功编译和链接,但它仍未具备运行所需的全部条件。 当操作系统加载应用时,它会查找 MathLibrary DLL。 如果在某些系统目录、环境路径或本地应用目录中找不到 DLL,则加载会失败。 根据操作系统,你将看到如下所示的错误消息:
避免此问题的一种方法是将 DLL 复制到包含客户端可执行文件的目录中,作为生成过程的一部分。 可将“后期生成事件”添加到项目中,以此添加一条命令,将 DLL 复制到生成输出目录 。 此处指定的命令仅在 DLL 丢失或发生更改时才复制它。 此命令使用宏根据生成配置在调试或发布位置之间进行复制。
在生成后事件中复制 DLL
右键单击“解决方案资源管理器”中的“MathClient”节点,然后选择“属性”以打开“属性页”对话框 。
在“配置” 下拉框中,选择“所有配置” (如果尚未选择)。
在左窗格中,选择“配置属性”>“生成时间”>“后期生成事件” 。
在属性窗格中,在“命令行”字段中选择编辑控件 。 如果已按照指示将客户端项目置于 DLL 项目的单独解决方案中,则输入以下命令:
xcopy /y /d "..\..\MathLibrary\$(IntDir)MathLibrary.dll" "$(OutDir)"
如果 DLL 和客户端项目在其他目录中,请更改 DLL 的相对路径以进行匹配。
选择“确定” 按钮以保存对项目属性所做的更改。
现在,客户端应用具备生成和运行所需的全部条件。 通过在菜单栏上选择“生成”>“生成解决方案”来生成应用程序 。 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 的客户端应用。
部署应用时,还必须部署它使用的 DLL。 若要使你生成的或从第三方加入的 DLL 可用于应用,最简单的方法就是将其放在应用所在的同一目录中。 这称为“应用本地部署” 。 有关部署的更多信息,请参阅 Deployment in Visual C++。
从 Visual Basic 应用程序调用 DLL 函数