zoukankan      html  css  js  c++  java
  • nodejs利用windows API读取文件属性(dll)

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

    此处需用到windows系统的version.dll,该dll位于C:WINDOWSSystem32下,是一个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中调用动态链接库

  • 相关阅读:
    Python中replace 不起作用的问题
    java 获取视频时长、大小
    MySQL 自定义排序
    加 synchronized 关键字进行同步
    SQL 查询当前周的开始、结束日期
    Java 按照一定的规则生成递增的编号
    Java中BigDecimal的8种舍入模式
    Lamada 表达式之 sort 排序
    搭建Java环境
    初识JAVA(学习记录)
  • 原文地址:https://www.cnblogs.com/CinYung/p/9295934.html
Copyright © 2011-2022 走看看