我正在尝试进行 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
对象。
在这篇文章之后,我跳过了对输入/输出参数的内省调用(但本质上是因为我的
GetMethod
调用因WBEM_E_ILLEGAL_OPERATION而失败,有什么线索吗?这对于内省并找出如何正确执行后续步骤可能很有用).
我仍然不知道应该如何将
in_params
传递给 ExecMethod
(并且可能必须使用它来传递会话名称)
我也看不到如何从
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: "" })
事实证明,这种特殊情况需要调用实例方法,比微软的官方示例需要更多的工作,该示例展示了调用类方法。这留下了相当多的工作“作为读者的练习”。
对
VARIANT
类型的直观处理还需要 此 windows-rs 更新,它是在 0.52 之后发布的(不要被它的摘要所迷惑,除了扩展示例之外,它确实带来了代码更改),所以直到 0.53 为止您需要从 git 获取 windows
箱。反过来,现在无法与 wmi-rs 一起工作,但它仍然可以与 0.48 一起工作。
整个过程必须走这条路:
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
来提取我们想要的返回值的命名属性。
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)?;
}