From 26d867d42fef8716ec7f33e582ec10418aaeafe9 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Mon, 20 Oct 2025 13:38:41 +0800 Subject: [PATCH] refactor(winreg): reorganize Windows registry utilities into extra module Move winreg_extra functionality into new extra/winreg module and restructure project layout Add Windows icon handling utilities in extra/windows module Update dependencies and clean up unused wincmd module --- Cargo.lock | 7 ++ wfassoc/Cargo.toml | 2 + wfassoc/src/extra.rs | 7 ++ wfassoc/src/extra/windows.rs | 96 +++++++++++++++++++ .../src/{winreg_extra.rs => extra/winreg.rs} | 0 wfassoc/src/lib.rs | 13 ++- wfassoc/src/wincmd.rs | 18 ---- wfassoc_exec/src/main.rs | 1 + 8 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 wfassoc/src/extra.rs create mode 100644 wfassoc/src/extra/windows.rs rename wfassoc/src/{winreg_extra.rs => extra/winreg.rs} (100%) delete mode 100644 wfassoc/src/wincmd.rs diff --git a/Cargo.lock b/Cargo.lock index 31d64ea..e372e77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,6 +739,7 @@ dependencies = [ "regex", "thiserror", "uuid", + "widestring", "windows-sys 0.60.2", "winreg", ] @@ -764,6 +765,12 @@ dependencies = [ "wfassoc", ] +[[package]] +name = "widestring" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" + [[package]] name = "winapi" version = "0.3.9" diff --git a/wfassoc/Cargo.toml b/wfassoc/Cargo.toml index 88b594d..a8785bc 100644 --- a/wfassoc/Cargo.toml +++ b/wfassoc/Cargo.toml @@ -12,9 +12,11 @@ windows-sys = { version = "0.60.2", features = [ "Win32_Security", "Win32_System_SystemServices", "Win32_UI_Shell", + "Win32_UI_WindowsAndMessaging", "Win32_System_Registry", ] } winreg = { version = "0.55.0", features = ["transactions"] } +widestring = "1.2.1" indexmap = "2.11.4" regex = "1.11.3" uuid = "1.18.1" diff --git a/wfassoc/src/extra.rs b/wfassoc/src/extra.rs new file mode 100644 index 0000000..3776c8a --- /dev/null +++ b/wfassoc/src/extra.rs @@ -0,0 +1,7 @@ +//! The extension for some existing crates. +//! Some imported crates are not enough for my project, +//! so I need create something to enrich them. + +pub mod winreg; +pub mod windows; + \ No newline at end of file diff --git a/wfassoc/src/extra/windows.rs b/wfassoc/src/extra/windows.rs new file mode 100644 index 0000000..255e38b --- /dev/null +++ b/wfassoc/src/extra/windows.rs @@ -0,0 +1,96 @@ +//! This module expand Windows-related stuff by `windows-sys` crate. +//! These features are not implemented in any crates (as I known scope) +//! and should be manually implemented for our file association use. + +use std::path::Path; +use thiserror::Error as TeError; +use widestring::WideCString; +use windows_sys::Win32::UI::Shell::ExtractIconExW; +use windows_sys::Win32::UI::WindowsAndMessaging::{DestroyIcon, HICON}; + +// region: Icon + +/// Error occurs when loading icon. +#[derive(Debug, TeError)] +#[error("error occurs when loading icon")] +pub enum LoadIconError { + EmbeddedNul(#[from] widestring::error::ContainsNul), + Other, +} + +/// The size kind of loaded icon +#[derive(Debug, Clone, Copy)] +pub enum IconSizeKind { + /// Small Icon + Small, + /// Large Icon + Large, +} + +/// The struct representing a loaded icon resource. +pub struct Icon { + icon: HICON, +} + +impl Icon { + pub fn new(file: &Path, index: i32, kind: IconSizeKind) -> Result { + let mut icon = HICON::default(); + let icon_ptr = &mut icon as *mut HICON; + let file = WideCString::from_os_str(file.as_os_str())?; + + let rv = unsafe { + match kind { + IconSizeKind::Small => { + ExtractIconExW(file.as_ptr(), index, Default::default(), icon_ptr, 1) + } + IconSizeKind::Large => { + ExtractIconExW(file.as_ptr(), index, icon_ptr, Default::default(), 1) + } + } + }; + + if rv != 1 || icon.is_null() { + Err(LoadIconError::Other) + } else { + Ok(Self { icon }) + } + } + + pub fn from_raw(hicon: HICON) -> Self { + Self { icon: hicon } + } + + pub fn into_raw(self) -> HICON { + self.icon + } +} + +impl Icon { + pub fn get_icon(&self) -> HICON { + self.icon + } +} + +impl Drop for Icon { + fn drop(&mut self) { + if !self.icon.is_null() { + unsafe { + DestroyIcon(self.icon); + } + } + } +} + +// endregion + +// region: Cmd Path + +pub struct CmdPath {} + +// endregion + +// region: Cmd Arguments + +pub struct CmdArgs {} + +// endregion diff --git a/wfassoc/src/winreg_extra.rs b/wfassoc/src/extra/winreg.rs similarity index 100% rename from wfassoc/src/winreg_extra.rs rename to wfassoc/src/extra/winreg.rs diff --git a/wfassoc/src/lib.rs b/wfassoc/src/lib.rs index eaf50bf..1940099 100644 --- a/wfassoc/src/lib.rs +++ b/wfassoc/src/lib.rs @@ -4,10 +4,9 @@ #[cfg(not(target_os = "windows"))] compile_error!("Crate wfassoc is only supported on Windows."); -pub mod assoc; +pub mod extra; pub mod utilities; -pub mod wincmd; -pub mod winreg_extra; +pub mod assoc; use assoc::{Ext, ProgId}; use indexmap::{IndexMap, IndexSet}; @@ -411,10 +410,10 @@ impl Program { // Open key for this extension. // If there is no such key, return directly. if let Some(subkey) = - winreg_extra::try_open_subkey_with_flags(&classes, ext.to_string(), KEY_WRITE)? + extra::winreg::try_open_subkey_with_flags(&classes, ext.to_string(), KEY_WRITE)? { // Only delete the default key if it is equal to our ProgId - if let Some(value) = winreg_extra::try_get_value::(&subkey, "")? { + if let Some(value) = extra::winreg::try_get_value::(&subkey, "")? { if value == prog_id.to_string() { // Delete the default key. subkey.delete_value("")?; @@ -449,10 +448,10 @@ impl Program { // Open key for this extension if possible let rv = - match winreg_extra::try_open_subkey_with_flags(&classes, ext.to_string(), KEY_READ)? { + match extra::winreg::try_open_subkey_with_flags(&classes, ext.to_string(), KEY_READ)? { Some(subkey) => { // Try get associated ProgId if possible - match winreg_extra::try_get_value::(&subkey, "")? { + match extra::winreg::try_get_value::(&subkey, "")? { Some(value) => Some(ProgId::from(value.as_str())), None => None, } diff --git a/wfassoc/src/wincmd.rs b/wfassoc/src/wincmd.rs deleted file mode 100644 index e264544..0000000 --- a/wfassoc/src/wincmd.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! This module involve Windows command line stuff, like argument splittor and path, -//! because they are different with POSIX standard. - -// region: Cmd Path - -pub struct CmdPath { - -} - -// endregion - -// region: Cmd Arguments - -pub struct CmdArgs { - -} - -// endregion diff --git a/wfassoc_exec/src/main.rs b/wfassoc_exec/src/main.rs index 344bc3f..ca633ea 100644 --- a/wfassoc_exec/src/main.rs +++ b/wfassoc_exec/src/main.rs @@ -117,6 +117,7 @@ fn run_unregister(cli: &Cli, target: &Target) -> Result<()> { fn run_query(cli: &Cli) -> Result<()> { let composition = Composition::new(cli)?; + // Show file association let mut table = Table::new(); table.set_header(["Extension", "Hybrid", "User", "System"]);