Skip to content

Instantly share code, notes, and snippets.

@leiless
Last active August 30, 2023 03:27
Show Gist options
  • Save leiless/769c9304424d9fc80018ac3b94e00942 to your computer and use it in GitHub Desktop.
Save leiless/769c9304424d9fc80018ac3b94e00942 to your computer and use it in GitHub Desktop.
Rust: how to resolve target location of a `.lnk` file
use windows::core::ComInterface;
// https://learn.microsoft.com/en-us/windows/win32/shell/links#resolving-a-shortcut
fn resolve_lnk_target(lnk_file: &str) -> Result<String, Box<dyn std::error::Error>> {
let psl: windows::Win32::UI::Shell::IShellLinkW = unsafe {
windows::Win32::System::Com::CoInitialize(None)?;
windows::Win32::System::Com::CoCreateInstance(
&windows::Win32::UI::Shell::ShellLink,
None,
windows::Win32::System::Com::CLSCTX_INPROC_SERVER,
)?
};
let sz_filename = windows::core::HSTRING::from(lnk_file);
let ppf = psl.cast::<windows::Win32::System::Com::IPersistFile>()?;
unsafe {
ppf.Load(&sz_filename, windows::Win32::System::Com::STGM_READ)?;
// SLR_NO_UI: Do not display a dialog box if the link cannot be resolved.
// https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishelllinkw-resolve#slr_no_ui-0x0001
psl.Resolve(windows::Win32::Foundation::HWND::default(), windows::Win32::UI::Shell::SLR_NO_UI.0 as _)?;
}
let mut psz_file = Vec::with_capacity(2048);
unsafe { psz_file.set_len(psz_file.capacity()); }
// If you don't need WIN32_FIND_DATAW, pass std::ptr::null_mut() as the parameter.
// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Storage/FileSystem/struct.WIN32_FIND_DATAW.html
let mut wfd = windows::Win32::Storage::FileSystem::WIN32_FIND_DATAW::default();
unsafe {
psl.GetPath(
psz_file.as_mut_slice(),
&mut wfd,
0,
)?;
}
let w_file = windows::core::PCWSTR::from_raw(psz_file.as_ptr());
let target_file_path = unsafe { w_file.to_string()? };
Ok(target_file_path)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let links = vec![
r#"C:\Users\foobar\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\回收站.lnk"#,
r#"C:\Users\foobar\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk"#,
];
for lnk_file in links {
let target_file_path = resolve_lnk_target(lnk_file)?;
println!("{}: {}", lnk_file, target_file_path);
}
Ok(())
}
@leiless
Copy link
Author

leiless commented Aug 23, 2023

$ ./rust-resolve-lnk-file
C:\Users\foobar\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\回收站.lnk: 
C:\Users\foobar\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk: C:\Users\foobar\AppData\Local\Microsoft\OneDrive\OneDrive.exe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment