Skip to main content

ib_shell_item/
id_list.rs

1/*!
2Shell item ID list (path).
3*/
4use std::{marker::PhantomData, mem, ops::Deref, os::raw::c_void};
5
6use derive_more::From;
7use windows::{
8    Win32::{
9        System::Com::{CLSCTX_ALL, CoCreateInstance, CoTaskMemFree},
10        UI::Shell::{Common::ITEMIDLIST, IPersistIDList, IShellItem, SHGetIDListFromObject},
11    },
12    core::{IUnknown, Interface, Param, Result},
13};
14
15use super::item::ShellItem;
16
17#[derive(Debug, Clone, From)]
18pub struct ChildID(pub *mut ITEMIDLIST);
19
20impl From<RelativeIDList> for ChildID {
21    fn from(value: RelativeIDList) -> Self {
22        let id = Self(value.0);
23        mem::forget(value);
24        id
25    }
26}
27
28impl ChildID {
29    pub fn to_ref(&self) -> ChildIDRef<'_> {
30        self.into()
31    }
32}
33
34impl Drop for ChildID {
35    fn drop(&mut self) {
36        unsafe { CoTaskMemFree(Some(self.0.cast())) }
37    }
38}
39
40/// This has the same memory representation as [`ChildID`],
41/// but represents a borrowed ID list pointer.
42///
43/// You should only use this in C arrays/structs, otherwise `&ChildID` is enough and more idiomatic.
44#[derive(Debug, Clone, Copy)]
45pub struct ChildIDRef<'a>(pub *const ITEMIDLIST, PhantomData<&'a ()>);
46
47impl<'a> ChildIDRef<'a> {
48    /// Creates a `ChildIDRef` from a raw pointer.
49    pub unsafe fn from_raw(ptr: *const ITEMIDLIST) -> Self {
50        Self(ptr, PhantomData)
51    }
52}
53
54impl<'a> From<&'a ChildID> for ChildIDRef<'a> {
55    fn from(id: &'a ChildID) -> Self {
56        Self(id.0, PhantomData)
57    }
58}
59
60impl<'a> Deref for ChildIDRef<'a> {
61    type Target = ChildID;
62
63    fn deref(&self) -> &Self::Target {
64        unsafe { mem::transmute(self) }
65    }
66}
67
68#[derive(Debug, Clone, From)]
69pub struct RelativeIDList(pub *mut ITEMIDLIST);
70
71impl Drop for RelativeIDList {
72    fn drop(&mut self) {
73        unsafe { CoTaskMemFree(Some(self.0.cast())) }
74    }
75}
76
77/// This has the same memory representation as [`RelativeIDList`],
78/// but represents a borrowed ID list pointer.
79///
80/// You should only use this in C arrays/structs, otherwise `&RelativeIDList` is enough and more idiomatic.
81#[derive(Debug, Clone, Copy)]
82pub struct RelativeIDListRef<'a>(pub *const ITEMIDLIST, PhantomData<&'a ()>);
83
84impl<'a> RelativeIDListRef<'a> {
85    /// Creates a `RelativeIDListRef` from a raw pointer.
86    pub unsafe fn from_raw(ptr: *const ITEMIDLIST) -> Self {
87        Self(ptr, PhantomData)
88    }
89}
90
91impl<'a> From<&'a RelativeIDList> for RelativeIDListRef<'a> {
92    fn from(id: &'a RelativeIDList) -> Self {
93        Self(id.0, PhantomData)
94    }
95}
96
97impl<'a> Deref for RelativeIDListRef<'a> {
98    type Target = RelativeIDList;
99
100    fn deref(&self) -> &Self::Target {
101        unsafe { mem::transmute(self) }
102    }
103}
104
105impl RelativeIDList {
106    /// Converts this [`RelativeIDList`] to a [`ChildID`], transferring ownership.
107    /// The underlying PIDL should be a valid child item ID.
108    ///
109    /// ## Safety
110    /// This is safe as most APIs either check or don't care.
111    pub fn into_child(self) -> ChildID {
112        self.into()
113    }
114
115    /// Converts this [`RelativeIDList`] to a [`ChildIDRef`] without transferring ownership.
116    ///
117    /// ## Safety
118    /// This is safe as most APIs either check or don't care.
119    pub fn to_child_ref(&self) -> ChildIDRef<'_> {
120        ChildIDRef(self.0, PhantomData)
121    }
122}
123
124#[derive(Debug, Clone, From)]
125pub struct AbsoluteIDList(pub *mut ITEMIDLIST);
126
127impl AbsoluteIDList {
128    pub fn from_raw_void_ref<'a>(pidl: &'a *mut c_void) -> &'a Self {
129        unsafe { mem::transmute(pidl) }
130    }
131
132    /// The following classes are not supported:
133    /// - [`PropertyStore`](crate::prop::store::PropertyStore): `CFSFolderPropertyStore`
134    ///
135    /// [SHGetIDListFromObject function (shobjidl_core.h)](https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-shgetidlistfromobject)
136    pub fn from_object(unk: impl Param<IUnknown>) -> Result<Self> {
137        unsafe { SHGetIDListFromObject(unk) }.map(AbsoluteIDList)
138    }
139}
140
141impl Drop for AbsoluteIDList {
142    fn drop(&mut self) {
143        unsafe { CoTaskMemFree(Some(self.0.cast())) }
144    }
145}
146
147/// This has the same memory representation as [`AbsoluteIDList`],
148/// but represents a borrowed ID list pointer.
149///
150/// You should only use this in C arrays/structs, otherwise `&AbsoluteIDList` is enough and more idiomatic.
151#[derive(Debug, Clone, Copy)]
152pub struct AbsoluteIDListRef<'a>(pub *const ITEMIDLIST, PhantomData<&'a ()>);
153
154impl<'a> AbsoluteIDListRef<'a> {
155    /// Creates an `AbsoluteIDListRef` from a raw pointer.
156    pub unsafe fn from_raw(ptr: *const ITEMIDLIST) -> Self {
157        Self(ptr, PhantomData)
158    }
159}
160
161impl<'a> From<&'a AbsoluteIDList> for AbsoluteIDListRef<'a> {
162    fn from(id: &'a AbsoluteIDList) -> Self {
163        Self(id.0, PhantomData)
164    }
165}
166
167impl<'a> Deref for AbsoluteIDListRef<'a> {
168    type Target = AbsoluteIDList;
169
170    fn deref(&self) -> &Self::Target {
171        unsafe { mem::transmute(self) }
172    }
173}
174
175/// [IPersistIDList (shobjidl_core.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ipersistidlist)
176pub trait PersistIDList {
177    fn new() -> Result<IPersistIDList>;
178
179    /// [IPersistIDList::GetIDList (shobjidl_core.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ipersistidlist-getidlist)
180    ///
181    /// ## References
182    /// [Simon Mourier's Blog - How to programmatically switch a File Explorer view to thumbnails?](https://www.simonmourier.com/blog/How-to-programmatically-switch-a-File-Explorer-view-to-thumbnails/)
183    fn get_id_list(&self) -> Result<AbsoluteIDList>;
184
185    fn to_shell_item(&self) -> Result<IShellItem>;
186}
187
188impl PersistIDList for IPersistIDList {
189    fn new() -> Result<IPersistIDList> {
190        unsafe { CoCreateInstance(&IPersistIDList::IID, None, CLSCTX_ALL) }
191    }
192
193    fn get_id_list(&self) -> Result<AbsoluteIDList> {
194        unsafe { self.GetIDList() }.map(AbsoluteIDList)
195    }
196
197    fn to_shell_item(&self) -> Result<IShellItem> {
198        let id_list = self.get_id_list()?;
199        let item = IShellItem::from_id_list(&id_list)?;
200        // unsafe { CoTaskMemFree(Some(id_list as _)) };
201        Ok(item)
202    }
203}