node/electron插件: 由监听 Windows 打印机状态功能深入理解原生node插件编写过程
写在前面
这里说的插件,其实是基于 node-addon-api 编写的插件。有人会说,其实 github 上已经有人开源的打印机相关的组件。 但是,它不是本人要的。 本人需要的是:第一时间知道打印机的及打印任务的所有状态! ## 最初实现 开始写第一个版本时,因为进度需要,本人快速实现了一个 dll 版本,然后在 electron 中通过 ffi 组件调用本人的 dll 。它工作得很好,但是它调用链中增加了一层 ffi ,让本人很是介意~有点强迫症!!! ## 重写版本 第一个版本功能稳定后,本人深入挖了一下 ffi 的功能实现(本人不是写前端的,node也是初次接触),Get 到它本身也是 C/C++ 实现的组件,然后看了下 node 官方对组件开发的相关介绍,决定绕过 ffi 把本人的 dll 直接变成 node 的插件。
开始填坑
为什么说是开始填坑? 因为本人的功能是 C/C++ & C# 混编的!这中间的坑只填过了,才知深浅。
坑1:项目配置 —— 托管 /clr
node 原生插件开发使用了 gyp
配置,为了方便大家使用,官方提供了开源配置项目 node-gyp,依葫芦画瓢,很快完成了
Hello World.,但是,咱怎么能忘记了混编呢?微软对于 C/C++ &
C# 混编的配置选项叫 /clr 。找到 MSVSSettings.py 中 /clr
注释对应的配置选项为 CompileAsManaged ,当然也有人在 issue 里提了在
AdditionalOptions 里面增加 /clr
,本人不反对,本人也没有验证,而是选择使用开源代码提供的
CompileAsManaged 选项。有过混编经验的都知道,光改完 /clr
是远远不够,还要改程序集等等一堆选项。这里有一个小技巧,就是可以依赖 npm
install 来处理,最终修改到的选项如下: 1
2
3
4
5
6
7"RuntimeLibrary": 2, #MultiThreadedDLL /MD
"Optimization": 2,
"RuntimeTypeInfo": "true",
"CompileAsManaged": "true", # /clr
"DebugInformationFormat": 3, #ProgramDatabase /Zi
"ExceptionHandling": 0, #Async /EHa
"BasicRuntimeChecks": 0, #Default
坑2:项目配置 —— win_delay_load_hook
踩过坑1后,开始写逻辑了,并且也顺利的实现了功能,开始调度时却被告之: >正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。
按第一版的实现,本人知道要在 dll 注册位置加上: 1
#pragma unmanaged
坑3:异步多次回调
node-addon-api 对异步工作有封装,详见 Napi::AsyncWorker 的使用,但是对于多次回调,这个类并没有支持得很好(也有可能是我使用不当),为了解决这个问题,本人翻了很多 github 上的项目,都没有很好的解决,后来在 github 上找到了 node-addon-examples 找到了 node-addon 的 C 实现 async_work_thread_safe_function 的 example 中有较好的实现,对比了它和 Napi::AsyncWorker 的逻辑过程,发现 Napi::AsyncWorker 应该是不能很好的完成本人需要的功能,所以决定自己实现,具体就是把 async_work_thread_safe_function 参照 Napi::AsyncWorker 改成了模板虚基类。感兴趣的可以联系。
坑4:打印机监控线程与回调 JS 线程同步
其实,多线程同步方式有很多,但是为了让 js 线程和工作线程不是一直处于工作状态中,而是有事件时才开始工作和回调,本人选择了 event & critical_section 一起来完成本工作,event 用于打印机事件到达后通知 js 线程取数据,而 critical_section 保证的是对于数据操作的唯一性。我相信大神们肯定有很多别的实现方式,比如说管道等。希望大家提供各种意见吧。
关键实现
1 |
|
1 |
|
```