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

InitPropVariantFromString missing #976

Closed
valarauca opened this issue Jul 12, 2021 · 11 comments
Closed

InitPropVariantFromString missing #976

valarauca opened this issue Jul 12, 2021 · 11 comments
Labels
enhancement New feature or request

Comments

@valarauca
Copy link

The function InitPropVariantFromString appears missing.

I receive the following error when I include it in my build.rs

error: `Windows.Win32.System.PropertiesSystem.InitPropVariantFromString` not found in metadata
  --> build.rs:13:51
   |
13 |         Windows::Win32::System::PropertiesSystem::InitPropVariantFromString,
   |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^

error: could not compile

It does not appear in the docs search.

The following exist InitPropVariantFromStringAsVector, InitPropVariantFromStringVector, and InitPropVariantFromStrRet.

@riverar
Copy link
Collaborator

riverar commented Jul 12, 2021

Hey @valarauca, per Microsoft Docs, InitPropVariantFromString is just a helper function in propvarutil.h

Remarks
This is an inline function, with its source code provided in the header. It is not included in any .dll or .lib file.

It performs some setup work, then passes that on to PropVariantInit macro, which ultimately ends up just calling memset to zero out a block of memory.

So while the tools are all there to properly initialize windows property structures, I think we could probably do better and provide some helpers. Or perhaps an example. Tagging @kennykerr for additional thoughts.

@riverar riverar added the enhancement New feature or request label Jul 12, 2021
@riverar
Copy link
Collaborator

riverar commented Jul 13, 2021

Related: #656

To unblock you, perhaps consider initializing a propvariant like so:

let mut str: Vec<u16> = "hello world".encode_utf16().collect();
let prop = PROPVARIANT {
    Anonymous: PROPVARIANT_0 {
        Anonymous: PROPVARIANT_0_0_abi {
            vt: VT_LPWSTR.0 as u16,
            wReserved1: 0,
            wReserved2: 0,
            wReserved3: 0,
            Anonymous: PROPVARIANT_0_0_0_abi {
                pwszVal: PWSTR(str.as_mut_ptr()),
            },
        },
    },
};

@kennykerr
Copy link
Collaborator

Yes, the metadata intentionally excludes functions with bodies as they aren't portable in the general sense. While support for unions and nested structs should improve, specialized APIs that rely heavily on macros - like VARIANT does - will likely always need hand-written libraries to provide the kind of idiomatic experience Rust developers are expecting.

@riverar
Copy link
Collaborator

riverar commented Jul 13, 2021

Closing this issue in favor of #656, thanks! If you feel this is in error, re-open / let us know.

@andy-westacott
Copy link

Hi @riverar
I've come across this code you mention above. I'm converting some C++ to Rust where I need to set the app id of a window using PKEY_AppUserModel_ID and SHGetPropertyStoreForWindow.

As mentioned InitPropVariantFromString not a function as such that can be called from Rust.

I've tried your workaround above but types such as PROPVARIANT_0_0_abi are missing?

Do you have an example somewhere that shows this in action?

My C++ equivalent looks like the following

PROPVARIANT pv;
::InitPropVariantFromString("my-app-group", &pv);
IPropertyStore* pps;
HRESULT hr = ::SHGetPropertyStoreForWindow(hWnd, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr))
{
	hr = pps->SetValue(PKEY_AppUserModel_ID, pv);
	pps->Commit();
	pps->Release();
}

Thanks

@riverar
Copy link
Collaborator

riverar commented Jul 7, 2023

Hey @andy-westacott, here's an updated sample. (docs)

use std::mem::ManuallyDrop;

use windows::{
    core::PWSTR,
    Win32::System::Com::{
        StructuredStorage::{PROPVARIANT, PROPVARIANT_0, PROPVARIANT_0_0, PROPVARIANT_0_0_0},
        VT_LPWSTR,
    },
};

fn main() -> windows::core::Result<()> {
    let mut str: Vec<u16> = "hello world\0".encode_utf16().collect();

    let value = PROPVARIANT_0_0 {
        vt: VT_LPWSTR,
        Anonymous: PROPVARIANT_0_0_0 {
            pwszVal: PWSTR(str.as_mut_ptr()),
        },
        ..Default::default()
    };

    let mut prop = PROPVARIANT {
        Anonymous: PROPVARIANT_0 {
            Anonymous: ManuallyDrop::new(value),
        },
    };

    // ...

    unsafe {
        ManuallyDrop::drop(&mut prop.Anonymous.Anonymous);
    }

    Ok(())
}

@kennykerr
Copy link
Collaborator

Related to #539

@andy-westacott
Copy link

@riverar ah thats great. many thanks.

A little related to this. What's the correct way of calling SHGetPropertyStoreForWindow in Rust and receiving an IPropertyStore? I've found several examples that have led me to something like the following. Which seem to panic when I actually call SetValue so I'm assuming the pointer back from SHGetPropertyStoreForWindow is invalid?

    let mut obj: *mut IPropertyStore = null_mut();
    if let Ok(()) = SHGetPropertyStoreForWindow(
        hwnd,
        &GUID::from_u128(0x886d8eeb_8cf2_4446_8d02_cdba1dbdcf99),
        transmute(&mut obj),
    ) {
        let PKEY_AppUserModel_ID = PROPERTYKEY {
            fmtid: GUID::from_u128(0x9f4c2855_9f79_4b39_a8d0_e1d42de1d5f3),
            pid: 5,
        };

        println!("**** PKEY_AppUserModel_ID: {:?}", PKEY_AppUserModel_ID);
        obj.as_mut()
            .unwrap()
            .SetValue(&PKEY_AppUserModel_ID, &mut prop);
        obj.as_mut().unwrap().Commit();
    } else {
        println!("Failed to get IPropertyStore");
        return;
    }

@kennykerr
Copy link
Collaborator

That's problematic because the Win32 metadata is missing the [ComOutPtr] attribute on SHGetPropertyStoreForWindow. I'm not sure if there's a bug open for that.

Until that is fixed, the following workaround will work:

let mut object: Option<IPropertyStore> = None;
SHGetPropertyStoreForWindow(handle, &IPropertyStore::IID, &mut object as *mut _ as _)?;
let object = object.unwrap();
// use object

Once the metadata is fixed, you'll be able to write:

let object: IPropertyStore = SHGetPropertyStoreForWindow(handle)?;
// use object

@andy-westacott
Copy link

Ah perfect. Thats works great now. Thank you. Thought I was going a little nuts ;)

@riverar
Copy link
Collaborator

riverar commented Jul 7, 2023

Kenny beat me to it, but here's a working sample. (Use his approach above for IPropertyStore.)

[dependencies.windows]
version = "0.48.0"
features = [
    "Win32_Foundation",
    "Win32_System_Com_StructuredStorage",
    "Win32_UI_Shell_PropertiesSystem",
    "Win32_Storage_EnhancedStorage",
    "Win32_UI_WindowsAndMessaging"
]
use windows::{
    core::{ComInterface, Interface},
    w,
    Win32::{
        Foundation::HWND,
        Storage::EnhancedStorage::PKEY_AppUserModel_ID,
        System::Com::VT_LPWSTR,
        UI::{
            Shell::PropertiesSystem::{IPropertyStore, SHGetPropertyStoreForWindow},
            WindowsAndMessaging::FindWindowW,
        },
    },
};

fn main() -> windows::core::Result<()> {
    unsafe {
        let hwnd = FindWindowW(None, w!("Microsoft Store"));
        assert_ne!(hwnd, HWND(0));

        let mut ptr: *mut std::ffi::c_void = std::ptr::null_mut();
        SHGetPropertyStoreForWindow(hwnd, &IPropertyStore::IID, &mut ptr)?;
        let store = IPropertyStore::from_raw(ptr);

        let value = store.GetValue(&PKEY_AppUserModel_ID)?;
        assert_eq!(value.Anonymous.Anonymous.vt, VT_LPWSTR);

        println!(
            "AUMID: {}",
            value.Anonymous.Anonymous.Anonymous.pwszVal.to_string()?
        );
    }

    Ok(())
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants