VS2017开发动态链接库并调试

一、创建动态链接库工程

  1. 启动VS2017,选择菜单栏文件 > 新建 > 项目 > 已安装 > Visual C++ > Windows桌面 > 具有导出项的(DLL)动态链接库,设置好工程名称和路径,单击确定完成工程创建。

    image-20201231152814332

  2. 动态链接库工程创建后,会生成一个模板工程,如图所示。在模板工程中给出了怎样导出类、变量和函数的例子。

    image-20201231153427026

  3. mydll.h文件中已经定义好了动态链接库的导出宏。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 下列 ifdef 块是创建使从 DLL 导出更简单的
    // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 MYDLL_EXPORTS
    // 符号编译的。在使用此 DLL 的
    // 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
    // MYDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
    // 符号视为是被导出的。
    #ifdef MYDLL_EXPORTS
    #define MYDLL_API __declspec(dllexport)
    #else
    #define MYDLL_API __declspec(dllimport)
    #endif
  4. 为了便于多文件导出目标函数,先对模板工程进行简单修改,在解决方案窗口中,右键mydll工程 > 添加 > 新建项 > 已安装 > Visual C++ > 头文件,将文件名修改为export.h,单击添加完成。

    image-20201231154144888

  5. export.h文件中添加如下代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #pragma once
    #ifndef MYDLL_EXPORT_H_
    #define MYDLL_EXPORT_H_
    // 下列 ifdef 块是创建使从 DLL 导出更简单的
    // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 MYDLL_EXPORTS
    // 符号编译的。在使用此 DLL 的
    // 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
    // MYDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
    // 符号视为是被导出的。
    #ifdef MYDLL_EXPORTS
    #define MYDLL_API __declspec(dllexport)
    #else
    #define MYDLL_API __declspec(dllimport)
    #endif
    #endif // !MYDLL_EXPORT_H_

    删除mydll.h中的如下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 下列 ifdef 块是创建使从 DLL 导出更简单的
    // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 MYDLL_EXPORTS
    // 符号编译的。在使用此 DLL 的
    // 任何项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将
    // MYDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的
    // 符号视为是被导出的。
    #ifdef MYDLL_EXPORTS
    #define MYDLL_API __declspec(dllexport)
    #else
    #define MYDLL_API __declspec(dllimport)
    #endif

    在最上方添加

    1
    #include "export.h"

    当动态链接库工程包含多个文件时,只需在各文件引入头文件export.h即可完成目标接口的导出。

  6. 在菜单栏选择生成 > 生成解决方案,生成成功后会生成如下文件。

    image-20201231155244299

    并可在VS2017输出窗口查看到编译成功的信息。

    1
    2
    3
    4
    5
    6
    7
    8
    1>------ 已启动生成: 项目: mydll, 配置: Debug Win32 ------
    1>pch.cpp
    1>dllmain.cpp
    1>mydll.cpp
    1>正在生成代码...
    1> 正在创建库 E:\Code-of-C++\vs2017_test\mydll\Debug\MYDLL.lib 和对象 E:\Code-of-C++\vs2017_test\mydll\Debug\MYDLL.exp
    1>mydll.vcxproj -> E:\Code-of-C++\vs2017_test\mydll\Debug\MYDLL.dll
    ========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
  7. 修改mydll.hmydll.cpp,实现一些具体接口,便于下一步进行调试。

    • mydll.h

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      #include "export.h"
      #include <iostream>
      #include <string>
      /**
      * @enum INFO_TYPE
      * @brief 消息等级
      */
      enum INFO_TYPE
      {
      MY_INFO, ///< 普通消息
      MY_WARNING, ///< 警告消息
      MY_ERROR, ///< 错误消息
      };
      /**
      * @struct MY_INFO
      * @brief 定义消息结构体
      */
      typedef struct MY_MSG {
      INFO_TYPE infoType; ///< 消息等级
      std::string msg; ///< 详细信息
      std::string funcName; ///< 函数名称
      } MY_MSG;
      /**
      * @class DllTest
      * @brief 动态链接库导出类测试
      */
      class MYDLL_API DllTest
      {
      public:
      /**
      * @brief 构造函数
      */
      DllTest(std::string &s);
      /**
      * @brief 析构函数
      */
      ~DllTest();
      DllTest &setStr(std::string &s);
      /**
      *******************************************************************************
      * @brief 输出字符串
      * @ref \n
      * @return void
      * - 返回值描述
      * @author AILEE
      * @date 2020-12-31
      * @par 示例:
      * @code
      * @endcode
      * @see
      *******************************************************************************
      */
      void printStr();
      private:
      std::string str;
      };
      /**
      *******************************************************************************
      * @brief 输出消息
      * @param[in] MY_MSG & msg : 消息结构体
      * @ref \n
      * @return void
      * - 无返回值
      * @author AILEE
      * @date 2020-12-31
      * @par 示例:
      * @code
      * @endcode
      * @see
      *******************************************************************************
      */
      MYDLL_API void printInfo(MY_MSG &msg);
    • mydll.cpp

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      // mydll.cpp : 定义 DLL 的导出函数。
      //
      #include "pch.h"
      #include "framework.h"
      #include "mydll.h"
      DllTest::DllTest(std::string & s) :str(s) {}
      DllTest::~DllTest() {}
      DllTest & DllTest::setStr(std::string & s)
      {
      str = s;
      return *this;
      }
      void DllTest::printStr()
      {
      std::cout << str << std::endl;
      }
      void printInfo(MY_MSG &msg)
      {
      switch (msg.infoType)
      {
      case MY_INFO:
      std::cout << "[INFO]: " << msg.msg << std::endl;
      break;
      case MY_WARNING:
      std::cout << "[WARNING]: " << msg.funcName << ": " << msg.msg << std::endl;
      break;
      case MY_ERROR:
      std::cout << "[ERROR]: " << msg.funcName << ": " << msg.msg << std::endl;
      break;
      default:
      break;
      }
      }
  8. 重新编译

2. 创建控制台应用工程,调用动态链接库并进行联合调试。

  1. 选择解决方案窗口,右键解决方案 > 添加 > 新建项目 > 已安装 > Visual C++ > Windows桌面 > 控制台应用,设置好项目名称,单击确定完成。

    image-20201231163729604

  2. 右键解决方案列表中的mydll_example项目,选择设为启动项目

  3. 右键解决方案列表中的mydll_example项目,选择生成依赖项 > 项目依赖项,在弹出的窗口中,勾选mydll

    image-20201231164830197

  4. 右键解决方案列表中的mydll_example项目,选择属性,在弹出的属性页中,将配置改为所有配置,然后选择链接器 > 常规 > 附加库目录 > 编辑 > 宏,进行如下配置。

    image-20201231165520424

    image-20201231165642079

    选择属性页 > 链接器 > 输入 > 附加依赖项 > 编辑,添加MYDLL.lib

    image-20201231165903415

    image-20201231170011142

    单击确定,完成属性设置。

  5. mydll_example.cpp的最上面添加,因为mydll工程和mydll_example工程是同一级目录,因此添加mydll.h头文件时需要先返回上一级目录。

    1
    #include "../mydll/mydll.h"
  6. mydll_example.cpp修改为如下内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include "../mydll/mydll.h"
    int main()
    {
    std::string str = "hello world!\n";
    // 创建对象
    DllTest dllTest(str);
    // 打印对象中的字符串
    dllTest.printStr();
    // 设置对象中的字符串
    str = "hello dll!\n";
    dllTest.setStr(str);
    // 打印对象中的字符串
    dllTest.printStr();
    // 调用打印消息函数,输出消息
    std::string warning_msg = "this is a waring.";
    MY_MSG msg;
    msg.infoType = MY_WARNING;
    msg.funcName = __FUNCTION__;
    msg.msg = warning_msg;
    printInfo(msg);
    }
  7. 选择菜单栏中调试 > 开始执行(不调试),将在命令行窗口输出如下内容。

    image-20201231172624267

  8. 设置好断点,选择菜单栏中调试 > 开始调试,即可进行调试。

    image-20201231172900378

  9. 切换到Release配置,重新生成解决方案,即可在mydll工程文件夹下的Release文件夹下找到MYDLL.dllMDLL.lib,将其拷贝一份,并复制工程对应的头文件,按如下方式组织,即可进行动态链接库的发布,方便以后使用。

    • mydll_x86_release
      • bin
        • mydll.dll
      • include
        • export.h
        • framework.h
        • mydll.h
        • pch.h
      • lib
        • mydll.lib

参考文献

-------------本文结束感谢您的阅读-------------
分享