From 1484af08dd37d73222ca4670f1c708de939a3d98 Mon Sep 17 00:00:00 2001 From: Ar3love Date: Mon, 9 Mar 2026 00:52:53 +0300 Subject: [PATCH 1/3] fix(opener): strip UNC prefix before passing path to ILCreateFromPathW on Windows Fixes #3304 --- plugins/opener/src/reveal_item_in_dir.rs | 61 +++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/plugins/opener/src/reveal_item_in_dir.rs b/plugins/opener/src/reveal_item_in_dir.rs index 18f940c80a..c3993e2c99 100644 --- a/plugins/opener/src/reveal_item_in_dir.rs +++ b/plugins/opener/src/reveal_item_in_dir.rs @@ -96,6 +96,17 @@ mod imp { }, }; + pub(super) fn normalize_path_for_shell(path: &Path) -> std::borrow::Cow<'_, Path> { + if let Some(s) = path.to_str() { + if let Some(unc) = s.strip_prefix(r"\\?\UNC\") { + return std::borrow::Cow::Owned( + PathBuf::from(format!(r"\\{}", unc)) + ); + } + } + std::borrow::Cow::Borrowed(path) + } + pub fn reveal_items_in_dir(paths: &[PathBuf]) -> crate::Result<()> { if paths.is_empty() { return Ok(()); @@ -166,11 +177,12 @@ mod imp { impl OwnedItemIdList { fn new(path: &Path) -> crate::Result { - let path_hstring = HSTRING::from(path); + let path = normalize_path_for_shell(path); + let path_hstring = HSTRING::from(path.as_ref()); let item_id_list = unsafe { ILCreateFromPathW(&path_hstring) }; if item_id_list.is_null() { Err(crate::Error::FailedToConvertPathToItemIdList( - path.to_owned(), + path.to_path_buf(), )) } else { Ok(Self { @@ -297,3 +309,48 @@ mod imp { Ok(()) } } + + +#[cfg(test)] +#[cfg(windows)] +mod tests { + use super::imp::normalize_path_for_shell; + use std::path::{Path, PathBuf}; + + fn simulate_canonicalize_unc() -> PathBuf { + PathBuf::from(r"\\?\UNC\server\share\file.mkv") + } + + #[test] + fn unc_path_should_not_have_extended_prefix() { + let canonicalized = simulate_canonicalize_unc(); + let result = normalize_path_for_shell(&canonicalized); + let s = result.as_ref().to_str().unwrap(); + assert!( + !s.starts_with(r"\\?\UNC\"), + "Path still has \\?\\UNC\\ prefix: {}", + s + ); + } + + #[test] + fn unc_path_converts_correctly() { + let input = Path::new(r"\\?\UNC\server\share\file.mkv"); + let result = normalize_path_for_shell(input); + assert_eq!(result.as_ref(), Path::new(r"\\server\share\file.mkv")); + } + + #[test] + fn local_path_unchanged() { + let input = Path::new(r"C:\Users\valeri\file.txt"); + let result = normalize_path_for_shell(input); + assert_eq!(result.as_ref(), input); + } + + #[test] + fn plain_unc_unchanged() { + let input = Path::new(r"\\server\share\file.mkv"); + let result = normalize_path_for_shell(input); + assert_eq!(result.as_ref(), input); + } +} \ No newline at end of file From d72e44d9bb15bc30bb631311c5fe8ddf1df38192 Mon Sep 17 00:00:00 2001 From: Ar3love Date: Mon, 9 Mar 2026 23:09:51 +0300 Subject: [PATCH 2/3] style: apply rustfmt formatting --- plugins/opener/src/reveal_item_in_dir.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/opener/src/reveal_item_in_dir.rs b/plugins/opener/src/reveal_item_in_dir.rs index c3993e2c99..f15b412c8a 100644 --- a/plugins/opener/src/reveal_item_in_dir.rs +++ b/plugins/opener/src/reveal_item_in_dir.rs @@ -99,9 +99,7 @@ mod imp { pub(super) fn normalize_path_for_shell(path: &Path) -> std::borrow::Cow<'_, Path> { if let Some(s) = path.to_str() { if let Some(unc) = s.strip_prefix(r"\\?\UNC\") { - return std::borrow::Cow::Owned( - PathBuf::from(format!(r"\\{}", unc)) - ); + return std::borrow::Cow::Owned(PathBuf::from(format!(r"\\{}", unc))); } } std::borrow::Cow::Borrowed(path) @@ -310,7 +308,6 @@ mod imp { } } - #[cfg(test)] #[cfg(windows)] mod tests { @@ -353,4 +350,4 @@ mod tests { let result = normalize_path_for_shell(input); assert_eq!(result.as_ref(), input); } -} \ No newline at end of file +} From c17b0aabb99624f84eb19e1c61d9c0603fa29389 Mon Sep 17 00:00:00 2001 From: Ar3love Date: Wed, 11 Mar 2026 19:54:46 +0300 Subject: [PATCH 3/3] fix: use generic path in test and add edge case tests --- plugins/opener/src/reveal_item_in_dir.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/opener/src/reveal_item_in_dir.rs b/plugins/opener/src/reveal_item_in_dir.rs index f15b412c8a..7e0ff8f7e0 100644 --- a/plugins/opener/src/reveal_item_in_dir.rs +++ b/plugins/opener/src/reveal_item_in_dir.rs @@ -339,7 +339,7 @@ mod tests { #[test] fn local_path_unchanged() { - let input = Path::new(r"C:\Users\valeri\file.txt"); + let input = Path::new(r"C:\path\to\file.txt"); let result = normalize_path_for_shell(input); assert_eq!(result.as_ref(), input); } @@ -350,4 +350,18 @@ mod tests { let result = normalize_path_for_shell(input); assert_eq!(result.as_ref(), input); } + + #[test] + fn unc_path_with_spaces_converts_correctly() { + let input = Path::new(r"\\?\UNC\server\my share\my file.mkv"); + let result = normalize_path_for_shell(input); + assert_eq!(result.as_ref(), Path::new(r"\\server\my share\my file.mkv")); + } + + #[test] + fn local_verbatim_path_unchanged() { + let input = Path::new(r"\\?\C:\path\to\file.txt"); + let result = normalize_path_for_shell(input); + assert_eq!(result.as_ref(), input); + } }