nodejs调用delphi编写的dll中,使用了dll调用windows api转读取文件属性,感觉使用nodejs也可直接调用windows api。

此处需用到windows系统的version.dll,该dll位于C:\WINDOWS\System32\下,是一个32位的dll,故此处直接使用32位版本的node。

 

一、安装所需模块(node-gyp、ffi、ref、iconv-lite)

npm install node-gyp -g
npm install ffi -- save
npm install ref --save
npm install iconv-lite --save

其中:node-gyp直接全局安装,ffi、ref、iconv-lite安装在项目中即可

PS:

1. ffi与ref的安装需要用到python,需先装好。

2. ffi用来调用dll

3. ref用来设置buffer

4. iconv-lite用来转码GBK字符

 

二、示例,使用nodejs读取文件属性

 1 const ffi = require('ffi');
 2 const ref = require('ref');
 3 const iconvLite = require('iconv-lite');
 4 
 5 // 定义dll
 6 const version = ffi.Library('C://WINDOWS//System32//version', {
 7     'GetFileVersionInfoSizeA': [ 'int', ['string', 'int'] ],
 8     'GetFileVersionInfoA': ['int', ['string', 'int', 'int', ref.refType(ref.types.char)]],
 9     'VerQueryValueA': ['int', [ref.refType(ref.types.char), 'string', ref.refType(ref.types.CString), ref.refType('int')]]
10 });
11 
12 const Int16Format4 = function (num) {
13     const s = '0000';
14     const f = num.toString(16);
15     return s.substr(0, 4 - f.length) + f;
16 };
17 
18 try {
19     console.log('Begin Test');
20     // 转码,windows使用AnsiChar,利用iconv-lite使用gbk解码
21     const file = iconvLite.encode('C:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe', 'gbk');
22 
23     // 获取文件属性大小
24     const size = version.GetFileVersionInfoSizeA(file, 0);
25     console.log('fileInfoSize: ' + size);
26 
27     // 读取文件属性buffer
28     const buf = new Buffer(size);
29     buf.type = ref.types.char;
30     version.GetFileVersionInfoA(file, 0, size, buf);
31 
32     // 读取文件属性的LanguageID和CodePage,用来合成属性查找标记
33     const table = ref.alloc('int32').ref(), length = ref.alloc('int');
34     version.VerQueryValueA(buf, '\\VarFileInfo\\Translation', table, length);
35     const tableBuf = table.deref(); // 其中tableBuf中的值应为int32
36     const codePage = tableBuf.readUInt16LE(0); // codePage应取tableBuf的高16位
37     const languageID = tableBuf.readUInt16LE(2); // languageID应取tableBuf的低16位
38     console.log('codePage: ' + codePage.toString(16));
39     console.log('languageID: ' + languageID.toString(16));
40 
41     // 合成属性查找标记
42     // 不同的语言地区,该值可能不一样,据我所知,中文"\\StringFileInfo\\080403A8\\"、英文"\\StringFileInfo\\040904E4\\",故需通过该方式合成最可靠
43     const pre = '\\StringFileInfo\\' + Int16Format4(codePage) + Int16Format4(languageID) + '\\';
44     console.log('pre: ' + pre);
45 
46     const versionBuf = new Buffer(1000).ref();
47     version.VerQueryValueA(buf, pre + 'FileVersion', versionBuf, length);
48     const infoBuf = versionBuf.deref().slice(0, length.deref());
49     const info = iconvLite.decode(infoBuf, 'gbk');
50     console.log('FileVersion: ' + info);
51     console.log('End Test');
52 } catch(err) {
53     console.log(err);
54 }

运行结果如下:

PS:

1. 传参file最好使用gbk或者gb2312解码,否则如果file中含有中文时,将无法正确读到size、buf

2. versionBuf应尽量给的长一些,再通过length截断,避免数据取值不全

3. pre最好使用这种方式合成得到,否则可能存在读不到属性的情况

 

三、稍微改写上述代码

 1 'use strict';
 2 
 3 /**
 4  *
 5  *
 6  * @author Mai
 7  * @date 2018/7/12
 8  * @version
 9  */
10 
11 const ffi = require('ffi');
12 const ref = require('ref');
13 const iconvLite = require('iconv-lite');
14 
15 // 定义dll
16 const version = ffi.Library('C://WINDOWS//System32//version', {
17     'GetFileVersionInfoSizeA': [ 'int', ['string', 'int'] ],
18     'GetFileVersionInfoA': ['int', ['string', 'int', 'int', ref.refType(ref.types.char)]],
19     'VerQueryValueA': ['int', [ref.refType(ref.types.char), 'string', ref.refType(ref.types.CString), ref.refType('int')]]
20 });
21 
22 const FileVersion = function () {};
23 const proto = FileVersion.prototype;
24 
25 // 16进制format(%4.x)
26 proto._int16Format4 = function (num) {
27     const s = '0000';
28     const f = num.toString(16);
29     return s.substr(0, 4 - f.length) + f;
30 };
31 
32 // 根据属性名读取文件属性
33 proto._getInfo = function (buf, pre, name) {
34     const infoBufPointer = new Buffer(1000).ref(); // 定义指向Buffer地址的指针,Buffer尽量定义长一点
35     const length = ref.alloc('int'); // 定义指向整数的指针
36     if (version.VerQueryValueA(buf, pre + name, infoBufPointer, length) !== 0) {
37         // 根据length,在Buf中截取属性
38         const infoBuf = infoBufPointer.deref().slice(0, length.deref() - 1);
39         return iconvLite.decode(infoBuf, 'gbk');
40     } else {
41         return undefined;
42     }
43 };
44 
45 // 获取属性查找标记
46 proto._getPre = function (buf) {
47     // 读取文件属性的LanguageID和CodePage,用来合成属性查找标记
48     const table = ref.alloc('int32').ref(), length = ref.alloc('int');
49     version.VerQueryValueA(buf, '\\VarFileInfo\\Translation', table, length);
50     const tableBuf = table.deref(); // 其中tableBuf中的值应为int32
51     const codePage = tableBuf.readUInt16LE(0); // codePage应取tableBuf的高16位
52     const languageID = tableBuf.readUInt16LE(2); // languageID应取tableBuf的低16位
53     // 合成属性查找标记
54     // 不同的语言,该值不一样,其中英文是"\\StringFileInfo\\080403A8\\",中文是"\\StringFileInfo\\040904E4\\",需通过该方式合成
55     return '\\StringFileInfo\\' + this._int16Format4(codePage) + this._int16Format4(languageID) + '\\';
56 };
57 
58 // 读取文件属性
59 proto.getFileVersionInfo = function (path) {
60     // 转码,windows使用AnsiChar,利用iconv-lite使用gb2312解码
61     const file = iconvLite.encode(path, 'gb2312');
62     // 获取文件属性大小
63     const size = version.GetFileVersionInfoSizeA(file, 0);
64 
65     // 读取文件属性buffer
66     const buf = new Buffer(size);
67     buf.type = ref.types.char;
68     version.GetFileVersionInfoA(file, 0, size, buf);
69 
70     // 获取文件属性查找标记
71     const pre = this._getPre(buf);
72 
73     // 读取文件属性
74     const fileVersionInfo = {};
75     fileVersionInfo.CompanyName = this._getInfo(buf, pre, 'CompanyName');
76     fileVersionInfo.FileDescription = this._getInfo(buf, pre, 'FileDescription');
77     fileVersionInfo.FileVersion = this._getInfo(buf, pre, 'FileVersion');
78     fileVersionInfo.InternalName = this._getInfo(buf, pre, 'InternalName');
79     fileVersionInfo.LegalCopyright = this._getInfo(buf, pre, 'LegalCopyright');
80     fileVersionInfo.LegalTrademarks = this._getInfo(buf, pre, 'LegalTrademarks');
81     fileVersionInfo.OriginalFilename = this._getInfo(buf, pre, 'OriginalFilename');
82     fileVersionInfo.ProductName = this._getInfo(buf, pre, 'ProductName');
83     fileVersionInfo.ProductVersion = this._getInfo(buf, pre, 'ProductVersion');
84     fileVersionInfo.Comments = this._getInfo(buf, pre, 'Comments');
85     return fileVersionInfo;
86 };
87 
88 module.exports = FileVersion;

测试代码:

1 const FileVersion = require('./versionInfo');
2 
3 try {
4     const fileVersionReader = new FileVersion();
5     const info = fileVersionReader.getFileVersionInfo('C:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe', 'gb2312');
6     console.log(info);
7 } catch (err) {
8     console.log(err);
9 }

测试结果如下:

 

参考:

NodeJS和NW通过ffi调用dll/so动态库

通过ffi在node.js中调用动态链接库

posted on 2018-07-12 11:44  今木。非昔木  阅读(1749)  评论(0编辑  收藏  举报