diff --git a/crates/rattler_menuinst/Cargo.toml b/crates/rattler_menuinst/Cargo.toml index a355cc193..240af876f 100644 --- a/crates/rattler_menuinst/Cargo.toml +++ b/crates/rattler_menuinst/Cargo.toml @@ -30,8 +30,14 @@ shell-words = "1.1.0" known-folders = "1.2.0" [target.'cfg(target_os = "windows")'.dependencies] -winapi = "0.3.9" winreg = "0.52.0" +windows = { version = "0.58", features = [ + "Win32_System_Com", + "Win32_UI_Shell", + "Win32_UI_Shell_PropertiesSystem", + "Win32_Foundation", + "Win32_System_Com_StructuredStorage", +] } [dev-dependencies] insta = { workspace = true } diff --git a/crates/rattler_menuinst/src/windows.rs b/crates/rattler_menuinst/src/windows.rs index b8829cb17..8d2313bb9 100644 --- a/crates/rattler_menuinst/src/windows.rs +++ b/crates/rattler_menuinst/src/windows.rs @@ -1,12 +1,15 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use crate::{schema, MenuInstError, MenuMode}; +use fs_err as fs; mod knownfolders; mod registry; +mod create_shortcut; struct Directories { start_menu_location: PathBuf, - quick_launch_location: PathBuf, - desktop_location: PathBuf, + quick_launch_location: Option, + desktop_location: Option, } impl Directories { @@ -15,11 +18,9 @@ impl Directories { known_folders::get_known_folder_path(known_folders::KnownFolder::StartMenu) .expect("Failed to get start menu location"); let quick_launch_location = - known_folders::get_known_folder_path(known_folders::KnownFolder::QuickLaunch) - .expect("Failed to get quick launch location"); + known_folders::get_known_folder_path(known_folders::KnownFolder::QuickLaunch); let desktop_location = - known_folders::get_known_folder_path(known_folders::KnownFolder::Desktop) - .expect("Failed to get desktop location"); + known_folders::get_known_folder_path(known_folders::KnownFolder::Desktop); Directories { start_menu_location, @@ -30,8 +31,54 @@ impl Directories { } pub struct WindowsMenu { - name: String, - mode: String, + directories: Directories, + item: schema::Windows, + mode: MenuMode, prefix: PathBuf, - base_prefix: PathBuf, } + +impl WindowsMenu { + pub fn new(item: schema::Windows, prefix: &Path, mode: MenuMode) -> WindowsMenu { + let directories = Directories::create(); + + WindowsMenu { + directories, + item, + mode, + prefix: prefix.to_path_buf(), + } + } + + pub fn create(&self) -> Result { + tracing::debug!("Creating {:?}", self.directories.start_menu_location); + fs::create_dir_all(&self.directories.start_menu_location)?; + + if let Some(ref quick_launch_location) = self.directories.quick_launch_location { + fs::create_dir_all(quick_launch_location)?; + } + + if let Some(ref desktop_location) = self.directories.desktop_location { + fs::create_dir_all(desktop_location)?; + } + + Ok(self.directories.start_menu_location.clone()) + } + + pub fn remove(&self) -> Result { + let menu_location = &self.directories.start_menu_location; + if menu_location.exists() { + if menu_location.read_dir()?.next().is_none() { + tracing::info!("Removing {menu_location:?}", ); + fs::remove_dir_all(menu_location)?; + } + } + Ok(self.directories.start_menu_location.clone()) + } +} + +struct WindowsMenuItem { + item: schema::Windows, + prefix: PathBuf, +} + + diff --git a/crates/rattler_menuinst/src/windows/create_shortcut.rs b/crates/rattler_menuinst/src/windows/create_shortcut.rs new file mode 100644 index 000000000..f63e68e67 --- /dev/null +++ b/crates/rattler_menuinst/src/windows/create_shortcut.rs @@ -0,0 +1,68 @@ +use std::path::Path; +use windows::{ + core::*, + Win32::System::Com::*, + Win32::System::Com::StructuredStorage::*, + Win32::UI::Shell::*, + Win32::UI::Shell::PropertiesSystem::*, +}; + +#[derive(thiserror::Error, Debug)] +enum CreateShortcutFail { + #[error("Failed to create shortcut: {0}")] + WindowsError(#[from] windows::core::Error), + + #[error("Failed to initialize COM")] + CoInitializeFail, +} + +/// Create a shortcut at the specified path. +pub(crate) fn create_shortcut( + path: &Path, + description: &str, + filename: &Path, + arguments: Option<&str>, + workdir: Option<&Path>, + iconpath: Option<&Path>, + iconindex: i32, + app_id: Option<&str>, +) -> std::result::Result<(), CreateShortcutFail> { + unsafe { + if !CoInitialize(None).is_ok() { + return Err(CreateShortcutFail::CoInitializeFail); + } + + let shell_link: IShellLinkW = CoCreateInstance(&ShellLink, None, CLSCTX_INPROC_SERVER)?; + let persist_file: IPersistFile = shell_link.cast()?; + + shell_link.SetPath(&HSTRING::from(path))?; + shell_link.SetDescription(&HSTRING::from(description))?; + + if let Some(args) = arguments { + shell_link.SetArguments(&HSTRING::from(args))?; + } + + if let Some(icon) = iconpath { + shell_link.SetIconLocation(&HSTRING::from(icon), iconindex)?; + } + + if let Some(dir) = workdir { + shell_link.SetWorkingDirectory(&HSTRING::from(dir))?; + } + + if let Some(id) = app_id { + let property_store: IPropertyStore = shell_link.cast()?; + let mut prop_variant = PROPVARIANT::default(); + // PropVariantInit(&mut prop_variant); + // SetPropStringValue(PCWSTR(PWSTR::from_raw(to_wide_string(id).as_mut_ptr())), &mut prop_variant)?; + // property_store.SetValue(&PROPERTYKEY_AppUserModel_ID, &prop_variant)?; + property_store.Commit()?; + PropVariantClear(&mut prop_variant)?; + } + + persist_file.Save(&HSTRING::from(filename), true)?; + + CoUninitialize(); + Ok(()) + } +} \ No newline at end of file