windows: Fix `RevealInFileManager` (#36592)

张小白 created

Closes #36314

This PR takes inspiration from [Electron’s
implementation](https://github.com/electron/electron/blob/dd54e84a58531b52680f7f736f593ee887eff6a7/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

Change summary

crates/gpui/src/platform/windows/platform.rs | 111 +++++++++++++--------
1 file changed, 67 insertions(+), 44 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -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");
+        if path.as_os_str().is_empty() {
             return;
-        };
+        }
+        let path = path.to_path_buf();
         self.background_executor()
             .spawn(async move {
-                let Some(path) = file_full_path.to_str() else {
-                    return;
-                };
-                if path.is_empty() {
-                    return;
-                }
-                open_target_in_explorer(path);
+                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());
-        }
+        )
+    };
+    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 {
-        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(