本文是小白学习c++时,需要编写dll给node addon测试调用使用,如果是c++高手,请忽略。。。

编写dll动态链接

环境

  1. vs 2019 企业版本

    创建dll-Function

    dll是动态链接文件,简单来说就是开发c++或c#等code时,将重复的函数库封装起来,进行重复使用,节省开发成本。dll包含动态链接和静态链接两种,这篇文章不做深入讲解。只实现基础的dll编写和调用。调用采用vs调用和c++code两种方式。

    创建dll项目

    1. 创建新项目–>动态链接库(DLL)

    image.png
    然后下一步并创建一个dll基础项目,结果如下:
    image.png
    vs默认会创建好 dllmain.cpp pch.cpp 已经相应的头文件;

    2. 编写函数

    打开pch.cpp 文件,编写: ```cpp

    include

    using namespace std; int myAdd(int a, int b) { return a + b; }

int myMax(int a, int b) { return a > b ? a : b; }

  1. 打开 pch.h 文件,编写:
  2. ```cpp
  3. #ifndef PCH_H
  4. #define PCH_H
  5. // 添加要在此处预编译的标头
  6. #include "framework.h"
  7. extern "C" _declspec(dllexport) int myAdd(int a, int b);
  8. extern "C" _declspec(dllexport) int myMax(int a, int b);
  9. #endif //PCH_H

3. 生成dll

执行: 生成-》生成解决方案:
image.png
打开文件夹可以看到:
image.png
生成的dll和lib文件,此时dll编译生成就结束了。后面我们使用编译出来的dll文件。

运行dll

vs调用

1. 新建控制台应用

image.png
点击下一步并创建名为 exampleTestDll 的测试项目文件,项目初始默认创建好main主入口函数:
image.png

2. 调用dll

修改 exampleTestDll.cpp 文件:

// exampleTestDll.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

#include <iostream>
#include "pch.h"
using namespace std;

int main()
{
    std::cout << "Hello World!\n";

    int a = myAdd(5, 4);
    cout << a << endl;
    int b = myMax(5, 4);
    cout << b << endl;
    return 0;
}

将上一节生成的dll、lib、和pch.h以及framework.h文件copy到当前项目的工程目录下:
image.png
然后,在解决方案项目上的头文件选项,将 pch.h 头文件引进来:
image.png
在导航栏出选择头文件,右击-》添加-》现有项,选择dll项目中的 pch.h 头文件到项目中:
image.png

3. 添加dll配置

将添加的lib文件名加入到附加依赖项中,否则会报错:
image.png
完成之后,开始测试执行:
image.png
再打开的控制台中输出结果:
image.png
调用成功~~

c++调用

注意,此调用方式是在c++ addon环境中进行测试使用,也可以使用c++项目编写:
在主函数文件头部编写:

typedef int (*ptrSub)(int, int);

// dll目录的路径一定要正确,否则会访问不到
HMODULE hMod = LoadLibrary(".\\build\\Release\\Testdll.dll");

在函数体中使用:

void XYLib::TestDll(const Napi::CallbackInfo &info)
{
  if (hMod != NULL)
  {
    ptrSub myAdd = (ptrSub)GetProcAddress(hMod, "myAdd");
    ptrSub myMax = (ptrSub)GetProcAddress(hMod, "myMax");

    std::cout << "10 + 6 = " << myAdd(10, 6) << std::endl;
    std::cout << "max 10 & 6 = " << myMax(10, 6) << std::endl;

    FreeLibrary(hMod);
  }
  else
  {
    std::cout << "not found hmod" << std::endl;
  }
}

执行编译addon文件,输出后使用nodejs调用测试得到结果:
image.png
执行正确,说明c++加载调用dll没有问题。完美~~

创建dll-Class

// employee.h

// __declspec(dllexport)用于Windows中的动态库中,声明导出函数、类、对象等供外面调用,省略给出.def文件。
// 即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等。
// __declspec(dllimport)用于Windows中,从别的动态库中声明导入函数、类、对象等供本动态库或exe文件使用。
#ifdef Employee_Lib
#define DECLSPEC __declspec(dllexport)
#else
#define DECLSPEC __declspec(dllimport)
#endif

class  CEmployee
{
private:
    int _empid;

public:
    CEmployee();
    ~CEmployee();

    int GetId();
    void PrintString();
    static CEmployee* createMyClass();
};

// extern声明引用外部函数
// 导出函数,加extern "C",是为了保证编译时生成的函数名不变,这样动态调用dll时才能
// 正确获取函数的地址
extern "C"
{
    DECLSPEC CEmployee* createMyClass();
};

typedef CEmployee* (*InstanceClass)();
// employee.cpp

#include <string>
#include <iostream>

CEmployee::CEmployee()
{
    _empid = 666;
}

CEmployee::~CEmployee()
{
};

int CEmployee::GetId()
{
    return _empid;
}

void CEmployee::PrintString()
{
    std::cout << "hello i have very happy" << std::endl;
}

CEmployee* CEmployee::createMyClass() {
    return new CEmployee();
}

在解决方案-》项目属性-》c/c++ -》预处理器-》预处理器定义 添加:Employee_Lib
然后:
生成-》生成解决方案;

使用class dll

// 加载模块
HMODULE hMod = LoadLibrary(".\\build\\Release\\ClassDll.dll");

if (hMod != NULL)
{
    InstanceClass createMyClass = (InstanceClass)GetProcAddress(hMod, "createMyClass");

    CEmployee *ce = createMyClass();

    int eid = ce->GetId();

    ce->PrintString();

    std::cout << "get id: " << ce << std::endl;

    FreeLibrary(hMod);
}
else
{
    std::cout << "not found hmod" << std::endl;
}

参考

VS2019 C++动态链接库的创建使用(1) - 创建使用dll