在 Electron(Node.js)中调用 Windows DLL,主流有 纯 JS 直接调用 、C++ 原生扩展 、进程间通信 三大类方案。下面按 推荐度 + 上手难度 详细说明。
一、纯 JS 直接调用(最常用,无 C++ 代码)
1. Koffi(现代首选,2025--2026 推荐)
- 优势 :维护活跃、性能好、兼容新版 Electron、支持同步/异步、结构体、回调、
stdcall/cdecl - 依赖 :
koffi(无需ref/ref-struct) - 安装
bash
npm install koffi
- 示例(调用 DLL 函数)
javascript
const koffi = require('koffi');
// 1. 加载 DLL
const lib = koffi.load('./mydll.dll'); // 或绝对路径
// 2. 声明函数:返回类型 + [参数类型]
// 例:int Add(int a, int b);
const Add = lib.func('Add', 'int', ['int', 'int']);
// 3. 调用
const sum = Add(10, 20);
console.log(sum); // 30
// 4. 结构体/指针(简单示例)
const Point = koffi.struct({ x: 'int', y: 'int' });
const GetPoint = lib.func('GetPoint', Point, []);
const pt = GetPoint();
console.log(pt.x, pt.y);
- 适用 :绝大多数场景(硬件 SDK、系统 DLL、第三方库)
2. ffi-napi + ref-napi(传统主流)
- 优势:生态成熟、文档多、大量旧项目在用
- 问题 :Electron 20.3.8+ 可能内存保护报错,需用 fork 版或降级
- 安装
bash
npm install ffi-napi ref-napi ref-struct-napi
- 示例
javascript
const ffi = require('ffi-napi');
const ref = require('ref-napi');
const lib = ffi.Library('./mydll.dll', {
Add: ['int', ['int', 'int']]
});
const sum = lib.Add(10, 20);
console.log(sum);
- 适用 :老项目维护 ;不推荐新项目
二、C++ Node 扩展(性能/复杂交互首选)
1. Node-API (N-API) + node-addon-api
- 原理 :用 C++ 写一层 Node 原生扩展(.node) ,在 C++ 里
LoadLibrary/调用 DLL,再暴露给 JS - 优势 :最稳定、性能最高、复杂结构体/回调/内存完全可控
- 劣势 :必须会 C++ 、需配置
binding.gyp、跨平台编译 - 步骤
- 初始化
bash
npm init
npm install node-addon-api
npm install -g node-gyp
- 编写
binding.gyp
json
{
"targets": [{
"target_name": "mydll_bridge",
"sources": ["bridge.cpp"],
"include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
"libraries": ["../libs/mydll.lib"] // 配套 .lib
}]
}
- 编写
bridge.cpp(C++ 桥接)
cpp
#include <napi.h>
#include <windows.h>
typedef int (*AddFunc)(int, int);
Napi::Value Add(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
HMODULE dll = LoadLibraryA("./mydll.dll");
AddFunc add = (AddFunc)GetProcAddress(dll, "Add");
int a = info[0].As<Napi::Number>();
int b = info[1].As<Napi::Number>();
int res = add(a, b);
FreeLibrary(dll);
return Napi::Number::New(env, res);
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("Add", Napi::Function::New(env, Add));
return exports;
}
NODE_API_MODULE(mydll_bridge, Init)
- 编译
bash
node-gyp configure
node-gyp build --arch=x64
- JS 调用
javascript
const bridge = require('./build/Release/mydll_bridge.node');
console.log(bridge.Add(10, 20)); // 30
- 适用 :高频调用、复杂回调、硬件 SDK、内存敏感场景
三、其他方案(特殊场景)
1. edge-js(C#/.NET DLL)
- 直接调用 .NET Framework/.NET Core DLL(C#/VB)
bash
npm install edge-js
javascript
const edge = require('edge-js');
const hello = edge.func(() => {
/*
async (string input) => {
return "Hello " + input;
}
*/
});
hello("Electron", (err, res) => { console.log(res); });
- 适用 :仅 .NET DLL
2. 进程间通信(IPC/管道/HTTP)
- 写一个 C++/C# 控制台程序 封装 DLL,Electron 通过
child_process/ WebSocket / HTTP 通信 - 优势:完全隔离崩溃、跨语言、不污染主进程
- 劣势 :性能低、通信复杂
- 适用 :DLL 不稳定易崩溃、需独立进程隔离
3. Win32 API 直接调用(系统 DLL)
user32.dll/kernel32.dll用 Koffi/ffi-napi 直接调用
javascript
// Koffi 调用 MessageBoxW
const user32 = koffi.load('user32.dll');
const MessageBoxW = user32.func('MessageBoxW', 'int', ['ptr', 'wstr', 'wstr', 'uint']);
MessageBoxW(null, 'Hello Electron', 'Title', 0);
四、方案对比(2026 最新)
| 方案 | 上手难度 | 稳定性 | 性能 | 维护 | 适用场景 |
|---|---|---|---|---|---|
| Koffi | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 活跃 | 新项目首选、绝大多数 DLL |
| ffi-napi | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | 停滞 | 老项目、兼容旧 Electron |
| Node-API 扩展 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 官方 | 高性能、复杂交互、硬件 SDK |
| edge-js | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | 一般 | 仅 .NET DLL |
| IPC 进程 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐ | 高 | 隔离不稳定 DLL |
五、常见坑与解决
- 架构不匹配
- DLL 是 x86 → Electron 必须用 32 位(
--arch=ia32) - DLL 是 x64 → Electron 必须用 64 位
- DLL 是 x86 → Electron 必须用 32 位(
- 依赖缺失(报错 126/127)
- 用 Dependency Walker 查看缺少 VC++ 运行库/Qt 等
- 把依赖 DLL 放到 同目录
- 调用约定
- Koffi:自动支持
stdcall(Windows API)/cdecl - ffi-napi:
ffi.C(cdecl)/ffi.Win32(stdcall)
- Koffi:自动支持
- Electron 版本兼容
- 优先 Koffi:兼容最新版
- ffi-napi:20.3.8+ 报错 → 用
ffi-napi@latest或 fork 版
六、推荐路线
- 新项目、无 C++ → Koffi
- 老项目、已用 ffi-napi → 继续维护
- 高性能/硬件 SDK/复杂回调 → Node-API 扩展
- 仅 .NET DLL → edge-js
- DLL 易崩溃 → 独立进程 IPC