QT 进程共享内存可以借助 QT 提供的 QSharedMemory 类实现,其是跨平台的。在 Windows 下,其基于 Windows File Mapping 技术实现内存共享。
NodeJS 进程需要通过编写 Addon(插件)的方式来访问 Window File Mapping API。编写 NodeJS Addon(插件)需要借助 node-addon-api 技术。
有关 Window File Mapping API,微软官方提供了 创建命名的共享内存 - Microsoft Learn 简单示例。
1 QT 写进程
#include <QCoreApplication>
#include <QSharedMemory>
#include <QDebug>
#include <cstring>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 创建 QSharedMemory 实例
QSharedMemory sharedMemory;
// 注意:设置本地 KEY
sharedMemory.setNativeKey(QString::fromUtf8("MyTestSharedMemory"));
// 创建共享内存,会自动调用 attach()
if (!sharedMemory.create(1024, QSharedMemory::ReadWrite)) {
qDebug() << "created failed";
return -1;
}
qDebug() << "created successfully";
// 绑定共享内存
if (!sharedMemory.isAttached()) {
qDebug() << "attached failed";
return -1;
}
qDebug() << "attached successfully";
// 定时写入动态数据
QTimer timer;
int count = 0;
QObject::connect(&timer, &QTimer::timeout, [&]() {
QString msg("hello data from shared memory!, index: ");
msg.append(QString::number(count));
const char * data_shm = msg.toStdString().c_str();
// 向共享内存写入数据
std::memcpy(sharedMemory.data(), data_shm, std::strlen(data_shm) + 1);
qDebug() << "write data: " << data_shm;
count += 1;
});
timer.start(1000); // 每秒更新一次
return a.exec();
}
⚠️ 注意
由于此需求需要 QT 进程与 Node.js 进程通过共享通信,属于 QT 进程与非 QT 进程的交互场景,在创建 QSharedMemory 实例后,需要通过 setNativeKey() 方法设置本地 KEY。
2 Node.js 读进程
2.1 Node.js 读进程
node-filemap-buf Gitee 项目,其基于 node-addon-api 编写插件,通过调用 Window File Mapping 的 API 实现进程间内存共享。
- 将 node-filemap-buf 项目克隆下来。将 node-filemap-buf\addon 目录下 addon.cc、binding.gyp、filemap.cc、filemap.h 拷贝到你的 Node.js 工程根目录下。
- 使用 npm install -g node-gyp 命令全局安装 node-gyp 编译工具。
- 使用 npm install node-addon-api 命令本地安装 node-addon-api 依赖库
- 使用 node-gyp configure 命令配置工程,会生成 build 目录
- 使用 node-gyp build 编译工程,编译完成,会在 build/Release 目录下生成 addon.node 模块。
使用 build/release/addon.node 模块编写 Node.js 读进程代码:
var addon = require('./3rd/addon')
var mapName = "MyTestSharedMemory";
var bufSize = 1024;
var fileMap = new addon.FileMap();
var buf = fileMap.open(mapName,bufSize,0);
if(buf)
{
console.log(`buf size=${buf.length}`);
console.log(new String(buf));
}
2.2 Electron 读进程
过程完全参考 2.1 Node.js 读进程,但是对 node-filemap-buf 中的 filemap.cc 代码需要进行一点点修改,从而兼容 Electron 环境。
filemap.cc 中
Napi::Value FileMap::create(const Napi::CallbackInfo& info)
{
...
return Napi::Buffer<char>::New(env,(char*)pBuf, bufSize);
}
和
Napi::Value FileMap::open(const Napi::CallbackInfo& info)
{
...
return Napi::Buffer<char>::New(env,(char*)buffer, bufSize);
}
的 return 语句与 Electron 不兼容。错误原因为:Napi::Buffer::New 方法直接将共享内存的指针暴露给 JavaScript。这种方式在标准 Node.js 环境中可能有效,但在 Electron 的 Node.js 环境中,外部缓冲区的管理受到限制,尤其是涉及共享内存时。
Electron 的 Node.js 环境可能会拒绝外部缓冲区的使用,因为它无法保证共享内存的生命周期和安全性。
为了解决上述问题,需要将 return 语句替换成:
// 创建一个新的缓冲区并复制数据
Napi::Buffer<char> resultBuffer = Napi::Buffer<char>::Copy(env, buffer, bufSize);
return resultBuffer;
参考链接:nodejs如何实现基于window File Mapping的进程间通信(IPC)_node addon发送ipc-优快云博客