use std::path::Path;
use windows::{
Win32::System::Com::{
CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED, CoCreateInstance, CoInitializeEx,
CoUninitialize, IPersistFile,
},
Win32::UI::Shell::{IShellLinkW, ShellLink},
core::{HSTRING, Interface},
};
use crate::error::{Error, Result};
pub fn create_shortcut(
link: &Path,
target: &Path,
arguments: Option<&str>,
working_directory: Option<&Path>,
icon: Option<&Path>,
description: Option<&str>,
) -> Result<()> {
let _com = Apartment::enter()?;
let result = (|| -> windows::core::Result<()> {
let shell_link: IShellLinkW =
unsafe { CoCreateInstance(&ShellLink, None, CLSCTX_INPROC_SERVER)? };
unsafe { shell_link.SetPath(&HSTRING::from(target.as_os_str()))? };
if let Some(arguments) = arguments {
unsafe { shell_link.SetArguments(&HSTRING::from(arguments))? };
}
let working = working_directory
.map(Path::to_path_buf)
.or_else(|| target.parent().map(Path::to_path_buf));
if let Some(working) = working {
unsafe { shell_link.SetWorkingDirectory(&HSTRING::from(working.as_os_str()))? };
}
if let Some(icon) = icon {
unsafe { shell_link.SetIconLocation(&HSTRING::from(icon.as_os_str()), 0)? };
}
if let Some(description) = description {
unsafe { shell_link.SetDescription(&HSTRING::from(description))? };
}
let persist: IPersistFile = shell_link.cast()?;
unsafe { persist.Save(&HSTRING::from(link.as_os_str()), true)? };
Ok(())
})();
result.map_err(|e| Error::hresult("create_shortcut", e.code().0))
}
struct Apartment;
impl Apartment {
fn enter() -> Result<Self> {
let hr = unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED) };
if hr.is_err() {
return Err(Error::hresult("CoInitializeEx", hr.0));
}
Ok(Self)
}
}
impl Drop for Apartment {
fn drop(&mut self) {
unsafe { CoUninitialize() };
}
}