#[cfg(windows)]
pub mod inner {
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use winapi::shared::minwindef::{DWORD, LPVOID};
use winapi::um::winver::{GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW};
#[allow(dead_code)]
struct FIXEDFILEINFO {
signature: DWORD,
struc_version: DWORD,
file_version_ms: DWORD,
file_version_ls: DWORD,
product_version_ms: DWORD,
product_version_ls: DWORD,
file_flags_mask: DWORD,
file_flags: DWORD,
file_os: DWORD,
file_type: DWORD,
file_subtype: DWORD,
file_date_ms: DWORD,
file_date_ls: DWORD,
}
type LPFIXEDFILEINFO = *mut FIXEDFILEINFO;
pub fn get_version(path: &Path) -> Option<String> {
unsafe {
let ext = path.extension()?.to_str()?.to_lowercase();
if ext == "exe" || ext == "dll" {
let data = query_data(path)?;
let info = query_value(&data)?;
let major = ((*info).file_version_ms >> 16) & 0xffff;
let minor = (*info).file_version_ms & 0xffff;
let debug = ((*info).file_version_ls >> 16) & 0xffff;
let patch = (*info).file_version_ls & 0xffff;
let version = format!("{}.{}.{}.{}", major, minor, debug, patch);
return Some(version);
}
return None;
}
}
fn query_data(path: &Path) -> Option<Vec<u8>> {
unsafe {
let path = path.to_str()?;
let path = create_string(path);
let mut handle = 0u32;
let size = GetFileVersionInfoSizeW(path.as_ptr(), &mut handle);
let mut data: Vec<u8> = Vec::new();
data.resize(size as usize, 0);
let success = GetFileVersionInfoW(path.as_ptr(), 0, size, data.as_mut_ptr().cast());
return if success != 0 { Some(data) } else { None };
}
}
fn query_value(data: &Vec<u8>) -> Option<LPFIXEDFILEINFO> {
unsafe {
let sub = create_string("\\");
let mut info: LPVOID = 0 as LPVOID;
let mut size = 0;
let success = VerQueryValueW(data.as_ptr().cast(), sub.as_ptr(), &mut info, &mut size);
return if success != 0 { Some(info as LPFIXEDFILEINFO) } else { None };
}
}
fn create_string(text: &str) -> Vec<u16> {
OsStr::new(text).encode_wide().chain(once(0)).collect()
}
}