diff --git a/wfassoc/src/highlevel/program.rs b/wfassoc/src/highlevel/program.rs index 34485a4..b6160a6 100644 --- a/wfassoc/src/highlevel/program.rs +++ b/wfassoc/src/highlevel/program.rs @@ -402,6 +402,7 @@ impl Program { // Add this progid to file extension "open with" list. let ext_key = &mut program_key.ext_key; + ext_key.ensure(scope)?; ext_key.add_into_open_with_progids(scope, progid_key.inner())?; } @@ -438,9 +439,12 @@ impl Program { // there is no need to reset the default open way of file extension. // So we simply remove it from "open with" list. - // Remove this ProgId from file extension "open with" list. + // Remove this ProgId from file extension "open with" list, + // if this file extension is existing let ext_key = &mut program_key.ext_key; - ext_key.remove_from_open_with_progids(scope, progid_key.inner())?; + if ext_key.is_exist(scope.into())? { + ext_key.remove_from_open_with_progids(scope, progid_key.inner())?; + } // Delete ProgId subkey progid_key.delete(scope)?; diff --git a/wfassoc/src/highlevel/schema.rs b/wfassoc/src/highlevel/schema.rs index 1b8183c..dcced4d 100644 --- a/wfassoc/src/highlevel/schema.rs +++ b/wfassoc/src/highlevel/schema.rs @@ -156,7 +156,7 @@ impl Schema { } pub(super) fn get_behavior(&self) -> Option<&str> { - self.icon.as_ref().map(|v| v.as_str()) + self.behavior.as_ref().map(|v| v.as_str()) } pub(super) fn get_strs(&self) -> &HashMap { diff --git a/wfassoc/src/lowlevel/applications_key.rs b/wfassoc/src/lowlevel/applications_key.rs index 405ad93..a5263c4 100644 --- a/wfassoc/src/lowlevel/applications_key.rs +++ b/wfassoc/src/lowlevel/applications_key.rs @@ -257,7 +257,7 @@ impl ApplicationsKey { } None => { // Delete this key - key.delete_value(Self::NAMEOF_FRIENDLY_APP_NAME)?; + regext::arbitrarily_delete_value(&key, Self::NAMEOF_FRIENDLY_APP_NAME)?; } } @@ -329,7 +329,7 @@ impl ApplicationsKey { if flag { key.set_value(Self::NAMEOF_NO_OPEN_WITH, &"")?; } else { - key.delete_value(Self::NAMEOF_NO_OPEN_WITH)?; + regext::arbitrarily_delete_value(&key, Self::NAMEOF_NO_OPEN_WITH)?; } Ok(()) } diff --git a/wfassoc/src/lowlevel/ext_key.rs b/wfassoc/src/lowlevel/ext_key.rs index 7cc19dc..b2cda8a 100644 --- a/wfassoc/src/lowlevel/ext_key.rs +++ b/wfassoc/src/lowlevel/ext_key.rs @@ -136,7 +136,7 @@ impl ExtKey { } None => { // Delete this key - key.delete_value(Self::NAMEOF_DEFAULT)?; + regext::arbitrarily_delete_value(&key, Self::NAMEOF_DEFAULT)?; } } @@ -214,7 +214,10 @@ impl ExtKey { None => return Ok(()), }; // Remove given key - open_with_progids_key.delete_value(pid.to_string())?; + regext::arbitrarily_delete_value( + &open_with_progids_key, + regext::blank_path_guard(pid.to_string())?, + )?; Ok(()) } } diff --git a/wfassoc/src/lowlevel/progid_key.rs b/wfassoc/src/lowlevel/progid_key.rs index bc1514d..1a7be7b 100644 --- a/wfassoc/src/lowlevel/progid_key.rs +++ b/wfassoc/src/lowlevel/progid_key.rs @@ -138,7 +138,7 @@ impl ProgIdKey { } None => { // Delete this key - key.delete_value(Self::NAMEOF_DEFAULT)?; + regext::arbitrarily_delete_value(&key, Self::NAMEOF_DEFAULT)?; } } @@ -252,7 +252,7 @@ impl ProgIdKey { } None => { // Delete this key - key.delete_value(Self::NAMEOF_FRIENDLY_TYPE_NAME)?; + regext::arbitrarily_delete_value(&key, Self::NAMEOF_FRIENDLY_TYPE_NAME)?; } } diff --git a/wfassoc/src/win32/regext.rs b/wfassoc/src/win32/regext.rs index 2d9e37f..5bdf5da 100644 --- a/wfassoc/src/win32/regext.rs +++ b/wfassoc/src/win32/regext.rs @@ -66,7 +66,7 @@ pub fn try_get_value>( /// This function was invented to fix the shortcoming of [RegKey::delete_subkey_all]. /// This function always delete given path of given key no matter it is existing. /// Oppositely, [RegKey::delete_subkey_all] will return error if there is no such path. -/// +/// /// Return true if we successfully delete this key, /// otherwise false indicating there is no such key (already deleted). pub fn arbitrarily_delete_subkey_all>( @@ -85,6 +85,30 @@ pub fn arbitrarily_delete_subkey_all>( } } +/// Delete given value key of given key anyway. +/// +/// This function was invented to fix the shortcoming of [RegKey::delete_value]. +/// This function always delete given value key of given key no matter it is existing. +/// Oppositely, [RegKey::delete_value] will return error if there is no such value key. +/// +/// Return true if we successfully delete this value key, +/// otherwise false indicating there is no such value key (already deleted). +pub fn arbitrarily_delete_value>( + regkey: &RegKey, + name: N, +) -> std::io::Result { + match regkey.delete_value(name) { + Ok(()) => Ok(true), + Err(e) => match e.raw_os_error() { + Some(errno) => match errno as u32 { + ERROR_FILE_NOT_FOUND => Ok(false), + _ => Err(e), + }, + _ => Err(e), + }, + } +} + /// Get the name of only subkey in given key. /// /// If there is only one subkey in given key, the return value is its name. diff --git a/wfassoc/tests/common.rs b/wfassoc/tests/common.rs index c66a0a8..c8f73ef 100644 --- a/wfassoc/tests/common.rs +++ b/wfassoc/tests/common.rs @@ -9,8 +9,19 @@ pub fn check_sandbox() { std::env::var("SANDBOXIE").is_ok(), concat!( "Non-sandbox environment detected. ", - "Executing these test in non-sandbox environment is VERY dangerous. ", - "Please set \"SANDBOXIE\" environment variable to explicitly indicate you are running these test in sandbox environment." + "Executing these tests in non-sandbox environment is VERY dangerous. ", + "Please set \"SANDBOXIE\" environment variable to explicitly indicate you are running these tests in sandbox environment." + ) + ) +} + +pub fn check_privilege() { + assert!( + wfassoc::win32::utilities::has_privilege(), + concat!( + "You are running test without privilege. ", + "These tests must be run with some privilege because it need to manipulate Windows Registry. ", + "Please give it privilege in your sandbox environment." ) ) } diff --git a/wfassoc/tests/highlevel.rs b/wfassoc/tests/highlevel.rs index f983388..879809e 100644 --- a/wfassoc/tests/highlevel.rs +++ b/wfassoc/tests/highlevel.rs @@ -13,8 +13,8 @@ fn make_valid_schema() -> Schema { schema.set_clsid(CLSID); schema.add_str("main_name", "Passoc Application").unwrap(); schema.add_str("ext_name", "Pacfg File").unwrap(); - schema.add_icon("main_icon", r"notepad.exe,0").unwrap(); - schema.add_icon("ext_icon", r"notepad.exe,0").unwrap(); + schema.add_icon("main_icon", "notepad.exe,0").unwrap(); + schema.add_icon("ext_icon", "notepad.exe,0").unwrap(); schema.add_behavior("main_behavior", "notepad.exe %1").unwrap(); schema.add_behavior("ext_behavior", "notepad.exe %1").unwrap(); schema.set_name(Some("main_name")); @@ -29,21 +29,20 @@ fn make_valid_schema() -> Schema { #[test] fn test_schema() { common::check_sandbox(); + common::check_privilege(); // valid schema -> valid program let schema = make_valid_schema(); let rv = schema.into_program(); assert!(rv.is_ok()); - // missing identifier - let mut schema = Schema::new(); - schema.set_path(APP_PATH); + // missing essential parts (schema, path and etc) + let schema = Schema::new(); let rv = schema.into_program(); assert!(rv.is_err()); // invalid path - let mut schema = Schema::new(); - schema.set_identifier(IDENTIFIER); + let mut schema = make_valid_schema(); schema.set_path(r"C:\"); let rv = schema.into_program(); assert!(rv.is_err()); @@ -93,6 +92,7 @@ fn test_schema() { #[test] fn test_program() { common::check_sandbox(); + common::check_privilege(); fn tester(scope: Scope, view: View) { // build program diff --git a/wfassoc/tests/lowlevel.rs b/wfassoc/tests/lowlevel.rs index d497791..299f565 100644 --- a/wfassoc/tests/lowlevel.rs +++ b/wfassoc/tests/lowlevel.rs @@ -22,6 +22,7 @@ static VERB: LazyLock = LazyLock::new(|| { #[test] fn test_app_paths_key() { common::check_sandbox(); + common::check_privilege(); static APP_PATH: &str = r"C:\Program Files\Passoc\passoc.exe"; static APP_DIR: &str = r"C:\Program Files\Passoc"; @@ -31,7 +32,6 @@ fn test_app_paths_key() { // delete and ensure let rv = key.delete(scope); - eprintln!("{rv:?}"); assert!(rv.is_ok()); let rv = key.is_exist(scope); assert!(rv.is_ok()); @@ -76,6 +76,7 @@ fn test_app_paths_key() { #[test] fn test_applications_key() { common::check_sandbox(); + common::check_privilege(); static FRIENDLY_APP_NAME: LazyLock = LazyLock::new(|| "Passoc Application".into()); @@ -178,6 +179,7 @@ fn test_applications_key() { #[test] fn test_ext_key() { common::check_sandbox(); + common::check_privilege(); fn tester(scope: Scope, view: View) { let mut key = ExtKey::new(EXT.clone()); @@ -247,6 +249,7 @@ fn test_ext_key() { #[test] fn test_prog_id_key() { common::check_sandbox(); + common::check_privilege(); static LEGACY_NAME: LazyLock = LazyLock::new(|| "Passoc Pacfg File".into()); static FRIENDLY_TYPE_NAME: LazyLock =