mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Remove C++ shortcut code
This commit is contained in:
86
src/Rust/Cargo.lock
generated
86
src/Rust/Cargo.lock
generated
@@ -91,9 +91,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355"
|
||||
|
||||
[[package]]
|
||||
name = "as-slice"
|
||||
@@ -215,7 +215,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.42",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -229,10 +229,10 @@ name = "clowd_squirrel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cc",
|
||||
"chrono",
|
||||
"clap",
|
||||
"codesign-verify",
|
||||
"com",
|
||||
"derivative",
|
||||
"file-rotate",
|
||||
"fs_extra",
|
||||
@@ -253,6 +253,7 @@ dependencies = [
|
||||
"strum",
|
||||
"ureq",
|
||||
"wait-timeout",
|
||||
"widestring",
|
||||
"windows",
|
||||
"windows-sys 0.52.0",
|
||||
"winres",
|
||||
@@ -284,6 +285,37 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "com"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a30a2b2a013da986dc5cc3eda3d19c0d59d53f835be1b2356eb8d00f000c793"
|
||||
dependencies = [
|
||||
"com_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "com_macros"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7606b05842fea68ddcc89e8053b8860ebcb2a0ba8d6abfe3a148e5d5a8d3f0c1"
|
||||
dependencies = [
|
||||
"com_macros_support",
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "com_macros_support"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97e9a6d20f4ac8830e309a455d7e9416e65c6af5a97c88c55fbb4c2012e107da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@@ -320,9 +352,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.16"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||
checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -599,9 +631,9 @@ checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.0"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375"
|
||||
checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -728,7 +760,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.42",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -766,9 +798,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
@@ -806,9 +838,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -988,7 +1020,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.42",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1065,7 +1097,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.42",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1081,9 +1113,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.41"
|
||||
version = "2.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
|
||||
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1114,9 +1146,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
|
||||
checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
@@ -1136,9 +1168,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
|
||||
checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
@@ -1273,7 +1305,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.42",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -1295,7 +1327,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.41",
|
||||
"syn 2.0.42",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -1312,6 +1344,12 @@ version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -1581,7 +1619,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "winsafe"
|
||||
version = "0.0.18"
|
||||
source = "git+https://github.com/caesay/winsafe.git?branch=cs/persistfile-and-lnk#1d03f22443547441ed08a16081fa4b5d60a45da4"
|
||||
source = "git+https://github.com/caesay/winsafe.git?branch=cs/only-ipersistfile#e9aadd0b7d8337364865039bf098e73be40d11a1"
|
||||
|
||||
[[package]]
|
||||
name = "xml"
|
||||
|
||||
@@ -36,7 +36,7 @@ memmap2 = "0.9"
|
||||
pretty-bytes-rust = "0.3"
|
||||
xml = "0.8"
|
||||
os_info = { git = "https://github.com/stanislav-tkach/os_info.git", branch = "master", default-features = false } # public releases don't yet have processor arch info
|
||||
winsafe = { git = "https://github.com/caesay/winsafe.git", branch = "cs/persistfile-and-lnk", features = [
|
||||
winsafe = { git = "https://github.com/caesay/winsafe.git", branch = "cs/only-ipersistfile", features = [
|
||||
"kernel",
|
||||
"version",
|
||||
"user",
|
||||
@@ -90,8 +90,9 @@ remove_dir_all = { git = "https://github.com/caesay/remove_dir_all.git", feature
|
||||
glob = "0.3"
|
||||
normpath = "1.0.1"
|
||||
codesign-verify = { git = "https://github.com/caesay/codesign-verify-rs.git" }
|
||||
com = "0.2.0"
|
||||
widestring = "1.0.2"
|
||||
|
||||
[build-dependencies]
|
||||
winres = "0.1"
|
||||
semver = "1.0"
|
||||
cc = "1.0"
|
||||
semver = "1.0"
|
||||
@@ -2,13 +2,13 @@ use semver;
|
||||
|
||||
extern crate winres;
|
||||
fn main() {
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.file("src/platform/windows/shortcuts.cpp")
|
||||
.define("UNICODE", None)
|
||||
.define("_UNICODE", None)
|
||||
.compile("lib_shortcuts");
|
||||
println!("cargo:rerun-if-changed=src/platform/windows/shortcuts.cpp");
|
||||
// cc::Build::new()
|
||||
// .cpp(true)
|
||||
// .file("src/platform/windows/shortcuts.cpp")
|
||||
// .define("UNICODE", None)
|
||||
// .define("_UNICODE", None)
|
||||
// .compile("lib_shortcuts");
|
||||
// println!("cargo:rerun-if-changed=src/platform/windows/shortcuts.cpp");
|
||||
|
||||
let ver = env!("CARGO_PKG_VERSION");
|
||||
let ver = semver::Version::parse(&ver).unwrap();
|
||||
|
||||
@@ -48,7 +48,7 @@ pub fn show_restart_required(app: &Manifest) {
|
||||
}
|
||||
|
||||
let hwnd = w::HWND::GetDesktopWindow();
|
||||
let _ = w::task_dlg::warn(
|
||||
let _ = warn(
|
||||
&hwnd,
|
||||
format!("{} Setup {}", app.title, app.version).as_str(),
|
||||
Some("Restart Required"),
|
||||
@@ -62,7 +62,7 @@ pub fn show_missing_dependencies_dialog(app: &Manifest, depedency_string: &str)
|
||||
}
|
||||
|
||||
let hwnd = w::HWND::GetDesktopWindow();
|
||||
w::task_dlg::ok_cancel(
|
||||
ok_cancel(
|
||||
&hwnd,
|
||||
format!("{} Setup {}", app.title, app.version).as_str(),
|
||||
Some(format!("{} has missing system dependencies.", app.title).as_str()),
|
||||
@@ -157,3 +157,130 @@ extern "system" fn task_dialog_callback(_: w::HWND, msg: co::TDN, _: usize, _: i
|
||||
}
|
||||
return co::HRESULT::S_OK; // close dialog on button press
|
||||
}
|
||||
|
||||
|
||||
pub fn error(
|
||||
hparent: &w::HWND,
|
||||
title: &str,
|
||||
header: Option<&str>,
|
||||
body: &str,
|
||||
) -> w::HrResult<()>
|
||||
{
|
||||
generate(hparent, title, header, body, None,
|
||||
co::TDCBF::OK, co::TD_ICON::ERROR)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn warn(
|
||||
hparent: &w::HWND,
|
||||
title: &str,
|
||||
header: Option<&str>,
|
||||
body: &str,
|
||||
) -> w::HrResult<()>
|
||||
{
|
||||
generate(hparent, title, header, body, None,
|
||||
co::TDCBF::OK, co::TD_ICON::WARNING)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn info(
|
||||
hparent: &w::HWND,
|
||||
title: &str,
|
||||
header: Option<&str>,
|
||||
body: &str,
|
||||
) -> w::HrResult<()>
|
||||
{
|
||||
generate(hparent, title, header, body, None,
|
||||
co::TDCBF::OK, co::TD_ICON::INFORMATION)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn ok_cancel(
|
||||
hparent: &w::HWND,
|
||||
title: &str,
|
||||
header: Option<&str>,
|
||||
body: &str,
|
||||
ok_text: Option<&str>,
|
||||
) -> w::HrResult<bool>
|
||||
{
|
||||
let mut btns = co::TDCBF::CANCEL;
|
||||
if ok_text.is_none() {
|
||||
btns |= co::TDCBF::OK;
|
||||
}
|
||||
|
||||
generate(hparent, title, header, body, ok_text,
|
||||
btns, co::TD_ICON::WARNING)
|
||||
.map(|dlg_id| dlg_id == co::DLGID::OK)
|
||||
}
|
||||
|
||||
pub fn yes_no(
|
||||
hparent: &w::HWND,
|
||||
title: &str,
|
||||
header: Option<&str>,
|
||||
body: &str,
|
||||
) -> w::HrResult<bool>
|
||||
{
|
||||
generate(hparent, title, header, body, None,
|
||||
co::TDCBF::YES | co::TDCBF::NO, co::TD_ICON::WARNING)
|
||||
.map(|dlg_id| dlg_id == co::DLGID::YES)
|
||||
}
|
||||
|
||||
pub fn yes_no_cancel(
|
||||
hparent: &w::HWND,
|
||||
title: &str,
|
||||
header: Option<&str>,
|
||||
body: &str,
|
||||
) -> w::HrResult<co::DLGID>
|
||||
{
|
||||
generate(hparent, title, header, body, None,
|
||||
co::TDCBF::YES | co::TDCBF::NO | co::TDCBF::CANCEL, co::TD_ICON::WARNING)
|
||||
}
|
||||
|
||||
fn generate(
|
||||
hparent: &w::HWND,
|
||||
title: &str,
|
||||
header: Option<&str>,
|
||||
body: &str,
|
||||
ok_text: Option<&str>,
|
||||
btns: co::TDCBF,
|
||||
ico: co::TD_ICON,
|
||||
) -> w::HrResult<co::DLGID>
|
||||
{
|
||||
let mut ok_text_buf = WString::from_opt_str(ok_text);
|
||||
let mut custom_btns = if ok_text.is_some() {
|
||||
let mut td_btn = w::TASKDIALOG_BUTTON::default();
|
||||
td_btn.set_nButtonID(co::DLGID::OK.into());
|
||||
td_btn.set_pszButtonText(Some(&mut ok_text_buf));
|
||||
|
||||
let mut custom_btns = Vec::with_capacity(1);
|
||||
custom_btns.push(td_btn);
|
||||
custom_btns
|
||||
} else {
|
||||
Vec::<w::TASKDIALOG_BUTTON>::default()
|
||||
};
|
||||
|
||||
let mut tdc = w::TASKDIALOGCONFIG::default();
|
||||
tdc.hwndParent = unsafe { hparent.raw_copy() };
|
||||
tdc.dwFlags = co::TDF::ALLOW_DIALOG_CANCELLATION | co::TDF::POSITION_RELATIVE_TO_WINDOW;
|
||||
tdc.dwCommonButtons = btns;
|
||||
tdc.set_pszMainIcon(w::IconIdTdicon::Tdicon(ico));
|
||||
|
||||
if ok_text.is_some() {
|
||||
tdc.set_pButtons(Some(&mut custom_btns));
|
||||
}
|
||||
|
||||
let mut title_buf = WString::from_str(title);
|
||||
tdc.set_pszWindowTitle(Some(&mut title_buf));
|
||||
|
||||
let mut header_buf = WString::from_opt_str(header);
|
||||
if header.is_some() {
|
||||
tdc.set_pszMainInstruction(Some(&mut header_buf));
|
||||
}
|
||||
|
||||
let mut body_buf = WString::from_str(body);
|
||||
tdc.set_pszContent(Some(&mut body_buf));
|
||||
|
||||
w::TaskDialogIndirect(&tdc, None)
|
||||
.map(|(dlg_id, _)| dlg_id)
|
||||
}
|
||||
|
||||
504
src/Rust/src/platform/windows/ishelllink.rs
Normal file
504
src/Rust/src/platform/windows/ishelllink.rs
Normal file
@@ -0,0 +1,504 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{ffi::c_void, os::raw::c_int};
|
||||
use widestring::U16CString;
|
||||
|
||||
use com::{
|
||||
com_interface,
|
||||
runtime::create_instance,
|
||||
interfaces::iunknown::IUnknown,
|
||||
sys::{HRESULT, IID, FAILED},
|
||||
};
|
||||
|
||||
// unfortunately, paths/descriptions/etc. of ShellLinks are all
|
||||
// constrained to `MAX_PATH`.
|
||||
// TODO: figure out how this works with LongPathAware?
|
||||
const MAX_PATH: usize = 260;
|
||||
|
||||
const INFOTIPSIZE: usize = 1024;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SimpleError(String);
|
||||
|
||||
impl SimpleError {
|
||||
pub unsafe fn ret(self, p: *mut *mut XString) {
|
||||
let xs = XString { data: self.0 };
|
||||
*p = Box::into_raw(Box::new(xs));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for SimpleError
|
||||
where
|
||||
T: std::error::Error,
|
||||
{
|
||||
fn from(e: T) -> Self {
|
||||
Self(format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
struct NulTerminatedUtf16(Vec<u16>);
|
||||
|
||||
impl NulTerminatedUtf16 {
|
||||
fn to_u16_c_string(self, _method: &str) -> Result<U16CString, SimpleError> {
|
||||
let res = U16CString::from_vec_truncate(self.0);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn to_string(self, method: &str) -> Result<String, SimpleError> {
|
||||
let res = self
|
||||
.to_u16_c_string(method)?
|
||||
.to_string()
|
||||
.map_err(|_| SimpleError(format!("invalid utf-16 data in {}", method)))?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn to_path_buf(self, method: &str) -> Result<PathBuf, SimpleError> {
|
||||
let res = self.to_u16_c_string(method)?;
|
||||
let res = res.to_os_string();
|
||||
Ok(res.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoStringSimple {
|
||||
fn into_string_simple(self, method: &str) -> Result<String, SimpleError>;
|
||||
}
|
||||
|
||||
impl IntoStringSimple for PathBuf {
|
||||
fn into_string_simple(self, method: &str) -> Result<String, SimpleError> {
|
||||
let s = self.into_os_string();
|
||||
let s = s
|
||||
.into_string()
|
||||
.map_err(|_| SimpleError(format!("invalid utf-16 data in {} result", method)))?;
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
trait Checked
|
||||
where
|
||||
Self: Sized + Copy,
|
||||
{
|
||||
fn check(self, method: &str) -> Result<(), SimpleError>;
|
||||
fn format(self, method: &str) -> SimpleError;
|
||||
}
|
||||
|
||||
impl Checked for HRESULT {
|
||||
fn check(self, method: &str) -> Result<(), SimpleError> {
|
||||
if FAILED(self) {
|
||||
Err(self.format(method))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn format(self, method: &str) -> SimpleError {
|
||||
SimpleError(format!(
|
||||
"{} returned system error {} (0x{:x})",
|
||||
method, self, self
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
pub enum ReturnCode {
|
||||
Ok = 0,
|
||||
Error = 1,
|
||||
}
|
||||
|
||||
/// Handle for an IShellLink instance
|
||||
pub struct ShellLink {
|
||||
instance: com::ComRc<dyn IShellLinkW>,
|
||||
}
|
||||
|
||||
/// Exchange string: passes Rust strings to C
|
||||
pub struct XString {
|
||||
data: String,
|
||||
}
|
||||
|
||||
impl From<String> for XString {
|
||||
fn from(data: String) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellLink {
|
||||
pub fn new() -> Result<Self, SimpleError> {
|
||||
let instance = create_instance::<dyn IShellLinkW>(&CLSID_SHELL_LINK)
|
||||
.map_err(|hr| hr.check("create_instance<IShellLinkW>").unwrap_err())?;
|
||||
Ok(Self {
|
||||
instance,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load<P: AsRef<Path>>(&self, path: P) -> Result<(), SimpleError> {
|
||||
let pf = self.instance.get_interface::<dyn IPersistFile>().unwrap();
|
||||
let path = U16CString::from_os_str(path.as_ref())?;
|
||||
unsafe { pf.load(path.as_ptr(), STGM_READ) }.check("IPersistFile::Load")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), SimpleError> {
|
||||
let pf = self.instance.get_interface::<dyn IPersistFile>().unwrap();
|
||||
let path = U16CString::from_os_str(path.as_ref())?;
|
||||
unsafe { pf.save(path.as_ptr(), false) }.check("IPersistFile::Save")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_path(&self) -> Result<PathBuf, SimpleError> {
|
||||
let mut v = vec![0u16; MAX_PATH + 1];
|
||||
unsafe {
|
||||
self.instance.get_path(
|
||||
v.as_mut_ptr(),
|
||||
v.len() as _,
|
||||
std::ptr::null_mut(),
|
||||
SLGP_RAWPATH,
|
||||
)
|
||||
}
|
||||
.check("IShellLinkW::GetPath")?;
|
||||
Ok(NulTerminatedUtf16(v).to_path_buf("IShellLinkW::GetPath")?)
|
||||
}
|
||||
|
||||
/// Sets the shell link's path (what it points to)
|
||||
pub fn set_path<P: AsRef<Path>>(&self, path: P) -> Result<(), SimpleError> {
|
||||
let path = U16CString::from_os_str(path.as_ref())?;
|
||||
unsafe { self.instance.set_path(path.as_ptr()) }.check("IShellLinkW::SetPath")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_arguments(&self) -> Result<String, SimpleError> {
|
||||
let mut v = vec![0u16; INFOTIPSIZE + 1];
|
||||
unsafe { self.instance.get_arguments(v.as_mut_ptr(), v.len() as _) }
|
||||
.check("IShellLinkW::GetArguments")?;
|
||||
Ok(NulTerminatedUtf16(v).to_string("IShellLinkW::GetArguments")?)
|
||||
}
|
||||
|
||||
pub fn set_arguments(&self, path: &str) -> Result<(), SimpleError> {
|
||||
let path = U16CString::from_str(path)?;
|
||||
unsafe { self.instance.set_arguments(path.as_ptr()) }.check("IShellLinkW::SetArguments")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_description(&self) -> Result<String, SimpleError> {
|
||||
let mut v = vec![0u16; INFOTIPSIZE + 1];
|
||||
unsafe { self.instance.get_description(v.as_mut_ptr(), v.len() as _) }
|
||||
.check("IShellLinkW::GetDescription")?;
|
||||
Ok(NulTerminatedUtf16(v).to_string("IShellLinkW::GetDescription")?)
|
||||
}
|
||||
|
||||
pub fn set_description(&self, path: &str) -> Result<(), SimpleError> {
|
||||
let path = U16CString::from_str(path)?;
|
||||
unsafe { self.instance.set_description(path.as_ptr()) }
|
||||
.check("IShellLinkW::SetDescription")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_working_directory(&self) -> Result<PathBuf, SimpleError> {
|
||||
let mut v = vec![0u16; INFOTIPSIZE + 1];
|
||||
unsafe {
|
||||
self.instance
|
||||
.get_working_directory(v.as_mut_ptr(), v.len() as _)
|
||||
}
|
||||
.check("IShellLinkW::GetWorkingDirectory")?;
|
||||
Ok(NulTerminatedUtf16(v).to_path_buf("IShellLinkW::GetWorkingDirectory")?)
|
||||
}
|
||||
|
||||
pub fn set_working_directory<P: AsRef<Path>>(&self, path: P) -> Result<(), SimpleError> {
|
||||
let path = U16CString::from_os_str(path.as_ref().as_os_str())?;
|
||||
unsafe { self.instance.set_working_directory(path.as_ptr()) }
|
||||
.check("IShellLinkW::SetWorkingDirectory")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_icon_location(&self) -> Result<(PathBuf, i32), SimpleError> {
|
||||
let mut v = vec![0u16; INFOTIPSIZE + 1];
|
||||
let mut index = 0;
|
||||
unsafe {
|
||||
self.instance
|
||||
.get_icon_location(v.as_mut_ptr(), v.len() as _, &mut index)
|
||||
}
|
||||
.check("IShellLinkW::GetIconLocation")?;
|
||||
|
||||
let s = NulTerminatedUtf16(v).to_path_buf("IShellLinkW::GetIconLocation")?;
|
||||
Ok((s, index as _))
|
||||
}
|
||||
|
||||
pub fn set_icon_location<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
index: i32,
|
||||
) -> Result<(), SimpleError> {
|
||||
let path = U16CString::from_os_str(path.as_ref().as_os_str())?;
|
||||
unsafe { self.instance.set_icon_location(path.as_ptr(), index as _) }
|
||||
.check("IShellLinkW::SetIconLocation")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! checked {
|
||||
($x: expr, $p_err: ident) => {
|
||||
match $x {
|
||||
Err(e) => {
|
||||
SimpleError::from(e).ret($p_err);
|
||||
return ReturnCode::Error;
|
||||
}
|
||||
Ok(x) => x,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_new(
|
||||
p_link: *mut *mut ShellLink,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let sl = checked!(ShellLink::new(), p_err);
|
||||
*p_link = Box::into_raw(Box::new(sl));
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_load(
|
||||
link: *mut ShellLink,
|
||||
path_data: *mut u8,
|
||||
path_len: usize,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let v = std::slice::from_raw_parts(path_data, path_len);
|
||||
let path = checked!(std::str::from_utf8(v), p_err);
|
||||
checked!((*link).load(path), p_err);
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_save(
|
||||
link: *mut ShellLink,
|
||||
path_data: *mut u8,
|
||||
path_len: usize,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let v = std::slice::from_raw_parts(path_data, path_len);
|
||||
let path = checked!(std::str::from_utf8(v), p_err);
|
||||
checked!((*link).save(path), p_err);
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_get_path(
|
||||
link: *mut ShellLink,
|
||||
path: *mut *mut XString,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let res = checked!((*link).get_path(), p_err);
|
||||
let res: String = checked!(res.into_string_simple("shell_link_get_path"), p_err);
|
||||
*path = Box::into_raw(Box::new(res.into()));
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_set_path(
|
||||
link: *mut ShellLink,
|
||||
path_data: *mut u8,
|
||||
path_len: usize,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let v = std::slice::from_raw_parts(path_data, path_len);
|
||||
let path = checked!(std::str::from_utf8(v), p_err);
|
||||
checked!((*link).set_path(path), p_err);
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_get_arguments(
|
||||
link: *mut ShellLink,
|
||||
arguments: *mut *mut XString,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let res = checked!((*link).get_arguments(), p_err);
|
||||
*arguments = Box::into_raw(Box::new(XString::from(res)));
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_set_arguments(
|
||||
link: *mut ShellLink,
|
||||
arguments_data: *mut u8,
|
||||
arguments_len: usize,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let v = std::slice::from_raw_parts(arguments_data, arguments_len);
|
||||
let arguments = checked!(std::str::from_utf8(v), p_err);
|
||||
checked!((*link).set_arguments(arguments), p_err);
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_get_description(
|
||||
link: *mut ShellLink,
|
||||
description: *mut *mut XString,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let res = checked!((*link).get_description(), p_err);
|
||||
*description = Box::into_raw(Box::new(XString::from(res)));
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_set_description(
|
||||
link: *mut ShellLink,
|
||||
description_data: *mut u8,
|
||||
description_len: usize,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let v = std::slice::from_raw_parts(description_data, description_len);
|
||||
let description = checked!(std::str::from_utf8(v), p_err);
|
||||
checked!((*link).set_description(description), p_err);
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_get_working_directory(
|
||||
link: *mut ShellLink,
|
||||
working_directory: *mut *mut XString,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let res = checked!((*link).get_working_directory(), p_err);
|
||||
let res: String = checked!(
|
||||
res.into_string_simple("shell_link_get_working_directory"),
|
||||
p_err
|
||||
);
|
||||
*working_directory = Box::into_raw(Box::new(res.into()));
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_set_working_directory(
|
||||
link: *mut ShellLink,
|
||||
working_directory_data: *mut u8,
|
||||
working_directory_len: usize,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let v = std::slice::from_raw_parts(working_directory_data, working_directory_len);
|
||||
let working_directory = checked!(std::str::from_utf8(v), p_err);
|
||||
checked!((*link).set_working_directory(working_directory), p_err);
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_get_icon_location(
|
||||
link: *mut ShellLink,
|
||||
icon_location: *mut *mut XString,
|
||||
icon_index: *mut i32,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let (res, res_index) = checked!((*link).get_icon_location(), p_err);
|
||||
let res = checked!(
|
||||
res.into_string_simple("shell_link_get_icon_location"),
|
||||
p_err
|
||||
);
|
||||
*icon_location = Box::into_raw(Box::new(res.into()));
|
||||
*icon_index = res_index;
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_set_icon_location(
|
||||
link: *mut ShellLink,
|
||||
icon_location_data: *mut u8,
|
||||
icon_location_len: usize,
|
||||
icon_index: i32,
|
||||
p_err: *mut *mut XString,
|
||||
) -> ReturnCode {
|
||||
let v = std::slice::from_raw_parts(icon_location_data, icon_location_len);
|
||||
let icon_location = checked!(std::str::from_utf8(v), p_err);
|
||||
checked!((*link).set_icon_location(icon_location, icon_index), p_err);
|
||||
ReturnCode::Ok
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn shell_link_free(link: *mut ShellLink) {
|
||||
drop(Box::from_raw(link))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xstring_free(xs: *mut XString) {
|
||||
drop(Box::from_raw(xs))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xstring_data(xs: *mut XString) -> *const u8 {
|
||||
(*xs).data.as_ptr()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xstring_len(xs: *mut XString) -> usize {
|
||||
(*xs).data.len()
|
||||
}
|
||||
|
||||
|
||||
pub type DWORD = u32;
|
||||
pub const SLGP_RAWPATH: DWORD = 0x4;
|
||||
pub const STGM_READ: DWORD = 0x0;
|
||||
|
||||
#[com_interface("000214F9-0000-0000-C000-000000000046")]
|
||||
pub trait IShellLinkW: IUnknown {
|
||||
// DO NOT REORDER THESE - they must match the ABI
|
||||
unsafe fn get_path(
|
||||
&self,
|
||||
psz_file: *mut u16,
|
||||
cch: c_int,
|
||||
pfd: *const c_void,
|
||||
fflags: DWORD,
|
||||
) -> HRESULT;
|
||||
|
||||
unsafe fn get_id_list(&self) -> !;
|
||||
unsafe fn set_id_list(&self) -> !;
|
||||
|
||||
unsafe fn get_description(&self, psz_name: *mut u16, cch: c_int) -> HRESULT;
|
||||
unsafe fn set_description(&self, psz_name: *const u16) -> HRESULT;
|
||||
|
||||
unsafe fn get_working_directory(&self, psz_dir: *mut u16, cch: c_int) -> HRESULT;
|
||||
unsafe fn set_working_directory(&self, psz_dir: *const u16) -> HRESULT;
|
||||
|
||||
unsafe fn get_arguments(&self, psz_args: *mut u16, cch: c_int) -> HRESULT;
|
||||
unsafe fn set_arguments(&self, psz_args: *const u16) -> HRESULT;
|
||||
|
||||
unsafe fn get_hotkey(&self) -> !;
|
||||
unsafe fn set_hotkey(&self) -> !;
|
||||
|
||||
unsafe fn get_show_cmd(&self) -> !;
|
||||
unsafe fn set_show_cmd(&self) -> !;
|
||||
|
||||
unsafe fn get_icon_location(
|
||||
&self,
|
||||
psz_icon_path: *mut u16,
|
||||
cch: c_int,
|
||||
pi_icon: *mut c_int,
|
||||
) -> HRESULT;
|
||||
unsafe fn set_icon_location(&self, psz_icon_path: *const u16, i_icon: c_int) -> HRESULT;
|
||||
|
||||
unsafe fn set_relative_path(&self) -> !;
|
||||
|
||||
unsafe fn resolve(&self) -> !;
|
||||
unsafe fn set_path(&self, psz_file: *const u16) -> HRESULT;
|
||||
}
|
||||
|
||||
#[com_interface("0000010C-0000-0000-C000-000000000046")]
|
||||
pub trait IPersist: IUnknown {
|
||||
// DO NOT REORDER THESE - they must match the ABI
|
||||
unsafe fn get_class_id(&self, pclass_id: *mut IID) -> HRESULT;
|
||||
}
|
||||
|
||||
#[com_interface("0000010B-0000-0000-C000-000000000046")]
|
||||
pub trait IPersistFile: IPersist {
|
||||
// DO NOT REORDER THESE - they must match the ABI
|
||||
unsafe fn is_dirty(&self) -> !;
|
||||
unsafe fn load(&self, psz_file_name: *const u16, mode: DWORD) -> HRESULT;
|
||||
unsafe fn save(&self, psz_file_name: *const u16, fremember: bool) -> HRESULT;
|
||||
unsafe fn save_completed(&self) -> !;
|
||||
unsafe fn get_cur_file(&self) -> !;
|
||||
}
|
||||
|
||||
pub const CLSID_SHELL_LINK: IID = IID {
|
||||
data1: 0x00021401,
|
||||
data2: 0x0000,
|
||||
data3: 0x0000,
|
||||
data4: [0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46],
|
||||
};
|
||||
@@ -2,6 +2,7 @@ mod dialogs;
|
||||
mod self_delete;
|
||||
mod shortcuts;
|
||||
mod util;
|
||||
mod ishelllink;
|
||||
|
||||
pub use dialogs::*;
|
||||
pub use self_delete::*;
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#include "windows.h"
|
||||
#include "winnls.h"
|
||||
#include "shobjidl.h"
|
||||
#include "objbase.h"
|
||||
#include "objidl.h"
|
||||
#include "shlguid.h"
|
||||
#include "strsafe.h"
|
||||
|
||||
// all ripped from the following link and then modified
|
||||
// https://learn.microsoft.com/en-us/windows/win32/shell/links
|
||||
|
||||
extern "C" HRESULT CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink, LPCWSTR lpszWorkDir)
|
||||
{
|
||||
HRESULT hres;
|
||||
IShellLink* psl;
|
||||
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
|
||||
if (SUCCEEDED(hres)) {
|
||||
IPersistFile* ppf;
|
||||
psl->SetPath(lpszPathObj);
|
||||
psl->SetWorkingDirectory(lpszWorkDir);
|
||||
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
|
||||
if (SUCCEEDED(hres)) {
|
||||
hres = ppf->Save(lpszPathLink, TRUE);
|
||||
ppf->Release();
|
||||
}
|
||||
psl->Release();
|
||||
}
|
||||
return hres;
|
||||
}
|
||||
|
||||
extern "C" HRESULT ResolveLink(LPWSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferSize, LPWSTR lpszWorkDir, int iWorkDirBufferSize)
|
||||
{
|
||||
HRESULT hres;
|
||||
IShellLink* psl;
|
||||
WCHAR szGotPath[MAX_PATH];
|
||||
WCHAR szWorkDir[MAX_PATH];
|
||||
WIN32_FIND_DATA wfd;
|
||||
*lpszPath = 0; // Assume failure
|
||||
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
|
||||
if (SUCCEEDED(hres)) {
|
||||
IPersistFile* ppf;
|
||||
hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
|
||||
if (SUCCEEDED(hres)) {
|
||||
hres = ppf->Load(lpszLinkFile, STGM_READ);
|
||||
if (SUCCEEDED(hres)) {
|
||||
hres = psl->Resolve(0, 0x2 | 0x1 | (1 << 16));
|
||||
if (SUCCEEDED(hres)) {
|
||||
hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_UNCPRIORITY);
|
||||
if (SUCCEEDED(hres)) {
|
||||
hres = psl->GetWorkingDirectory(szWorkDir, MAX_PATH);
|
||||
if (SUCCEEDED(hres)) {
|
||||
hres = StringCbCopy(lpszPath, iPathBufferSize, szGotPath);
|
||||
if (SUCCEEDED(hres)) {
|
||||
hres = StringCbCopy(lpszWorkDir, iWorkDirBufferSize, szWorkDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ppf->Release();
|
||||
}
|
||||
psl->Release();
|
||||
}
|
||||
return hres;
|
||||
}
|
||||
@@ -1,52 +1,88 @@
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use glob::glob;
|
||||
use std::path::Path;
|
||||
use winsafe::{self as w, co, HrResult, WString};
|
||||
use winsafe::{self as w, co, prelude::*};
|
||||
|
||||
use super::ishelllink::*;
|
||||
use crate::platform;
|
||||
use crate::util;
|
||||
|
||||
type PCSTR = *const u16;
|
||||
extern "C" {
|
||||
fn CreateLink(lpszPathObj: PCSTR, lpszPathLink: PCSTR, lpszWorkDir: PCSTR) -> u32;
|
||||
fn ResolveLink(lpszLinkFile: PCSTR, lpszPath: PCSTR, iPathBufferSize: i32, lpszWorkDir: PCSTR, iWorkDirBufferSize: i32) -> u32;
|
||||
}
|
||||
// type PCSTR = *const u16;
|
||||
// extern "C" {
|
||||
// fn CreateLink(lpszPathObj: PCSTR, lpszPathLink: PCSTR, lpszWorkDir: PCSTR) -> u32;
|
||||
// fn ResolveLink(lpszLinkFile: PCSTR, lpszPath: PCSTR, iPathBufferSize: i32, lpszWorkDir: PCSTR, iWorkDirBufferSize: i32) -> u32;
|
||||
// }
|
||||
|
||||
pub fn resolve_lnk(link_path: &str) -> HrResult<(String, String)> {
|
||||
let _comguard = w::CoInitializeEx(co::COINIT::APARTMENTTHREADED)?;
|
||||
pub fn resolve_lnk(link_path: &str) -> Result<(String, String)> {
|
||||
let _comguard = w::CoInitializeEx(co::COINIT::APARTMENTTHREADED | co::COINIT::DISABLE_OLE1DDE)?;
|
||||
_resolve_lnk(link_path)
|
||||
}
|
||||
|
||||
pub fn create_lnk(output: &str, target: &str, work_dir: &str) -> HrResult<()> {
|
||||
let _comguard = w::CoInitializeEx(co::COINIT::APARTMENTTHREADED)?;
|
||||
pub fn create_lnk(output: &str, target: &str, work_dir: &str) -> w::HrResult<()> {
|
||||
let _comguard = w::CoInitializeEx(co::COINIT::APARTMENTTHREADED | co::COINIT::DISABLE_OLE1DDE)?;
|
||||
_create_lnk(output, target, work_dir)
|
||||
}
|
||||
|
||||
fn _create_lnk(output: &str, target: &str, work_dir: &str) -> HrResult<()> {
|
||||
let _comguard = w::CoInitializeEx(co::COINIT::APARTMENTTHREADED)?;
|
||||
let hr = unsafe { CreateLink(WString::from_str(target).as_ptr(), WString::from_str(output).as_ptr(), WString::from_str(work_dir).as_ptr()) };
|
||||
match unsafe { co::HRESULT::from_raw(hr) } {
|
||||
co::HRESULT::S_OK => Ok(()),
|
||||
hr => Err(hr),
|
||||
}
|
||||
fn _create_lnk(output: &str, target: &str, work_dir: &str) -> w::HrResult<()> {
|
||||
let me = w::CoCreateInstance::<w::IShellLink>(&co::CLSID::ShellLink, None, co::CLSCTX::INPROC_SERVER)?;
|
||||
me.SetPath(target)?;
|
||||
me.SetWorkingDirectory(work_dir)?;
|
||||
let pf = me.QueryInterface::<w::IPersistFile>()?;
|
||||
pf.Save(Some(output), true)?;
|
||||
Ok(())
|
||||
|
||||
// let _comguard = w::CoInitializeEx(co::COINIT::APARTMENTTHREADED)?;
|
||||
// let hr = unsafe { CreateLink(WString::from_str(target).as_ptr(), WString::from_str(output).as_ptr(), WString::from_str(work_dir).as_ptr()) };
|
||||
// match unsafe { co::HRESULT::from_raw(hr) } {
|
||||
// co::HRESULT::S_OK => Ok(()),
|
||||
// hr => Err(hr),
|
||||
// }
|
||||
}
|
||||
|
||||
fn _resolve_lnk(link_path: &str) -> HrResult<(String, String)> {
|
||||
let mut path_buf = WString::new_alloc_buf(1024);
|
||||
let mut work_dir_buf = WString::new_alloc_buf(1024);
|
||||
let hr = unsafe {
|
||||
ResolveLink(
|
||||
WString::from_str(link_path).as_ptr(),
|
||||
path_buf.as_mut_ptr(),
|
||||
path_buf.buf_len() as _,
|
||||
work_dir_buf.as_mut_ptr(),
|
||||
work_dir_buf.buf_len() as _,
|
||||
)
|
||||
};
|
||||
match unsafe { co::HRESULT::from_raw(hr) } {
|
||||
co::HRESULT::S_OK => Ok((path_buf.to_string(), work_dir_buf.to_string())),
|
||||
hr => Err(hr),
|
||||
}
|
||||
fn _resolve_lnk(link_path: &str) -> Result<(String, String)> {
|
||||
let lnk = ShellLink::new().map_err(|e| anyhow!("Failed to create ShellLink: {:?}", e))?;
|
||||
lnk.load(link_path).map_err(|e| anyhow!("Failed to load link: {:?}", e))?;
|
||||
let path = lnk.get_path().map_err(|e| anyhow!("Failed to get link path: {:?}", e))?;
|
||||
let work = lnk.get_working_directory().map_err(|e| anyhow!("Failed to get link working directory: {:?}", e))?;
|
||||
Ok((path.to_string_lossy().to_string(), work.to_string_lossy().to_string()))
|
||||
|
||||
// let me = w::CoCreateInstance::<w::IShellLink>(&co::CLSID::ShellLink, None, co::CLSCTX::INPROC_SERVER)?;
|
||||
// let pf = me.QueryInterface::<w::IPersistFile>()?;
|
||||
// pf.Load(link_path, co::STGM::READ)?;
|
||||
// info!("5");
|
||||
// // let flags_with_timeout = unsafe { co::SLR::from_raw(65539) };
|
||||
// info!("{:?}", w::HWND::NULL.ptr());
|
||||
// me.Resolve(&w::HWND::NULL, co::SLR::NO_UI);
|
||||
|
||||
// info!("6");
|
||||
// let path = me.GetPath(None, co::SLGP::UNCPRIORITY)?;
|
||||
|
||||
// let workdir = me.GetWorkingDirectory()?;
|
||||
// info!("7");
|
||||
// Ok((path, workdir))
|
||||
|
||||
// let me = w::CoCreateInstance::<w::IShellLink>(&co::CLSID::ShellLink, None, co::CLSCTX::INPROC_SERVER)?;
|
||||
// let pf = me.QueryInterface::<w::IPersistFile>()?;
|
||||
// pf.Load(file_path, co::STGM::READ)?;
|
||||
// let flags = (co::SLR::NO_UI | co::SLR::ANY_MATCH).raw() | (1 << 16);
|
||||
// me.Resolve(&w::HWND::NULL, flags_with_timeout)?;
|
||||
// Ok(Box::new(Lnk { me, pf }))
|
||||
|
||||
// let mut path_buf = WString::new_alloc_buf(1024);
|
||||
// let mut work_dir_buf = WString::new_alloc_buf(1024);
|
||||
// let hr = unsafe {
|
||||
// ResolveLink(
|
||||
// WString::from_str(link_path).as_ptr(),
|
||||
// path_buf.as_mut_ptr(),
|
||||
// path_buf.buf_len() as _,
|
||||
// work_dir_buf.as_mut_ptr(),
|
||||
// work_dir_buf.buf_len() as _,
|
||||
// )
|
||||
// };
|
||||
// match unsafe { co::HRESULT::from_raw(hr) } {
|
||||
// co::HRESULT::S_OK => Ok((path_buf.to_string(), work_dir_buf.to_string())),
|
||||
// hr => Err(hr),
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn remove_all_for_root_dir<P: AsRef<Path>>(root_dir: P) -> Result<()> {
|
||||
@@ -113,3 +149,119 @@ fn shortcut_full_integration_test() {
|
||||
remove_all_for_root_dir(root).unwrap();
|
||||
assert!(!link_location.exists());
|
||||
}
|
||||
|
||||
// pub struct Lnk {
|
||||
// me: w::IShellLink,
|
||||
// pf: w::IPersistFile,
|
||||
// }
|
||||
|
||||
// pub trait ShellLinkReadOnly {
|
||||
// fn get_arguments(&self) -> w::HrResult<String>;
|
||||
// fn get_description(&self) -> w::HrResult<String>;
|
||||
// fn get_icon_location(&self) -> w::HrResult<(String, i32)>;
|
||||
// fn get_path(&self) -> w::HrResult<String>;
|
||||
// fn get_show_cmd(&self) -> w::HrResult<co::SW>;
|
||||
// fn get_working_directory(&self) -> w::HrResult<String>;
|
||||
// }
|
||||
|
||||
// pub trait ShellLinkWriteOnly {
|
||||
// fn set_arguments(&mut self, path: &str) -> w::HrResult<()>;
|
||||
// fn set_description(&mut self, path: &str) -> w::HrResult<()>;
|
||||
// fn set_icon_location(&mut self, path: &str, index: i32) -> w::HrResult<()>;
|
||||
// fn set_path(&mut self, path: &str) -> w::HrResult<()>;
|
||||
// fn set_show_cmd(&mut self, show_cmd: co::SW) -> w::HrResult<()>;
|
||||
// fn set_working_directory(&mut self, path: &str) -> w::HrResult<()>;
|
||||
// fn save(&self) -> w::HrResult<()>;
|
||||
// fn save_as(&self, path: &str) -> w::HrResult<()>;
|
||||
// }
|
||||
|
||||
// impl ShellLinkReadOnly for Lnk {
|
||||
// fn get_arguments(&self) -> w::HrResult<String> {
|
||||
// Ok(self.me.GetArguments()?)
|
||||
// }
|
||||
|
||||
// fn get_description(&self) -> w::HrResult<String> {
|
||||
// Ok(self.me.GetDescription()?)
|
||||
// }
|
||||
|
||||
// fn get_icon_location(&self) -> w::HrResult<(String, i32)> {
|
||||
// Ok(self.me.GetIconLocation()?)
|
||||
// }
|
||||
|
||||
// fn get_path(&self) -> w::HrResult<String> {
|
||||
// Ok(self.me.GetPath(None, co::SLGP::UNCPRIORITY)?)
|
||||
// }
|
||||
|
||||
// fn get_show_cmd(&self) -> w::HrResult<co::SW> {
|
||||
// Ok(self.me.GetShowCmd()?)
|
||||
// }
|
||||
|
||||
// fn get_working_directory(&self) -> w::HrResult<String> {
|
||||
// Ok(self.me.GetWorkingDirectory()?)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl ShellLinkWriteOnly for Lnk {
|
||||
// fn set_arguments(&mut self, path: &str) -> w::HrResult<()> {
|
||||
// Ok(self.me.SetArguments(path)?)
|
||||
// }
|
||||
|
||||
// fn set_description(&mut self, path: &str) -> w::HrResult<()> {
|
||||
// Ok(self.me.SetDescription(path)?)
|
||||
// }
|
||||
|
||||
// fn set_icon_location(&mut self, path: &str, index: i32) -> w::HrResult<()> {
|
||||
// Ok(self.me.SetIconLocation(path, index)?)
|
||||
// }
|
||||
|
||||
// fn set_path(&mut self, path: &str) -> w::HrResult<()> {
|
||||
// Ok(self.me.SetPath(path)?)
|
||||
// }
|
||||
|
||||
// fn set_show_cmd(&mut self, show_cmd: co::SW) -> w::HrResult<()> {
|
||||
// Ok(self.me.SetShowCmd(show_cmd)?)
|
||||
// }
|
||||
|
||||
// fn set_working_directory(&mut self, path: &str) -> w::HrResult<()> {
|
||||
// Ok(self.me.SetWorkingDirectory(path)?)
|
||||
// }
|
||||
|
||||
// fn save(&self) -> w::HrResult<()> {
|
||||
// Ok(self.pf.Save(None, true)?)
|
||||
// }
|
||||
|
||||
// fn save_as(&self, path: &str) -> w::HrResult<()> {
|
||||
// Ok(self.pf.Save(Some(path), true)?)
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub trait ShellLinkReadWrite: ShellLinkReadOnly + ShellLinkWriteOnly {}
|
||||
// impl ShellLinkReadWrite for Lnk {}
|
||||
|
||||
// impl Lnk {
|
||||
// pub fn open_read(file_path: &str) -> w::HrResult<Box<dyn ShellLinkReadOnly>> {
|
||||
// let me = w::CoCreateInstance::<w::IShellLink>(&co::CLSID::ShellLink, None, co::CLSCTX::INPROC_SERVER)?;
|
||||
// let pf = me.QueryInterface::<w::IPersistFile>()?;
|
||||
// pf.Load(file_path, co::STGM::READ)?;
|
||||
// let flags = (co::SLR::NO_UI | co::SLR::ANY_MATCH).raw() | (1 << 16);
|
||||
// let flags_with_timeout = unsafe { co::SLR::from_raw(flags) };
|
||||
// me.Resolve(&w::HWND::NULL, flags_with_timeout)?;
|
||||
// Ok(Box::new(Lnk { me, pf }))
|
||||
// }
|
||||
|
||||
// pub fn open_write(file_path: &str) -> w::HrResult<Box<dyn ShellLinkReadWrite>> {
|
||||
// let me = w::CoCreateInstance::<w::IShellLink>(&co::CLSID::ShellLink, None, co::CLSCTX::INPROC_SERVER)?;
|
||||
// let pf = me.QueryInterface::<w::IPersistFile>()?;
|
||||
// pf.Load(file_path, co::STGM::READWRITE)?;
|
||||
// let flags = (co::SLR::NO_UI | co::SLR::ANY_MATCH).raw() | (1 << 16);
|
||||
// let flags_with_timeout = unsafe { co::SLR::from_raw(flags) };
|
||||
// me.Resolve(&w::HWND::NULL, flags_with_timeout)?;
|
||||
// Ok(Box::new(Lnk { me, pf }))
|
||||
// }
|
||||
|
||||
// pub fn create_new() -> w::HrResult<Box<dyn ShellLinkReadWrite>> {
|
||||
// let me = w::CoCreateInstance::<w::IShellLink>(&co::CLSID::ShellLink, None, co::CLSCTX::INPROC_SERVER)?;
|
||||
// let pf = me.QueryInterface::<w::IPersistFile>()?;
|
||||
// Ok(Box::new(Lnk { me, pf }))
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user