在Rust中检索给定升级代码的相关产品列表

qaxu7uf2  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(176)

我在Rust中使用windows crate来检索给定升级代码的相关产品列表,但是我的代码返回了error 87 (-2147024809) The parameter is set incorrectly
我试着这样做:

use windows::Win32::System::ApplicationInstallationAndServicing::MsiEnumRelatedProductsA;
use winapi::shared::winerror::HRESULT_FROM_WIN32;
fn main() {
    let upgrade_code = "23170F69-40C1-2702-0000-000004000000";    
    // Enumerate related products and print their names
    let mut index = 0;
    loop {
        let mut product_buf = vec![0u8; 1024];
        let result = unsafe {
            MsiEnumRelatedProductsA(
                windows::core::PCSTR(upgrade_code.as_ptr()),
                0,
                index,
                windows::core::PSTR(product_buf.as_mut_ptr()),
            )
        };
        if result == 0 {
            // Convert the null-terminated byte array to a string and print the results
            let product_list = String::from_utf8(product_buf).unwrap_or("".to_string());
            println!("Related products: {}", product_list);
            break;
        } else if result != 259 { //259 = No additional data available
            println!("Error code: {} ({})",result, HRESULT_FROM_WIN32(result));
            break;
        }
        index += 1;
    }
}
jyztefdp

jyztefdp1#

这里有两个不相关的问题:

  1. Rust中的字符串不是零终止的,但API期望零终止的字符串。调用时,API将读取字符串末尾以外的内容,直到遇到零值。行为未定义。
    1.升级代码需要用花括号括起来。MsiEnumRelatedProductsA的文档在其输出缓冲区的描述中提供了提示。它需要容纳38个字符的空间,比GUID字符串文本当前的多两个字符。
    为了解决这个问题,你必须引入花括号,以及一个显式的零结束符(\0)。使用最少更改量的解决方案就像替换
let upgrade_code = "23170F69-40C1-2702-0000-000004000000";

let upgrade_code = "{23170F69-40C1-2702-0000-000004000000}\0";

你也可以使用windows crate提供的s!宏,它在编译时构造一个PCSTR,也是零终止的,所以没有人会受到伤害:

MsiEnumRelatedProductsA(
    s!("{23170F69-40C1-2702-0000-000004000000}"),
    0,
    index,
    windows::core::PSTR(product_buf.as_mut_ptr()),
)

不过我还是建议做一些修改,特别是使用Unicode(UTF-16)API以及正确的错误报告。一个完整的实现可能看起来像这样:

use std::{ffi::OsStr, mem::MaybeUninit};

use windows::{
    core::{Result, HSTRING, PWSTR},
    Win32::{
        Foundation::{ERROR_NO_MORE_ITEMS, ERROR_SUCCESS, WIN32_ERROR},
        System::ApplicationInstallationAndServicing::MsiEnumRelatedProductsW,
    },
};

fn main() -> Result<()> {
    let products = related_products("{23170F69-40C1-2702-0000-000004000000}")?;
    println!("{products:?}");

    Ok(())
}

fn related_products(upgrade_code: impl AsRef<OsStr>) -> Result<Vec<String>> {
    let mut products = Vec::new();

    for index in 0.. {
        const GUID_LEN: usize = 38;
        let mut out_buf = MaybeUninit::<[u16; GUID_LEN + 1]>::uninit();
        let result = unsafe {
            MsiEnumRelatedProductsW(
                &HSTRING::from(upgrade_code.as_ref()),
                0,
                index,
                PWSTR(out_buf.as_mut_ptr() as *mut _),
            )
        };
        if result == ERROR_SUCCESS.0 {
            let out_buf = unsafe { out_buf.assume_init() };
            let product = String::from_utf16(&out_buf[..GUID_LEN])?;
            products.push(product);
        } else if result == ERROR_NO_MORE_ITEMS.0 {
            break;
        } else {
            return Err(WIN32_ERROR(result).into());
        }
    }

    Ok(products)
}
  • Cargo.toml* 依赖项:
[dependencies.windows]
version = "0.48.0"
features = [
    "Win32_Foundation",
    "Win32_System_ApplicationInstallationAndServicing",
]

相关问题