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:
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.
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"
]