microsoft/windows-rs

ExecMethod in ISWbemObject returns always 0

Closed this issue · 3 comments

Summary

Hi,

First of all, thanks for your hard work! I really enjoy using windows-rs so far.

I am trying to access a remote machine. The execution of my wanted logoff with a given session ID (obtained through query session) did work just fine. However if you look closely into my code below you will see, that I want to give some feedback if the command logoffdid fail. But no matter the session ID it will always proceed with "ok" or 0 in my case:

use std::mem::ManuallyDrop;
use windows::{
    core::{ComInterface, BSTR},
    Win32::System::{
        Com::IDispatch,
        Variant::{VARIANT, VARIANT_0, VT_BSTR, VT_I4},
        Wmi::{ISWbemObject, ISWbemServices},
    },
};

pub fn logoff_session(server: &ISWbemServices, session_id: u32) -> windows::core::Result<()> {
    unsafe {
        println!("RUNNING logoff_session...");

        // Define the WMI class and method to be used
        let class_name = BSTR::from("Win32_Process");
        let method_name = BSTR::from("Create");
        let command = format!("logoff {}", session_id);

        // Get the Win32_Process class object
        let process_class_object: ISWbemObject = server.Get(&class_name, 0, None)?;
        println!("Got the process class object.");

        // Get the method from the class object
        let methods = process_class_object.Methods_()?;
        let create_method = methods.Item(&method_name, 0)?;
        println!("Got the Create method from the process class.");

        // Get the input parameters definition and spawn an instance
        let in_params_def = create_method.InParameters()?;
        let in_params = in_params_def.SpawnInstance_(0)?;
        println!("Spawned an instance of the input parameters.");

        // Set the CommandLine property with the logoff command
        let command_line_prop = in_params
            .Properties_()?
            .Item(&BSTR::from("CommandLine"), 0)?;
        println!("Got the CommandLine property.");

        // Prepare the VARIANT for the command line argument
        let mut command_variant = VARIANT {
            Anonymous: VARIANT_0 {
                Anonymous: ManuallyDrop::new(std::mem::zeroed()),
            },
        };
        (*command_variant.Anonymous.Anonymous).vt = VT_BSTR;
        (*command_variant.Anonymous.Anonymous).Anonymous.bstrVal =
            ManuallyDrop::new(BSTR::from(command.as_str()));
        println!("Prepared the VARIANT for the command.");

        // Set the CommandLine property value
        command_line_prop.SetValue(&command_variant)?;
        println!("Set the CommandLine property value to logoff command.");

        // Convert in_params to IDispatch reference
        let in_params_dispatch = in_params.cast::<IDispatch>()?;
        println!("Converted input parameters to IDispatch.");

        // Execute the method with the input parameters
        let out_params = server.ExecMethod(
            &class_name,
            &method_name,
            Some(&in_params_dispatch),
            0,
            None,
        )?;
        println!("Executed the Create method.");

        // Check the return value to determine success
        let return_value_prop = out_params
            .Properties_()?
            .Item(&BSTR::from("ReturnValue"), 0)?;
        let return_value: VARIANT = return_value_prop.Value()?;
        println!(
            "Return value variant type: {:?}",
            return_value.Anonymous.Anonymous.vt
        );
        println!(
            "Return value: {:?}",
            return_value.Anonymous.Anonymous.Anonymous.lVal
        );

        // Access the value correctly based on the VARIANT type
        if return_value.Anonymous.Anonymous.vt == VT_I4 {
            if return_value.Anonymous.Anonymous.Anonymous.lVal == 0 {
                println!("Session {} logged off successfully.", session_id);
            } else {
                println!("Failed to log off session {}.", session_id);
            }
        } else {
            println!(
                "Unexpected VARIANT type: {:?}",
                return_value.Anonymous.Anonymous.vt
            );
        }

        println!("FINISHED logoff_session!");

        Ok(())
    }
}

Here is the loggin output:

RUNNING logoff_session...
Got the process class object.
Got the Create method from the process class.
Spawned an instance of the input parameters.
Got the CommandLine property.
Prepared the VARIANT for the command.
Set the CommandLine property value to logoff command.
Converted input parameters to IDispatch.
Executed the Create method.
Return value variant type: VARENUM(3)
Return value: 0
Session 999999 logged off successfully.
FINISHED logoff_session!

This feels like a bug to me.

Crate manifest

[package]
name = "windows_interface_test"
version = "1.0.0"
edition = "2021"

[lib]
name = "windows_interface_test"
path = "src/lib.rs"

[[bin]]
name = "windows_interface_test_bin"
path = "src/main.rs"

[dependencies]
windows = { version = "0.52.0", features = [
    "implement",
    "Win32_Foundation",
    "Win32_Security",
    "Win32_System_Com",
    "Win32_System_Ole",
    "Win32_System_Rpc",
    "Win32_System_Wmi",
    "Win32_System_Variant",
    ] }

Crate code

No response

Hey, glad you like windows-rs! Can you try the latest version? There are various improvements to both WMI and VARIANT support. #2786

Here's an example:

https://github.com/microsoft/windows-rs/tree/master/crates/samples/windows/wmi

Win32_Process::Create returns a value of 0 (zero) if the process was successfully created, and any other number to indicate an error. (doc) It does not return the status code returned by logoff.exe. To verify things are working correctly, try executing abcdef instead of logoff.

Hi @kennykerr, hi @riverar,

First off, thank you for your quick responses and the continuous improvements to this project!


Feedback on Version Update (0.52 -> 0.58):

I've recently attempted to upgrade from version 0.52 to 0.58 (noting that 0.59 isn't available on crates.io yet). The number of changes, particularly around the handling of ISWbemObject and VARIANT, has been a bit overwhelming. For instance, the following code that worked in 0.52 is no longer functional:

if let Some(dispatch) = var.Anonymous.Anonymous.Anonymous.pdispVal.as_ref() {
    let item: ISWbemObject = dispatch.cast()?;
    let instance = T::from_wmi_object(&item)?;
    results.push(instance);
}

In the newer versions, this now returns a &c_void (when accessed with var.as_raw().Anonymous.Anonymous.Anonymous.pdispVal.as_ref()) instead of the previous ManuallyDrop<Option<IDispatch>>. This change has significantly impacted my ability to run the desired queries as I heavily rely on VARENUMS and patterns like the one above.

Given the scope of the changes, I found it difficult to update my code to work with versions beyond 0.52. I do see the value in the new VARIANT methods for value access, and when I have more time, I plan to refactor my code to match the newer versions. However, for now, the update is more than I can manage.

PS.: Please note that the samples are using WbemObject whereas I am using SWbemObject.


Success Code Always 0 Feedback:

Regarding the ExecMethod function, I've noticed that it returns a value of 0 when the process exists and can be created (thanks for pointing that out @riverar). However, the actual result of that process isn't returned. This isn't a crate-specific issue, but I thought it worth mentioning as I continue to explore this behavior further.


Thanks again for all your hard work on this project. I look forward to integrating the new updates when I have the bandwidth to do so!

Best regards