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读取文件属性

 const ffi = require('ffi');
const ref = require('ref');
const iconvLite = require('iconv-lite'); // 定义dll
const version = ffi.Library('C://WINDOWS//System32//version', {
'GetFileVersionInfoSizeA': [ 'int', ['string', 'int'] ],
'GetFileVersionInfoA': ['int', ['string', 'int', 'int', ref.refType(ref.types.char)]],
'VerQueryValueA': ['int', [ref.refType(ref.types.char), 'string', ref.refType(ref.types.CString), ref.refType('int')]]
}); const Int16Format4 = function (num) {
const s = '0000';
const f = num.toString(16);
return s.substr(0, 4 - f.length) + f;
}; try {
console.log('Begin Test');
// 转码,windows使用AnsiChar,利用iconv-lite使用gbk解码
const file = iconvLite.encode('C:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe', 'gbk'); // 获取文件属性大小
const size = version.GetFileVersionInfoSizeA(file, 0);
console.log('fileInfoSize: ' + size); // 读取文件属性buffer
const buf = new Buffer(size);
buf.type = ref.types.char;
version.GetFileVersionInfoA(file, 0, size, buf); // 读取文件属性的LanguageID和CodePage,用来合成属性查找标记
const table = ref.alloc('int32').ref(), length = ref.alloc('int');
version.VerQueryValueA(buf, '\\VarFileInfo\\Translation', table, length);
const tableBuf = table.deref(); // 其中tableBuf中的值应为int32
const codePage = tableBuf.readUInt16LE(0); // codePage应取tableBuf的高16位
const languageID = tableBuf.readUInt16LE(2); // languageID应取tableBuf的低16位
console.log('codePage: ' + codePage.toString(16));
console.log('languageID: ' + languageID.toString(16)); // 合成属性查找标记
// 不同的语言地区,该值可能不一样,据我所知,中文"\\StringFileInfo\\080403A8\\"、英文"\\StringFileInfo\\040904E4\\",故需通过该方式合成最可靠
const pre = '\\StringFileInfo\\' + Int16Format4(codePage) + Int16Format4(languageID) + '\\';
console.log('pre: ' + pre); const versionBuf = new Buffer(1000).ref();
version.VerQueryValueA(buf, pre + 'FileVersion', versionBuf, length);
const infoBuf = versionBuf.deref().slice(0, length.deref());
const info = iconvLite.decode(infoBuf, 'gbk');
console.log('FileVersion: ' + info);
console.log('End Test');
} catch(err) {
console.log(err);
}

运行结果如下:

PS:

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

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

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

三、稍微改写上述代码

 'use strict';

 /**
*
*
* @author Mai
* @date 2018/7/12
* @version
*/ const ffi = require('ffi');
const ref = require('ref');
const iconvLite = require('iconv-lite'); // 定义dll
const version = ffi.Library('C://WINDOWS//System32//version', {
'GetFileVersionInfoSizeA': [ 'int', ['string', 'int'] ],
'GetFileVersionInfoA': ['int', ['string', 'int', 'int', ref.refType(ref.types.char)]],
'VerQueryValueA': ['int', [ref.refType(ref.types.char), 'string', ref.refType(ref.types.CString), ref.refType('int')]]
}); const FileVersion = function () {};
const proto = FileVersion.prototype; // 16进制format(%4.x)
proto._int16Format4 = function (num) {
const s = '0000';
const f = num.toString(16);
return s.substr(0, 4 - f.length) + f;
}; // 根据属性名读取文件属性
proto._getInfo = function (buf, pre, name) {
const infoBufPointer = new Buffer(1000).ref(); // 定义指向Buffer地址的指针,Buffer尽量定义长一点
const length = ref.alloc('int'); // 定义指向整数的指针
if (version.VerQueryValueA(buf, pre + name, infoBufPointer, length) !== 0) {
// 根据length,在Buf中截取属性
const infoBuf = infoBufPointer.deref().slice(0, length.deref() - 1);
return iconvLite.decode(infoBuf, 'gbk');
} else {
return undefined;
}
}; // 获取属性查找标记
proto._getPre = function (buf) {
// 读取文件属性的LanguageID和CodePage,用来合成属性查找标记
const table = ref.alloc('int32').ref(), length = ref.alloc('int');
version.VerQueryValueA(buf, '\\VarFileInfo\\Translation', table, length);
const tableBuf = table.deref(); // 其中tableBuf中的值应为int32
const codePage = tableBuf.readUInt16LE(0); // codePage应取tableBuf的高16位
const languageID = tableBuf.readUInt16LE(2); // languageID应取tableBuf的低16位
// 合成属性查找标记
// 不同的语言,该值不一样,其中英文是"\\StringFileInfo\\080403A8\\",中文是"\\StringFileInfo\\040904E4\\",需通过该方式合成
return '\\StringFileInfo\\' + this._int16Format4(codePage) + this._int16Format4(languageID) + '\\';
}; // 读取文件属性
proto.getFileVersionInfo = function (path) {
// 转码,windows使用AnsiChar,利用iconv-lite使用gb2312解码
const file = iconvLite.encode(path, 'gb2312');
// 获取文件属性大小
const size = version.GetFileVersionInfoSizeA(file, 0); // 读取文件属性buffer
const buf = new Buffer(size);
buf.type = ref.types.char;
version.GetFileVersionInfoA(file, 0, size, buf); // 获取文件属性查找标记
const pre = this._getPre(buf); // 读取文件属性
const fileVersionInfo = {};
fileVersionInfo.CompanyName = this._getInfo(buf, pre, 'CompanyName');
fileVersionInfo.FileDescription = this._getInfo(buf, pre, 'FileDescription');
fileVersionInfo.FileVersion = this._getInfo(buf, pre, 'FileVersion');
fileVersionInfo.InternalName = this._getInfo(buf, pre, 'InternalName');
fileVersionInfo.LegalCopyright = this._getInfo(buf, pre, 'LegalCopyright');
fileVersionInfo.LegalTrademarks = this._getInfo(buf, pre, 'LegalTrademarks');
fileVersionInfo.OriginalFilename = this._getInfo(buf, pre, 'OriginalFilename');
fileVersionInfo.ProductName = this._getInfo(buf, pre, 'ProductName');
fileVersionInfo.ProductVersion = this._getInfo(buf, pre, 'ProductVersion');
fileVersionInfo.Comments = this._getInfo(buf, pre, 'Comments');
return fileVersionInfo;
}; module.exports = FileVersion;

测试代码:

 const FileVersion = require('./versionInfo');

 try {
const fileVersionReader = new FileVersion();
const info = fileVersionReader.getFileVersionInfo('C:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe', 'gb2312');
console.log(info);
} catch (err) {
console.log(err);
}

测试结果如下:

参考:

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

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

nodejs利用windows API读取文件属性(dll)的更多相关文章

  1. 利用 Windows API Code Pack 修改音乐的 ID3 信息

    朋友由于抠门 SD 卡买小了,结果音乐太多放不下,又不舍得再买新卡,不得已决定重新转码,把音乐码率压低一点,牺牲点音质来换空间(用某些人的话说,反正不是搞音乐的,听不出差别)… 结果千千静听(百度音乐 ...

  2. Windows API学习---插入DLL和挂接API

    插入DLL和挂接API 在Microsoft Windows中,每个进程都有它自己的私有地址空间.当使用指针来引用内存时,指针的值将引用你自己进程的地址空间中的一个内存地址.你的进程不能创建一个其引用 ...

  3. 利用windows api共享内存通讯

    主要涉及CreateFile,CreateFileMapping,GetLastError,MapViewOfFile,sprintf,OpenFileMapping,CreateProcess Cr ...

  4. C#利用Windows API 实现关机、注销、重启等操作

    using System; using System.Text; using System.Diagnostics; using System.Runtime.InteropServices; nam ...

  5. 【整理】c# 调用windows API(user32.dll)

    User32.dll提供了很多可供调用的接口,大致如下(转自http://blog.csdn.net/zhang399401/article/details/6978803) using System ...

  6. 后端Nodejs利用node-xlsx模块读取excel

    后端Nodejs(利用node-xlsx模块) /** * Created by zh on 16-9-14. */ var xlsx = require("node-xlsx") ...

  7. 【转】c# 调用windows API(user32.dll)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.R ...

  8. Windows API Hooking in Python

    catalogue . 相关基础知识 . Deviare API Hook Overview . 使用ctypes调用Windows API . pydbg . winappdbg . dll inj ...

  9. Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术

    catalogue 1. 引言2. 使用注册表注入DLL3. 使用Windows挂钩来注入DLL4. 使用远程线程来注入DLL5. 使用木马DLL来注入DLL6. 把DLL作为调试器来注入7. 使用c ...

随机推荐

  1. linux nginx完全卸载

        Nginx虽然好用,但是一旦关键配置文件被修改,想要卸载重装却是相当困难.本人因为采用apt-get方式安装后又源码安装了Nginx,结果出现冲 突,卸载不了,安装不上,很是蛋疼.主要的问题还 ...

  2. Effective java -- 7 通用程序设计

    第四十五条:将局部变量的作用域最小化 第四十六条:加强版for循环优于传统for循环 第四十七条:了解和使用类库书中提到了一个生成随机数的例子.正好需要. public static void mai ...

  3. 0522 HTML表单 CSS基础

    一.列表标签 列表标签分为三种. 1.无序列表<ul>,无序列表中的每一项是<li> 英文单词解释如下: ul:unordered list,“无序列表”的意思. li:lis ...

  4. 如何在 Eclipse 中使用命令行

    虽然我们已经有了像 Eclipse 这样高级的 IDE,但是我们有时候也是需要在开发的时候使用 Windows 的命令行,来运行一些独立的程序.在两个程序中切换来切换去是很麻烦的.所以 Eclipse ...

  5. android OTA升级包制作【转】

    本文转载自:http://www.thinksaas.cn/topics/0/445/445670.html 0.签名 java -Xmx2048m -jar out/host/linux-x86/f ...

  6. keeplive使用

    一.简介 Keepalived是一个免费开源的,用C编写的类似于layer3, 4 & 7交换机制软件,具备我们平时说的第3层.第4层和第7层交换机的功能.主要提供loadbalancing( ...

  7. Nginix安装教程(Ubuntu)

    安装gcc g++的依赖库 #apt-get install build-essential #apt-get install libtool   安装 pcre依赖库 #sudo apt-get u ...

  8. [深入学习C#]C#实现多线程的方法:线程(Thread类)和线程池(ThreadPool)

    简介 使用线程的主要原因:应用程序中一些操作需要消耗一定的时间,比如对文件.数据库.网络的访问等等,而我们不希望用户一直等待到操作结束,而是在此同时可以进行一些其他的操作.  这就可以使用线程来实现. ...

  9. 第二章 python基础(三)

    第十六节 MySQLdb win64位安装python-mysqldb1.2.5 ubuntu下安装MySQLdb sudo apt-get install python-MySQLdb 导入MySQ ...

  10. codeforces 633D D. Fibonacci-ish(dfs+暴力+map)

    D. Fibonacci-ish time limit per test 3 seconds memory limit per test 512 megabytes input standard in ...