看到标题首先想到的就是利用Visual studio创建各种各样的窗口应用,我这里讲的是利用node构建Windows窗口应用,窗口中运行Html。我这里主要讲的是nodejs与c++交互。搭建Windows窗口应用看看下面两篇文章
nodejs的桌面应用(electron)
用nodejs开发桌面应用
electron 地址
环境
node -v v10.15.3
依赖库
"dependencies": {
"bindings": "^1.5.0",
"electron": "^5.0.2",
"electron-packager": "^13.1.1",
"electron-squirrel-startup": "^1.0.0",
"nan": "^2.14.0",
"say": "^0.16.0"
},
"devDependencies": {
"electron-rebuild": "^1.8.5",
"electron-winstaller": "^3.0.4"
},
nan库 nodejs与c++
say库 语音播放
bindings 打包后的c++通过这个引入模块(var kinect = require('bindings')('kinect.node')
)
Hello world!
应用创建成功会有以下生命周期函数
/*窗口初始化*/
function createWindow() {
//创建浏览器窗口
homeWinodw = new BrowserWindow({
width: 3300,
height: 1200,
frame: false,
fullscreen:true,
webPreferences: {
nodeIntegration: true
},
backgroundColor: "#2577c2"
})
homeWinodw.loadFile('Home.html')
//打开开发者工具
//homeWinodw.webContents.openDevTools()
//createChildWindow()
homeWinodw.on("close", () => {
homeWinodw = null
})
}
/*生命周期*/
app.on("ready", createWindow)
app.on("window-all-closed", () => {
console.log("close")
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (homeWinodw === null) {
createWindow();
}
})
nodejs 与c++交互- 打包引用
node.js 查了很多资料可以说是单线程工作的,在与c++ 交互过程中 c++如果是一个循环就是阻塞nodejs 当前线程,为防止线程阻塞,node采用开进程来实现多线程工作
//主进程
var fork = require('child_process').fork;
var child = fork('./resources/app/child.js');
/*子进程监听发送事件*/
child.on('message', function(msg){
console.log('来自子进程消息: ' + msg);
homeWinodw.webContents.send('firstPage', msg);
});
child.on('uncaughtExpection', function(err){
console.log(err);
});
child.send('我向子进程发送消息');
//child.js
process.on('message', function(msg){
console.log('来自主进程: ' + msg)
}
process.send('我向主进程发送消息');
在子进程中引入模块 这里引入的手势控制脚本,代码c++编写先要打包为node插件,这里用了 node-gyp进行打包。
node-gyp安装
npm install -g node-gyp
npm install --global --production windows-build-tools
use
node-gyp configure
node-gyp build
node-gyp rebuild
node-gyp configure错误 可能是python版本错误 需要2.7 也可能是node-gyp.js路径不对 /home/xxx/.nvm/versions/node/v7.8.0/bin/node" "/home/xxx/.nvm/versions/node/v7.8.0/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
需要根据提示找到node-gyp.js所在文件夹node-gyp拷贝一份到指定目录,该目录不存在需要创建。目前是通过拷贝来解决的,也是node-gyp在windows下的一个BUG,别的问题请点击这里
node-gyp build 依赖 binding.gyp 文件
{
"targets": [
{
"target_name": "kinect",
"sources": [ "kinectLib/kinect.cpp"],
"include_dirs": [
"<!(node -e \"require('nan')\")",
"$(KINECTSDK20_DIR)\\inc",
"kinectLib",
"kinectLib\\someThing",
"$(WindowsSDK_IncludePath)",
"$(PATH)",
"$(MSBuildToolsPath32)",
"$(WindowsSDK_ExecutablePath_x86)",
"$(SystemRoot)\SysWow64",
"$(FxCopDir)",
#"D:\\vs2012\\VC\\atlmfc\\include",
"$(VSINSTALLPATH)\\VC\\atlmfc\\include"
],
'library_dirs': [
"kinectLib",
"$(WindowsSDK_MetadataPath)",
#"D:\\vs2012\\VC\\atlmfc\\lib",
"$(VSINSTALLPATH)\\VC\\atlmfc\\lib"
],
"conditions" : [
["target_arch=='ia32'", {
"libraries": [ "-l$(KINECTSDK20_DIR)\\lib\\x86\\kinect20.lib"
#,"-lD:\\sapi\\lib\\sapi.lib"
,"-l$(SPEECH11_DIR)\\lib\\sapi.lib"
]
}],
["target_arch=='x64'", {
"libraries": [ "-l$(KINECTSDK20_DIR)\\lib\\x64\\kinect20.lib"
#,"-lD:\\sapi\\lib\\sapi.lib"
,"-l$(SPEECH11_DIR)\\lib\\sapi.lib"
]
}]
],
"cflags!": [ "-fno-exceptions" ],
"cflags": [ "-std=c++11","-D_FILE_OFFSET_BITS=64"],
"cflags_cc!": [ "-fno-exceptions" ]
}
]
}
有关gyp语法参考此文章 gyp语法规则,打包文件为.cc 或.cpp 文件 要将.h 的内容拷贝至.cpp 文件
nodejs 与c++交互- 交互
交互用到的库 nan库
npm install --save nan
child.js 代码
var kinect = require('bindings')('kinect.node')
// 语音回调函数
kinect.audioReadCallback(function (school) {
console.log(Buffer.from(school.index,'base64').toString());
})
执行代码
kinect.openAudio();
kinect.openAudioEn();
Kinect.cpp
// 出入的是function (school) 将function转化为Callback
NAN_METHOD(audioReadCallback){
if(audioCallback)
{
delete audioCallback;
audioCallback = NULL;
}
audioCallback = new Callback(To<v8::Function>(info[0]).ToLocalChecked());
//设置异步回掉资源
if (!audioAsyncResource)
{
audioAsyncResource = new AsyncResource("nan:audio.nancallback");
}
}
//异步回调识别到的语音代码 传回js
void async_call_Audio(uv_async_t* req){
AudioCallBackData * my_data = static_cast<AudioCallBackData *>(req->data);
if (audioAsyncResource && audioCallback)
{
v8::Local<v8::Object> obj = Nan::New<v8::Object>();
Nan::Set(obj,Nan::New("index").ToLocalChecked(),Nan::New(my_data->audioStr).ToLocalChecked());
Nan::Set(obj,Nan::New("status").ToLocalChecked(),Nan::New<v8::Integer>(my_data->status));
const unsigned argc = 1;
Local<Value> argv[1] = {
obj
};
(*audioCallback).Call(GetCurrentContext()->Global(),argc,argv,audioAsyncResource);
}
uv_close((uv_handle_t*)req,NULL);
delete req;
}
// 请求data
struct AudioCallBackData
{
uv_async_t request;
string audioStr;
AudioCallStatus status;
};
//c++的回调函数 函数中异步请求发送str status给js
void __stdcall onAudioCallBack(string str,AudioCallStatus status)
{
AudioCallBackData * req_data = new AudioCallBackData;
req_data->request.data = req_data;
req_data->audioStr = str;
req_data->status = status;
uv_async_init(uv_default_loop(),&req_data->request,async_call_Audio);
uv_async_send (&req_data->request);
}
//声明对象指针
KinectApp *app;
MKSpeech *speech;
//打开手势控制
NAN_METHOD(open) {
if(SUCCEEDED(OleInitialize(0)))
{
app = new KinectApp();
app->m_setActionCallBack(onActionCallBack);
app->m_setErrorActionCallBack(onErrorCallBack);
app->m_setPanActionCallBack(onPanActionCallBack);
app->open();
speech = new MKSpeech();
speech->m_setErrorActionCallBack(onErrorCallBack);
speech->m_setAudioCallBack(onAudioCallBack);
}
}
//关闭手势控制
NAN_METHOD(close){
CoUninitialize();
}
//打开语音
NAN_METHOD(openAudio)
{
speech->open(0);
}
//打开英文语音
NAN_METHOD(openAudioEn)
{
speech->open(1);
}
//关闭语音
NAN_METHOD(closeAudio)
{
speech->close();
}
//初始化
NAN_MODULE_INIT(Init){
NAN_EXPORT(target, tapGestureCallBack);
NAN_EXPORT(target, panGestureCallBack);
NAN_EXPORT(target, audioReadCallback);
NAN_EXPORT(target, open);
NAN_EXPORT(target, close);
NAN_EXPORT(target, pause);
NAN_EXPORT(target, openAudio);
NAN_EXPORT(target, openAudioEn);
NAN_EXPORT(target, closeAudio);
}
NODE_MODULE(demo, Init)
关于Say库
安装
npm install say --save
使用
使用之前需要修改node_modules下的say库的文件,文件名是win32.js
。修改的内容如下`
// psCommand += `$speak.Speak([Console]::In.ReadToEnd())`
//
// pipedData += text
psCommand += `$speak.Speak('${text}')`
使用的例子如下:
// automatically pick platform
const say = require('say')
// or, override the platform
const Say = require('say').Say
const say1 = new Say('win32')
// Use default system voice and speed
// 第一个参数是文字,第二个参数是语音库的名字,第三个字是语速,第四个是阅读完毕回调函数
say1.speak('你好', null, 1, function(res) {
if (res) {
console.log('语音出错')
} else {
console.log("hhh")
}
})
注意
经过反复尝试,在c++线程中调用回调函数 回调函数中不可以直接调用Callback函数 将值传给js 所以采用了 libuv
多线程之间传递消息 Nan库中也有Nan::AsyncWorker
可调用 都是用 V8 JS引擎
来传递数据。Nan库 我也只了解了个皮毛🤦♀️ 就介绍到这里啦 本文没有介绍到js 传基本数据给c++,只传递了函数 如果我没有说明白 您可以查看这篇文章 C++与Nodejs的交互
如有疑问可联系249086205@qq.com 欢迎交流