ex_cli/util/
version.rs

1#[cfg(windows)]
2pub mod inner {
3    use std::ffi::OsStr;
4    use std::iter::once;
5    use std::os::windows::ffi::OsStrExt;
6    use std::path::Path;
7    use std::ptr::addr_of_mut;
8    use winapi::shared::minwindef::{DWORD, LPVOID, WORD};
9    use winapi::um::libloaderapi::{FindResourceW, LoadResource, LockResource};
10    use winapi::um::winnt::{IMAGE_DOS_SIGNATURE, PIMAGE_DOS_HEADER, WCHAR};
11    use winapi::um::winuser::RT_VERSION;
12    use winapi::um::winver::{GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW};
13
14    #[allow(dead_code)]
15    #[repr(C)]
16    struct FIXEDFILEINFO {
17        signature: DWORD,
18        struc_version: DWORD,
19        file_version_ms: DWORD,
20        file_version_ls: DWORD,
21        product_version_ms: DWORD,
22        product_version_ls: DWORD,
23        file_flags_mask: DWORD,
24        file_flags: DWORD,
25        file_os: DWORD,
26        file_type: DWORD,
27        file_subtype: DWORD,
28        file_date_ms: DWORD,
29        file_date_ls: DWORD,
30    }
31
32    #[allow(dead_code)]
33    #[repr(C)]
34    struct VERSIONINFO {
35        length: WORD,
36        value_length: WORD,
37        value_type: WORD,
38        key: [WCHAR; 16],
39        padding1: WORD,
40        value: FIXEDFILEINFO,
41        padding2: WORD,
42        children: WORD,
43    }
44
45    type LPFIXEDFILEINFO = *mut FIXEDFILEINFO;
46    type LPVERSIONINFO = *mut VERSIONINFO;
47
48    pub fn test_extension(path: &Path) -> bool {
49        if let Some(ext) = path.extension() {
50            if let Some(ext) = ext.to_str() {
51                let ext = ext.to_lowercase();
52                return ext == "exe" || ext == "dll";
53            }
54        }
55        false
56    }
57
58    pub fn query_file(path: &Path) -> Option<String> {
59        unsafe {
60            let path = path.to_str()?;
61            let path = create_string(path);
62            let mut handle = 0u32;
63            let size = GetFileVersionInfoSizeW(path.as_ptr(), &mut handle);
64            let mut data = vec![0; size as usize];
65            if GetFileVersionInfoW(path.as_ptr(), 0, size, data.as_mut_ptr().cast()) != 0 {
66                let sub = create_string("\\");
67                let mut info = 0 as LPVOID;
68                let mut size = 0;
69                if VerQueryValueW(data.as_ptr().cast(), sub.as_ptr(), &mut info, &mut size) != 0 {
70                    let info = info as LPFIXEDFILEINFO;
71                    let version = format_version(info);
72                    return Some(version);
73                }
74            }
75            None
76        }
77    }
78
79    // https://www.codeproject.com/Articles/23166/A-Fast-Way-to-Get-at-the-File-s-Version
80    pub fn query_buffer(buffer: &mut [u8]) -> Option<String> {
81        unsafe {
82            let header = buffer.as_ptr() as PIMAGE_DOS_HEADER;
83            if (*header).e_magic == IMAGE_DOS_SIGNATURE {
84                let module = &mut buffer[1..];
85                let name = create_string("#1");
86                let resource = FindResourceW(module.as_mut_ptr().cast(), name.as_ptr(), RT_VERSION);
87                if resource as usize != 0 {
88                    let global = LoadResource(module.as_mut_ptr().cast(), resource);
89                    if global as usize != 0 {
90                        let memory = LockResource(global);
91                        if memory as usize != 0 {
92                            let info = memory as LPVERSIONINFO;
93                            let info = addr_of_mut!((*info).value);
94                            let version = format_version(info);
95                            return Some(version);
96                        }
97                    }
98                }
99            }
100            None
101        }
102    }
103
104    fn format_version(info: LPFIXEDFILEINFO) -> String {
105        unsafe {
106            let major = ((*info).file_version_ms >> 16) & 0xffff;
107            let minor = (*info).file_version_ms & 0xffff;
108            let debug = ((*info).file_version_ls >> 16) & 0xffff;
109            let patch = (*info).file_version_ls & 0xffff;
110            format!("{major}.{minor}.{debug}.{patch}")
111        }
112    }
113
114    fn create_string(text: &str) -> Vec<u16> {
115        OsStr::new(text).encode_wide().chain(once(0)).collect()
116    }
117}