From 3b0080849dd66f9dad2fd1d0e79c082c2764424a Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Mon, 18 May 2026 20:46:31 +0800 Subject: [PATCH] fix: fix expand string error --- Cargo.lock | 7 +++++++ wfassoc/Cargo.toml | 3 +++ wfassoc/src/highlevel.rs | 22 +++++++++++++++------- wfassoc/src/lowlevel.rs | 28 ++++++++++++++++++++++++++-- wfassoc/src/win32/concept.rs | 6 +++--- wfassoc/tests/concept.rs | 36 ++++++++++++++++++++++++++++-------- 6 files changed, 82 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65d8d7c..8f8f210 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -480,6 +480,12 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "strfmt" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fdc163db75f7b5ffa3daf0c5a7136fb0d4b2f35523cd1769da05e034159feb" + [[package]] name = "strsim" version = "0.11.1" @@ -683,6 +689,7 @@ version = "0.1.0" dependencies = [ "indexmap", "regex", + "strfmt", "thiserror", "uuid", "widestring", diff --git a/wfassoc/Cargo.toml b/wfassoc/Cargo.toml index c75066a..d8017b7 100644 --- a/wfassoc/Cargo.toml +++ b/wfassoc/Cargo.toml @@ -22,3 +22,6 @@ widestring = "1.2.1" indexmap = "2.11.4" regex = "1.11.3" uuid = { version = "1.18.1", features = ["v4"] } + +[dev-dependencies] +strfmt = "0.2.5" diff --git a/wfassoc/src/highlevel.rs b/wfassoc/src/highlevel.rs index c1d16d4..a750dda 100644 --- a/wfassoc/src/highlevel.rs +++ b/wfassoc/src/highlevel.rs @@ -614,7 +614,7 @@ impl Program { let ext_key = &mut program_key.ext_key; let progid_key = &program_key.progid_key; debug_println!( - "Linking ProgId \"{0}\" to extension \"{1}\"subkey...", + "Linking ProgId \"{0}\" to extension \"{1}\" subkey...", progid_key.inner().to_string(), ext_key.inner().to_string() ); @@ -622,10 +622,14 @@ impl Program { // Before setting it, we must make sure this extension is existing ext_key.ensure(scope)?; ext_key.set_default(scope, Some(progid_key.inner()))?; - Ok(()) } - None => Err(ProgramError::BadIndex), - } + None => return Err(ProgramError::BadIndex), + }; + + // Everything is okey. + // Notify changes and return + win32::utilities::notify_assoc_changed(); + Ok(()) } pub fn unlink_ext(&mut self, scope: Scope, index: usize) -> Result<(), ProgramError> { @@ -633,17 +637,21 @@ impl Program { Some(program_key) => { let ext_key = &mut program_key.ext_key; debug_println!( - "Unlinking for extension \"{0}\"subkey...", + "Unlinking for extension \"{0}\" subkey...", ext_key.inner().to_string() ); // Before setting it, we must make sure this extension is existing ext_key.ensure(scope)?; ext_key.set_default(scope, None)?; - Ok(()) } - None => Err(ProgramError::BadIndex), + None => return Err(ProgramError::BadIndex), } + + // Everything is okey. + // Notify changes and return + win32::utilities::notify_assoc_changed(); + Ok(()) } pub fn query_ext( diff --git a/wfassoc/src/lowlevel.rs b/wfassoc/src/lowlevel.rs index 4142bae..9a92204 100644 --- a/wfassoc/src/lowlevel.rs +++ b/wfassoc/src/lowlevel.rs @@ -20,6 +20,8 @@ pub enum Error { #[error("{0}")] UnexpectedBlankKey(#[from] regext::BlankPathError), + #[error("{0}")] + ExpandEnvVar(#[from] concept::ExpandEnvVarError), #[error("{0}")] LoadIconRc(#[from] concept::LoadIconRcError), #[error("{0}")] @@ -159,7 +161,19 @@ impl IconResVariant { pub fn extract(&self, kind: concept::IconSizeKind) -> Result { let rc = match self { IconResVariant::Plain(v) => concept::IconRc::with_ico_file(v.as_str(), kind)?, - IconResVariant::RefStr(v) => concept::IconRc::new(v.get_path(), v.get_index(), kind)?, + IconResVariant::RefStr(v) => { + // Try expand path part if possible + let path_part = match concept::ExpandString::new(v.get_path()) { + Ok(expand_string) => expand_string.expand()?, + Err(_) => v.get_path().to_string(), + }; + eprintln!("{path_part}"); + // Get index part. + let index_part = v.get_index(); + eprintln!("{index_part}"); + // Resolve icon resource + concept::IconRc::new(&path_part, index_part, kind)? + }, }; Ok(rc) } @@ -214,7 +228,17 @@ impl StrResVariant { StrResVariant::Plain(v) => v.clone(), // For string reference string, we try to resolve it. StrResVariant::RefStr(v) => { - let rc = concept::StrRc::new(v.get_path(), v.get_index())?; + // Try expand path part if possible + let path_part = match concept::ExpandString::new(v.get_path()) { + Ok(expand_string) => expand_string.expand()?, + Err(_) => v.get_path().to_string(), + }; + eprintln!("{path_part}"); + // Get index part + let index_part = v.get_index(); + eprintln!("{index_part}"); + // Resolve string resource + let rc = concept::StrRc::new(&path_part, index_part)?; rc.into_string() } }; diff --git a/wfassoc/src/win32/concept.rs b/wfassoc/src/win32/concept.rs index be19c88..2f4d730 100644 --- a/wfassoc/src/win32/concept.rs +++ b/wfassoc/src/win32/concept.rs @@ -724,16 +724,16 @@ impl ExpandString { /// Expand the variables located in this string /// and produce the final usable string. - pub fn expand_string(&self) -> Result { + pub fn expand(&self) -> Result { use windows_sys::Win32::System::Environment::ExpandEnvironmentStringsW; // Fetch the size of expand result let source = WideCString::from_str(self.inner.as_str())?; + // The return size is including null terminal. let size = unsafe { ExpandEnvironmentStringsW(source.as_ptr(), std::ptr::null_mut(), 0) }; if size == 0 { return Err(ExpandEnvVarError::ExpandFunction); } - let size_no_nul = size.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?; // Allocate buffer for it. let len: usize = size.try_into()?; @@ -741,7 +741,7 @@ impl ExpandString { let mut buffer = vec![0; len]; // Receive result let size = - unsafe { ExpandEnvironmentStringsW(source.as_ptr(), buffer.as_mut_ptr(), size_no_nul) }; + unsafe { ExpandEnvironmentStringsW(source.as_ptr(), buffer.as_mut_ptr(), size) }; if size == 0 { return Err(ExpandEnvVarError::ExpandFunction); } diff --git a/wfassoc/tests/concept.rs b/wfassoc/tests/concept.rs index aed579e..833782c 100644 --- a/wfassoc/tests/concept.rs +++ b/wfassoc/tests/concept.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{collections::HashMap, str::FromStr}; use wfassoc::win32::concept::*; // region: File Extension @@ -218,16 +218,36 @@ fn test_str_rc() { #[test] fn test_expand_string() { - fn tester(s: &str) { - let rv = ExpandString::new(s); - assert!(rv.is_ok()); - let rv = rv.unwrap(); + fn tester(fmt: &str, var_name: &str) { + // We first insert variable name into format string to get the final string + let mut vars = HashMap::new(); + vars.insert("0".to_string(), var_name.to_string()); + let final_string = strfmt::strfmt(fmt, &vars).unwrap(); - let rv = rv.expand_string(); - assert!(rv.is_ok()); + // The we try expanding final string first + let expand_final_string = ExpandString::new(&final_string); + assert!(expand_final_string.is_ok()); + let expand_final_string = expand_final_string.unwrap(); + let expanded_final_string = expand_final_string.expand(); + assert!(expanded_final_string.is_ok()); + let expanded_final_string= expanded_final_string.unwrap(); + + // Then we expand variable name individually + let expand_var_name = ExpandString::new(var_name); + assert!(expand_var_name.is_ok()); + let expand_var_name = expand_var_name.unwrap(); + let expanded_var_name = expand_var_name.expand(); + assert!(expanded_var_name.is_ok()); + let expanded_var_name = expanded_var_name.unwrap(); + + // Finally, we directly insert expanded variable name into format string + // to get the string which can be compared with final string. + vars.insert("0".to_string(), expanded_var_name.clone()); + let built_final_string = strfmt::strfmt(fmt, &vars).unwrap(); + assert_eq!(expanded_final_string.to_string(), built_final_string); } - tester(r#"%SystemRoot%\System32\shell32.dll"#); + tester(r#"{0}\System32\shell32.dll"#, "%SystemRoot%"); } // endregion