用Napi编写nodejs Addon并调用dll
npdejs调用C++ addon并没有先前那篇随笔那么复杂,这是一篇补充说明:说明如何使用c++项目的include头文件以及lib,dll引入到addon内使用
一、使用VS编写DLL导出项目
- 步骤1:通过Visual Studio(推荐2019及其以上)新建一个“具有导出项的(DLL)动态链接库”
- 步骤2:编写代码
必须准守如下规则:
- 1、所有的dll导出必须在堆空间内,包括struct属性或者class属性(详细看底下代码样例)
- 2、必须使用dll导出的释放函数进行释放,遵循“谁申请谁释放,申请多少释放多少”
- 3、只能使用C基础数据类型,不能使用C++类型(例如string,map,vector等)但允许自定义class,属性也必须是C基础数据类型,且准守第1条。
//DllExportTemplate.h
#ifdef DLLEXPORTTEMPLATE_EXPORTS
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
#include "pch.h"
#include "framework.h"
#include <string>
#include <iostream>
using namespace std;
//释放指针
DLL_EXPORT void freePointer(void* any);
DLL_EXPORT void deletePointer(void* any);
// 导出常量
extern DLL_EXPORT string version = "1.0.0";
// 导出指针
DLL_EXPORT char* getHello();
class DLL_EXPORT People {
public:
char* name;
int age;
People(char* name, int age) {
this->name = name;
this->age = age;
}
void showInfo() {
cout << this->name << " " << this->age << endl;
}
char* getName() {
return this->name;
}
int getAge() {
return this->age;
}
void destroy() { //会出错,我也不知道为什么
delete(this->name);
delete this;
}
};
DLL_EXPORT People* getPeople();
// DllExportTemplate.cpp 头文件实现
#include "framework.h"
#include "DllExportTemplate.h"
#include <string>
using namespace std;
DLL_EXPORT void freePointer(void* any) {
free(any);
}
DLL_EXPORT void deletePointer(void* any) {
delete any;
}
// 导出指针
DLL_EXPORT char* getHello(){
char* hello = new char[] {"hello\0"};
return hello;
}
DLL_EXPORT People* getPeople() {
//遵守第1条
People* p = new People(new char[] {"John"}, 100);
return p;
}
在完成上述dll代码后,正常按照“生成解决方案”生成dll即可(推荐使用C++14标准,因为其他的我没测过,目测C++17也没问题)
二、编写addon项目
之后转到nodejs的addon项目(node-gpy项目)
项目结构如下
把刚刚dll项目的所有头文件(如果头文件引入了别的头文件,也需要)复制到项目include下(如果复制过来报错,记得安装vscode的cpp扩展并配置好,例如cl的位置,include包含目录等,具体网上有教学)
主要代码如下:
//Main.cpp
#include "DllExportTemplate.h"
#include <iostream>
#include <napi.h>
using namespace std;
Napi::Object Initialize(Napi::Env env, Napi::Object exports){
People* p = getPeople();
p->showInfo();
string name = p->getName();
int age = p->getAge();
char* hello = getHello();
// 不能调用destroy,我也不知道为什么会错,必须一个一个释放
// p->destroy();
freePointer(p->name);
deletePointer(p);
exports.Set("version",Napi::String::New(env, version));
exports.Set("hello",Napi::String::New(env, hello));
exports.Set("name",Napi::String::New(env, name));
exports.Set("age",Napi::Number::New(env, age));
return exports;
}
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Initialize)
随后配置好binding.gyp
{
"targets": [{
"target_name": "cppTemplate", //目标名称
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [ "-fno-exceptions" ],
"sources": [
"addons/cppTemplate/Main.cpp",//这是我的路径
],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")",
"addons/cppTemplate/include",//将头文件引入
],
"library_dirs": [
"addons/cppTemplate/lib" //配置lib目录
],
"libraries": [
"DllExportTemplate.lib" //将编译好的.lib文件引入编译项目
],
"dependencies": [
"<!(node -p \"require('node-addon-api').gyp\")"
],
"defines": [ 'NAPI_DISABLE_CPP_EXCEPTIONS']
}
]
}
之后正常通过编译即可,得到如下内容:
主要是得到.node文件,然后将.node文件放到测试目录下,再把运行时的dll也放到根目录(相对于.node而言,如果是electron,可能是electron根目录,这个我没测过)下。为了方便测试,我直接在release目录下新建test.js做测试
const e = require("./cppTemplate.node");
console.log(e)
结果:
PS ~\build\Release> node .\test.js
John 100
{ version: '1.0.0', hello: 'hello', name: 'John', age: 100 }
可能看完会头疼,我还是总结一下流程吧:
- 1:正常使用VS编写dll,只需要遵守上面三条规则
- 2:编写addon项目(具体网络上有很多教程,例如如何在项目中配置node-gyp,如何配置vs,如何配置环境变量,如何配置c++环境等等等(已憋死))
- 3:配置bingding.gyp(网上都有现成的模板,直接抄我上面的也行,记得删掉注释)
- 4:启动node-gyp编译即可