mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-19 13:55:40 -04:00
Fix: Improve Windows file opening and app listing
Co-authored-by: ijamespine <ijamespine@me.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
use file_opening::{FileOpener, OpenResult, OpenWithApp};
|
||||
use std::path::{Path, PathBuf};
|
||||
use windows::{
|
||||
core::*, Win32::Foundation::*, Win32::System::Com::*, Win32::UI::Shell::*,
|
||||
Win32::UI::WindowsAndMessaging::*,
|
||||
};
|
||||
use std::path::Path;
|
||||
use windows::core::*;
|
||||
use windows::Win32::Foundation::*;
|
||||
use windows::Win32::System::Com::*;
|
||||
use windows::Win32::UI::Shell::*;
|
||||
use windows::Win32::UI::WindowsAndMessaging::*;
|
||||
|
||||
// Thread-local COM initialization
|
||||
thread_local! {
|
||||
@@ -24,7 +25,7 @@ fn ensure_com_initialized() {
|
||||
pub struct WindowsFileOpener;
|
||||
|
||||
impl FileOpener for WindowsFileOpener {
|
||||
fn get_apps_for_file(&self, path: &Path) -> Result<Vec<OpenWithApp>, String> {
|
||||
fn get_apps_for_file(&self, path: &Path) -> std::result::Result<Vec<OpenWithApp>, String> {
|
||||
ensure_com_initialized();
|
||||
|
||||
let ext = path
|
||||
@@ -40,7 +41,7 @@ impl FileOpener for WindowsFileOpener {
|
||||
list_apps_for_extension(&ext)
|
||||
}
|
||||
|
||||
fn open_with_default(&self, path: &Path) -> Result<OpenResult, String> {
|
||||
fn open_with_default(&self, path: &Path) -> std::result::Result<OpenResult, String> {
|
||||
ensure_com_initialized();
|
||||
|
||||
let path_str = path.to_string_lossy();
|
||||
@@ -49,17 +50,17 @@ impl FileOpener for WindowsFileOpener {
|
||||
unsafe {
|
||||
let result = ShellExecuteW(None, w!("open"), &h_path, None, None, SW_SHOWNORMAL);
|
||||
|
||||
if result.0 as i32 > 32 {
|
||||
if result.0 as isize > 32 {
|
||||
Ok(OpenResult::Success)
|
||||
} else {
|
||||
Ok(OpenResult::PlatformError {
|
||||
message: format!("ShellExecute failed with code {}", result.0),
|
||||
message: format!("ShellExecute failed with code {}", result.0 as isize),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_with_app(&self, path: &Path, app_id: &str) -> Result<OpenResult, String> {
|
||||
fn open_with_app(&self, path: &Path, app_id: &str) -> std::result::Result<OpenResult, String> {
|
||||
ensure_com_initialized();
|
||||
|
||||
let ext = path
|
||||
@@ -80,29 +81,37 @@ impl FileOpener for WindowsFileOpener {
|
||||
SHAssocEnumHandlers(&HSTRING::from(ext.as_str()), ASSOC_FILTER_RECOMMENDED)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
for handler in handlers {
|
||||
let handler = handler.map_err(|e| e.to_string())?;
|
||||
let name = handler
|
||||
.GetName()
|
||||
.map_err(|e| e.to_string())?
|
||||
.to_string()
|
||||
.map_err(|e| e.to_string())?;
|
||||
loop {
|
||||
let mut handler_array: [Option<IAssocHandler>; 1] = [None];
|
||||
let mut fetched = 0u32;
|
||||
|
||||
if handlers.Next(&mut handler_array, Some(&mut fetched)).is_err() || fetched == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if name == app_id {
|
||||
// Create shell item from file path
|
||||
let path_str = path.to_string_lossy();
|
||||
let h_path = HSTRING::from(&*path_str);
|
||||
|
||||
let shell_item: IShellItem =
|
||||
SHCreateItemFromParsingName(&h_path, None).map_err(|e| e.to_string())?;
|
||||
|
||||
let data_object: IDataObject = shell_item
|
||||
.BindToHandler(None, &BHID_DataObject)
|
||||
if let Some(handler) = &handler_array[0] {
|
||||
let name = handler
|
||||
.GetName()
|
||||
.map_err(|e| e.to_string())?
|
||||
.to_string()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
handler.Invoke(&data_object).map_err(|e| e.to_string())?;
|
||||
if name == app_id {
|
||||
// Create shell item from file path
|
||||
let path_str = path.to_string_lossy();
|
||||
let h_path = HSTRING::from(&*path_str);
|
||||
|
||||
return Ok(OpenResult::Success);
|
||||
let shell_item: IShellItem =
|
||||
SHCreateItemFromParsingName(&h_path, None).map_err(|e| e.to_string())?;
|
||||
|
||||
let data_object: IDataObject = shell_item
|
||||
.BindToHandler(None, &BHID_DataObject)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
handler.Invoke(&data_object).map_err(|e| e.to_string())?;
|
||||
|
||||
return Ok(OpenResult::Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,25 +122,34 @@ impl FileOpener for WindowsFileOpener {
|
||||
}
|
||||
}
|
||||
|
||||
fn list_apps_for_extension(ext: &str) -> Result<Vec<OpenWithApp>, String> {
|
||||
fn list_apps_for_extension(ext: &str) -> std::result::Result<Vec<OpenWithApp>, String> {
|
||||
unsafe {
|
||||
let handlers = SHAssocEnumHandlers(&HSTRING::from(ext), ASSOC_FILTER_RECOMMENDED)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut apps = Vec::new();
|
||||
for handler in handlers {
|
||||
let handler = handler.map_err(|e| e.to_string())?;
|
||||
let name = handler
|
||||
.GetName()
|
||||
.map_err(|e| e.to_string())?
|
||||
.to_string()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
loop {
|
||||
let mut handler_array: [Option<IAssocHandler>; 1] = [None];
|
||||
let mut fetched = 0u32;
|
||||
|
||||
if handlers.Next(&mut handler_array, Some(&mut fetched)).is_err() || fetched == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
apps.push(OpenWithApp {
|
||||
id: name.clone(),
|
||||
name,
|
||||
icon: None,
|
||||
});
|
||||
if let Some(handler) = &handler_array[0] {
|
||||
let name = handler
|
||||
.GetName()
|
||||
.map_err(|e| e.to_string())?
|
||||
.to_string()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
apps.push(OpenWithApp {
|
||||
id: name.clone(),
|
||||
name,
|
||||
icon: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
apps.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
Reference in New Issue
Block a user