ib_shell_item/hook/prop/
mod.rs1use std::{cell::SyncUnsafeCell, ffi::c_void, mem::MaybeUninit, ptr};
2
3use bon::Builder;
4use serde::{Deserialize, Serialize};
5use tracing::{debug, error};
6use windows::{
7 Win32::UI::Shell::{
8 IShellItem2,
9 PropertiesSystem::{GETPROPERTYSTOREFLAGS, IPropertyStore},
10 },
11 core::{GUID, HRESULT, Interface},
12};
13
14use crate::hook::HOOK_CONFIG;
15
16pub mod magic;
17pub mod system;
18pub mod value;
19
20#[derive(Default, Serialize, Deserialize, Clone, Builder, Debug)]
21#[builder(on(Vec<u16>, into))]
22pub struct PropertyHookConfig {
23 str_prefix: Option<Vec<u16>>,
25
26 #[cfg(feature = "everything")]
27 #[builder(default)]
28 size_from_everything: bool,
29
30 system: Option<system::PropertySystemHookConfig>,
31}
32
33pub(crate) type GetPropertyStoreFn = unsafe extern "system" fn(
34 *mut c_void,
35 GETPROPERTYSTOREFLAGS,
36 *const GUID,
37 *mut *mut c_void,
38) -> HRESULT;
39
40pub(crate) struct GetPropertyStoreState {
42 pub original_get_store: Option<GetPropertyStoreFn>,
43 pub real_get_store: MaybeUninit<GetPropertyStoreFn>,
44}
45
46pub(crate) static GET_PROPERTY_STORE_STATE: SyncUnsafeCell<GetPropertyStoreState> =
47 SyncUnsafeCell::new(GetPropertyStoreState {
48 original_get_store: None,
49 real_get_store: MaybeUninit::uninit(),
50 });
51
52pub(crate) unsafe extern "system" fn get_property_store(
56 this: *mut c_void,
57 flags: GETPROPERTYSTOREFLAGS,
58 riid: *const GUID,
59 ppv: *mut *mut c_void,
60) -> HRESULT {
61 let state = GET_PROPERTY_STORE_STATE.get();
62 let real = || unsafe { (*state).real_get_store.assume_init()(this, flags, riid, ppv) };
63
64 let config = HOOK_CONFIG.read().unwrap();
65 let Some(_config) = &config.property else {
66 return real();
67 };
68
69 let result = real();
71
72 if result.is_ok() {
73 debug!(?flags, "GetPropertyStore called");
74 let store = unsafe { IPropertyStore::from_raw_borrowed(&*ppv) }.unwrap();
75 if let Err(e) = value::enable_hook(store) {
76 error!(%e, "Failed to hook GetValue");
77 }
78 }
79
80 result
81}
82
83fn hook(enable: bool) -> windows::core::Result<()> {
84 let state = GET_PROPERTY_STORE_STATE.get();
85 let res = unsafe {
86 slim_detours_sys::SlimDetoursInlineHook(
87 enable as _,
88 (*state).real_get_store.as_mut_ptr().cast(),
89 get_property_store as _,
90 )
91 };
92 windows::core::HRESULT(res).ok()
93}
94
95pub(crate) fn enable_hook(item2: &IShellItem2) -> windows::core::Result<()> {
96 let get_property_store = item2.vtable().GetPropertyStore;
97
98 let state = unsafe { &mut *GET_PROPERTY_STORE_STATE.get() };
100 match state.original_get_store {
101 Some(f) if ptr::fn_addr_eq(f, get_property_store) => Ok(()),
102 None => {
103 let config = HOOK_CONFIG.read().unwrap();
104 if let Err(e) = system::apply(config.property.as_ref().and_then(|p| p.system.clone())) {
105 error!(?e, "system");
106 };
107
108 state.original_get_store = Some(get_property_store);
110 (*state).real_get_store.write(get_property_store);
111 debug!(?get_property_store, "Hooking GetPropertyStore");
112 hook(true)
113 }
114 Some(f) => {
115 error!(?f, ?get_property_store, "Multi GetPropertyStore");
117 windows::core::HRESULT(1).ok()
118 }
119 }
120}
121
122pub(crate) fn disable_hook() -> windows::core::Result<()> {
123 _ = system::apply(None);
124
125 let state = GET_PROPERTY_STORE_STATE.get();
126 if unsafe { (*state).original_get_store.is_some() } {
127 debug!("Unhooking GetPropertyStore");
129 hook(false)?;
130 }
131 value::disable_hook()
132}