I'm trying to make WMI method calls (to the xeniface driver). The WMI crate does not yet wrap ExecMethod so we have to get down to windows-rs calls, and I'm having trouble getting it right.
Note, current wmi-rs 0.13.1 still uses windows-rs 0.48.
For reference, the Python equivalent of the code below would be the last line in:
wmi_connection = wmi.WMI(namespace=r"root\wmi")
xs_base = wmi_connection.CitrixXenStoreBase()[0]
sid = xs_base.AddSession("MyNewSession")[0]
This function accepts the CitrixXenStoreBase object retrieved using the wmi crate.
Following this post I skipped the introspection call for in/out parameters (but essentially because my
GetMethodcall fails with WBEM_E_ILLEGAL_OPERATION, any clue why? It could be useful for introspection and finding out how to get nte next steps right).I still don't see how I should pass
in_paramstoExecMethod(and will likely have to use this to pass the session name)I can't see how to extract info from
out_paramseither, current attempt to extractReturnValuefails with WBEM_E_NOT_FOUND. I'd like to be able to introspect thatIWbemClassObject, but could not find any details on how to do that
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)
}
Current output goes like:
GetObject -> Ok(())
ExecMethod -> Ok(())
out_params: Some(IWbemClassObject(IUnknown(0x129ecc1cd10)))
Get -> Err(Error { code: HRESULT(0x80041002), message: "" })
It turns out this particular case, which requires calling an instance method, requires more work than the official example from Microsoft, which showcases calling a class method. This left quite some work "as an exercise for the reader".
Intuitive handling of the
VARIANTtype also requires this windows-rs update which comes post-0.52 (don't get fooled by its summary, it does bring code changes in addition to extending the example), so until 0.53 is out you'll need to fetch thewindowscrate from git. Which in turns cannot work today with wmi-rs, which still works with 0.48.The full process must then go this route:
Wmi::IWbemServiceshandle (essentially copied fromwmi-rsand lifted to 0.52 API) -wmi_init()belowGetObject("CitrixXenStoreBase")on the service to get the class object to get theCitrixXenStoreBaseclass object (aWmi::IWbemClassObject) -wmi_get_object()belowExecQuery ("SELECT __Path FROM CitrixXenStoreBase")on the service to get the instances of that class (otherWmi::IWbemClassObjects), only collecting their__Pathattribute, which is apparently all we need. Since this class is a singleton, we only have to care about the first element returned by that enumerator. Insidemain()below.GetMethod("AddSession")on theCitrixXenStoreBaseclass object, to get a class object (yet anotherWmi::IWbemClassObject) describing the input parameters for that methodSpawnInstance()method to get an instance object (a... did you follow?Wmi::IWbemClassObjecttoo) suitable to hold the actual input parameters for the method callPut()on this object to set the sole input parameter as a named property, wrapping first the actual (string) value in aVARIANTExecMethodon the service (passing it the instance object's__Pathand not the instance object itself) and get output parameters as a lastWmi::IWbemClassObjectGeton that output parameters object to extract the named property for the return value we're after.As noticed by @iinspectable, yes it was quite a hike. Thanks @kenny-kerr for hinting me to the right direction and providing
windows-rsupdates!This is just a starting point obviously, this code will evolve on GitHub, and maybe at some point we'll find the right level of abstraction to add to
wmi-rs.