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 }
测试结果如下:
参考: