Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling Option<&mut Option<IWbemClassObject>> InParams #2040

Closed
disassembledd opened this issue Sep 20, 2022 · 8 comments · Fixed by #2041
Closed

Handling Option<&mut Option<IWbemClassObject>> InParams #2040

disassembledd opened this issue Sep 20, 2022 · 8 comments · Fixed by #2041
Labels
question Further information is requested

Comments

@disassembledd
Copy link

I am attempting to use IWbemServices::GetObject in order to subsequently call IWbemClassObject::{GetMethod, SpawnInstance, Put} in an effort to call WMI methods. Below is my current attempt with the issue being it feels extremely clunky trying to handle pObject or just do anything with it period. GetObject doesn't seem to be returning the object to me and I'm attempting to do this as closely as I did in winapi where my existing code is working, to no avail. What am I doing wrong?

unsafe {
    let mut pObject = ptr::null_mut::<Option<IWbemClassObject>>();
    services.GetObject(
        &BSTR::from("Win32_Process"),
        0,
        InParam::null(),
        pObject.as_mut(),
        None
    )?;
}

Note: This is the signature of GetObject:

GetObject<'a, P0>(&self, strobjectpath: &::windows::core::BSTR, lflags: i32, pctx: P0, ppobject: ::core::option::Option<&mut ::core::option::Option<IWbemClassObject>>, ppcallresult: ::core::option::Option<&mut ::core::option::Option<IWbemCallResult>>) -> ::windows::core::Result<()>
where
    P0: ::std::convert::Into<::windows::core::InParam<'a, IWbemContext>>
@disassembledd
Copy link
Author

disassembledd commented Sep 20, 2022

I changed my ptr::null_mut to mem::zeroed and everything is now successful. Now my question is, is this the correct method of doing so?

unsafe {
    let mut pObject = std::mem::zeroed::<Option<IWbemClassObject>>();
    services.GetObject(
        &BSTR::from("Win32_Process"),
        0,
        InParam::null(),
        Some(&mut pObject),
        None
    )?;
}

I am also receiving "the trait bound InParam<'_, IWbemClassObject>: From<IWbemClassObject> is not satisfied" when attempting to pass either Option<IWbemClassObject> or IWbemClassObject to ExecMethod.

@kennykerr
Copy link
Collaborator

Does this help: #2041

It takes advantage of some improvements to the windows crate since the 0.40 release, so you'll have to use a git dependency until 0.41 is released.

@kennykerr kennykerr added the question Further information is requested label Sep 20, 2022
@disassembledd
Copy link
Author

That is a great example for querying. Unfortunately my case is the least documented across wmi-rs, winapi and here as it involves executing methods on objects. I have successfully figured it all out however. Below is a minimum reproduceable example of changing the start mode of the aspnet_state service, please excuse the helper methods that exclude some code.

fn main() -> Result<(), Box<dyn std::error::Error>> {
    initialize()?;
    let locator = create_locator()?;
    let services = create_services(&locator, r"root\cimv2")?;
    set_proxy(&services)?;

    unsafe {
        let mut pObject = mem::zeroed::<Option<IWbemClassObject>>();
        services.GetObject(
            &BSTR::from("Win32_Service"),
            0,
            InParam::null(),
            Some(&mut pObject),
            None
        )?;

        if let Some(pObject) = pObject {
            let mut pInParams = mem::zeroed::<Option<IWbemClassObject>>();
            let mut pOutParams = mem::zeroed::<Option<IWbemClassObject>>();
            pObject.GetMethod(
                w!("ChangeStartMode"), 
                0, 
                &mut pInParams, 
                &mut pOutParams
            )?;

            let mut pInParams = if let Some(pInParams) = pInParams {
                Some(pInParams.SpawnInstance(0)?)
            } else {
                None
            };

            let mut pOutParams = if let Some(pOutParams) = pOutParams {
                Some(pOutParams.SpawnInstance(0)?)
            } else {
                None
            };

            if let Some(pInParams) = &pInParams {
                let mut cmd = VARIANT::default();
                let mut mode = BSTR::from("Manual");

                (*cmd.Anonymous.Anonymous).vt = VT_BSTR;
                *(*cmd.Anonymous.Anonymous).Anonymous.bstrVal = mode;

                pInParams.Put(
                    w!("StartMode"), 
                    0, 
                    &cmd,
                    0
                )?;

                VariantClear(&mut cmd)?;
            }

            services.ExecMethod(
                &BSTR::from("Win32_Service.Name=\"aspnet_state\""),
                &BSTR::from("ChangeStartMode"),
                0,
                InParam::null(),
                &pInParams.unwrap(),
                None,
                None
            )?;
        }
    }

    Ok(())
}

@kennykerr
Copy link
Collaborator

OK, I see using the WMI API in this way is overly complicated. Let me get that fixed and update the sample.

@disassembledd
Copy link
Author

The most egregious example that would need the most attention are the various VARIANT scenarios. The "basic" types are somewhat straightforward, however, when you need to pass another VARIANT or even a new IWbemClassObject instance, it becomes difficult to discern what the right course of action is (for me at least).

@kennykerr
Copy link
Collaborator

Yes, I tried simplifying your example but the VARIANT is definitely the main challenge. The rest seems to be just that WMI is hard to use. Here's a slightly simpler version, avoiding all the zeroed in your example:

let mut object = None;
server.GetObject(&BSTR::from("Win32_Service"), 0, None, Some(&mut object), None)?;

if let Some(object) = object {
    let mut in_params = None;
    let mut out_params = None;

    object.GetMethod(w!("ChangeStartMode"), 0, &mut in_params, &mut out_params)?;

    let in_params = if let Some(in_params) = in_params { Some(in_params.SpawnInstance(0)?) } else { None };

    if let Some(in_params) = &in_params {
        let mut value = VARIANT::default();
        (*value.Anonymous.Anonymous).vt = VT_BSTR;
        *(*value.Anonymous.Anonymous).Anonymous.bstrVal = BSTR::from("Manual");

        in_params.Put(w!("StartMode"), 0, &value, 0)?;
        VariantClear(&mut value)?;

        server.ExecMethod(&BSTR::from("Win32_Service.Name=\"Fax\""), &BSTR::from("ChangeStartMode"), 0, None, in_params, None, None)?;
    }
}

I hope to get to VARIANT soon. #539

@kennykerr
Copy link
Collaborator

Didn't realize the PR closed this issue. Let me know if there's anything I can do to help.

@disassembledd
Copy link
Author

All is well! I fumbled my way through getting it to work. Now I just have a heap corruption error that I need to find what is causing it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants