3

I'm trying to convert the C++ COM code for TaskScheduler to Rust and am stuck with the VARIANT argument of ITaskService::Connect:

extern crate winapi;
use winapi::{
    ctypes::c_void,
    shared::{
        guiddef::{GUID, REFCLSID, REFIID},
        ntdef::{HRESULT, NULL},
        rpcdce::{RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE},
        winerror::FAILED,
        wtypes::VARENUM,
        wtypesbase::CLSCTX_INPROC_SERVER,
    },
    um::{
        combaseapi::{CoCreateInstance, CoInitializeEx, CoInitializeSecurity, CoUninitialize},
        oaidl,
        objbase::COINIT_MULTITHREADED,
        taskschd::{ITaskService, TaskScheduler},
    },
    Class, Interface,
}; // 0.3.7

fn main() {
    //  Create an instance of the Task Service.
    let mut p_service: *mut ITaskService = unsafe { std::mem::zeroed() };

    let hr = unsafe {
        CoCreateInstance(
            &TaskScheduler::uuidof(),
            std::ptr::null_mut(),
            CLSCTX_INPROC_SERVER,
            &ITaskService::uuidof(),
            &mut p_service as *mut *mut ITaskService as *mut *mut c_void,
        )
    };

    // [...]
    // //  Connect to the task service.
    // let hr = pService->Connect(_variant_t(), _variant_t(),
    //     _variant_t(), _variant_t());

    let hr = unsafe {
        (&mut *p_service).Connect(
            oaidl::VARIANT { /* VT_EMPTY HERE */ },
            oaidl::VARIANT { /* VT_EMPTY HERE */ },
            oaidl::VARIANT { /* VT_EMPTY HERE */ },
            oaidl::VARIANT { /* VT_EMPTY HERE */ },
        )
    };
}

The docs say:

_variant_t( ) Constructs an empty _variant_t object, VT_EMPTY.

I found no mention on how to use the variant as stated in the README:

Use std::mem::zeroed() to create an instance of the union, and then assign the value you want using one of the variant methods.

When checking the docs for "variant", there's no _variant_t fn on the list, so not sure what it is called in Rust

Errors

field `0` of struct `winapi::um::oaidl::VARIANT_n1` is private

field `0` is private

for

  oaidl::VARIANT {
    n1: oaidl::VARIANT_n1 {
      0: [0, 0, 0]
    }
  }
6
  • _variant_t is a c++ class so I suppose you must bind something to one of its constructor. Bindgen should be able to do it for you. How do you bind all this API so far ? Your question lack of detail. Commented Jun 12, 2019 at 23:58
  • 2
    _variant_t is just a C++ helper class that wraps a COM VARIANT, it automates memory management and reference counting for VARIANT values. _variant_t doesn't exist in other languages, like Rust, so you will have to construct a VARIANT manually and populate it as needed. Commented Jun 13, 2019 at 0:07
  • 2
    A COM VARIANT is a tagged union, have a look at How Rust Implements Tagged Unions and A quick tour of Rust’s Type System Part 1: Sum Types (a.k.a. Tagged Unions) Commented Jun 13, 2019 at 0:08
  • to be completely clear: I'm stuck and have not made any progress, there's no reproducible example because of this, and winapi::um must have winuser enabled in Cargo features. VARIANT_n1 is private and can't be used when using VARIANT { n1: /*...*/ }. Commented Jun 13, 2019 at 21:55
  • @pocesar did you ever find a solution? Im trying to create a scheduled task in windows with rust as well but no luck so far. Can you help? Commented Apr 8, 2021 at 16:52

2 Answers 2

2

Initializing unions is explained in winapi's README:

How do I create an instance of a union?

Use std::mem::zeroed() to create an instance of the union, and then assign the value you want using one of the variant methods.

For instance, for VARIANT_n1, the methods are n2, n2_mut, decVal and decVal_mut. You will naturally need to use a _mut method to assign a value.

Note that, starting with winapi 0.3.7, you can also use Default::default() to obtain a zeroed union instead of using the unsafe std::mem::zeroed(). You need to enable the impl-default feature on the winapi crate for the Default trait to be implemented.

Sign up to request clarification or add additional context in comments.

4 Comments

the trait 'std::default::Default' is not implemented for '*mut winapi::um::taskschd::ITaskService' and the trait 'std::default::Default' is not implemented for 'winapi::um::oaidl::VARIANT_n1'. while searching for those methods, it seems that they are defined in another external crate, called oaidl (that makes working with those variants less painful)
But _variant_t is not an union. It's class or I miss something
@pocesar Default is implemented for VARIANT_N1.
@pocesar Make sure you're using winapi 0.3.7 or later. The Default implementations were added in 0.3.7. Use cargo update to update your dependencies.
0

You can construct a variant with

use winapi::um::oaidl::VARIANT;

fn doStuff() {
  unsafe {
    let mut variant: VARIANT = std::mem::zeroed();
    // set values here
  }
}

variant.n1.n2().vt contains the type (e.g. winapi::shared::wtypes::VT_BOOL or winapi::shared::wtypes::VT_EMPTY, msdn describes the possible values here), the variant.n1.n2().n3 union contains the value.

1 Comment

Thanks for taking your time but I am still unable to instantiate a variant with a bstr value in order to pass as an userId to RegisterTaskDefinition()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.