windows: Fix RevealInFileManager (#36592)

Closes #36314

This PR takes inspiration from [Electron’s
implementation](dd54e84a58/shell/common/platform_util_win.cc (L268-L314)).

Before and after:



https://github.com/user-attachments/assets/53eec5d3-23c7-4ee1-8477-e524b0538f60



Release Notes:

- N/A
This commit is contained in:
张小白 2025-08-21 08:08:54 +08:00 committed by GitHub
parent 3dd362978a
commit 8ef9ecc91f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,5 +1,6 @@
use std::{ use std::{
cell::RefCell, cell::RefCell,
ffi::OsStr,
mem::ManuallyDrop, mem::ManuallyDrop,
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc, rc::Rc,
@ -460,13 +461,15 @@ impl Platform for WindowsPlatform {
} }
fn open_url(&self, url: &str) { fn open_url(&self, url: &str) {
if url.is_empty() {
return;
}
let url_string = url.to_string(); let url_string = url.to_string();
self.background_executor() self.background_executor()
.spawn(async move { .spawn(async move {
if url_string.is_empty() { open_target(&url_string)
return; .with_context(|| format!("Opening url: {}", url_string))
} .log_err();
open_target(url_string.as_str());
}) })
.detach(); .detach();
} }
@ -514,37 +517,29 @@ impl Platform for WindowsPlatform {
} }
fn reveal_path(&self, path: &Path) { fn reveal_path(&self, path: &Path) {
let Ok(file_full_path) = path.canonicalize() else { if path.as_os_str().is_empty() {
log::error!("unable to parse file path");
return; return;
}; }
let path = path.to_path_buf();
self.background_executor() self.background_executor()
.spawn(async move { .spawn(async move {
let Some(path) = file_full_path.to_str() else { open_target_in_explorer(&path)
return; .with_context(|| format!("Revealing path {} in explorer", path.display()))
}; .log_err();
if path.is_empty() {
return;
}
open_target_in_explorer(path);
}) })
.detach(); .detach();
} }
fn open_with_system(&self, path: &Path) { fn open_with_system(&self, path: &Path) {
let Ok(full_path) = path.canonicalize() else { if path.as_os_str().is_empty() {
log::error!("unable to parse file full path: {}", path.display());
return; return;
}; }
let path = path.to_path_buf();
self.background_executor() self.background_executor()
.spawn(async move { .spawn(async move {
let Some(full_path_str) = full_path.to_str() else { open_target(&path)
return; .with_context(|| format!("Opening {} with system", path.display()))
}; .log_err();
if full_path_str.is_empty() {
return;
};
open_target(full_path_str);
}) })
.detach(); .detach();
} }
@ -735,39 +730,67 @@ pub(crate) struct WindowCreationInfo {
pub(crate) disable_direct_composition: bool, pub(crate) disable_direct_composition: bool,
} }
fn open_target(target: &str) { fn open_target(target: impl AsRef<OsStr>) -> Result<()> {
unsafe { let target = target.as_ref();
let ret = ShellExecuteW( let ret = unsafe {
ShellExecuteW(
None, None,
windows::core::w!("open"), windows::core::w!("open"),
&HSTRING::from(target), &HSTRING::from(target),
None, None,
None, None,
SW_SHOWDEFAULT, SW_SHOWDEFAULT,
); )
if ret.0 as isize <= 32 { };
log::error!("Unable to open target: {}", std::io::Error::last_os_error()); if ret.0 as isize <= 32 {
} Err(anyhow::anyhow!(
"Unable to open target: {}",
std::io::Error::last_os_error()
))
} else {
Ok(())
} }
} }
fn open_target_in_explorer(target: &str) { fn open_target_in_explorer(target: &Path) -> Result<()> {
let dir = target.parent().context("No parent folder found")?;
let desktop = unsafe { SHGetDesktopFolder()? };
let mut dir_item = std::ptr::null_mut();
unsafe { unsafe {
let ret = ShellExecuteW( desktop.ParseDisplayName(
HWND::default(),
None, None,
windows::core::w!("open"), &HSTRING::from(dir),
windows::core::w!("explorer.exe"),
&HSTRING::from(format!("/select,{}", target).as_str()),
None, None,
SW_SHOWDEFAULT, &mut dir_item,
); std::ptr::null_mut(),
if ret.0 as isize <= 32 { )?;
log::error!(
"Unable to open target in explorer: {}",
std::io::Error::last_os_error()
);
}
} }
let mut file_item = std::ptr::null_mut();
unsafe {
desktop.ParseDisplayName(
HWND::default(),
None,
&HSTRING::from(target),
None,
&mut file_item,
std::ptr::null_mut(),
)?;
}
let highlight = [file_item as *const _];
unsafe { SHOpenFolderAndSelectItems(dir_item as _, Some(&highlight), 0) }.or_else(|err| {
if err.code().0 == ERROR_FILE_NOT_FOUND.0 as i32 {
// On some systems, the above call mysteriously fails with "file not
// found" even though the file is there. In these cases, ShellExecute()
// seems to work as a fallback (although it won't select the file).
open_target(dir).context("Opening target parent folder")
} else {
Err(anyhow::anyhow!("Can not open target path: {}", err))
}
})
} }
fn file_open_dialog( fn file_open_dialog(