构建Windows桌面应用

看到标题首先想到的就是利用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": [
        "

有关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(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(req->data);
    if (audioAsyncResource && audioCallback)
    {
        v8::Local obj = Nan::New();
        Nan::Set(obj,Nan::New("index").ToLocalChecked(),Nan::New(my_data->audioStr).ToLocalChecked());
        Nan::Set(obj,Nan::New("status").ToLocalChecked(),Nan::New(my_data->status));
        const unsigned argc = 1;
        Local 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的交互

如有疑问可联系[email protected] 欢迎交流

你可能感兴趣的:(构建Windows桌面应用)