1
0

feat: add application subkey shell verb function

This commit is contained in:
2026-05-17 14:28:29 +08:00
parent 9d02a2187f
commit 9d3467ea26
2 changed files with 94 additions and 20 deletions

View File

@@ -20,14 +20,16 @@ pub enum Error {
#[error("registry operation error: {0}")] #[error("registry operation error: {0}")]
BadRegOp(#[from] std::io::Error), BadRegOp(#[from] std::io::Error),
#[error("{0}")] #[error("{0}")]
BadSoleSubKey(#[from] regext::OnlySubKeyError),
#[error("{0}")]
UnexpectedBlankKey(#[from] regext::BlankPathError), UnexpectedBlankKey(#[from] regext::BlankPathError),
#[error("{0}")] #[error("{0}")]
LoadIconRc(#[from] concept::LoadIconRcError), LoadIconRc(#[from] concept::LoadIconRcError),
#[error("{0}")] #[error("{0}")]
LoadStrRc(#[from] concept::LoadStrRcError), LoadStrRc(#[from] concept::LoadStrRcError),
#[error("{0}")]
ParseVerb(#[from] concept::ParseVerbError),
#[error("{0}")]
ParseCmdLine(#[from] concept::ParseCmdLineError),
} }
// endregion // endregion
@@ -458,6 +460,9 @@ impl AppPathsKey {
.ok_or(Error::InexistantKey) .ok_or(Error::InexistantKey)
} }
// YYC MARK:
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/app-registration#using-the-app-paths-subkey
const NAMEOF_DEFAULT: &str = ""; const NAMEOF_DEFAULT: &str = "";
/// ///
@@ -592,6 +597,9 @@ impl ApplicationsKey {
Ok(()) Ok(())
} }
// YYC MARK:
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/app-registration#using-the-applications-subkey
fn open_view_for_getter(&self, view: View) -> Result<RegKey, Error> { fn open_view_for_getter(&self, view: View) -> Result<RegKey, Error> {
self.open_view_for_read(view)? self.open_view_for_read(view)?
.this_key .this_key
@@ -606,13 +614,83 @@ impl ApplicationsKey {
const NAMEOF_SHELL_VERB_PART1: &str = "shell"; const NAMEOF_SHELL_VERB_PART1: &str = "shell";
const NAMEOF_SHELL_VERB_PART3: &str = "command"; const NAMEOF_SHELL_VERB_PART3: &str = "command";
const NAMEOF_SHELL_VERB_PART4: &str = "";
pub fn get_shell_verb(&self, view: View) -> Result<Option<ShellVerb>, Error> { pub fn get_shell_verb(&self, view: View) -> Result<Option<ShellVerb>, Error> {
todo!() let key = self.open_view_for_getter(view)?;
// Get shell subkey
let shell_key = match regext::try_open_subkey_with_flags(
&key,
Self::NAMEOF_SHELL_VERB_PART1,
KEY_READ,
)? {
Some(key) => key,
None => return Ok(None),
};
// Get verb subkey name, then get subkey itself.
let verb_key_name = match regext::get_sole_subkey_name(&shell_key)? {
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),
};
// Get command subkey.
let command_key = match regext::try_open_subkey_with_flags(
&verb_key,
Self::NAMEOF_SHELL_VERB_PART3,
KEY_READ,
)? {
Some(key) => key,
None => return Ok(None),
};
// Get the default value of command subkey
let command_default_value = match regext::try_get_value::<String, _>(
&command_key,
Self::NAMEOF_SHELL_VERB_PART4,
)? {
Some(value) => value,
None => return Ok(None),
};
// Okey, return value.
Ok(Some(ShellVerb::new(
verb_key_name.parse()?,
command_default_value.parse()?,
)))
} }
pub fn set_shell_verb(&mut self, scope: Scope, sv: Option<&ShellVerb>) -> Result<(), Error> { pub fn set_shell_verb(&mut self, scope: Scope, sv: Option<&ShellVerb>) -> Result<(), Error> {
todo!() let key = self.open_scope_for_setter(scope)?;
match sv {
Some(sv) => {
// Create shell subkey
let (shell_key, _) = key.create_subkey_with_flags(
Self::NAMEOF_SHELL_VERB_PART1,
KEY_READ | KEY_WRITE,
)?;
// Create verb key
let (verb_key, _) = shell_key
.create_subkey_with_flags(sv.get_verb().inner(), KEY_READ | KEY_WRITE)?;
// Create command key
let (command_key, _) = verb_key.create_subkey_with_flags(
Self::NAMEOF_SHELL_VERB_PART3,
KEY_READ | KEY_WRITE,
)?;
// Set command key default value
command_key.set_value(Self::NAMEOF_SHELL_VERB_PART4, &sv.get_command().full())?;
}
None => {
// Delete shell and its all subkey.
key.delete_subkey_all(regext::blank_path_guard(Self::NAMEOF_SHELL_VERB_PART1)?)?;
}
}
Ok(())
} }
const NAMEOF_DEFAULT_ICON_PART1: &str = "DefaultIcon"; const NAMEOF_DEFAULT_ICON_PART1: &str = "DefaultIcon";
@@ -771,6 +849,8 @@ impl ExtKey {
} }
// YYC MARK: // YYC MARK:
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-file-types#setting-optional-subkeys-and-file-type-extension-attributes
//
// We do not support "Content Type" and "PerceivedType" // We do not support "Content Type" and "PerceivedType"
// because current interface are enough to use, // because current interface are enough to use,
// and these types has not been made as concept struct in Rust. // and these types has not been made as concept struct in Rust.
@@ -916,6 +996,8 @@ impl ProgIdKey {
} }
// YYC MARK: // YYC MARK:
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids#programmatic-identifier-elements-used-by-file-associations
//
// Currently we only support (Default), FriendlyTypeName and DefaultIcon // Currently we only support (Default), FriendlyTypeName and DefaultIcon
// to just cover the basic usage. // to just cover the basic usage.

View File

@@ -61,34 +61,26 @@ pub fn try_get_value<T: FromRegValue, N: AsRef<OsStr>>(
} }
} }
/// The error occurs when fetching the name of only subkey.
#[derive(Debug, TeError)]
pub enum OnlySubKeyError {
#[error("registry operation error: {0}")]
Io(#[from] std::io::Error),
#[error("there is no any subkey in given RegKey")]
NoSubKey,
#[error("there is more than one subkey in given RegKey")]
TooManySubKey,
}
/// Get the name of only subkey in given key. /// Get the name of only subkey in given key.
/// ///
/// If there is only one subkey in given key, the return value is its name.
/// If there is no any subkey, or has multiple subkeys, return None instead.
/// If error occurs when fetching data, return Err(_).
///
/// This is usually used for ShellVerb fetching. /// This is usually used for ShellVerb fetching.
pub fn get_sole_subkey_name(regkey: &RegKey) -> Result<String, OnlySubKeyError> { pub fn get_sole_subkey_name(regkey: &RegKey) -> std::io::Result<Option<String>> {
let mut subkey_enumerator = regkey.enum_keys(); let mut subkey_enumerator = regkey.enum_keys();
// Get first one. // Get first one.
let rv = match subkey_enumerator.next() { let rv = match subkey_enumerator.next() {
Some(key) => key?, Some(key) => key?,
None => return Err(OnlySubKeyError::NoSubKey), None => return Ok(None),
}; };
// Check whether there is second one. // Check whether there is second one.
match subkey_enumerator.next() { match subkey_enumerator.next() {
Some(_) => Err(OnlySubKeyError::TooManySubKey), Some(_) => Ok(None),
None => Ok(rv), None => Ok(Some(rv)),
} }
} }