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
- 提供基础的编译环境,nodejs modules在重新编译的时候需要依赖它
- 下载地址 : https://www.python.org/ftp/py...
4.nodeJS 0.12.3
- 一般dll文件都是32位的,ffi调用时需要使用32位版本的nodejs
- 下载地址:https://nodejs.org/download/r... 文件是msi格式,直接安装即可,环境变量自动配好不用管。
5.nwjs 0.12.3
- 为保证程序可以在xp下使用不是使用太高的版本我选择了0.12.3版本
- 下载地址:http://dl.nwjs.io/v0.12.3/nwj...
开始动手
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}; } }
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。