首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用windows获取FileInfo

使用windows获取FileInfo
EN

Stack Overflow用户
提问于 2022-03-11 23:01:04
回答 1查看 179关注 0票数 0

我有点生锈了。所以我试着从windows机箱里提取FileDescription。我未能将descriptionBuffer从VerQueryValueA转换为utf8字符串。我不知道我做错了什么。

代码语言:javascript
复制
fn get_proc_data(pid: u32) -> Option<String> {
    let mut path = None;
    unsafe {
        let h_snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid);

        if h_snap != INVALID_HANDLE_VALUE {
            let mut mod_entry: MODULEENTRY32 = MODULEENTRY32 {
                ..Default::default()
            };
            mod_entry.dwSize = size_of_val(&mod_entry) as u32;
            if Module32First(h_snap, &mut mod_entry).as_bool() {
                let char_vec = mod_entry.szExePath.iter().map(|f| f.0).collect::<Vec<u8>>();

                path = match from_utf8(&char_vec) {
                    Ok(s) => Some(String::from(s.to_string().trim_end_matches(char::from(0)))),
                    Err(_) => None,
                };
            }
        }
        CloseHandle(h_snap);

        if path.is_some() {
            let mut infoBuffer: [u8; 2048] = [0; 2048];
            let pat = path.as_ref().unwrap();
            let lpvoid: *mut c_void = infoBuffer.as_mut_ptr() as *mut c_void;
            let c_str = CString::new(pat.as_str()).unwrap();
            let pstr = PSTR(c_str.as_ptr() as *const u8);
            let verInfoLen = GetFileVersionInfoSizeA(pstr, &mut 0);
            let ok = GetFileVersionInfoA(pstr, 0, verInfoLen, lpvoid);

            let mut descriptionBuffer: [u8; 256] = [0; 256];
            let descriptionPtr: *mut *mut c_void =
                descriptionBuffer.as_mut_ptr() as *mut *mut c_void;
            let mut descriptionLen = 0;
            if ok.as_bool()
                && VerQueryValueA(
                    lpvoid,
                    "\\StringFileInfo\\040904E4\\FileDescription",
                    descriptionPtr,
                    &mut descriptionLen,
                )
                .as_bool()
            {
                info!("{:?}", descriptionBuffer);

                let res = from_utf8_lossy(&descriptionBuffer);
                info!("{:?} {:?}", path, res);
            }
        }
    }
    return path;
}

示例输出

info!("{:?}", descriptionBuffer);

信息- 180,162,146,165,206,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

info!("{:?} {:?}", path, res);

信息-一些(“C:\Users\acoop\AppData\Local\Amazon\AmazonMusic.exe”) "�����\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}“

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-15 09:39:28

查询文件信息属性是一个多步骤的过程:

  1. 通过调用GetFileVersionInfoSizeW确定版本信息大小
  2. 分配足够大小的缓冲区;Vec提供所需的一切(连续内存、运行时动态大小、低堆栈内存开销)
  3. 通过调用GetFileVersionInfoW将整个版本信息读入分配的缓冲区
  4. 使用VerQueryValueW查询感兴趣的信息;在成功时,该函数将指针/大小对返回到上面分配的缓冲区

这是在提供的代码中最终出错的最后一步。API需要存储结果的指针变量的地址。然而,代码传递的是descriptionBuffer的第一个元素的地址,这就是API写入的地方(想必,代码是为64位目标编译的,这意味着前8个字节是指针值)。

本质上,代码确实成功地生成了指针/大小对,但未能根据API协议对它们进行解释。查询二进制文件描述的改进版本可能如下所示:

代码语言:javascript
复制
fn get_file_description(path: impl AsRef<Path>) -> Result<String, Box<dyn Error>> {
    // Determine version info size
    let size = unsafe { GetFileVersionInfoSizeW(path.as_ref().as_os_str(), null_mut()) };
    if size == 0 {
        return Err(core::Error::from_win32().into());
    }

    // Allocate buffer
    let mut buffer = vec![0u8; size as usize];
    // Read version info
    unsafe {
        GetFileVersionInfoW(
            path.as_ref().as_os_str(),
            0,
            size,
            buffer.as_mut_ptr() as *mut std::ffi::c_void,
        )
    }
    .ok()?;

    // Declare pointer/size pair for output
    let mut ptr = null_mut();
    let mut len = 0;
    // Query for file description
    let success = unsafe {
        VerQueryValueW(
            buffer.as_ptr() as *const std::ffi::c_void,
            "\\StringFileInfo\\040904B0\\FileDescription",
            &mut ptr,
            &mut len,
        )
    }
    // The API call doesn't set the last error code so we cannot use `.ok()?` here
    .as_bool();
    if !success {
        return Err("Failed to query file description".into());
    }

    // `len` here is in elements (as opposed to bytes)
    let descr = unsafe { slice::from_raw_parts(ptr as *const u16, len as usize) };
    // Optionally use `from_utf16_lossy` if you don't need to handle invalid UTF-16
    let descr = String::from_utf16(descr)?;

    Ok(descr)
}

这是更好的,但肯定不是十全十美的任何一个术语。大多数改进都围绕着字符编码的微妙性,当涉及到Windows上的锈蚀时,这是最痛苦的一点。在Rust中没有专门的字符串类型可以在Windows的本机字符编码UTF-16中存储字符串。

值得注意的变化:

  • 该函数接受一个可转换为Path引用的参数。底层存储是OsStr类型的,它具有一个轻松的UTF-8版本,能够表示任何UTF-16代码单元的序列,格式良好或其他。这一点至关重要,因为Windows不对文件系统对象提供字符编码保证。除了少数保留值外,几乎任何16位值序列都是允许的.你的计划需要为此做好准备。
  • 所有窄字符集版本的API调用都已被宽字符版本所取代(有关背景信息,请参见功能原型的约定 )。在处理不受控制的数据(例如任意二进制文件的版本信息资源)时,这是唯一安全的选项。这里要特别注意的是,windows机箱提供了从&OsStrPCWSTR的隐式转换,因此将Path传递到宽字符API并不完全不方便(但它们确实需要转换和分配)。
  • 同样,windows机箱提供从&strPCWSTR的转换,这是在"alloc"特性下设置的。这允许在调用VerQueryValueW时使用字符串文字,并根据需要将所有内容进行转换(同样,还有转换和分配成本)。

要将其转换为一个完全工作的示例,生成一个命令行应用程序接受二进制图像的路径名作为其第一个参数,只需添加以下Cargo.toml

代码语言:javascript
复制
[package]
name = "fileinfo"
version = "0.0.0"
edition = "2021"

[dependencies.windows]
version = "0.33.0"
features = [
    "alloc",
    "Win32_Foundation",
    "Win32_Storage_FileSystem",
]

并将以下内容添加到src/main.rs文件中:

代码语言:javascript
复制
use std::{env, error::Error, path::Path, ptr::null_mut, slice};
use windows::{
    core,
    Win32::Storage::FileSystem::{GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW},
};

fn main() -> Result<(), Box<dyn Error>> {
    let input = env::args_os()
        .nth(1)
        .ok_or("Expected 1 command line argument")?;
    let path = Path::new(&input);
    let descr = get_file_description(&path)?;

    println!("File description: \"{}\"", &descr);

    Ok(())
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71445517

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档