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::{
cell::RefCell,
ffi::OsStr,
mem::ManuallyDrop,
path::{Path, PathBuf},
rc::Rc,
@ -460,13 +461,15 @@ impl Platform for WindowsPlatform {
}
fn open_url(&self, url: &str) {
if url.is_empty() {
return;
}
let url_string = url.to_string();
self.background_executor()
.spawn(async move {
if url_string.is_empty() {
return;
}
open_target(url_string.as_str());
open_target(&url_string)
.with_context(|| format!("Opening url: {}", url_string))
.log_err();
})
.detach();
}
@ -514,37 +517,29 @@ impl Platform for WindowsPlatform {
}
fn reveal_path(&self, path: &Path) {
let Ok(file_full_path) = path.canonicalize() else {
log::error!("unable to parse file path");
return;
};
self.background_executor()
.spawn(async move {
let Some(path) = file_full_path.to_str() else {
return;
};
if path.is_empty() {
if path.as_os_str().is_empty() {
return;
}
open_target_in_explorer(path);
let path = path.to_path_buf();
self.background_executor()
.spawn(async move {
open_target_in_explorer(&path)
.with_context(|| format!("Revealing path {} in explorer", path.display()))
.log_err();
})
.detach();
}
fn open_with_system(&self, path: &Path) {
let Ok(full_path) = path.canonicalize() else {
log::error!("unable to parse file full path: {}", path.display());
if path.as_os_str().is_empty() {
return;
};
}
let path = path.to_path_buf();
self.background_executor()
.spawn(async move {
let Some(full_path_str) = full_path.to_str() else {
return;
};
if full_path_str.is_empty() {
return;
};
open_target(full_path_str);
open_target(&path)
.with_context(|| format!("Opening {} with system", path.display()))
.log_err();
})
.detach();
}
@ -735,39 +730,67 @@ pub(crate) struct WindowCreationInfo {
pub(crate) disable_direct_composition: bool,
}
fn open_target(target: &str) {
unsafe {
let ret = ShellExecuteW(
fn open_target(target: impl AsRef<OsStr>) -> Result<()> {
let target = target.as_ref();
let ret = unsafe {
ShellExecuteW(
None,
windows::core::w!("open"),
&HSTRING::from(target),
None,
None,
SW_SHOWDEFAULT,
);
)
};
if ret.0 as isize <= 32 {
log::error!("Unable to open target: {}", std::io::Error::last_os_error());
}
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 {
let ret = ShellExecuteW(
desktop.ParseDisplayName(
HWND::default(),
None,
windows::core::w!("open"),
windows::core::w!("explorer.exe"),
&HSTRING::from(format!("/select,{}", target).as_str()),
&HSTRING::from(dir),
None,
SW_SHOWDEFAULT,
);
if ret.0 as isize <= 32 {
log::error!(
"Unable to open target in explorer: {}",
std::io::Error::last_os_error()
);
&mut dir_item,
std::ptr::null_mut(),
)?;
}
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(