演练:创建和使用自己的动态链接库 (C++)Walkthrough: Create and use your own Dynamic Link Library (C++)

此分布演练演示如何使用 Visual Studio IDE 通过 Microsoft C++ (MSVC) 编写自己的动态链接库 (DLL)。This step-by-step walkthrough shows how to use the Visual Studio IDE to create your own dynamic link library (DLL) written in Microsoft C++ (MSVC). 然后,该演练演示如何从其他 C++ 应用中使用 DLL。Then it shows how to use the DLL from another C++ app. DLL(在基于 UNIX 的操作系统中也称为“共享库”)是最有用的 Windows 组件类型之一 。DLLs (also known as shared libraries in UNIX-based operating systems) are one of the most useful kinds of Windows components. 可以将其用作共享代码和资源、缩小应用大小的一种方法。You can use them as a way to share code and resources, and to shrink the size of your apps. DLL 甚至可使应用更易于维护和扩展。DLLs can even make it easier to service and extend your apps.

在本演练中,你将创建一个 DLL 并实现一些数学函数。In this walkthrough, you'll create a DLL that implements some math functions. 然后再创建一个控制台应用来使用 DLL 中的这些函数。Then you'll create a console app that uses the functions from the DLL. 你还将了解 Windows DLL 中使用的一些编程技术和约定。You'll also get an introduction to some of the programming techniques and conventions used in Windows DLLs.

本演练涵盖以下任务:This walkthrough covers these tasks:

  • 在 Visual Studio 中创建 DLL 项目。Create a DLL project in Visual Studio.

  • 将导出的函数和变量添加到该 DLL。Add exported functions and variables to the DLL.

  • 在 Visual Studio 中创建一个控制台应用项目。Create a console app project in Visual Studio.

  • 在该控制台应用中使用从 DLL 导入的函数和变量。Use the functions and variables imported from the DLL in the console app.

  • 运行已完成的应用。Run the completed app.

与静态链接库一样,DLL 也是按名称导出变量、函数和资源 。Like a statically linked library, a DLL exports variables, functions, and resources by name. 客户端应用导入名称以使用这些变量、函数和资源 。A client app imports the names to use those variables, functions, and resources. 与静态链接库不同的是,Windows 在加载时或在运行时将应用中的导入连接到 DLL 中的导出,而不是在链接时连接它们。Unlike a statically linked library, Windows connects the imports in your app to the exports in a DLL at load time or at run time, instead of connecting them at link time. Windows 需要不属于标准 C++ 编译模型的额外信息才能建立这些连接。Windows requires extra information that isn't part of the standard C++ compilation model to make these connections. MSVC 编译器实现了一些 Microsoft 专用 C++ 扩展,以提供此额外信息。The MSVC compiler implements some Microsoft-specific extensions to C++ to provide this extra information. 接下来我们将介绍这些扩展。We explain these extensions as we go.

本演练将创建两个 Visual Studio 解决方案;一个生成 DLL,另一个生成客户端应用。This walkthrough creates two Visual Studio solutions; one that builds the DLL, and one that builds the client app. DLL 使用 C 调用约定。The DLL uses the C calling convention. 只要平台、调用约定和链接约定匹配,便可从采用其他编程语言编写的应用中进行调用。It can be called from apps written in other programming languages, as long as the platform, calling conventions, and linking conventions match. 客户端应用使用隐式链接 ,其中 Windows 在加载时将应用链接到 DLL。The client app uses implicit linking, where Windows links the app to the DLL at load-time. 此链接允许应用调用 DLL 提供的函数,就像调用静态链接库中的函数一样。This linking lets the app call the DLL-supplied functions just like the functions in a statically linked library.

本演练并不涵盖一些常见的情况。This walkthrough doesn't cover some common situations. 此代码不会演示其他编程语言对 C++ DLL 的使用。The code doesn't show the use of C++ DLLs by other programming languages. 它不会演示如何创建纯资源 DLL,也不会演示如何使用显式链接在运行时(而不是在加载时)加载 DLL。It doesn't show how to create a resource-only DLL, or how to use explicit linking to load DLLs at run-time rather than at load-time. 请放心,可以使用 MSVC 和 Visual Studio 来执行所有这些操作。Rest assured, you can use MSVC and Visual Studio to do all these things.

有关 DLL 的详细信息的链接,请参阅在 Visual Studio 中创建 C/C++ DLLFor links to more information about DLLs, see Create C/C++ DLLs in Visual Studio. 有关隐式链接和显式链接的详细信息,请参阅确定要使用的链接方法For more information about implicit linking and explicit linking, see Determine which linking method to use. 有关创建用于使用 C 语言链接约定的编程语言的 C++ DLL 的信息,请参阅导出 C++ 函数以用于 C 语言可执行文件For information about creating C++ DLLs for use with programming languages that use C-language linkage conventions, see Exporting C++ functions for use in C-language executables. 有关如何创建用于 .NET 语言的 DLL 的信息,请参阅从 Visual Basic 应用程序调用 DLL 函数For information about how to create DLLs for use with .NET languages, see Calling DLL Functions from Visual Basic Applications.

先决条件Prerequisites

  • 运行 Microsoft Windows 7 或更高版本的计算机。A computer that runs Microsoft Windows 7 or later versions. 建议使用 Windows 10 以实现最佳开发体验。We recommend Windows 10 for the best development experience.
  • Visual Studio 的副本。A copy of Visual Studio. 有关如何下载和安装 Visual Studio 的信息,请参阅安装 Visual StudioFor information on how to download and install Visual Studio, see Install Visual Studio. 运行安装程序时,请务必选中“使用 C++ 的桌面开发” 工作负载。When you run the installer, make sure that the Desktop development with C++ workload is checked. 如果在安装 Visual Studio 时未安装此工作负载,请不要担心。Don't worry if you didn't install this workload when you installed Visual Studio. 可以再次运行安装程序并立即安装。You can run the installer again and install it now.

    使用 C++ 的桌面开发Desktop development with C++

  • Visual Studio 的副本。A copy of Visual Studio. 有关如何下载和安装 Visual Studio 2015 的信息,请参阅安装 Visual Studio 2015For information on how to download and install Visual Studio 2015, see Install Visual Studio 2015. 由于默认不安装 C++ 编译器和工具,请使用“自定义”安装进行安装 。Use a Custom installation to install the C++ compiler and tools, since they're not installed by default.
  • 了解使用 Visual Studio IDE 的基础知识。An understanding of the basics of using the Visual Studio IDE. 如果你之前使用过 Windows 桌面应用,可能具备一定的相关知识。If you've used Windows desktop apps before, you can probably keep up. 有关简介,请参阅 Visual Studio IDE 功能导览For an introduction, see Visual Studio IDE feature tour.

  • 了解足够的 C++ 语言基础知识以供继续操作。An understanding of enough of the fundamentals of the C++ language to follow along. 别担心,我们不会执行过于复杂的操作。Don't worry, we don't do anything too complicated.

备注

本演练假定你使用的是 Visual Studio 2017 版本 15.9 或更高版本。This walkthrough assumes you're using Visual Studio 2017 version 15.9 or later. 某些早期版本的 Visual Studio 2017 的代码模板存在缺陷,或使用了不同的用户界面对话框。Some earlier versions of Visual Studio 2017 had defects in the code templates, or used different user interface dialogs. 若要避免出现问题,请使用 Visual Studio 安装程序将 Visual Studio 2017 更新为版本 15.9 或更高版本。To avoid problems, use the Visual Studio Installer to update Visual Studio 2017 to version 15.9 or later.

创建 DLL 项目Create the DLL project

在本系列的任务中,将创建一个 DLL 项目,添加代码,并生成它。In this set of tasks, you create a project for your DLL, add code, and build it. 首先,启动 Visual Studio IDE,并在需要时登录。To begin, start the Visual Studio IDE, and sign in if you need to. 根据使用的 Visual Studio 版本,操作说明会略有不同。The instructions vary slightly depending on which version of Visual Studio you're using. 请确保在本页左上角的控件中选择了正确的版本。Make sure you have the correct version selected in the control in the upper left of this page.

在 Visual Studio 2019 中创建 DLL 项目To create a DLL project in Visual Studio 2019

  1. 在菜单栏上,选择“文件”>“新建”>“项目”,打开“创建新项目”对话框 。On the menu bar, choose File > New > Project to open the Create a New Project dialog box.

    创建新的 DLL 项目Create a new DLL project

  2. 在对话框顶部,将“语言” 设置为“C++” ,将“平台” 设置为“Windows” ,并将“项目类型” 设置为“库” 。At the top of the dialog, set Language to C++, set Platform to Windows, and set Project type to Library.

  3. 从经过筛选的项目类型列表中,选择“动态链接库(DLL)”,然后选择“下一步” 。From the filtered list of project types, select Dynamic-link Library (DLL), and then choose Next.

  4. 在“配置新项目”页面,在“项目名称”框中输入“MathLibrary”,以指定项目的名称 。In the Configure your new project page, enter MathLibrary in the Project name box to specify a name for the project. 保留默认“位置”和“解决方案名称”值 。Leave the default Location and Solution name values. 将“解决方案”设置为“创建新解决方案” 。Set Solution to Create new solution. 如果“将解决方案和项目放在同一目录中”已选中,则取消选中 。Uncheck Place solution and project in the same directory if it's checked.

  5. 选择“创建” 按钮创建项目。Choose the Create button to create the project.

创建解决方案后,就可以在 Visual Studio 中的“解决方案资源管理器”窗口中看到生成的项目和源文件了 。When the solution is created, you can see the generated project and source files in the Solution Explorer window in Visual Studio.

已突出显示数学库的 Visual Studio 2019“解决方案资源管理器”窗口的屏幕截图。Screenshot of the Visual Studio 2019 Solution Explorer window with the Math Library highlighted.

在 Visual Studio 2017 中创建 DLL 项目To create a DLL project in Visual Studio 2017

  1. 在菜单栏上,选择“文件”>“新建”>“项目”,打开“新建项目”对话框 。On the menu bar, choose File > New > Project to open the New Project dialog box.

  2. 在“新建项目”对话框的左窗格中,选择“已安装” > “Visual C++” > “Windows 桌面” 。In the left pane of the New Project dialog box, select Installed > Visual C++ > Windows Desktop. 在中心窗格中,选择“动态链接库(DLL)” 。In the center pane, select Dynamic-Link Library (DLL). 在“名称”框中输入“MathLibrary”,以指定项目的名称 。Enter MathLibrary in the Name box to specify a name for the project. 保留默认“位置”和“解决方案名称”值 。Leave the default Location and Solution name values. 将“解决方案”设置为“创建新解决方案” 。Set Solution to Create new solution. 选中“创建解决方案的目录”(如果未选中) 。Check Create directory for solution if it's unchecked.

    在“名称”文本框中显示数学库的 Visual Studio 2017“新建项目”对话框的屏幕截图。Screenshot of the Visual Studio 2017 New Project dialog box showing Math Library in the Name text box.

  3. 选择 “确定” 按钮,创建单元测试项目。Choose the OK button to create the project.

创建解决方案后,就可以在 Visual Studio 中的“解决方案资源管理器”窗口中看到生成的项目和源文件了 。When the solution is created, you can see the generated project and source files in the Solution Explorer window in Visual Studio.

已突出显示数学库的 Visual Studio 2017“解决方案资源管理器”窗口的屏幕截图。Screenshot of the Visual Studio 2017 Solution Explorer window with the Math Library highlighted.

在 Visual Studio 2015 及更早版本中创建 DLL 项目To create a DLL project in Visual Studio 2015 and older versions

  1. 在菜单栏上,依次选择“文件” >“新建” >“项目” 。On the menu bar, choose File > New > Project.

  2. 在“新建项目” 对话框的左窗格中,展开“已安装” > “模板” ,并选择“Visual C++” ,然后在中心窗格中,选择“Win32 控制台应用程序” 。In the left pane of the New Project dialog box, expand Installed > Templates, and select Visual C++, and then in the center pane, select Win32 Console Application. 在“名称”编辑框中输入“MathLibrary”,以指定项目的名称 。Enter MathLibrary in the Name edit box to specify a name for the project. 保留默认“位置”和“解决方案名称”值 。Leave the default Location and Solution name values. 将“解决方案”设置为“创建新解决方案” 。Set Solution to Create new solution. 选中“创建解决方案的目录”(如果未选中) 。Check Create directory for solution if it's unchecked.

    在“名称”文本框中显示数学库的 Visual Studio 2015“新建项目”对话框的屏幕截图。Screenshot of the Visual Studio 2015 New Project dialog box showing Math Library in the Name text box.

  3. 选择“确定” 按钮以关闭“新建项目” 对话框并启动“Win32 应用程序向导” 。Choose the OK button to dismiss the New Project dialog and start the Win32 Application Wizard.

    Win32 应用程序向导概述Win32 Application Wizard Overview

  4. 选择“下一步”按钮 。Choose the Next button. 在“应用程序设置”页的“应用程序类型”下,选择“DLL” 。On the Application Settings page, under Application type, select DLL.

    使用 Win32 应用程序向导创建 DLLCreate DLL in Win32 Application Wizard

  5. 选择 “完成” 按钮创建项目。Choose the Finish button to create the project.

向导完成解决方案后,就可以在 Visual Studio 中的“解决方案资源管理器” 窗口中看到生成的项目和源文件了。When the wizard completes the solution, you can see the generated project and source files in the Solution Explorer window in Visual Studio.

已突出显示数学库的 Visual Studio 2015“解决方案资源管理器”窗口的屏幕截图。Screenshot of the Visual Studio 2015 Solution Explorer window with the Math Library highlighted.

目前,此 DLL 并没有起到很大的作用。Right now, this DLL doesn't do very much. 接下来,创建一个头文件来声明 DLL 导出的函数,然后将函数定义添加到 DLL,使其具备更强大的功能。Next, you'll create a header file to declare the functions your DLL exports, and then add the function definitions to the DLL to make it more useful.

将头文件添加到 DLLTo add a header file to the DLL

  1. 若要为函数创建头文件,请在菜单栏上选择“项目” > “添加新项” 。To create a header file for your functions, on the menu bar, choose Project > Add New Item.

  2. 在“添加新项”对话框的左窗格中,选择“Visual C++” 。In the Add New Item dialog box, in the left pane, select Visual C++. 在中间窗格中,选择 “头文件(.h)”In the center pane, select Header File (.h). 指定 MathLibrary.h 作为头文件的名称 。Specify MathLibrary.h as the name for the header file.

    在“添加新项”对话框中添加标头Add header in Add New Item dialog

  3. 选择“添加” 按钮以生成一个空白头文件,该文件显示在新的编辑器窗口中。Choose the Add button to generate a blank header file, which is displayed in a new editor window.

    编辑器中的空 MathLibrary.h 文件Empty MathLibrary.h file in editor

  4. 将头文件的内容替换为以下代码:Replace the contents of the header file with this code:

    // 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 序列,给定了两个初始值。This header file declares some functions to produce a generalized Fibonacci sequence, given two initial values. 调用 fibonacci_init(1, 1) 会生成熟悉的 Fibonacci 数字序列。A call to fibonacci_init(1, 1) generates the familiar Fibonacci number sequence.

请注意文件顶部的预处理器语句。Notice the preprocessor statements at the top of the file. DLL 项目的新项目模板会将 PROJECTNAME_EXPORTS 添加到定义预处理器宏 。The new project template for a DLL project adds PROJECTNAME_EXPORTS to the defined preprocessor macros. 在此示例中,Visual Studio 在生成 MathLibrary DLL 项目时定义 MATHLIBRARY_EXPORTS 。In this example, Visual Studio defines MATHLIBRARY_EXPORTS when your MathLibrary DLL project is built.

定义 MATHLIBRARY_EXPORTS 宏时,MATHLIBRARY_API 宏会对函数声明设置 __declspec(dllexport) 修饰符。When the MATHLIBRARY_EXPORTS macro is defined, the MATHLIBRARY_API macro sets the __declspec(dllexport) modifier on the function declarations. 此修饰符指示编译器和链接器从 DLL 导出函数或变量,以便其他应用程序可以使用它。This modifier tells the compiler and linker to export a function or variable from the DLL for use by other applications. 如果未定义 MATHLIBRARY_EXPORTS (例如,当客户端应用程序包含头文件时),MATHLIBRARY_API 会将 __declspec(dllimport) 修饰符应用于声明。When MATHLIBRARY_EXPORTS is undefined, for example, when the header file is included by a client application, MATHLIBRARY_API applies the __declspec(dllimport) modifier to the declarations. 此修饰符可优化应用程序中函数或变量的导入。This modifier optimizes the import of the function or variable in an application. 有关详细信息,请参阅 dllexport、dllimportFor more information, see dllexport, dllimport.

向 DLL 添加实现To add an implementation to the DLL

  1. 在“解决方案资源管理器”中,右键单击“源文件”节点并选择“添加” > “新建项目” 。In Solution Explorer, right-click on the Source Files node and choose Add > New Item. 使用上一步中添加新头文件的相同方式,创建名为 MathLibrary.cpp 的新 .cpp 文件 。Create a new .cpp file called MathLibrary.cpp, in the same way that you added a new header file in the previous step.

  2. 在编辑器窗口中,选择 MathLibrary.cpp 的选项卡(如果已打开)。In the editor window, select the tab for MathLibrary.cpp if it's already open. 如果未打开,请在“解决方案资源管理器”中,双击 MathLibrary 项目的“Source Files”文件夹中的 MathLibrary.cpp,将其打开 。If not, in Solution Explorer, double-click MathLibrary.cpp in the Source Files folder of the MathLibrary project to open it.

  3. 在编辑器中,将 MathLibrary.cpp 文件的内容替换为以下代码:In the editor, replace the contents of the MathLibrary.cpp file with the following code:

    // MathLibrary.cpp : Defines 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 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_;
    }
    
  1. 在编辑器窗口中,选择 MathLibrary.cpp 的选项卡(如果已打开)。In the editor window, select the tab for MathLibrary.cpp if it's already open. 如果未打开,请在“解决方案资源管理器”中,双击 MathLibrary 项目的“Source Files”文件夹中的 MathLibrary.cpp,将其打开 。If not, in Solution Explorer, double-click MathLibrary.cpp in the Source Files folder of the MathLibrary project to open it.

  2. 在编辑器中,将 MathLibrary.cpp 文件的内容替换为以下代码:In the editor, replace the contents of the MathLibrary.cpp file with the following code:

    // 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_;
    }
    

若要验证到目前为止是否一切正常,请编译动态链接库。To verify that everything works so far, compile the dynamic link library. 若要编译,请在菜单栏上选择“生成” > “生成解决方案” 。To compile, choose Build > Build Solution on the menu bar. DLL 和相关编译器输出放在解决方案文件夹正下方的“Debug”文件夹中 。The DLL and related compiler output are placed in a folder called Debug directly below the solution folder. 如果创建发布版本,该输出会放置在“Release”文件夹中 。If you create a Release build, the output is placed in a folder called Release. 输出应类似于:The output should look something like this:

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 ==========
1>------ Build started: Project: MathLibrary, Configuration: Debug Win32 ------
1>stdafx.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 ==========
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!Congratulations, you've created a DLL using Visual Studio! 接下来,将创建一个使用 DLL 导出的函数的客户端应用。Next, you'll create a client app that uses the functions exported by the DLL.

创建可使用 DLL 的客户端应用Create a client app that uses the DLL

创建 DLL 时,请考虑客户端应用如何使用它。When you create a DLL, think about how client apps may use it. 若要调用函数或访问由 DLL 导出的数据,客户端源代码必须在编译时具有可用的声明。To call the functions or access the data exported by a DLL, client source code must have the declarations available at compile time. 在链接时间,链接器需要信息来解析函数调用或数据访问。At link time, the linker requires information to resolve the function calls or data accesses. 而 DLL 在“导入库”中提供此信息,导入库是包含有关如何查找函数和数据的信息的文件,而不是实际代码 。A DLL supplies this information in an import library, a file that contains information about how to find the functions and data, instead of the actual code. 而在运行时,DLL 必须可供客户端使用,位于操作系统可以找到的位置。And at run time, the DLL must be available to the client, in a location that the operating system can find.

无论是你自己的还是来自第三方的信息,客户端应用项目都需要几条信息才能使用 DLL。Whether it's your own or from a third-party, your client app project needs several pieces of information to use a DLL. 它需要查找声明 DLL 导出的标头、链接器的导入库和 DLL 本身。It needs to find the headers that declare the DLL exports, the import libraries for the linker, and the DLL itself. 一种解决方案是将所有这些文件复制到客户端项目中。One solution is to copy all of these files into your client project. 对于在客户端处于开发阶段时不太可能更改的第三方 DLL,此方法可能是使用它们的最佳方法。For third-party DLLs that are unlikely to change while your client is in development, this method may be the best way to use them. 但是,如果还要同时生成 DLL,最好避免重复。However, when you also build the DLL, it's better to avoid duplication. 如果创建了正在开发的 DLL 文件的本地副本,可能会意外更改一个副本而不是另一个中的头文件,或使用过期的库。If you make a local copy of DLL files that are under development, you may accidentally change a header file in one copy but not the other, or use an out-of-date library.

为避免不同步的代码,建议在客户端项目中设置包含路径,使其直接包括 DLL 项目中的 DLL 头文件。To avoid out-of-sync code, we recommend you set the include path in your client project to include the DLL header files directly from your DLL project. 此外,在客户端项目中设置库路径以包括 DLL 项目中的 DLL 导入库。Also, set the library path in your client project to include the DLL import libraries from the DLL project. 最后,将生成的 DLL 从 DLL 项目复制到客户端生成输出目录中。And finally, copy the built DLL from the DLL project into your client build output directory. 此步骤允许客户端应用使用生成的同一 DLL 代码。This step allows your client app to use the same DLL code you build.

在 Visual Studio 中创建客户端应用To create a client app in Visual Studio

  1. 在菜单栏上,选择“文件”>“新建”>“项目”,打开“创建新项目”对话框 。On the menu bar, choose File > New > Project to open the Create a new project dialog box.

  2. 在对话框顶部,将“语言” 设置为“C++” ,将“平台” 设置为“Windows” ,并将“项目类型” 设置为“控制台” 。At the top of the dialog, set Language to C++, set Platform to Windows, and set Project type to Console.

  3. 从筛选的项目类型列表中,选择“控制台应用”,然后选择“下一步” 。From the filtered list of project types, choose Console App then choose Next.

  4. 在“配置新项目”页面,在“项目名称”框中输入“MathClient”,以指定项目的名称 。In the Configure your new project page, enter MathClient in the Project name box to specify a name for the project. 保留默认“位置”和“解决方案名称”值 。Leave the default Location and Solution name values. 将“解决方案”设置为“创建新解决方案” 。Set Solution to Create new solution. 如果“将解决方案和项目放在同一目录中”已选中,则取消选中 。Uncheck Place solution and project in the same directory if it's checked.

    已突出显示“控制台应用”选项的“创建新项目”对话框的屏幕截图。Screenshot of the Create a new project dialog box with the Console App option highlighted.

  5. 选择“创建” 按钮创建客户端项目。Choose the Create button to create the client project.

将为你创建一个最小的控制台应用程序项目。A minimal console application project is created for you. 主源文件的名称与你之前输入的项目名称相同。The name for the main source file is the same as the project name that you entered earlier. 在本例中,命名为 MathClient.cpp 。In this example, it's named MathClient.cpp. 可以生成它,但它还不会使用你的 DLL。You can build it, but it doesn't use your DLL yet.

在 Visual Studio 2017 中创建客户端应用To create a client app in Visual Studio 2017

  1. 要创建 C++ 应用以使用创建的 DLL,请在菜单栏上,选择“文件”>“新建”>“项目” 。To create a C++ app that uses the DLL that you created, on the menu bar, choose File > New > Project.

  2. 在“新建项目” 对话框的左窗格中,选择“已安装” > “Visual C++” 下的“Windows 桌面” 。In the left pane of the New Project dialog, select Windows Desktop under Installed > Visual C++. 在中间窗格中,选择“Windows 控制台应用程序” 。In the center pane, select Windows Console Application. 在“名称”编辑框中,为项目指定名称 MathClient 。Specify the name for the project, MathClient, in the Name edit box. 保留默认“位置”和“解决方案名称”值 。Leave the default Location and Solution name values. 将“解决方案”设置为“创建新解决方案” 。Set Solution to Create new solution. 选中“创建解决方案的目录”(如果未选中) 。Check Create directory for solution if it's unchecked.

    已选中“已安装”>“Visual C++”>“Windows 桌面”,已突出显示 Windows 控制台应用程序且已在“名称”文本框中键入数学客户端的“新建项目”对话框的屏幕截图。Screenshot of the New Project dialog box with Installed > Visual C plus plus > Windows Desktop selected, Windows Console Application highlighted, and Math Client typed in the Name text box.

  3. 选择“确定”以创建客户端应用项目 。Choose OK to create the client app project.

将为你创建一个最小的控制台应用程序项目。A minimal console application project is created for you. 主源文件的名称与你之前输入的项目名称相同。The name for the main source file is the same as the project name that you entered earlier. 在本例中,命名为 MathClient.cpp 。In this example, it's named MathClient.cpp. 可以生成它,但它还不会使用你的 DLL。You can build it, but it doesn't use your DLL yet.

在 Visual Studio 2015 中创建客户端应用To create a client app in Visual Studio 2015

  1. 要创建 C++ 应用以使用创建的 DLL,请在菜单栏上,选择“文件”>“新建”>“项目” 。To create a C++ app that uses the DLL that you created, on the menu bar, choose File > New > Project.

  2. 在“新建项目” 对话框的左窗格中,选择“已安装” > “模板” > “Visual C++” 下的“Win32” 。In the left pane of the New Project dialog, select Win32 under Installed > Templates > Visual C++. 在中间窗格中,选择 “Win32 控制台应用程序”In the center pane, select Win32 Console Application. 在“名称”编辑框中,为项目指定名称 MathClient 。Specify the name for the project, MathClient, in the Name edit box. 保留默认“位置”和“解决方案名称”值 。Leave the default Location and Solution name values. 将“解决方案”设置为“创建新解决方案” 。Set Solution to Create new solution. 选中“创建解决方案的目录”(如果未选中) 。Check Create directory for solution if it's unchecked.

    已选中“已安装”>“模板”>“Visual C++”>“Win32”,已突出显示 Win32 控制台应用程序 Visual C++ 且已在“名称”文本框中键入数学客户端的“新建项目”对话框的屏幕截图。Screenshot of the New Project dialog box with Installed > Templates > Visual C plus plus > Win32 selected, Win32 Console Application Visual C plus plus highlighted, and Math Client typed in the Name text box.

  3. 选择“确定” 按钮以关闭“新建项目” 对话框并启动“Win32 应用程序向导” 。Choose the OK button to dismiss the New Project dialog and start the Win32 Application Wizard. “Win32 应用程序向导” 对话框的 “概述” 页上,选择 “下一步” 按钮。On the Overview page of the Win32 Application Wizard dialog box, choose the Next button.

  4. 在“应用程序设置” 页的“应用程序类型” 下,选择“控制台应用程序” (如果尚未选择)。On the Application Settings page, under Application type, select Console application if it isn't already selected.

  5. 选择 “完成” 按钮创建项目。Choose the Finish button to create the project.

向导完成后,将为你创建一个最小的控制台应用程序项目。When the wizard finishes, a minimal console application project is created for you. 主源文件的名称与你之前输入的项目名称相同。The name for the main source file is the same as the project name that you entered earlier. 在本例中,命名为 MathClient.cpp 。In this example, it's named MathClient.cpp. 可以生成它,但它还不会使用你的 DLL。You can build it, but it doesn't use your DLL yet.

接下来,要在源代码中调用 MathLibrary 函数,你的项目必须包括 MathLibrary.h 文件 。Next, to call the MathLibrary functions in your source code, your project must include the MathLibrary.h file. 可以将此头文件复制到客户端应用项目中,然后将其作为现有项添加到项目中。You could copy this header file into your client app project, then add it to the project as an existing item. 对于第三方库,此方法可能是一个不错的选择。This method can be a good choice for third-party libraries. 但是,如果同时处理 DLL 的代码和客户端的代码,则头文件可能会变为不同步。要避免此问题,请设置项目中的“附加包含目录” 路径,使其包含指向原始标头的路径。However, if you're working on the code for your DLL and your client at the same time, the header files could get out of sync. To avoid this issue, set the Additional Include Directories path in your project to include the path to the original header.

将 DLL 标头添加到包含路径To add the DLL header to your include path

  1. 右键单击“解决方案资源管理器” 中的“MathClient” 节点以打开“属性页” 对话框。Right-click on the MathClient node in Solution Explorer to open the Property Pages dialog.

  2. 在“配置”下拉框中,选择“所有配置”(如果尚未选择) 。In the Configuration drop-down box, select All Configurations if it's not already selected.

  3. 在左窗格中,选择“配置属性” > “C/C++” > “常规” 。In the left pane, select Configuration Properties > C/C++ > General.

  4. 在属性窗格中,选择“附加包含目录” 编辑框旁的下拉控件,然后选择“编辑” 。In the property pane, select the drop-down control next to the Additional Include Directories edit box, and then choose Edit.

    编辑“附加包含目录”属性Edit the Additional Include Directories property

  5. 在“附加包含目录” 对话框的顶部窗格中双击以启用编辑控件。Double-click in the top pane of the Additional Include Directories dialog box to enable an edit control. 或者,选择文件夹图标以创建新条目。Or, choose the folder icon to create a new entry.

  6. 在编辑控件中,指定指向 MathLibrary.h 头文件的位置的路径。In the edit control, specify the path to the location of the MathLibrary.h header file. 可选择省略号 (...) 控件浏览到正确的文件夹 。You can choose the ellipsis (...) control to browse to the correct folder.

    还可将客户端源文件中的相对路径输入到包含 DLL 头文件的文件夹。You can also enter a relative path from your client source files to the folder that contains the DLL header files. 如果已按照指示将客户端项目置于 DLL 的单独解决方案中,则相对路径应如下所示:If you followed the directions to put your client project in a separate solution from the DLL, the relative path should look like this:

    ..\..\MathLibrary\MathLibrary

    如果 DLL 和客户端项目位于同一解决方案中,则相对路径可能如下所示:If your DLL and client projects are in the same solution, the relative path might look like this:

    ..\MathLibrary

    如果 DLL 和客户端项目位于其他文件夹中,请调整相对路径以进行匹配。When the DLL and client projects are in other folders, adjust the relative path to match. 或者,使用省略号控件浏览文件夹。Or, use the ellipsis control to browse for the folder.

    将标头位置添加到“附加包含目录”属性Add the header location to the Additional Include Directories property

  7. 在“附加包含项目”对话框中输入标头文件的路径后,选择“确定”按钮 。After you've entered the path to the header file in the Additional Include Directories dialog box, choose the OK button. 在“属性页”对话框中,选择“确定”按钮以保存更改 。In the Property Pages dialog box, choose the OK button to save your changes.

现在可以包括 MathLibrary.h 文件,并使用它在客户端应用程序中声明的函数。You can now include the MathLibrary.h file and use the functions it declares in your client application. 使用以下代码替换 MathClient.cpp 的内容:Replace the contents of MathClient.cpp by using this code:

// 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;
}

此代码可进行编译,但不能链接。This code can be compiled, but not linked. 如果现在生成客户端应用,则错误列表会显示几个 LNK2019 错误。If you build the client app now, the error list shows several LNK2019 errors. 这是因为项目丢失了一些信息:你尚未指定项目在 MathLibrary.lib 库上有依赖项 。That's because your project is missing some information: You haven't specified that your project has a dependency on the MathLibrary.lib library yet. 而且,你尚未告诉链接器如何查找 MathLibrary.lib 文件 。And, you haven't told the linker how to find the MathLibrary.lib file.

要解决此问题,可以直接将库文件复制到客户端应用项目中。To fix this issue, you could copy the library file directly into your client app project. 链接器将自动查找并使用它。The linker would find and use it automatically. 但是,如果库和客户端应用都处于开发过程中,则可能会导致一个副本中的更改未在另一个副本中显示。However, if both the library and the client app are under development, that might lead to changes in one copy that aren't shown in the other. 要避免此问题,可以设置“附加依赖项”属性,告诉生成系统项目依赖于 MathLibrary.lib 。To avoid this issue, you can set the Additional Dependencies property to tell the build system that your project depends on MathLibrary.lib. 此外,还可设置项目中的“附加库目录” 路径,使其在链接时包含指向原始库的路径。And, you can set an Additional Library Directories path in your project to include the path to the original library when you link.

将 DLL 导入库添加到项目中To add the DLL import library to your project

  1. 右键单击“解决方案资源管理器”中的“MathClient”节点,然后选择“属性”以打开“属性页”对话框 。Right-click on the MathClient node in Solution Explorer and choose Properties to open the Property Pages dialog.

  2. 在“配置”下拉框中,选择“所有配置”(如果尚未选择) 。In the Configuration drop-down box, select All Configurations if it's not already selected. 它可确保任何属性更改同时应用于调试和发布版本。It ensures that any property changes apply to both Debug and Release builds.

  3. 在左窗格中,选择“配置属性” > “链接器” > “输入” 。In the left pane, select Configuration Properties > Linker > Input. 在属性窗格中,选择“附加依赖项” 编辑框旁的下拉控件,然后选择“编辑” 。In the property pane, select the drop-down control next to the Additional Dependencies edit box, and then choose Edit.

    编辑“附加依赖项”属相Edit the Additional Dependencies property

  4. 在“附加依赖项”对话框中,将 MathLibrary.lib 添加到顶部编辑控件的列表中 。In the Additional Dependencies dialog, add MathLibrary.lib to the list in the top edit control.

    添加库依赖项Add the library dependency

  5. 选择“确定” 返回到“属性页” 对话框。Choose OK to go back to the Property Pages dialog box.

  6. 在左窗格中,选择“配置属性” > “链接器” > “常规” 。In the left pane, select Configuration Properties > Linker > General. 在属性窗格中,选择“附加库目录” 编辑框旁的下拉控件,然后选择“编辑” 。In the property pane, select the drop-down control next to the Additional Library Directories edit box, and then choose Edit.

    编辑“附加库目录”属性Edit the Additional Library Directories property

  7. 在“附加库目录” 对话框的顶部窗格中双击以启用编辑控件。Double-click in the top pane of the Additional Library Directories dialog box to enable an edit control. 在编辑控件中,指定指向 MathLibrary.lib 文件位置的路径。In the edit control, specify the path to the location of the MathLibrary.lib file. 默认情况下,它位于 DLL 解决方案文件夹下的“Debug”文件夹中 。By default, it's in a folder called Debug directly under the DLL solution folder. 如果创建发布版本,该文件会放置在“Release”文件夹中 。If you create a release build, the file is placed in a folder called Release. 可以使用 $(IntDir) 宏,这样无论创建的是哪种版本,链接器都可找到 DLL。You can use the $(IntDir) macro so that the linker can find your DLL, no matter which kind of build you create. 如果已按照指示将客户端项目置于 DLL 项目的单独解决方案中,则相对路径应如下所示:If you followed the directions to put your client project in a separate solution from the DLL project, the relative path should look like this:

    ..\..\MathLibrary\$(IntDir)

    如果 DLL 和客户端项目位于其他位置,请调整相对路径以进行匹配。If your DLL and client projects are in other locations, adjust the relative path to match.

    添加库目录Add the library directory

  8. 在“附加库目录”对话框中输入指向库文件的路径后,选择“确定”按钮返回到“属性页”对话框 。Once you've entered the path to the library file in the Additional Library Directories dialog box, choose the OK button to go back to the Property Pages dialog box. 选择“确定”以保存属性更改 。Choose OK to save the property changes.

客户端应用现在可以成功编译和链接,但它仍未具备运行所需的全部条件。Your client app can now compile and link successfully, but it still doesn't have everything it needs to run. 当操作系统加载应用时,它会查找 MathLibrary DLL。When the operating system loads your app, it looks for the MathLibrary DLL. 如果在某些系统目录、环境路径或本地应用目录中找不到 DLL,则加载会失败。If it can't find the DLL in certain system directories, the environment path, or the local app directory, the load fails. 根据操作系统,你将看到如下所示的错误消息:Depending on the operating system, you'll see an error message like this:

“找不到 MathLibrary DLL”错误MathLibrary DLL not found error

避免此问题的一种方法是将 DLL 复制到包含客户端可执行文件的目录中,作为生成过程的一部分。One way to avoid this issue is to copy the DLL to the directory that contains your client executable as part of the build process. 可将“后期生成事件”添加到项目中,以此添加一条命令,将 DLL 复制到生成输出目录 。You can add a Post-Build Event to your project, to add a command that copies the DLL to your build output directory. 此处指定的命令仅在 DLL 丢失或发生更改时才复制它。The command specified here copies the DLL only if it's missing or has changed. 此命令使用宏根据生成配置在调试或发布位置之间进行复制。It uses macros to copy to and from the Debug or Release locations, based on your build configuration.

在生成后事件中复制 DLLTo copy the DLL in a post-build event

  1. 右键单击“解决方案资源管理器”中的“MathClient”节点,然后选择“属性”以打开“属性页”对话框 。Right-click on the MathClient node in Solution Explorer and choose Properties to open the Property Pages dialog.

  2. 在“配置” 下拉框中,选择“所有配置” (如果尚未选择)。In the Configuration drop-down box, select All Configurations if it isn't already selected.

  3. 在左窗格中,选择“配置属性” > “生成时间” > “后期生成事件” 。In the left pane, select Configuration Properties > Build Events > Post-Build Event.

  4. 在属性窗格中,在“命令行”字段中选择编辑控件 。In the property pane, select the edit control in the Command Line field. 如果已按照指示将客户端项目置于 DLL 项目的单独解决方案中,则输入以下命令:If you followed the directions to put your client project in a separate solution from the DLL project, then enter this command:

    xcopy /y /d "..\..\MathLibrary\$(IntDir)MathLibrary.dll" "$(OutDir)"

    如果 DLL 和客户端项目在其他目录中,请更改 DLL 的相对路径以进行匹配。If your DLL and client projects are in other directories, change the relative path to the DLL to match.

    添加后期生成命令Add the post-build command

  5. 选择“确定” 按钮以保存对项目属性所做的更改。Choose the OK button to save your changes to the project properties.

现在,客户端应用具备生成和运行所需的全部条件。Now your client app has everything it needs to build and run. 通过在菜单栏上选择“生成” > “生成解决方案”来生成应用程序 。Build the application by choosing Build > Build Solution on the menu bar. Visual Studio 中的“输出”窗口的示例应如下所示,具体取决于你的 Visual Studio 版本 :The Output window in Visual Studio should have something like the following example depending on your version of 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 中的函数的应用程序。Congratulations, you've created an application that calls functions in your DLL. 现在运行应用程序以查看它执行的操作。Now run your application to see what it does. 在菜单栏上,选择“调试” > “启动而不调试” 。On the menu bar, choose Debug > Start Without Debugging. 此时,Visual Studio 会打开一个命令窗口,供程序在其中运行。Visual Studio opens a command window for the program to run in. 输出的最后一部分应如下所示:The last part of the output should look like:

在不调试的情况下启动客户端应用Start the client app without debugging

按任意键关闭命令窗口。Press any key to dismiss the command window.

现在,你已创建一个 DLL 和一个客户端应用程序,可以进行试验。Now that you've created a DLL and a client application, you can experiment. 尝试在客户端应用的代码中设置断点,并在调试器中运行该应用。Try setting breakpoints in the code of the client app, and run the app in the debugger. 看看单步执行库调用时会发生什么情况。See what happens when you step into a library call. 将其他函数添加到库中,或编写另一个使用 DLL 的客户端应用。Add other functions to the library, or write another client app that uses your DLL.

部署应用时,还必须部署它使用的 DLL。When you deploy your app, you must also deploy the DLLs it uses. 若要使你生成的或从第三方加入的 DLL 可用于应用,最简单的方法就是将其放在应用所在的同一目录中。The simplest way to make the DLLs that you build, or that you include from third parties, available is to put them in the same directory as your app. 这称为“应用本地部署” 。It's known as app-local deployment. 有关部署的更多信息,请参阅 Deployment in Visual C++For more information about deployment, see Deployment in Visual C++.

请参阅See also

从 Visual Basic 应用程序调用 DLL 函数Calling DLL Functions from Visual Basic Applications