IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 创建和使用您自己的动态链接库 (C++) -> 正文阅读

[C++知识库]创建和使用您自己的动态链接库 (C++)

此分步演练演示如何使用 Visual Studio IDE 创建自己的动态链接库 (DLL),这些库是用 Microsoft C++ (MSVC) 编写的。然后,它展示了如何使用另一个C++应用程序中的 DLL。DLL(在基于 UNIX 的操作系统中也称为共享库)是最有用的 Windows 组件类型之一。你可以使用它们来共享代码和资源,并缩小应用的大小。DLL 甚至可以更轻松地服务和扩展应用。

在本演练中,您将创建一个实现某些数学函数的 DLL。然后,你将创建一个使用 DLL 中的函数的控制台应用。您还将了解 Windows DLL 中使用的一些编程技术和约定。

本演练涵盖以下任务:

  • 在 Visual Studio 中创建一个 DLL 项目。

  • 将导出的函数和变量添加到 DLL。

  • 在 Visual Studio 中创建控制台应用项目。

  • 在控制台应用中使用从 DLL 导入的函数和变量。

  • 运行已完成的应用。

与静态链接库一样,DLL 按名称导出变量、函数和资源。客户端应用导入名称以使用这些变量、函数和资源。与静态链接库不同,Windows 在加载时或运行时将应用中的导入连接到 DLL 中的导出,而不是在链接时连接它们。Windows 需要不属于标准C++编译模型中的额外信息来建立这些连接。MSVC 编译器实现了一些特定于 Microsoft 的扩展,以C++提供此额外信息。我们边走边解释这些扩展。

本演练创建两个 Visual Studio 解决方案;一个用于生成 DLL,另一个用于生成客户端应用。该 DLL 使用 C 调用约定。它可以从用其他编程语言编写的应用程序调用,只要平台,调用约定和链接约定匹配即可。客户端应用使用隐式链接,其中 Windows 在加载时将应用链接到 DLL。此链接允许应用调用 DLL 提供的函数,就像静态链接库中的函数一样。

本演练不涵盖某些常见情况。该代码不显示其他编程语言使用C++ DLL。它不演示如何创建纯资源 DLL,也不演示如何使用显式链接在运行时而不是在加载时加载 DLL。请放心,您可以使用MSVC和Visual Studio来执行所有这些事情。

尽管 DLL 的代码是用C++编写的,但我们对导出的函数使用了 C 样式的接口。这主要有两个原因:首先,许多其他语言支持导入C风格函数。客户端应用不必以C++编写。其次,它避免了与导出的类和成员函数相关的一些常见陷阱。导出类时很容易出现难以诊断的错误,因为类声明中引用的所有内容都必须具有也导出的实例化。此限制适用于 DLL,但不适用于静态库。如果您的类是普通的旧数据样式,则不应遇到此问题。

有关 DLL 的详细信息的链接,请参阅在 Visual Studio 中创建 C/C++ DLL。有关隐式链接和显式链接的详细信息,请参阅确定要使用的链接方法。有关创建C++ DLL 以用于使用 C 语言链接约定的编程语言的信息,请参阅导出C++函数以在 C 语言可执行文件中使用。有关如何创建用于 .NET 语言的 DLL 的信息,请参见从 Visual Basic 应用程序调用 DLL 函数

先决条件

  • 运行 Microsoft Windows 7 或更高版本的计算机。我们建议使用最新版本的 Windows 以获得最佳开发体验。
  • Visual Studio 的副本。有关如何下载和安装 Visual Studio 2015 的信息,请参阅安装 Visual Studio 2015。使用自定义安装来安装C++编译器和工具,因为它们默认情况下未安装。
  • 了解使用 Visual Studio IDE 的基础知识。如果你以前使用过 Windows 桌面应用,则可能会跟上步伐。有关简介,请参阅?Visual Studio IDE 功能导览

  • 对C++语言的足够基本原理的理解。别担心,我们不会做任何太复杂的事情。

创建 DLL 项目

在这组任务中,您将为 DLL 创建一个项目,添加代码并生成它。首先,启动 Visual Studio IDE,并在需要时登录。说明略有不同,具体取决于您使用的 Visual Studio 版本。请确保在页左上角的控件中选择了正确的版本。

在 Visual Studio 2015 及更早版本中创建 DLL 项目

  1. 在菜单栏上,选择"文件>新建>项目"

  2. 在"新建项目"对话框的左窗格中,展开"已安装>模板",选择"Visual C++",然后在中心窗格中选择"Win32 控制台应用程序"。在名称编辑框中输入?MathLibrary?以指定项目的名称。保留默认的"位置""解决方案名称"值。将"解决方案"设置为"创建新解决方案"。选中"创建解决方案的目录"(如果未选中)。

  3. 选择"确定"按钮以关闭"新建项目"对话框并启动"Win32 应用程序向导"

  4. 选择"下一步"按钮。在"应用程序设置"页上的"应用程序类型"下,选择"DLL"。

  5. 选择"完成"按钮以创建项目。

向导完成解决方案后,可以在 Visual Studio 的"解决方案资源管理器"窗口中看到生成的项目和源文件。

现在,此 DLL 的功能不多。接下来,您将创建一个头文件来声明 DLL 导出的函数,然后将函数定义添加到 DLL 以使其更有用。

将头文件添加到 DLL

  1. 若要为函数创建头文件,请在菜单栏上选择"项目>添加新项"。

  2. "添加新项"对话框的左窗格中,选择"可视C++"。在中心窗格中,选择"头文件 (.h)"。指定?MathLibrary.h?作为头文件的名称。

  3. 选择"添加"按钮以生成空白头文件,该文件将显示在新的编辑器窗口中。

  4. 将头文件的内容替换为以下代码:

    C++复制
    // MathLibrary.h - Contains declarations of math functions
    #pragma 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 sequence is initialized F(0) = 1, F(1) = 1,
    // then this relation 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 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.
    // Returns 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 current value in the sequence.
    extern "C" MATHLIBRARY_API unsigned fibonacci_index();
    

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

请注意文件顶部的预处理器语句。DLL 项目的新项目模板将添加到定义的预处理器宏中。在此示例中,Visual Studio 定义了构建 MathLibrary DLL 项目的时间。<PROJECTNAME>_EXPORTSMATHLIBRARY_EXPORTS

定义宏时,宏将在函数声明上设置修饰符。此修饰符告诉编译器和链接器从 DLL 导出函数或变量以供其他应用程序使用。when 未定义(例如,当客户端应用程序包含头文件时),将修饰符应用于声明。此修饰符优化函数或变量在应用程序中的导入。有关详细信息,请参阅?dllexport,dllimportMATHLIBRARY_EXPORTSMATHLIBRARY_API__declspec(dllexport)MATHLIBRARY_EXPORTSMATHLIBRARY_API__declspec(dllimport)

向 DLL 添加实现

  1. 在编辑器窗口中,选择?MathLibrary?的选项卡.cpp(如果该选项卡已打开)。如果没有,请在"解决方案资源管理器"中,双击?MathLibrary?项目的"源文件"文件夹中的"MathLibrary.cpp将其打开。

  2. 在编辑器中,将 MathLibrary.cpp 文件的内容替换为以下代码:

    C++复制
    // MathLibrary.cpp : Defines the exported functions for the DLL.
    #include "stdafx.h" // use pch.h in Visual Studio 2019 and later
    #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 current_;   // Current sequence value
    static unsigned index_;               // 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
    }
    
    // Produce the next value in the sequence.
    // Returns 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)
        {
            // otherwise, 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 index position in the sequence.
    unsigned fibonacci_index()
    {
        return index_;
    }
    

若要验证到目前为止是否一切正常,请编译动态链接库。若要编译,请选择菜单栏上的"生成>生成解决方案"。DLL 和相关编译器输出放置在解决方案文件夹正下方名为?Debug?的文件夹中。如果创建"发布"版本,则输出将放在名为"发布"的文件夹中。输出应如下所示:

输出复制
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 2015 中创建客户端应用

  1. 若要创建使用你创建的 DLL 的C++应用,请在菜单栏上选择"文件">"新建>项目"

  2. 在"新建项目"对话框的左窗格中,选择"已安装>模板"下的"Win32?>?Visual C++"。在中心窗格中,选择"Win32 控制台应用程序"。在"名称"编辑框中指定项目的名称?MathClient。保留默认的"位置""解决方案名称"值。将"解决方案"设置为"创建新解决方案"。选中"创建解决方案的目录"(如果未选中)。

  3. 选择"确定"按钮以关闭"新建项目"对话框并启动"Win32 应用程序向导"。在"Win32 应用程序向导"对话框的"概述"页上,选择"下一步"按钮。

  4. "应用程序设置"页上的"应用程序类型"下,选择"控制台应用程序"(如果尚未选择)。

  5. 选择"完成"按钮以创建项目。

向导完成后,将为您创建一个最小的控制台应用程序项目。主源文件的名称与您之前输入的项目名称相同。在此示例中,它被命名为?MathClient.cpp。您可以生成它,但它尚未使用您的 DLL。

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

将 DLL 标头添加到包含路径

  1. 右键单击"解决方案资源管理器"中的"MathClient"节点以打开"属性页"对话框。

  2. "配置"下拉框中,选择"所有配置"(如果尚未选中)。

  3. 在左窗格中,选择"配置属性">"C/C++>常规"。

  4. 在属性窗格中,选择"其他包含目录"编辑框旁边的下拉控件,然后选择"编辑"

  5. 双击"其他包含目录"对话框的顶部窗格以启用编辑控件。或者,选择文件夹图标以创建新条目。

  6. 在编辑控件中,指定?MathLibrary.h?头文件位置的路径。可以选择省略号 (...) 控件以浏览到正确的文件夹。

    还可以输入从客户端源文件到包含 DLL 头文件的文件夹的相对路径。如果按照说明将客户端项目放在与 DLL 不同的解决方案中,则相对路径应如下所示:

    ..\..\MathLibrary\MathLibrary

    如果 DLL 和客户端项目位于同一解决方案中,则相对路径可能如下所示:

    ..\MathLibrary

    当 DLL 和客户端项目位于其他文件夹中时,请调整相对路径以匹配。或者,使用省略号控件浏览该文件夹。

  7. 在"其他包含目录"对话框中输入头文件的路径后,选择"确定"按钮。在"属性页"对话框中,选择"确定"按钮以保存更改。

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

C++复制
// 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.
    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 integer." << std::endl;
}

此代码可以编译,但不能链接。如果现在生成客户端应用,则错误列表会显示多个 LNK2019 错误。这是因为您的项目缺少一些信息:您尚未指定您的项目依赖于?MathLibrary.lib?库。而且,您还没有告诉链接器如何查找?MathLibrary.lib?文件。

若要解决此问题,可以将库文件直接复制到客户端应用项目中。链接器将自动查找并使用它。但是,如果库和客户端应用都在开发中,则可能会导致一个副本中的更改在另一个副本中未显示。若要避免此问题,可以设置"其他依赖项"属性,以告知生成系统你的项目依赖于?MathLibrary.lib。而且,您可以在项目中设置"其他库目录"路径,以便在链接时包含原始库的路径。

将 DLL 导入库添加到项目中

  1. 右键单击"解决方案资源管理器"中的"MathClient"节点,然后选择"属性"以打开"属性页"对话框。

  2. "配置"下拉框中,选择"所有配置"(如果尚未选中)。它确保任何属性更改都适用于"调试"和"发布"版本。

  3. 在左窗格中,选择"配置属性">"链接器>输入"。在属性窗格中,选择"其他依赖项"编辑框旁边的下拉控件,然后选择"编辑"

  4. 在"其他依赖项"对话框中,将?MathLibrary.lib?添加到顶部编辑控件的列表中。

  5. 选择"确定"返回到"属性页"对话框。

  6. 在左窗格中,选择"配置属性">"链接器>常规"。在属性窗格中,选择"其他库目录"编辑框旁边的下拉控件,然后选择"编辑"

  7. 在"其他库目录"对话框的顶部窗格中双击以启用编辑控件。在编辑控件中,指定?MathLibrary.lib?文件位置的路径。默认情况下,它位于 DLL 解决方案文件夹正下方名为"调试"的文件夹中。如果创建发布版本,则该文件将放在名为"发布"的文件夹中。可以使用该宏,以便链接器可以找到您的 DLL,无论您创建哪种类型的生成。如果按照说明将客户端项目放在与 DLL 项目不同的解决方案中,则相对路径应如下所示:$(IntDir)

    ..\..\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 的其他客户端应用。

部署应用时,还必须部署它使用的 DLL。要使你生成的 DLL 或你从第三方包含的 DLL 可用,最简单的方法是将它们放在与应用相同的目录中。这称为应用本地部署。有关部署的详细信息,请参阅?Visual C++中的部署

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:12:28  更:2022-02-26 11:12:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:38:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码