windowswinapirustcom-interfacewindows-rs

Binding a Pidl with function BindToObject?


Here is my Rust code:

use std::mem::ManuallyDrop;
use windows::core::ComInterface;
use windows::Win32::System::Com::*;
use windows::Win32::UI::Shell::Common::ITEMIDLIST;
use windows::{core::Result, Win32::UI::Shell::*};

struct Com;
impl Drop for Com {
    fn drop(&mut self) {
        unsafe { CoUninitialize() };
    }
}

struct Variant(VARIANT);
impl Drop for Variant {
    fn drop(&mut self) {
        unsafe {
            match self.0.Anonymous.Anonymous.vt {
                VT_BSTR => {
                    ManuallyDrop::drop(&mut ((*self.0.Anonymous.Anonymous).Anonymous.bstrVal))
                }
                VT_DISPATCH => {
                    ManuallyDrop::drop(&mut ((*self.0.Anonymous.Anonymous).Anonymous.pdispVal))
                }
                _ => (),
            }
            ManuallyDrop::drop(&mut self.0.Anonymous.Anonymous);
        }
    }
}

fn main() -> Result<()> {
    unsafe {
        CoInitialize(None)?;
        let _com = Com;
        //https://learn.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-createbindctx
        let ibindctx = CreateBindCtx(0u32).unwrap();
        let itemID_list = ITEMIDLIST::default();
        let desktop_folder = SHGetDesktopFolder()?;

        let pidl: [u16; 1] = [0x14]; // convert this into ITEMIDLIST

        desktop_folder.BindToObject::<&IBindCtx>(&itemID_list, &ibindctx)?;
    }
    Ok(())
}

When I try to compile, I have the following error:

image

with the following toml dependencies:

[dependencies.windows]  
version = "0.46"  
features = [  
"Win32_Foundation",  
"Win32_System_Com",  
"Win32_System_Ole",  
"Win32_UI_Shell", 
"Win32_UI_Shell_Common"
]  

I have tried to follow the following documentation from Microsoft:

https://learn.microsoft.com/en-us/windows/win32/shell/folder-info#using-the-ishellfolder-interface

The purpose of this code is to convert a know PIDL from a folder to a display name. Unfortunately, the documentation of the windows crate is not beginner friendly.

Can someone help me, please?

I have tried to follow the C++ documentation of Microsoft for this function, without success.


Solution

  • As I said in the comment, IShellFolder is an old and clunky interface that's not easy to work with. One of the newer interfaces that have been introduced since Windows Vista is IShellItem which offer a simple wrapper over IShellFolder and friends and ususally avoids "talking PIDL" directly which can be a real pain (absolute vs relative, etc.).

    Here is some sample code that demonstrates how to use it if you already have an absolute PIDL:

    use windows::{core::*, Win32::System::Com::*, Win32::UI::Shell::*};
    
    fn main() -> Result<()> {
        unsafe {
            _ = CoInitialize(None)?;
            
            // get some pidl (here the pidl for c:\temp\rust for demonstration)
            let item: IShellItem = SHCreateItemFromParsingName(w!("c:\\temp\\rust"), None)?;
            let pidl = SHGetIDListFromObject(&item)?;
            
            // get a IShellItem from an absolute PIDL
            let other_item : IShellItem = SHCreateItemFromIDList(pidl)?;
    
            // get of IShellItem's display names
            // use SIGDN_NORMALDISPLAY for in-folder name
            let other_name = other_item.GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING)?.to_string()?;
            println!("{other_name}"); // prints c:\temp\rust obviously
            Ok(())
        }
    }
    

    And here is another code that reads a PIDL from the registry (note: a PIDL is a serialized array of bytes of arbitrary size composed of multiple segments, each segment being created and parsable only by the Shell folder which created it https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/cc144089(v=vs.85)#item-id-lists) and displays it's full name (it should corresponds to one of the file that was opened on your disk):

    use ::core::ffi::*;
    use windows::{
        core::*, Win32::System::Com::*, Win32::System::Registry::*, Win32::UI::Shell::Common::*,
        Win32::UI::Shell::*,
    };
    
    fn main() -> Result<()> {
        unsafe {
            _ = CoInitialize(None)?;
    
            let path = w!(
                "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\OpenSavePidlMRU\\*"
            );
    
            let value = w!("0");
    
            // get some registry buffer.
            // step 1: get size
            let mut size = 0;
            SHRegGetValueW(
                HKEY_CURRENT_USER,
                path,
                value,
                SRRF_RT_REG_BINARY as i32,
                None,
                None,
                Some(&mut size),
            );
    
            // step 2: allocate & read buffer
            let mut buffer = vec![0u8; size as usize];
            SHRegGetValueW(
                HKEY_CURRENT_USER,
                path,
                value,
                SRRF_RT_REG_BINARY as i32,
                None,
                Some(buffer.as_mut_ptr() as *mut c_void),
                Some(&mut size),
            );
    
            // resolve this PIDL's absolute path
            let other_item: IShellItem =
                SHCreateItemFromIDList(buffer.as_mut_ptr() as *mut ITEMIDLIST)?;
            let other_name = other_item
                .GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING)?
                .to_string()?;
            println!("{other_name}");
            Ok(())
        }
    }
    

    Needs this in cargo.toml:

    [dependencies.windows]  
    features = [  
    "Win32_Foundation",
    "Win32_System_Com",  
    "Win32_UI_Shell", 
    "Win32_UI_Shell_Common",
    "Win32_System_Registry"
    ]