1
0
Files
wfassoc/wfassoc/src/lowlevel/applications_key.rs

337 lines
12 KiB
Rust

use super::{
Error, IconResVariant, OpenKeyPurpose, OpenKeyTerritory, OpenedKey, PERM_R, PERM_RW, Result,
Scope, ShellVerb, StrResVariant, View, check_privilege,
};
use crate::win32::{concept, regext};
use winreg::RegKey;
use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ApplicationsKey {
key_name: concept::FileName,
}
impl ApplicationsKey {
pub fn new(inner: concept::FileName) -> Self {
Self { key_name: inner }
}
pub fn inner(&self) -> &concept::FileName {
&self.key_name
}
}
impl ApplicationsKey {
const FULL_APPLICATIONS: &str = "Software\\Classes\\Applications";
const PARTIAL_APPLICATIONS: &str = "Applications";
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
// check privilege
check_privilege(territory, purpose)?;
// Fetch the permission
let perms = purpose.to_permission();
// get the root key
let hk = match territory {
OpenKeyTerritory::User => RegKey::predef(HKEY_CURRENT_USER),
OpenKeyTerritory::System => RegKey::predef(HKEY_LOCAL_MACHINE),
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)?
}
};
// open app key if possible
let this_app = regext::try_open_subkey_with_flags(
&applications,
regext::blank_path_guard(self.key_name.inner())?,
perms,
)?;
// okey
Ok(OpenedKey::new(applications, this_app))
}
fn open_view_for_read(&self, view: View) -> Result<OpenedKey> {
self.open_key(view.into(), OpenKeyPurpose::Read)
}
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
}
pub fn is_exist(&self, view: View) -> Result<bool> {
let key = self.open_view_for_read(view)?.this_key;
Ok(key.is_some())
}
/// Ensure this application key is presented in Applications key.
///
/// Return true if we newly create this key,
/// otherwise false indicating there already is an existing key.
pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?;
if let None = key.this_key {
let _ = key.parent_key.create_subkey_with_flags(
regext::blank_path_guard(self.key_name.inner())?,
PERM_RW,
)?;
Ok(true)
} else {
Ok(false)
}
}
/// Delete this application key from Applications key.
///
/// Return true if we successfully delete this key,
/// otherwise false indicating there is no such key (already deleted).
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?;
Ok(regext::arbitrarily_delete_subkey_all(
&key.parent_key,
regext::blank_path_guard(self.key_name.inner())?,
)?)
}
// 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> {
self.open_view_for_read(view)?
.this_key
.ok_or(Error::InexistantKey)
}
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
self.open_scope_for_write(scope)?
.this_key
.ok_or(Error::InexistantKey)
}
const NAMEOF_SHELL_VERB_PART1: &str = "shell";
const NAMEOF_SHELL_VERB_PART3: &str = "command";
const NAMEOF_SHELL_VERB_PART4: &str = "";
pub fn get_shell_verb(&self, view: View) -> Result<Option<ShellVerb>> {
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,
PERM_R,
)? {
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,
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,
PERM_R,
)? {
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<()> {
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, PERM_RW)?;
// Create verb key
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, PERM_RW)?;
// 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.
regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_SHELL_VERB_PART1)?;
}
}
Ok(())
}
const NAMEOF_DEFAULT_ICON_PART1: &str = "DefaultIcon";
const NAMEOF_DEFAULT_ICON_PART2: &str = "";
pub fn get_default_icon(&self, view: View) -> Result<Option<IconResVariant>> {
let key = self.open_view_for_getter(view)?;
// Get default icon subkey
let default_icon_key = match regext::try_open_subkey_with_flags(
&key,
Self::NAMEOF_DEFAULT_ICON_PART1,
PERM_R,
)? {
Some(key) => key,
None => return Ok(None),
};
// Get the default value of default icon subkey
let default_icon_default_value =
regext::try_get_value::<String, _>(&default_icon_key, Self::NAMEOF_DEFAULT_ICON_PART2)?;
// Transform it as result
Ok(default_icon_default_value.map(|v| IconResVariant::from(v.as_str())))
}
pub fn set_default_icon(&mut self, scope: Scope, icon: Option<&IconResVariant>) -> Result<()> {
let key = self.open_scope_for_setter(scope)?;
match icon {
Some(icon) => {
// Create default icon subkey
let (default_icon_key, _) =
key.create_subkey_with_flags(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())?;
}
None => {
// Delete shell and its all subkey.
regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_DEFAULT_ICON_PART1)?;
}
}
Ok(())
}
const NAMEOF_FRIENDLY_APP_NAME: &str = "FriendlyAppName";
pub fn get_friendly_app_name(&self, view: View) -> Result<Option<StrResVariant>> {
let key = self.open_view_for_getter(view)?;
// Get value of it
let value = regext::try_get_value::<String, _>(&key, Self::NAMEOF_FRIENDLY_APP_NAME)?;
// Transform it as result
Ok(value.map(|v| StrResVariant::from(v.as_str())))
}
pub fn set_friendly_app_name(
&mut self,
scope: Scope,
name: Option<&StrResVariant>,
) -> Result<()> {
let key = self.open_scope_for_setter(scope)?;
match name {
Some(name) => {
// Set value for this key
key.set_value(Self::NAMEOF_FRIENDLY_APP_NAME, &name.to_string())?;
}
None => {
// Delete this key
regext::arbitrarily_delete_value(&key, Self::NAMEOF_FRIENDLY_APP_NAME)?;
}
}
Ok(())
}
const NAMEOF_SUPPORTED_TYPES: &str = "SupportedTypes";
pub fn get_supported_types(&self, view: View) -> Result<Option<Vec<concept::Ext>>> {
let key = self.open_view_for_getter(view)?;
// Get supported types subkey
let supported_types_key =
match regext::try_open_subkey_with_flags(&key, 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::<concept::Ext>())
.collect::<std::result::Result<Vec<_>, _>>()?;
// Return value
Ok(Some(exts))
}
pub fn set_supported_types(
&mut self,
scope: Scope,
tys: Option<&[&concept::Ext]>,
) -> Result<()> {
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(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(regext::blank_path_guard(ty.dotted_inner())?, &"")?;
}
}
None => {
// Delete this subkey.
regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_SUPPORTED_TYPES)?;
}
}
Ok(())
}
const NAMEOF_NO_OPEN_WITH: &str = "NoOpenWith";
pub fn get_no_open_with(&self, view: View) -> Result<bool> {
let key = self.open_view_for_getter(view)?;
match regext::try_get_value::<String, _>(&key, Self::NAMEOF_NO_OPEN_WITH)? {
Some(_) => Ok(true),
None => Ok(false),
}
}
pub fn set_no_open_with(&mut self, scope: Scope, flag: bool) -> Result<()> {
let key = self.open_scope_for_setter(scope)?;
if flag {
key.set_value(Self::NAMEOF_NO_OPEN_WITH, &"")?;
} else {
regext::arbitrarily_delete_value(&key, Self::NAMEOF_NO_OPEN_WITH)?;
}
Ok(())
}
}