nwjs ffi dll调用实现方案

nodejs ffi 调用dll 方法&过程

背景

Java 调用dll过程中 JNI的方式,存在隐患,dll经常挂掉,为此采用nodejs 调用dll进行封装

开发环境

  • win7 sp1 + nodejs 0.12.3 + python2.7 + vs2013

1.Win7 sp1 (x64操作系统)

  • Win7系统一定要是sp1的,没有sp1补丁自行到官网下载,sp1补丁(最好自己有系统盘,亲测安装补丁过程比安装系统还慢)
  • 如果这个补丁没有更新是装不上Visual Studio 2013的,还是乖乖的用这个版本吧。 (个人使用版本经历,让人泪奔 vs2015 —— vs2017 —— vs2013)

2.Visual Studio 2013

  • 提供基础的编译环境,nodejs modules在重新编译的时候需要依赖它
  • 上面已经说了,老老实实安装吧!!!

3.Python 2.7.6

4.nodeJS 0.12.3

  • 一般dll文件都是32位的,ffi调用时需要使用32位版本的nodejs
  • 下载地址:https://nodejs.org/download/r… 文件是msi格式,直接安装即可,环境变量自动配好不用管。

5.nwjs 0.12.3

开始动手

1.打开DOS界面,安装nw-gyp执行下面的命令:

npm install nw-gyp -g

2.设置vs版本:

npm config set msvs_version 2013 –global (2013是visual studio的版本,你也可以尝试其他版本,我是放弃了)

1.新建一个项目文件夹,创建index.html和package.json文件:
(测试是用的windows系统里的消息窗口dll文件)

<html lang=”en”>
<head>

   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <title>nwjs call DLL</title>
   <meta name="description" content="">
   <meta name="viewport" content="width=device-width, initial-scale=1">        

</head>
<body>
<script type=”text/javascript”>

   var FFI = require('ffi');
   function TEXT(text){
      return new Buffer(text, 'ucs2').toString('binary');
   }
   var user32 = new FFI.Library('user32', {
      'MessageBoxW': 
      [
         'int32', [ 'int32', 'string', 'string', 'int32' ]
      ]
   });
   var OK_or_Cancel = user32.MessageBoxW(
      0, TEXT('I am Node.JS!'), TEXT('Hello, World!'), 1
   );
   console.log(OK_or_Cancel);

</script>
</body>
</html>

package.json
{

“name”: “test”,

“version”: “1.0.1”,

“main”: “index.html”,

“webkit”:{

"plugin" : true<br>

}

}

注意package.json文件里需要添加webkit属性来说明你需要调用第三方包。

2.DOS界面下进入项目文件夹,安装ffi和ref:

npm install ffi

npm install ref

3.编译ffi和ref模块,因为ffi中包含ref所以先编译ref再编译ffi,依次的命令如下:

进入’项目目录node_modulesffinode_modulesref’,执行nw-gyp rebuild –target=0.12.3
(0.12.3是你的nwjs版本,如果不是这个版本改为你自己的即可)

进入’项目目录node_modulesffi’,执行nw-gyp rebuild –target=0.12.3

4.最后在你的项目文件夹下将所有文件打包为zip格式,将zip文件拖进node-webkit目录下的nw.exe文件上执行即可。

5.看到界面上弹出“I am Node.JS!”说明成功了!

参考:https://cnodejs.org/topic/541…

提示:

1.由于ffi模块是为C语言的dll包服务的,所以在编写的dll的C++源代码时必须要有 extern “C” 来修饰。例如在你的c++代码里需要这样声明函数才能有效(在.h文件中声明时使用):

extern “C” DLLSERVER_API int sumInDll(int n1, int n2);

2.ffi在使用时需要匹配对应的环境,如果是32位dll文件,那么在安装nodeJS,python和nwjs都需要用对应的32位版本;如果是64位dll文件,则对应的软件都需要是64位的;

3.ffi在用的时候还是挺方便的,只要环境匹配对就可以。唯一的麻烦是它需要对源dll代码声明时进行修改,这个就不如addon一劳永逸了。所以如果引用的dll文件比较多,或者是引入第三方dll文件,ffi的方法就不可取。

遇到问题列表

1、winxp 系统下dll错误

nodejs的ffi库是一个非常好用的调用dll的库,尤其是在使用nwjs进行桌面应用开发的时候。

安装、编译、使用ffi库的方法比较简单,网上也有很多教程,但是当我们在win7或者更高的Windows系统中编译好了ffi模块,开发好了应用后会发现在Windows XP上无法require我们的ffi,及时将编译好的ffi_bindings.node单独require也无法使用,require的时候会出现“Error: The specified procedure could not be found”的错误。

经过几天的研究,终于找到了解决方案。在能够正常编译和引用ffi的Windows系统上,进入ffi文件夹中的src文件夹,找到文件“win32-dlfcn.cc”,并将里面的地96行和第99行的两行代码(我是用的ffi版本为2.2.0,其他版本还没有确定是不是这两行),对应的代码应该是:

errorMode = GetErrorMode();

SetErrorMode(errorMode | SEM_FAILCRITICALERRORS);

将这两行代码注释掉,然后在从命令行进入到ffi根目录,执行“node-gyp rebuild”命令(或者“nw-gyp rebuild”命令,如果想要在nwjs中使用),编译出来的ffi库就可以在Windows XP上正常使用了。

参考文章:https://blog.csdn.net/wuya199…

2、nodejs 读取中文乱码问题 (iconv-lite)

var ref = require(“ref”);
var ffi = require(“ffi”);
var iconv = require(‘iconv-lite’);
var DLL = new ffi.Library(‘sample.dll’, {
‘readData’: [‘int’, [‘string’, ‘string’]],
‘sendMsg’: [‘string’, [‘string’, ‘string’]]
});

module.exports = {

sendMsg: function(sendMsg){
    var maxRTNNameLength = 4096;
    var rtnRef = new Buffer(maxRTNNameLength);
    console.log("send:",sendMsg);
    var rtn = DLL.sendMsg(sendMsg, rtnRef);
    var as_data = iconv.decode(rtnRef, 'GBK');//解码成utf8.
    var userbuffer = iconv.encode(as_data, 'gbk');//utf8数据编码成gbk
    var as_data = iconv.decode(rtnRef, 'GBK');//解码成utf8.
    return as_data;
},

readData:function(){
    var maxIdCardLength = 19;
    var idCardRef = new Buffer(maxIdCardLength);
    var maxNameLength = 31;
    var nameRef = new Buffer(maxNameLength);
    if (DLL.readData(idCardRef, nameRef) == 0) {
        var name = ref.readCString(nameRef, 0);
        var card = ref.readCString(idCardRef, 1);
        return {name: name, card: card};
    }
}

}

经过几天的努力完成了程序的调用,该遇到的问题基本都遇到了,希望本篇文章对大家有帮助!

    原文作者:jessy
    原文地址: https://segmentfault.com/a/1190000016970622
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞