nodejs利用windows API讀取文件屬性(dll)

nodejs調用delphi編寫的dll中,使用了dll調用windows api轉讀取文件屬性,感受使用nodejs也可直接調用windows api。html

此處需用到windows系統的version.dll,該dll位於C:\WINDOWS\System32\下,是一個32位的dll,故此處直接使用32位版本的node。node

 

1、安裝所需模塊(node-gyp、ffi、ref、iconv-lite)python

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

其中:node-gyp直接全局安裝,ffi、ref、iconv-lite安裝在項目中便可npm

PS:segmentfault

1. ffi與ref的安裝須要用到python,需先裝好。windows

2. ffi用來調用dllapi

3. ref用來設置buffer測試

4. iconv-lite用來轉碼GBK字符ui

 

2、示例,使用nodejs讀取文件屬性this

 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最好使用這種方式合成獲得,不然可能存在讀不到屬性的狀況

 

3、稍微改寫上述代碼

 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中調用動態連接庫

相關文章
相關標籤/搜索