关于在electron(Nodejs)中使用 Napi 的简单记录

当我们使用electron想要集成一个C++ SDK实现很底层的算法逻辑就有可能与C++ SDK进行数据通信。
Napi 应该是比较好的选择,因为C++本身的运行速度很快,使用Napi也能很大程度上保证软件的兼容性、又不会阻塞C++线程、还可以很简单的与C++ 实现数据传递。

开始使用

  1. 安装 C++ 运行环境 直接装一个 VS2022 (c++桌面端开发)
  2. 安装 python 3.8.2 (如果开发electron win32 最好装32位环境)
  3. npm install -g node-gyp
  4. 新建一个文件夹 addon (存放demo文件)
  5. addon内执行npm init -y
  6. addon内执行npm install node-addon-api
  7. 新建 src/addon.cpp (相当于Napi的入口文件)(在下方)
  8. 新建 binding.gyp 并配置(在下方)

构建好后只需要 build/Release/addon.node 其他的可以删除(如果没有其他 dll 依赖)

我的Demo目录结构

在这里插入图片描述

app.js源码 (也就是Nodejs 集成C++ 插件的起始文件)

const addon = require("./build/Release/addon");

function callback(data) {
    console.log("接收到C++传出: ", data);
}

console.log(addon.sayHello("Hello C++"));

// 传入回调函数
console.log("传入回调函数结果: ", addon.startSendingData(callback));

addon.cpp 源码 实现接收JS 传入的参数和回调函数 并使用JS传入的回调函数给JS传递数据

#include <napi.h>

#include <thread>

#include "helper.h"

using namespace std;

// 线程安全的回调
Napi::ThreadSafeFunction tsfn;
Napi::Function jsCallback;

// 这个函数用于在后台线程中运行
void BackgroundThread(Napi::ThreadSafeFunction tsfn) {
  // 在 JS 线程调用回调
  tsfn.BlockingCall([](Napi::Env env, Napi::Function jsCallback) {
    jsCallback.Call({Napi::String::New(env, "Hello Nodejs")});
  });

  // 释放线程安全函数
  tsfn.Release();
}

// 启动数据发送的函数
Napi::Value StartSendingData(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  // 检查参数,确保传入的是一个回调函数
  if (!info[0].IsFunction()) {
    Napi::TypeError::New(env, "C++ StartSendingData 回调异常").ThrowAsJavaScriptException();
    return env.Undefined();
  }

  jsCallback = info[0].As<Napi::Function>();

  // 创建线程安全的 JS 回调
  tsfn = Napi::ThreadSafeFunction::New(env, jsCallback, "ThreadSafeFunction", 0, 1);

  // 启动后台线程,传递线程安全的回调
  thread(BackgroundThread, tsfn).detach();

  return Napi::Boolean::New(env, true);
}

// 测试传参输出的函数
Napi::String SayHello(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  // 获取传入的名称参数
  string name = info[0].As<Napi::String>().Utf8Value();

  // 调用外部的 greet 函数
  string message = greet(name);

  return Napi::String::New(env, message);
}

// 定义模块并导出方法
Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set("startSendingData", Napi::Function::New(env, StartSendingData));
  exports.Set("sayHello", Napi::Function::New(env, SayHello));

  return exports;
}

// 绑定模块
NODE_API_MODULE(addon, Init);

helper.h 源码

#ifndef HELPER_H
#define HELPER_H

#include <string>

// 声明一个辅助函数
std::string greet(const std::string& name);

#endif

helper.cpp 源码

#include "helper.h"

std::string greet(const std::string& name) {
  return "接收到NodeJs传入: " + name;
}

binding.gyp 基础配置

{
  "variables": {
    "product_dir": "<(module_root_dir)/build/Release"
  },
  "targets": [
    {
      "target_name": "addon",
      "sources": [
        "src/addon.cpp",
        "src/helper.cpp"
      ],
      "include_dirs": [
        "<!@(node -p \"require('node-addon-api').include\")",
        "include"
      ],
      "libraries": [
        #"<(module_root_dir)/lib/agan_engine.lib"
      ],
      "dependencies": [
        "<!(node -p \"require('node-addon-api').gyp\")"
      ],
      "cflags!": ["-fno-exceptions"],
      "cflags_cc!": ["-fno-exceptions"],
      "defines": ["NAPI_CPP_EXCEPTIONS"],
    }
  ]
}

环境、目录和源码都配置粘贴好之后

  1. 执行 node-gyp rebuild
    在这里插入图片描述
  2. 运行 node ./app.js
    在这里插入图片描述
    注意:当nodejs 调用 build/Release/addon.node文件时传入的字符串在addon.cpp内接收时要转换为UTF8格式
string name = info[0].As<Napi::String>().Utf8Value();

上述Demo只是实现了一个很简单的Nodejs与C++实现数据交互的逻辑,在实际运用中可能会引入头文件(放到 include内) 还有dll、llib文件(放到lib目录内) 打包后如果缺失dll文件就会报错。又或是在C++线程回调中执行Nodejs传入的回调函数并传入C++执行结果,所以还需要在实践中一点一点积累经验。

void onCallStateChange(CALL_STATUS status) override {
    int* data = new int(status);

    tsfn.BlockingCall(data, [](Napi::Env env, Napi::Function callback, int* data) {
      callback.Call({Napi::Number::New(env, *data)});
      delete data;
    });
  }

我们看上述代码 重写了基类的虚函数,并使用 status 参数初始化了一块堆内存,然后将这个指针传给了线程安全函数,为什么不用栈内存? 是因为 onCallStateChange 执行结束后会销毁栈内存 导致空指针,所以使用堆内存是比较简单的做法。

结束!!!

做electron时间长了会发现我们要面对计算机的各种各样的功能和场景,而不仅仅是写前端页面和单纯的去了解electron,有时还要借助AI 使用C++、Python、Go等等实现一些前端触及不到的功能逻辑,使用第三方封装好的C++工具等等,当我们解决了一个又一个自己认为很难的问题就说明我们一直在成长,作为一个计算机软件爱好者,也希望我和大家的路越走越远。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984791.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vscode(cursor)配置python环境,含远程调试

一、本地配置 1.1 安装python插件 1.2 配置python环境 在右下角就可以切换python环境&#xff0c;太简单了&#xff01; 1.3 Debug说明 打断点直接开启&#xff01; 在debug的过程中&#xff0c;还可以输入打印中间变量或者做一些测试 二、远程连接 2.1 下载远程工具 2.2 连…

S19文件格式详解:汽车ECU软件升级中的核心镜像格式

文章目录 引言一、S19文件格式的起源与概述二、S19文件的核心结构三、S19在汽车ECU升级中的应用场景四、S19与其他格式的对比五、S19文件实例解析六、工具链支持与安全考量七、未来趋势与挑战结语引言 在汽车电子控制单元(ECU)的软件升级过程中,S19文件(也称为Motorola S-…

怎么使用数据集微调大模型LLM

怎么使用数据集微调大模型LLM 目录 怎么使用数据集微调大模型LLM项目运行后目录结构1. 导入必要的库2. 准备训练数据3. 加载模型与分词器4. 数据预处理5. 配置训练参数(CPU 专用)6. 训练与保存完整可运行代码,调试了2天,保证可用项目运行后目录结构 1. 导入必要的库 from …

深度评测DeepSeek、ChatGPT O1和谷歌Gemini AI应用开发场景 - DeepSeek性能完胜!

下面我会展示我为期一周的实验结果&#xff0c;创作不宜&#xff0c;希望大家关注我&#xff0c;以后多多互3&#xff01;前一阵我在互联网上看到很多关于DeepSeek R1的讨论&#xff0c;这个开源模型据说可以媲美&#xff0c;甚至优于像OpenAI o1这样的付费模型。 由于我在日常…

ubuntu局域网部署stable-diffusion-webui记录

需要局域网访问&#xff0c;如下设置&#xff1a; 过程记录查看源码&#xff1a; 查看源码&#xff0c;原来修改参数&#xff1a;--server-name 故启动&#xff1a; ./webui.sh --server-name0.0.0.0 安装下载记录&#xff1a; 快速下载可设置&#xff1a; export HF_ENDPOI…

数智读书笔记系列015 探索思维黑箱:《心智社会:从细胞到人工智能,人类思维的优雅解读》读书笔记

引言 《The Society of Mind》&#xff08;《心智社会》&#xff09;的作者马文・明斯基&#xff08;Marvin Minsky&#xff09;&#xff0c;是人工智能领域的先驱和奠基者之一 &#xff0c;1969 年获得图灵奖&#xff0c;被广泛认为是对人工智能领域影响最大的科学家之一。他…

2.1 Vite + Vue 3 + TS 项目脚手架深度配置

文章目录 **一、环境准备与技术选型****二、项目初始化与基础架构****三、工程化配置深度优化****四、代码规范与质量保障****五、Vue 3 深度集成****六、TypeScript 高级配置****七、第三方库集成****八、构建优化策略****九、企业级最佳实践****十、扩展配置参考****本章核心…

利用FatJar彻底解决Jar包冲突(一)

利用FatJar彻底解决Jar包冲突 序FatJar的加载与隔离⼀、 FatJar概念⼆、FatJar的加载三、FatJar的隔离四、隔离机制验证五、 FatJar的定位六、 打包注意点 序 今天整理旧电脑里的资料&#xff0c;偶然翻到大概10年前实习时写的笔记&#xff0c;之前经常遇到Java依赖冲突的问题…

C/C++蓝桥杯算法真题打卡(Day4)

一、P11041 [蓝桥杯 2024 省 Java B] 报数游戏 - 洛谷 算法代码&#xff1a; #include<bits/stdc.h> using namespace std;// 计算第 n 个满足条件的数 long long findNthNumber(long long n) {long long low 1, high 1e18; // 二分查找范围while (low < high) {lo…

DeepSeek大语言模型下几个常用术语

昨天刷B站看到复旦赵斌老师说的一句话“科幻电影里在人脑中植入芯片或许在当下无法实现&#xff0c;但当下可以借助AI人工智能实现人类第二脑”&#xff08;大概是这个意思&#xff09; &#x1f49e;更多内容&#xff0c;可关注公众号“ 一名程序媛 ”&#xff0c;我们一起从 …

快速从C过度C++(一):namespace,C++的输入和输出,缺省参数,函数重载

&#x1f4dd;前言&#xff1a; 本文章适合有一定C语言编程基础的读者浏览&#xff0c;主要介绍从C语言到C过度&#xff0c;我们首先要掌握的一些基础知识&#xff0c;以便于我们快速进入C的学习&#xff0c;为后面的学习打下基础。 这篇文章的主要内容有&#xff1a; 1&#x…

IDEA接入阿里云百炼中免费的通义千问[2025版]

安装deepseek 上一篇文章IDEA安装deepseek最新教程2025中说明了怎么用idea安装codeGPT插件&#xff0c;并接入DeepSeek&#xff0c;无奈接入的官方api已经不能使用了&#xff0c;所以我们尝试从其他地方接入 阿里云百炼https://bailian.console.aliyun.com/ 阿里云百炼‌是阿…

六十天前端强化训练之第十三天之JavaScript 原型与继承详解

欢迎来到编程星辰海的博客讲解 目录 一、原型系统底层探秘 1.1 对象体系构建原理 内存模型示意图 1.2 原型链工作机制 1.3 原型相关方法详解 (1) Object.getPrototypeOf (2) Object.setPrototypeOf (3) Object.create 1.4 构造函数运行机制 二、八种继承模式深度剖析…

大语言模型学习--向量数据库基础知识

1.向量 向量是多维数据空间中的一个坐标点。 向量类型 图像向量 文本向量 语音向量 Embedding 非结构化数据转换为向量过程 通过深度学习训练&#xff0c;将真实世界离散数据&#xff0c;投影到高维数据空间上&#xff0c;通过数据在空间中间的距离体现真实世界的相似度 V…

Python语法核心架构与核心知识点:从理论到实践

一、Python的核心设计哲学 Python以“简洁优雅”为核心理念&#xff0c;遵循以下原则&#xff1a; # Zen of Python&#xff08;输入 import this 可查看&#xff09; >>> import this The Zen of Python, by Tim Peters ... Simple is better than complex. Readab…

mac本地部署Qwq-32b记录

导语 昨天看到阿里开源了Qwq-32b&#xff0c;号称性能可以媲美Deepseek-R1。今天晚上有空就在Mac上折腾了一下&#xff0c;使用ollma进行了部署&#xff0c;效果感觉还不错&#xff0c;特此记录。 环境 硬件 型号&#xff1a;Macbook M1 Pro 14寸内存&#xff1a;512G 环境…

Vue3 Pinia 符合直觉的Vue.js状态管理库

Pinia 符合直觉的Vue.js状态管理库 什么时候使用Pinia 当两个关系非常远的组件&#xff0c;要传递参数时使用Pinia组件的公共参数使用Pinia

JAVA面试_进阶部分_深入理解socket网络异常

在各种网络异常情况的背后&#xff0c;TCP是怎么处理的&#xff1f;又是怎样把处理结果反馈给上层应用的&#xff1f;本文就来讨论这个问题。 分为两个场景来讨论 建立连接 1 正常情况下 进过三次握手&#xff0c;客户端连接成功&#xff0c;服务端有一个新连接到来。 2 客…

警惕AI神话破灭:深度解析大模型缺陷与禁用场景指南

摘要 当前AI大模型虽展现强大能力&#xff0c;但其本质缺陷可能引发系统性风险。本文从认知鸿沟、数据困境、伦理雷区、技术瓶颈四大维度剖析大模型局限性&#xff0c;揭示医疗诊断、法律决策等8类禁用场景&#xff0c;提出可信AI建设框架与用户防护策略。通过理论分析与实操案…

《用Python+PyGame开发双人生存游戏!源码解析+完整开发思路分享》

导语​ "你是否想过用Python开发一款可玩性高的双人合作游戏&#xff1f;本文将分享如何从零开始实现一款类《吸血鬼幸存者》的生存射击游戏&#xff01;包含完整源码解析、角色系统设计、敌人AI逻辑等核心技术点&#xff0c;文末提供完整代码包下载&#xff01;" 哈…