使用 Rust 中的 WMI `IWbemServices::ExecMethod`

问题描述 投票:0回答:1

我正在尝试进行 WMI 方法调用(对 xeniface 驱动程序)。 WMI 箱尚未包装

ExecMethod
,因此我们必须开始进行
windows-rs
调用,而我很难正确处理。

注意,当前的 wmi-rs 0.13.1 仍然使用 windows-rs 0.48。

作为参考,以下代码的 Python 等效项将是以下代码中的最后一行:

wmi_connection = wmi.WMI(namespace=r"root\wmi")
xs_base = wmi_connection.CitrixXenStoreBase()[0]
sid = xs_base.AddSession("MyNewSession")[0]

此函数接受使用

CitrixXenStoreBase
板条箱检索的
wmi
对象。

  1. 这篇文章之后,我跳过了对输入/输出参数的内省调用(但本质上是因为我的

    GetMethod
    调用因WBEM_E_ILLEGAL_OPERATION而失败,有什么线索吗?这对于内省并找出如何正确执行后续步骤可能很有用).

  2. 我仍然不知道应该如何将

    in_params
    传递给
    ExecMethod
    (并且可能必须使用它来传递会话名称)

  3. 我也看不到如何从

    out_params
    中提取信息,当前提取
    ReturnValue
    的尝试失败,并出现 WBEM_E_NOT_FOUND。我希望能够反思这一点
    IWbemClassObject
    ,但找不到任何有关如何做到这一点的细节

use windows::core::{BSTR, w};
use windows::Win32::System::{Com::VARIANT, Wmi::{IWbemClassObject, WBEM_FLAG_RETURN_WBEM_COMPLETE}};
use wmi::{Variant, WMIConnection, WMIError, WMIResult};

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct CitrixXenStoreBase {
    #[serde(rename = "__Path")]
    __path: String,
    instance_name: String,
}

pub fn add_session(cnx: &WMIConnection,
                   object: CitrixXenStoreBase) -> WMIResult<usize>
{
    let mut wmi_object = None;
    let object_path: &str = object.__path.as_str();
    let object_path = BSTR::from(object_path);
    unsafe {
        let ret = cnx.svc.GetObject(&object_path,
                                    WBEM_FLAG_RETURN_WBEM_COMPLETE.0 as _,
                                    None,
                                    Some(&mut wmi_object),
                                    None);
        eprintln!("GetObject -> {:?}", ret);
        ret.expect("GetObject failure");
    }
    let wmi_object: IWbemClassObject = wmi_object.ok_or(WMIError::NullPointerResult)?;

    //let mut in_params: Option<IWbemClassObject> = None;
    let mut out_params: Option<IWbemClassObject> = None;

//    unsafe {
//        let ret = wmi_object.GetMethod(w!("AddSession"), 0, &mut in_params, &mut out_params);
//        eprintln!("GetMethod -> {:?}", ret);
//        ret.expect("GetMethod failure");
//    }

    unsafe {
        let ret = cnx.svc.ExecMethod(
            &BSTR::from(&object.__path),
            &BSTR::from("AddSession"),
            0,
            None,
            None, //in_params,
            Some(&mut out_params),
            None,
        );

        eprintln!("ExecMethod -> {:?}", ret);
        ret.expect("ExecMethod failure");
    }

    eprintln!("out_params: {:?}", out_params);
    let out_params = out_params.expect("AddSession should have output params");

    let mut addsession_ret = VARIANT::default();
    unsafe {
        let ret = out_params.Get(w!("ReturnValue"), 0, &mut addsession_ret, None, None);
        eprintln!("Get -> {:?}", ret);
        ret.expect("Get failure");
    }
    let sid = Variant::from_variant(&addsession_ret)?;
    eprintln!("sid: {:#?}", sid);

    Ok(0)
}

当前输出如下:

GetObject -> Ok(())
ExecMethod -> Ok(())
out_params: Some(IWbemClassObject(IUnknown(0x129ecc1cd10)))
Get -> Err(Error { code: HRESULT(0x80041002), message: "" })
winapi rust wmi windows-rs
1个回答
0
投票

事实证明,这种特殊情况需要调用实例方法,比微软的官方示例需要更多的工作,该示例展示了调用类方法。这留下了相当多的工作“作为读者的练习”。

VARIANT
类型的直观处理还需要 此 windows-rs 更新,它是在 0.52 之后发布的(不要被它的摘要所迷惑,除了扩展示例之外,它确实带来了代码更改),所以直到 0.53 为止您需要从 git 获取
windows
箱。反过来,现在无法与 wmi-rs 一起工作,但它仍然可以与 0.48 一起工作。

整个过程必须走这条路:

  • 手动打开 WMI 会话以获取
    Wmi::IWbemServices
    句柄(本质上是从
    wmi-rs
    复制并提升到 0.52 API) -
    wmi_init()
    下面
  • 在服务上调用
    GetObject("CitrixXenStoreBase")
    来获取 class 对象 来获取
    CitrixXenStoreBase
    class 对象(a
    Wmi::IWbemClassObject
    ) - 下面
    wmi_get_object()
  • 在服务上调用
    ExecQuery ("SELECT __Path FROM CitrixXenStoreBase")
    来获取该类的实例(其他
    Wmi::IWbemClassObject
    ),仅收集它们的
    __Path
    属性,这显然是我们所需要的。由于此类是单例,因此我们只需关心该枚举器返回的第一个元素。在下面
    main()
    里面。
  • GetMethod("AddSession")
    类对象
    上调用 CitrixXenStoreBase,以获取描述该方法的输入参数的 类对象(又一个
    Wmi::IWbemClassObject
  • 调用其
    SpawnInstance()
    方法来获取适合保存方法调用的实际输入参数的 实例对象(a...你遵循了吗?
    Wmi::IWbemClassObject
  • 在此对象上调用
  • Put()
     将唯一的输入参数设置为命名属性,首先将实际(字符串)值包装在 
    VARIANT
  • 然后才在服务上调用
  • ExecMethod
    (将实例对象的 
    __Path
     传递给它,而不是实例对象本身)并获取输出参数作为最后一个 
    Wmi::IWbemClassObject
    
    
  • 在该输出参数对象上调用
  • Get
     来提取我们想要的返回值的命名属性。
正如@iinspectable 所注意到的,是的,这是一次相当大的徒步旅行。感谢@kenny-kerr 提示我正确的方向并提供

windows-rs

 更新!

这显然只是一个起点,此代码将

在 GitHub 上演进,也许在某个时候我们会找到正确的抽象级别来添加到wmi-rs

pub fn wmi_init(path: &str) -> Result<Wmi::IWbemServices, WinError> { unsafe { Com::CoInitializeEx(None, Com::COINIT_MULTITHREADED) }?; unsafe { Com::CoInitializeSecurity(None, -1, // let COM choose. None, None, Com::RPC_C_AUTHN_LEVEL_DEFAULT, Com::RPC_C_IMP_LEVEL_IMPERSONATE, None, Com::EOAC_NONE, None, ) }?; // COM locator let loc: Wmi::IWbemLocator = unsafe { Com::CoCreateInstance(&Wmi::WbemLocator, None, Com::CLSCTX_INPROC_SERVER) }?; // connection let svc = unsafe { loc.ConnectServer( &BSTR::from(path), &BSTR::new(), &BSTR::new(), &BSTR::new(), Wmi::WBEM_FLAG_CONNECT_USE_MAX_WAIT.0, &BSTR::new(), None, ) }?; // "set proxy" unsafe { Com::CoSetProxyBlanket( &svc, Rpc::RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx Rpc::RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx None, Com::RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx Com::RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx None, // client identity Com::EOAC_NONE, // proxy capabilities ) }?; Ok(svc) } pub fn wmi_get_object(svc: &Wmi::IWbemServices, name: &str) -> Result<Wmi::IWbemClassObject, WinError> { let mut wmi_object = None; unsafe { svc.GetObject(&BSTR::from(name), Wmi::WBEM_FLAG_RETURN_WBEM_COMPLETE, None, Some(&mut wmi_object), None) }?; // FIXME can this unwrap fail? if missing driver? Ok(wmi_object.unwrap()) } pub fn add_session(svc: &Wmi::IWbemServices, xs_base_class: &Wmi::IWbemClassObject, xs_base_path: &BSTR) -> Result<u32, WinError> { // get input params def let mut in_params_class: Option<Wmi::IWbemClassObject> = None; unsafe { xs_base_class.GetMethod(w!("AddSession"), 0, &mut in_params_class, std::ptr::null_mut()) }?; let in_params_class = in_params_class.unwrap(); // fill input params let in_params = unsafe { in_params_class.SpawnInstance(0) }?; let var_session_name = VARIANT::from("MySession"); unsafe { in_params.Put(w!("Id"), 0, &var_session_name, 0) }?; // method call let mut out_params = None; unsafe { svc.ExecMethod( xs_base_path, &BSTR::from("AddSession"), Default::default(), None, &in_params, Some(&mut out_params), None, ) }?; let out_params = out_params.unwrap(); // output params let mut sid = VARIANT::default(); unsafe { out_params.Get(w!("SessionId"), 0, &mut sid, None, None) }?; let sid = u32::try_from(&sid)?; eprintln!("sid: {:#?}", sid); Ok(sid) } main() { let wmi_service = wmi_extra::wmi_init(r#"root\wmi"#)?; // get all instances of .CitrixXenStoreBase let enumerator = unsafe { wmi_service.ExecQuery( &BSTR::from("WQL"), &BSTR::from("SELECT __Path FROM CitrixXenStoreBase"), Wmi::WBEM_FLAG_FORWARD_ONLY | Wmi::WBEM_FLAG_RETURN_IMMEDIATELY, None, ) }?; let mut objs = [None; 1]; let res = { let mut return_value = 0; unsafe { enumerator.Next(Wmi::WBEM_INFINITE, &mut objs, &mut return_value) } }; if let Err(e) = res.ok() { return Err(e.into()); } // get the singleton instance let xenstore_base = objs.into_iter().next().unwrap().unwrap(); let xenstore_base_class = wmi_extra::wmi_get_object(&wmi_service, "CitrixXenStoreBase")?; let mut xenstore_base_path = VARIANT::default(); unsafe { xenstore_base.Get(&BSTR::from("__Path"), 0, &mut xenstore_base_path, None, None) }?; let xenstore_base_path = BSTR::try_from(&xenstore_base_path)?; // ret ...> session id let ret = wmi_extra::add_session(&wmi_service, &xenstore_base_class, &xenstore_base_path)?; }
    
© www.soinside.com 2019 - 2024. All rights reserved.