From bbcea1f4d23340c744bbd222b6d530180dc87308 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Mon, 18 May 2026 15:43:16 +0800 Subject: [PATCH] feat: add more guard and const in lowlevel --- wfassoc-cdylib/codegen/wfassoc.h | 4 +- wfassoc-cdylib/src/lib.rs | 4 +- wfassoc/src/lowlevel.rs | 177 +++++++++++++++++++++---------- wfassoc/src/win32/regext.rs | 9 ++ 4 files changed, 136 insertions(+), 58 deletions(-) diff --git a/wfassoc-cdylib/codegen/wfassoc.h b/wfassoc-cdylib/codegen/wfassoc.h index 20da9ac..76e0845 100644 --- a/wfassoc-cdylib/codegen/wfassoc.h +++ b/wfassoc-cdylib/codegen/wfassoc.h @@ -105,9 +105,9 @@ bool WFProgramQueryExt(Token in_program, View in_view, size_t in_index, Token *o bool WFExtStatusDestroy(Token in_ext_status); -bool WFExtStatuGetName(Token in_ext_status, CStyleString *out_name); +bool WFExtStatusGetName(Token in_ext_status, CStyleString *out_name); -bool WFExtStatuGetIcon(Token in_ext_status, HICON *out_icon); +bool WFExtStatusGetIcon(Token in_ext_status, HICON *out_icon); #ifdef __cplusplus } // extern "C" diff --git a/wfassoc-cdylib/src/lib.rs b/wfassoc-cdylib/src/lib.rs index 982d2a5..9171efa 100644 --- a/wfassoc-cdylib/src/lib.rs +++ b/wfassoc-cdylib/src/lib.rs @@ -577,7 +577,7 @@ pub extern "C" fn WFExtStatusDestroy(in_ext_status: in_param_ty!(Token)) -> bool } #[unsafe(no_mangle)] -pub extern "C" fn WFExtStatuGetName( +pub extern "C" fn WFExtStatusGetName( in_ext_status: in_param_ty!(Token), out_name: out_param_ty!(CStyleString), ) -> bool { @@ -591,7 +591,7 @@ pub extern "C" fn WFExtStatuGetName( } #[unsafe(no_mangle)] -pub extern "C" fn WFExtStatuGetIcon( +pub extern "C" fn WFExtStatusGetIcon( in_ext_status: in_param_ty!(Token), out_icon: out_param_ty!(HICON), ) -> bool { diff --git a/wfassoc/src/lowlevel.rs b/wfassoc/src/lowlevel.rs index de10ddf..83564ab 100644 --- a/wfassoc/src/lowlevel.rs +++ b/wfassoc/src/lowlevel.rs @@ -30,6 +30,8 @@ pub enum Error { ParseVerb(#[from] concept::ParseVerbError), #[error("{0}")] ParseCmdLine(#[from] concept::ParseCmdLineError), + #[error("{0}")] + ParseExt(#[from] concept::ParseExtError), } // endregion @@ -303,6 +305,9 @@ impl From for OpenKeyTerritory { } } +const PERM_R: u32 = KEY_READ; +const PERM_RW: u32 = KEY_READ | KEY_WRITE; + /// The purpose of opening this key. #[derive(Debug, Copy, Clone)] enum OpenKeyPurpose { @@ -312,6 +317,15 @@ enum OpenKeyPurpose { ReadWrite, } +impl OpenKeyPurpose { + fn to_permission(&self) -> u32 { + match self { + OpenKeyPurpose::Read => PERM_R, + OpenKeyPurpose::ReadWrite => PERM_RW, + } + } +} + /// Check whether we have enough privilege when operating in given territory and purpose. /// If we have, simply return, otherwise return error. fn check_privilege(territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<(), Error> { @@ -382,10 +396,7 @@ impl AppPathsKey { // check privilege check_privilege(territory, purpose)?; // Fetch the permission - let perms = match purpose { - OpenKeyPurpose::Read => KEY_READ, - OpenKeyPurpose::ReadWrite => KEY_READ | KEY_WRITE, - }; + let perms = purpose.to_permission(); // get the root key let hk = match territory { @@ -395,7 +406,8 @@ impl AppPathsKey { OpenKeyTerritory::Hybrid => panic!("unexpected hybrid key territory"), }; // navigate to App Paths - let app_paths = hk.open_subkey_with_flags(Self::APP_PATHS, perms)?; + let app_paths = + hk.open_subkey_with_flags(regext::blank_path_guard(Self::APP_PATHS)?, perms)?; // open file name key if possible let this_app = regext::try_open_subkey_with_flags( &app_paths, @@ -429,7 +441,7 @@ impl AppPathsKey { if let None = key.this_key { let _ = key.parent_key.create_subkey_with_flags( regext::blank_path_guard(self.key_name.inner())?, - KEY_READ | KEY_WRITE, + PERM_RW, )?; Ok(true) } else { @@ -535,10 +547,7 @@ impl ApplicationsKey { // check privilege check_privilege(territory, purpose)?; // Fetch the permission - let perms = match purpose { - OpenKeyPurpose::Read => KEY_READ, - OpenKeyPurpose::ReadWrite => KEY_READ | KEY_WRITE, - }; + let perms = purpose.to_permission(); // get the root key let hk = match territory { @@ -547,12 +556,14 @@ impl ApplicationsKey { OpenKeyTerritory::Hybrid => RegKey::predef(HKEY_CLASSES_ROOT), }; let applications = match territory { - OpenKeyTerritory::User | OpenKeyTerritory::System => { - hk.open_subkey_with_flags(Self::FULL_APPLICATIONS, perms)? - } - OpenKeyTerritory::Hybrid => { - hk.open_subkey_with_flags(Self::PARTIAL_APPLICATIONS, perms)? - } + OpenKeyTerritory::User | OpenKeyTerritory::System => hk.open_subkey_with_flags( + regext::blank_path_guard(Self::FULL_APPLICATIONS)?, + perms, + )?, + OpenKeyTerritory::Hybrid => hk.open_subkey_with_flags( + regext::blank_path_guard(Self::PARTIAL_APPLICATIONS)?, + perms, + )?, }; // open app key if possible let this_app = regext::try_open_subkey_with_flags( @@ -582,7 +593,7 @@ impl ApplicationsKey { if let None = key.this_key { let _ = key.parent_key.create_subkey_with_flags( regext::blank_path_guard(self.key_name.inner())?, - KEY_READ | KEY_WRITE, + PERM_RW, )?; Ok(true) } else { @@ -622,8 +633,8 @@ impl ApplicationsKey { // Get shell subkey let shell_key = match regext::try_open_subkey_with_flags( &key, - Self::NAMEOF_SHELL_VERB_PART1, - KEY_READ, + regext::blank_path_guard(Self::NAMEOF_SHELL_VERB_PART1)?, + PERM_R, )? { Some(key) => key, None => return Ok(None), @@ -633,16 +644,19 @@ impl ApplicationsKey { Some(key) => key, None => return Ok(None), }; - let verb_key = - match regext::try_open_subkey_with_flags(&shell_key, &verb_key_name, KEY_READ)? { - Some(key) => key, - None => return Ok(None), - }; + let verb_key = match regext::try_open_subkey_with_flags( + &shell_key, + regext::blank_path_guard(&verb_key_name)?, + PERM_R, + )? { + Some(key) => key, + None => return Ok(None), + }; // Get command subkey. let command_key = match regext::try_open_subkey_with_flags( &verb_key, - Self::NAMEOF_SHELL_VERB_PART3, - KEY_READ, + regext::blank_path_guard(Self::NAMEOF_SHELL_VERB_PART3)?, + PERM_R, )? { Some(key) => key, None => return Ok(None), @@ -670,16 +684,16 @@ impl ApplicationsKey { Some(sv) => { // Create shell subkey let (shell_key, _) = key.create_subkey_with_flags( - Self::NAMEOF_SHELL_VERB_PART1, - KEY_READ | KEY_WRITE, + regext::blank_path_guard(Self::NAMEOF_SHELL_VERB_PART1)?, + PERM_RW, )?; // Create verb key - let (verb_key, _) = shell_key - .create_subkey_with_flags(sv.get_verb().inner(), KEY_READ | KEY_WRITE)?; + let (verb_key, _) = + shell_key.create_subkey_with_flags(sv.get_verb().inner(), PERM_RW)?; // Create command key let (command_key, _) = verb_key.create_subkey_with_flags( - Self::NAMEOF_SHELL_VERB_PART3, - KEY_READ | KEY_WRITE, + regext::blank_path_guard(Self::NAMEOF_SHELL_VERB_PART3)?, + PERM_RW, )?; // Set command key default value command_key.set_value(Self::NAMEOF_SHELL_VERB_PART4, &sv.get_command().full())?; @@ -701,8 +715,8 @@ impl ApplicationsKey { // Get default icon subkey let default_icon_key = match regext::try_open_subkey_with_flags( &key, - Self::NAMEOF_DEFAULT_ICON_PART1, - KEY_READ, + regext::blank_path_guard(Self::NAMEOF_DEFAULT_ICON_PART1)?, + PERM_R, )? { Some(key) => key, None => return Ok(None), @@ -725,8 +739,8 @@ impl ApplicationsKey { Some(icon) => { // Create default icon subkey let (default_icon_key, _) = key.create_subkey_with_flags( - Self::NAMEOF_DEFAULT_ICON_PART1, - KEY_READ | KEY_WRITE, + regext::blank_path_guard(Self::NAMEOF_DEFAULT_ICON_PART1)?, + PERM_RW, )?; // Set default value of default icon subkey. default_icon_key.set_value(Self::NAMEOF_DEFAULT_ICON_PART2, &icon.to_string())?; @@ -774,7 +788,25 @@ impl ApplicationsKey { const NAMEOF_SUPPORTED_TYPES: &str = "SupportedTypes"; pub fn get_supported_types(&self, view: View) -> Result>, Error> { - todo!() + let key = self.open_view_for_getter(view)?; + // Get supported types subkey + let supported_types_key = match regext::try_open_subkey_with_flags( + &key, + regext::blank_path_guard(Self::NAMEOF_SUPPORTED_TYPES)?, + PERM_R, + )? { + Some(key) => key, + None => return Ok(None), + }; + // Fetch all sub-values + let key_names = regext::get_all_string_subkey_names(&supported_types_key)?; + // Map the result + let exts = key_names + .into_iter() + .map(|name| name.parse::()) + .collect::, _>>()?; + // Return value + Ok(Some(exts)) } pub fn set_supported_types( @@ -782,7 +814,29 @@ impl ApplicationsKey { scope: Scope, tys: Option<&[&concept::Ext]>, ) -> Result<(), Error> { - todo!() + let key = self.open_scope_for_setter(scope)?; + + match tys { + Some(tys) => { + // Create supported types key + let (supported_types_key, _) = key.create_subkey_with_flags( + regext::blank_path_guard(Self::NAMEOF_SUPPORTED_TYPES)?, + PERM_RW, + )?; + // Clean all contents of this key + regext::clean_all_contents(&supported_types_key)?; + // Add file types one by one + for ty in tys { + supported_types_key.set_value(ty.dotted_inner(), &"")?; + } + } + None => { + // Delete this subkey. + key.delete_subkey_all(regext::blank_path_guard(Self::NAMEOF_SUPPORTED_TYPES)?)?; + } + } + + Ok(()) } const NAMEOF_NO_OPEN_WITH: &str = "NoOpenWith"; @@ -837,10 +891,7 @@ impl ExtKey { // check privilege check_privilege(territory, purpose)?; // Fetch the permission - let perms = match purpose { - OpenKeyPurpose::Read => KEY_READ, - OpenKeyPurpose::ReadWrite => KEY_READ | KEY_WRITE, - }; + let perms = purpose.to_permission(); // navigate to extension container let hk = match territory { @@ -850,9 +901,11 @@ impl ExtKey { }; let classes = match territory { OpenKeyTerritory::User | OpenKeyTerritory::System => { - hk.open_subkey_with_flags(Self::FULL_CLASSES, perms)? + hk.open_subkey_with_flags(regext::blank_path_guard(Self::FULL_CLASSES)?, perms)? + } + OpenKeyTerritory::Hybrid => { + hk.open_subkey_with_flags(regext::blank_path_guard(Self::PARTIAL_CLASSES)?, perms)? } - OpenKeyTerritory::Hybrid => hk.open_subkey_with_flags(Self::PARTIAL_CLASSES, perms)?, }; // open extension key if possible let this_ext = regext::try_open_subkey_with_flags( @@ -882,7 +935,7 @@ impl ExtKey { if let None = key.this_key { let _ = key.parent_key.create_subkey_with_flags( regext::blank_path_guard(self.ext.dotted_inner())?, - KEY_READ | KEY_WRITE, + PERM_RW, )?; Ok(true) } else { @@ -919,11 +972,28 @@ impl ExtKey { const NAMEOF_DEFAULT: &str = ""; pub fn get_default(&self, view: View) -> Result, Error> { - todo!() + let key = self.open_view_for_getter(view)?; + // Get value of it + let value = regext::try_get_value::(&key, Self::NAMEOF_DEFAULT)?; + // Transform it as result + Ok(value.map(|v| LosseProgId::from(v.as_str()))) } pub fn set_default(&mut self, scope: Scope, pid: Option<&LosseProgId>) -> Result<(), Error> { - todo!() + let key = self.open_scope_for_setter(scope)?; + + match pid { + Some(pid) => { + // Set value for this key + key.set_value(Self::NAMEOF_DEFAULT, &pid.to_string())?; + } + None => { + // Delete this key + key.delete_value(Self::NAMEOF_DEFAULT)?; + } + } + + Ok(()) } const NAMEOF_OPEN_WITH_PROGIDS: &str = "OpenWithProgIds"; @@ -984,10 +1054,7 @@ impl ProgIdKey { // check privilege check_privilege(territory, purpose)?; // Fetch the permission - let perms = match purpose { - OpenKeyPurpose::Read => KEY_READ, - OpenKeyPurpose::ReadWrite => KEY_READ | KEY_WRITE, - }; + let perms = purpose.to_permission(); // navigate to ProgId container let hk = match territory { @@ -997,9 +1064,11 @@ impl ProgIdKey { }; let classes = match territory { OpenKeyTerritory::User | OpenKeyTerritory::System => { - hk.open_subkey_with_flags(Self::FULL_CLASSES, perms)? + hk.open_subkey_with_flags(regext::blank_path_guard(Self::FULL_CLASSES)?, perms)? + } + OpenKeyTerritory::Hybrid => { + hk.open_subkey_with_flags(regext::blank_path_guard(Self::PARTIAL_CLASSES)?, perms)? } - OpenKeyTerritory::Hybrid => hk.open_subkey_with_flags(Self::PARTIAL_CLASSES, perms)?, }; // open ProgId key if possible let this_progid = regext::try_open_subkey_with_flags( @@ -1029,7 +1098,7 @@ impl ProgIdKey { if let None = key.this_key { let _ = key.parent_key.create_subkey_with_flags( regext::blank_path_guard(self.progid.to_string())?, - KEY_READ | KEY_WRITE, + PERM_RW, )?; Ok(true) } else { diff --git a/wfassoc/src/win32/regext.rs b/wfassoc/src/win32/regext.rs index 50edbf0..fb14ed0 100644 --- a/wfassoc/src/win32/regext.rs +++ b/wfassoc/src/win32/regext.rs @@ -104,6 +104,15 @@ pub fn get_all_string_subkey_names(regkey: &RegKey) -> std::io::Result, _>>() } +/// Delete all contents, including values and subkeys of given key. +/// +/// Deleting all contents of given key rely on giving a special parameter to [RegKey::delete_subkey_all]. +/// This is very dangerous and may be used by accident. +/// So I create this to explicitly indicate this behavior and avoid any mis-type in code. +pub fn clean_all_contents(regkey: &RegKey) -> std::io::Result<()> { + regkey.delete_subkey_all("") +} + // endregion // region: Blank Path Guard