Skip to content

Commit c452a8c

Browse files
committed
fix(windows): fix that_detached for UNC path of a directory
closes #106 This PR also fallsback to `ShellExecuteExW` if `SHOpenFolderAndSelectItems` fails
1 parent 2646ff8 commit c452a8c

File tree

2 files changed

+27
-7
lines changed

2 files changed

+27
-7
lines changed

Cargo.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ rust-version = "1.62"
2020
##
2121
## There may be other side-effects that when comparing to the command-based
2222
## opening of paths, which is why this feature is opt-in.
23-
shellexecute-on-windows = []
23+
shellexecute-on-windows = ["dep:dunce"]
2424

2525
[[bin]]
2626
test = false
@@ -35,3 +35,6 @@ is-wsl = "0.4.0"
3535

3636
[target."cfg(unix)".dependencies]
3737
libc = "0.2"
38+
39+
[target."cfg(windows)".dependencies]
40+
dunce = { version = "1", optional = true }

src/windows.rs

+23-6
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,34 @@ fn wrap_in_quotes<T: AsRef<OsStr>>(path: T) -> OsString {
3838

3939
#[cfg(feature = "shellexecute-on-windows")]
4040
pub fn that_detached<T: AsRef<OsStr>>(path: T) -> std::io::Result<()> {
41+
use std::path::Path;
42+
4143
let path = path.as_ref();
4244
let is_dir = std::fs::metadata(path).map(|f| f.is_dir()).unwrap_or(false);
4345

44-
let path = wide(path);
45-
4646
if is_dir {
47+
let path = dunce::simplified(Path::new(path));
48+
let path = wide(path);
4749
unsafe { ffi::CoInitialize(std::ptr::null()) };
4850
let folder = unsafe { ffi::ILCreateFromPathW(path.as_ptr()) };
49-
unsafe { SHOpenFolderAndSelectItems(folder, Some(&[folder]), 0)? };
50-
return Ok(());
51+
if unsafe { SHOpenFolderAndSelectItems(folder, Some(&[folder]), 0) }.is_ok() {
52+
return Ok(());
53+
}
54+
};
55+
56+
let path = wide(path);
57+
58+
let (verb, class) = if is_dir {
59+
(ffi::EXPLORE, ffi::FOLDER)
60+
} else {
61+
(std::ptr::null(), std::ptr::null())
5162
};
5263

5364
let mut info = ffi::SHELLEXECUTEINFOW {
5465
cbSize: std::mem::size_of::<ffi::SHELLEXECUTEINFOW>() as _,
5566
nShow: ffi::SW_SHOWNORMAL,
56-
lpVerb: std::ptr::null(),
57-
lpClass: std::ptr::null(),
67+
lpVerb: verb,
68+
lpClass: class,
5869
lpFile: path.as_ptr(),
5970
..unsafe { std::mem::zeroed() }
6071
};
@@ -145,6 +156,12 @@ mod ffi {
145156
/// <https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow>
146157
pub const SW_SHOWNORMAL: i32 = 1;
147158

159+
/// Null-terminated UTF-16 encoding of `explore`.
160+
pub const EXPLORE: *const u16 = [101, 120, 112, 108, 111, 114, 101, 0].as_ptr();
161+
162+
/// Null-terminated UTF-16 encoding of `folder`.
163+
pub const FOLDER: *const u16 = [102, 111, 108, 100, 101, 114, 0].as_ptr();
164+
148165
// Taken from https://docs.rs/windows-sys/latest/windows_sys/
149166
#[cfg_attr(not(target_arch = "x86"), repr(C))]
150167
#[cfg_attr(target_arch = "x86", repr(C, packed(1)))]

0 commit comments

Comments
 (0)