fix(desktop): resolve folder via IObjectWithSite on blank-area View online (#148)

Right-clicking empty space in a synced directory invokes the command
with no selection array — Invoke silently returned, so the menu item
appeared but did nothing.

Implement IObjectWithSite on the handler: Explorer now hands us its
site, and a selection-less invoke queries SID_SFolderView for the
IFolderView and resolves the current folder's path. Verified with
cargo check --target x86_64-pc-windows-gnu.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
pull/3587/head
Tomas Dvorak 2 weeks ago
parent eaa1ba3d60
commit 2f3e1c5bf2

@ -64,6 +64,7 @@ features = [
"Win32_System_CorrelationVector", "Win32_System_CorrelationVector",
"Win32_Storage_FileSystem", "Win32_Storage_FileSystem",
"Win32_System_Com", "Win32_System_Com",
"Win32_System_Ole",
"Win32_System_Threading", "Win32_System_Threading",
"Win32_UI_Shell", "Win32_UI_Shell",
"Win32_UI_Shell_Common", "Win32_UI_Shell_Common",

@ -2,19 +2,20 @@ use crate::drive::manager::DriveManager;
use crate::{drive::commands::ManagerCommand, utils::app::AppRoot}; use crate::{drive::commands::ManagerCommand, utils::app::AppRoot};
use rust_i18n::t; use rust_i18n::t;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::{Arc, Mutex};
use windows::{ use windows::{
Win32::{Foundation::*, System::Com::*, UI::Shell::*}, Win32::{Foundation::*, System::Com::*, System::Ole::*, UI::Shell::*},
core::*, core::*,
}; };
#[implement(IExplorerCommand)] /// shlguid.h: SID_SFolderView — resolves IFolderView from the command site.
const SID_S_FOLDER_VIEW: GUID = GUID::from_u128(0xcde725b0_ccc9_4519_917e_325d72fab4ce);
#[implement(IExplorerCommand, IObjectWithSite)]
pub struct ViewOnlineCommandHandler { pub struct ViewOnlineCommandHandler {
drive_manager: Arc<DriveManager>, drive_manager: Arc<DriveManager>,
app_root: AppRoot, app_root: AppRoot,
site: Mutex<Option<IUnknown>>,
#[allow(dead_code)]
site: Option<IUnknown>,
} }
impl ViewOnlineCommandHandler { impl ViewOnlineCommandHandler {
@ -22,7 +23,31 @@ impl ViewOnlineCommandHandler {
Self { Self {
drive_manager, drive_manager,
app_root, app_root,
site: None, site: Mutex::new(None),
}
}
fn send_view_online(&self, path: PathBuf) {
tracing::debug!(target: "shellext::context_menu", path = %path.display(), "View online requested");
let command_tx = self.drive_manager.get_command_sender();
if let Err(e) = command_tx.send(ManagerCommand::ViewOnline { path }) {
tracing::error!(target: "shellext::context_menu", error = %e, "Failed to send ViewOnline command");
}
}
}
impl IObjectWithSite_Impl for ViewOnlineCommandHandler_Impl {
fn SetSite(&self, punksite: Option<&IUnknown>) -> Result<()> {
*self.site.lock().unwrap() = punksite.cloned();
Ok(())
}
fn GetSite(&self, riid: *const GUID, ppvsite: *mut *mut core::ffi::c_void) -> Result<()> {
let site = self.site.lock().unwrap();
if let Some(site) = site.as_ref() {
unsafe { site.query(riid, ppvsite) }.ok()
} else {
Err(Error::from(E_FAIL))
} }
} }
} }
@ -82,17 +107,26 @@ impl IExplorerCommand_Impl for ViewOnlineCommandHandler_Impl {
// Get the first item // Get the first item
let item = items.GetItemAt(0)?; let item = items.GetItemAt(0)?;
let display_name = item.GetDisplayName(SIGDN_FILESYSPATH)?; let display_name = item.GetDisplayName(SIGDN_FILESYSPATH)?;
let path_str = display_name.to_string()?; let path = PathBuf::from(display_name.to_string()?);
let path = PathBuf::from(path_str.clone());
tracing::debug!(target: "shellext::context_menu", path = %path_str, "View online requested");
// Send command through channel to async processor self.send_view_online(path);
let command_tx = self.drive_manager.get_command_sender(); }
} else {
// Folder-background invoke carries no selection; resolve the
// current folder through the site Explorer gave us in SetSite.
let site = self.site.lock().unwrap().clone();
let Some(site) = site else {
return Ok(());
};
unsafe {
let service_provider: IServiceProvider = site.cast()?;
let folder_view: IFolderView =
service_provider.QueryService(&SID_S_FOLDER_VIEW)?;
let item: IShellItem = folder_view.GetFolder()?;
let display_name = item.GetDisplayName(SIGDN_FILESYSPATH)?;
let path = PathBuf::from(display_name.to_string()?);
if let Err(e) = command_tx.send(ManagerCommand::ViewOnline { path: path.clone() }) { self.send_view_online(path);
tracing::error!(target: "shellext::context_menu", error = %e, "Failed to send ViewOnline command");
}
} }
} }

Loading…
Cancel
Save