windows-rs icon indicating copy to clipboard operation
windows-rs copied to clipboard

Support for `VARIANT` data type

Open 1Dragoon opened this issue 3 years ago • 10 comments

Need support for VARIANT type for use with certain COM objects

From #536

VARIANT support is coming. Much like BSTR, there will likely be a helper struct named Variant that provides friendlier access to the underlying union.

1Dragoon avatar Feb 12 '21 19:02 1Dragoon

Thanks! This depends on #417, which I'll be wrapping up soon.

kennykerr avatar Feb 12 '21 20:02 kennykerr

Unions are now supported. VARIANT though has union members that need to be dropped. While the union's memory layout is now correct, it doesn't provide support for dropping, so I'll leave this issue open until I add that.

kennykerr avatar Mar 09 '21 16:03 kennykerr

Wrapping members in ManuallyDrop, as you mention in the other issue, sounds plausible.

But in the specific case of VARIANT, should we (additionally) manually impl Drop, by calling VariantClear?

iainnicol avatar Mar 09 '21 19:03 iainnicol

Yes, the code gen should inject a Drop impl for that. It already does that for BSTR.

kennykerr avatar Mar 09 '21 19:03 kennykerr

One more request to finish fleshing out VARIANT, please implement the Default trait as well - this makes it easier (and consistent with other types) to create one to pass-by-pointer to a function.

dpaoliello avatar May 21 '21 19:05 dpaoliello

Also eagerly awaiting this. Will be very helpful for my upcoming implementation of UI Automation provider interfaces in AccessKit.

mwcampbell avatar Aug 28 '21 14:08 mwcampbell

I'm getting closer to being able to tackle this. 😉

kennykerr avatar Aug 30 '21 18:08 kennykerr

I just implemented Raymond Chen's How can I launch an unelevated process from my elevated process and vice versa using windows-rs, which required VARIANT<BSTR> for the ShellExecute call.

It took a while, and I have no idea if I did the ManuallyDrop stuff correctly. Here's the result, with a basic VARIANT helper: https://gist.github.com/lunixbochs/c55f6a3b3184d800f096b52997421113

I would love first-party VARIANT, I think it would've saved me a lot of time.

If anyone else needs VARIANT support in the meantime for more than just string and i32, you should be able to copy my impl From<T> for Variant example and cross-reference these definitions to figure out what to pass to Variant::new(). If you implement one of the ManuallyDrop types, make sure to add it to the impl Drop for Variant code.

lunixbochs avatar Oct 21 '21 22:10 lunixbochs

A related issue I've had in this area: propvarutil.h has a bunch of VARIANT helpers but it seems like they're not all present in windows-rs.

For example, InitVariantFromInt32Array() exists but it seems like InitVariantFromInt32() does not. Not sure why.

rgwood avatar Jun 07 '22 23:06 rgwood

@rgwood See the Remarks on that page and this FAQ entry. 👍

This is an inline function, with its source code provided in the header.

riverar avatar Jun 08 '22 00:06 riverar

Seeing as this is not completed yet I am hoping that someone here could help me out and tell me if I'm headed in the correct direction... here's what I have for creating a boolean VARIANT:

use windows::Win32::System::Variant::{VARIANT, VARIANT_0, VARIANT_0_0, VARIANT_0_0_0, VT_BOOL};
use windows::Win32::Foundation::VARIANT_BOOL;

let b = true;
VARIANT {
	Anonymous: VARIANT_0 {
		Anonymous: ManuallyDrop::new(VARIANT_0_0 {
			vt: VT_BOOL,
			wReserved1: 0,
			wReserved2: 0,
			wReserved3: 0,
			Anonymous: VARIANT_0_0_0 {
				boolVal: VARIANT_BOOL::from(b),
			},
		})
	},
}

hcldan avatar Dec 19 '23 15:12 hcldan

Also... I am getting unsafe access warnings when trying to access the innards of this Variant, like when I need to check the value of something inside. The compiler tells me I need to wrap my code in unsafe blocks.

Is there a way around this? How should I be accessing this?

More specifically, if I call a COM method that returns a VARIANT that contains an IDispatch, how can I safely unwrap that and do I need to manually dispose of any memory?

hcldan avatar Dec 19 '23 15:12 hcldan

@hcldan VARIANTs are gnarly, and using them from Rust produces terrifying code.

tell me if I'm headed in the correct direction

The code you have properly instantiates a VARIANT of type VT_BOOL.

The compiler tells me I need to wrap my code in unsafe blocks.

That is correct. Unions in Rust can be initialized from safe Rust, but reading and writing union fields is an inherently unsafe operation. This cannot be changed, and the only way around this would be a safe abstraction (which is what this issue is about).

how can I safely unwrap that and do I need to manually dispose of any memory?

You cannot touch union fields in safe Rust. An unsafe block is always required. Depending on what any given VARIANT stores, you may or may not need to release (shared) ownership. You could determine this by inspecting the vt field and conditionally releasing ownership or having the system deal with the complexity. The VARIANT wrappers I'd implemented simply call VariantClear() from their Drop implementation. This is potentially more costly than strictly necessary, but it's also quite possibly more correct than any code logic I could come up with.

tim-weis avatar Dec 20 '23 09:12 tim-weis

In the meantime while windows crate gain support for ergonomic usage of VARIANT, variant-rs is a good alternative to use for working with the VARIANT type.

mominul avatar Dec 21 '23 04:12 mominul

See #2780 for VARIANT support.

kennykerr avatar Jan 09 '24 18:01 kennykerr