Skip to content

Commit 7595da7

Browse files
committed
fix: use SHOpenFolderAndSelectItems for folders
To respect 'expand to open folder' setting Fixes #103
1 parent c26d98c commit 7595da7

File tree

1 file changed

+62
-12
lines changed

1 file changed

+62
-12
lines changed

src/windows.rs

+62-12
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,18 @@ pub fn that_detached<T: AsRef<OsStr>>(path: T) -> std::io::Result<()> {
4343

4444
let path = wide(path);
4545

46-
let (verb, class) = if is_dir {
47-
(ffi::EXPLORE, ffi::FOLDER)
48-
} else {
49-
(std::ptr::null(), std::ptr::null())
46+
if is_dir {
47+
unsafe { ffi::CoInitialize(std::ptr::null()) };
48+
let folder = unsafe { ffi::ILCreateFromPathW(path.as_ptr()) };
49+
unsafe { SHOpenFolderAndSelectItems(folder, Some(&[folder]), 0)? };
50+
return Ok(());
5051
};
5152

5253
let mut info = ffi::SHELLEXECUTEINFOW {
5354
cbSize: std::mem::size_of::<ffi::SHELLEXECUTEINFOW>() as _,
5455
nShow: ffi::SW_SHOWNORMAL,
55-
lpVerb: verb,
56-
lpClass: class,
56+
lpVerb: std::ptr::null(),
57+
lpClass: std::ptr::null(),
5758
lpFile: path.as_ptr(),
5859
..unsafe { std::mem::zeroed() }
5960
};
@@ -104,6 +105,36 @@ pub unsafe fn ShellExecuteExW(info: *mut ffi::SHELLEXECUTEINFOW) -> std::io::Res
104105
}
105106
}
106107

108+
// Taken from https://microsoft.github.io/windows-docs-rs/doc/windows/
109+
/// Opens a Windows Explorer window with specified items in a particular folder selected.
110+
///
111+
/// <https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shopenfolderandselectitems>
112+
#[allow(non_snake_case)]
113+
#[cfg(feature = "shellexecute-on-windows")]
114+
pub unsafe fn SHOpenFolderAndSelectItems(
115+
pidlfolder: *const ffi::ITEMIDLIST,
116+
apidl: Option<&[*const ffi::ITEMIDLIST]>,
117+
dwflags: u32,
118+
) -> std::io::Result<()> {
119+
use std::convert::TryInto;
120+
121+
match ffi::SHOpenFolderAndSelectItems(
122+
pidlfolder,
123+
apidl
124+
.as_deref()
125+
.map_or(0, |slice| slice.len().try_into().unwrap()),
126+
core::mem::transmute(
127+
apidl
128+
.as_deref()
129+
.map_or(core::ptr::null(), |slice| slice.as_ptr()),
130+
),
131+
dwflags,
132+
) {
133+
0 => Ok(()),
134+
error_code => Err(std::io::Error::from_raw_os_error(error_code)),
135+
}
136+
}
137+
107138
#[cfg(feature = "shellexecute-on-windows")]
108139
#[allow(non_snake_case)]
109140
mod ffi {
@@ -114,12 +145,6 @@ mod ffi {
114145
/// <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow>
115146
pub const SW_SHOWNORMAL: i32 = 1;
116147

117-
/// Null-terminated UTF-16 encoding of `explore`.
118-
pub const EXPLORE: *const u16 = [101, 120, 112, 108, 111, 114, 101, 0].as_ptr();
119-
120-
/// Null-terminated UTF-16 encoding of `folder`.
121-
pub const FOLDER: *const u16 = [102, 111, 108, 100, 101, 114, 0].as_ptr();
122-
123148
// Taken from https://docs.rs/windows-sys/latest/windows_sys/
124149
#[cfg_attr(not(target_arch = "x86"), repr(C))]
125150
#[cfg_attr(target_arch = "x86", repr(C, packed(1)))]
@@ -149,8 +174,33 @@ mod ffi {
149174
pub hMonitor: isize,
150175
}
151176

177+
// Taken from https://microsoft.github.io/windows-docs-rs/doc/windows/
178+
#[repr(C, packed(1))]
179+
pub struct SHITEMID {
180+
pub cb: u16,
181+
pub abID: [u8; 1],
182+
}
183+
184+
// Taken from https://microsoft.github.io/windows-docs-rs/doc/windows/
185+
#[repr(C, packed(1))]
186+
pub struct ITEMIDLIST {
187+
pub mkid: SHITEMID,
188+
}
189+
152190
#[link(name = "shell32")]
153191
extern "system" {
154192
pub fn ShellExecuteExW(info: *mut SHELLEXECUTEINFOW) -> isize;
193+
pub fn ILCreateFromPathW(pszpath: *const u16) -> *mut ITEMIDLIST;
194+
pub fn SHOpenFolderAndSelectItems(
195+
pidlfolder: *const ITEMIDLIST,
196+
cidl: u32,
197+
apidl: *const *const ITEMIDLIST,
198+
dwflags: u32,
199+
) -> i32;
200+
}
201+
202+
#[link(name = "ole32")]
203+
extern "system" {
204+
pub fn CoInitialize(pvreserved: *const core::ffi::c_void) -> i32;
155205
}
156206
}

0 commit comments

Comments
 (0)