当前位置: 首页 > news >正文

在Electron中通过Node-API调用DLL导出函数的完整指南

文章参考:我是如何在electron上调用dll的导出函数的 - node-addon-api

一、背景与需求

在Electron项目中直接通过FFI库(如koffi)调用包含复杂C++标准库类型(如std::stringstd::vector)的DLL时,手动构造结构体和函数原型效率低下。本文提供一种高效方案:通过C++编写Node原生模块,桥接Electron与业务DLL,适用于已有大型C++项目与Electron集成场景。


二、技术选型与参考

  • 核心工具
    • Node-API:用于构建跨版本Node模块的C接口
    • node-addon-api:Node-API的C++封装库(GitHub)
    • node-gyp:Node原生模块构建工具
  • 参考文档
    • Node.js C++插件文档
    • Node-API官方指南

三、实现步骤

1. 环境准备

  • 安装 Visual Studio(确保包含C++桌面开发组件)
  • 安装 Python 3.xnode-gyp
    npm install -g node-gyp
    

2. 创建Node模块项目

mkdir electron-dll-bridge && cd electron-dll-bridge
npm init -y
npm install node-addon-api

3. 编写C++模块代码

module.cpp

#include <napi.h>
#include <Windows.h>
#include <string>

// 声明DLL导出函数(示例)
extern "C" __declspec(dllimport) int AddFunc(int a, int b);
extern "C" __declspec(dllimport) const char* StrAddFunc(const char* a, const char* b);

// 封装AddFunc
Napi::Number AddFuncWrapper(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  int a = info[0].As<Napi::Number>();
  int b = info[1].As<Napi::Number>();
  return Napi::Number::New(env, AddFunc(a, b));
}

// 封装StrAddFunc
Napi::String StrAddFuncWrapper(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  std::string a = info[0].As<Napi::String>();
  std::string b = info[1].As<Napi::String>();
  return Napi::String::New(env, StrAddFunc(a.c_str(), b.c_str()));
}

// 模块初始化
Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set("add", Napi::Function::New(env, AddFuncWrapper));
  exports.Set("strAdd", Napi::Function::New(env, StrAddFuncWrapper));
  return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)

4. 配置构建文件

binding.gyp

{
  "targets": [
    {
      "target_name": "bridge",
      "sources": ["module.cpp"],
      "include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
      "dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
      "libraries": ["$(ProjectDir)../libs/your_dll.lib"]  # 指向DLL的LIB文件
    }
  ]
}

5. 编译模块

node-gyp configure
node-gyp build --arch=x64 --msvs_version=2022

编译产物:build/Release/bridge.node


四、Electron集成

1. 文件部署

  • 将编译后的bridge.node和依赖的DLL文件(如your_dll.dll)放置于Electron项目的native目录
  • 配置package.json确保打包包含原生模块:
{
  "build": {
    "extraResources": [
      { "from": "native/*", "to": "./" }
    ]
  }
}

2. 在Electron中调用

import path from 'path';
import { app } from 'electron';

const nativeModule = require(path.join(app.getAppPath(), 'native', 'bridge.node'));

// 调用示例
console.log(nativeModule.add(3, 5)); // 输出: 8
console.log(nativeModule.strAdd("Hello", "World")); // 输出: HelloWorld

五、调试与打包优化

1. 调试配置

  • 附加调试进程:在Visual Studio中使用调试 -> 附加到进程,选择Electron主进程
  • 断点设置:确保调试版.node文件与Electron加载的模块路径一致

2. 自动更新模块

在Visual Studio的生成后事件中添加命令,自动复制新编译的模块:

xcopy /Y "$(ProjectDir)build\Release\bridge.node" "$(SolutionDir)electron-app\native\"

3. 打包问题解决

  • 问题:打包后DLL文件丢失
  • 解决方案
    1. electron-builder配置中声明依赖文件:
    "extraFiles": [
      "native/your_dll.dll"
    ]
    
    1. 运行时动态设置DLL搜索路径:
    #include <Windows.h>
    SetDllDirectory(L"native");
    

六、注意事项

  1. ABI兼容性:确保DLL与Node模块使用相同的运行时库(如MT/MD)
  2. 异常处理:使用Napi::Error::New().ThrowAsJavaScriptException()传递错误
  3. 内存管理:避免在C++中分配需由JavaScript释放的内存


文章转载自:

http://KWzzmIZu.zLxrg.cn
http://6xz2GBV5.zLxrg.cn
http://9BOCEslQ.zLxrg.cn
http://yF2sMHPI.zLxrg.cn
http://21TwO9XN.zLxrg.cn
http://JBtzsyG2.zLxrg.cn
http://4CbHjSmz.zLxrg.cn
http://8sONHAVE.zLxrg.cn
http://ilb2kyh0.zLxrg.cn
http://3DLK9xk3.zLxrg.cn
http://UhEMcUYK.zLxrg.cn
http://WIP4DJ9v.zLxrg.cn
http://BosuZ2Zi.zLxrg.cn
http://kFvKO8aL.zLxrg.cn
http://z4MNjB2d.zLxrg.cn
http://gwmVnXpD.zLxrg.cn
http://SNwEKxyj.zLxrg.cn
http://ffYWjWIb.zLxrg.cn
http://Svt6kses.zLxrg.cn
http://fCkLGC45.zLxrg.cn
http://fOO7oFjx.zLxrg.cn
http://zIgvLkHQ.zLxrg.cn
http://Qqzr3WLa.zLxrg.cn
http://ytLqP9Q5.zLxrg.cn
http://wNPi6yMd.zLxrg.cn
http://GicJSN6R.zLxrg.cn
http://3nnZIJV9.zLxrg.cn
http://Unvq0uOj.zLxrg.cn
http://4IevDNU6.zLxrg.cn
http://YMlljfTJ.zLxrg.cn
http://www.dtcms.com/a/52719.html

相关文章:

  • 神经网络前向微分和后向微分区别
  • 面试题汇总(一)
  • 机器学习4-PCA降维
  • CMake学习笔记(一):工程的新建和如何将源文件生成二进制文件
  • conda 更换镜像究极方法
  • 新品速递 | 多通道可编程衰减器+矩阵系统,如何破解复杂通信测试难题?
  • YOLO11改进-模块-引入多域学习MDL(Multi-Domain Learning) 使用频域增强图像特征
  • jQuery UI 简介
  • IntelliJ IDEA集成MarsCode AI
  • Java开发的AI应用框架简述——LangChain4j、Spring AI、Agent-Flex
  • 将PDF转为Word的在线工具
  • 从@Param注解开始,深入了解 MyBatis 参数映射的原理
  • 3.6V-30V宽压输入降压同步IC内置MOS,电流4A/5A/6A,可以满足汽车应急电源,BMS电池,电池组USB口输出等储能应用
  • SpringBoot 校园新闻网站
  • python网络爬虫开发实战之基本库使用
  • 基于qt的桌面宠物——纳西妲源码纯享
  • CS144 Lab Checkpoint 5: down the stack (the network interface)
  • http status是什么?常见的http状态码指的是什么意思?
  • FPGA开发,使用Deepseek V3还是R1(9):FPGA的全流程(详细版)
  • C语言_图书管理系统_借阅系统管理
  • 极狐GitLab 17.9 正式发布,40+ DevSecOps 重点功能解读【三】
  • 毕业项目推荐:基于yolov8/yolov5/yolo11的暴力行为检测识别系统(python+卷积神经网络)
  • C# Unity 唐老狮 No.6 模拟面试题
  • DRMPlaneType里有VIG, DMA,和RGB三种类型,这是不是说明DRMPlane就是代表DPU里的Pipeline
  • ESP32S3读取数字麦克风INMP441的音频数据
  • 尚庭公寓项目记录
  • el-tree右键节点动态位置展示菜单;el-tree的节点图片动态根据节点属性color改变背景色;加遮罩层(opacity)
  • 数据挖掘校招面经一
  • 算法比赛中处理输入和输出
  • rabbitmq版本升级并部署高可用