ib_shell_item/prop/attribute.rs
1use bitflags::bitflags;
2use windows::Win32::System::SystemServices::SFGAO_FLAGS;
3use windows::Win32::System::SystemServices::{
4 SFGAO_BROWSABLE, SFGAO_CANMONIKER, SFGAO_COMPRESSED, SFGAO_CONTENTSMASK, SFGAO_FILESYSANCESTOR,
5 SFGAO_FILESYSTEM, SFGAO_FOLDER, SFGAO_HASSTORAGE, SFGAO_HASSUBFOLDER, SFGAO_PKEYSFGAOMASK,
6 SFGAO_REMOVABLE, SFGAO_STORAGEANCESTOR, SFGAO_STORAGECAPMASK, SFGAO_STREAM, SFGAO_VALIDATE,
7};
8
9/// These flags represent attributes that can be retrieved on an item (file or folder) or set of items.
10///
11/// They are used with the [`ShellFolder`](crate::folder::ShellFolder) and [`ShellItem`](crate::item::ShellItem) APIs,
12/// most notably [`ShellFolder::get_attributes_of`](crate::folder::ShellFolder::get_attributes_of)
13/// and [`ShellItem::get_attributes`](crate::item::ShellItem::get_attributes).
14///
15/// [SFGAO (Shobjidl.h)](https://learn.microsoft.com/en-us/windows/win32/shell/sfgao)
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct ItemAttributes(u32);
18
19impl From<SFGAO_FLAGS> for ItemAttributes {
20 fn from(value: SFGAO_FLAGS) -> Self {
21 Self(value.0)
22 }
23}
24
25impl Into<SFGAO_FLAGS> for ItemAttributes {
26 fn into(self) -> SFGAO_FLAGS {
27 SFGAO_FLAGS(self.0)
28 }
29}
30
31bitflags! {
32 impl ItemAttributes: u32 {
33 /// The specified items can be copied.
34 const CanCopy = 0x00000001;
35
36 /// The specified items can be moved.
37 const CanMove = 0x00000002;
38
39 /// Shortcuts can be created for the specified items.
40 ///
41 /// This attribute has the same value as `DROPEFFECT_LINK`.
42 /// If a namespace extension returns this attribute, a `Create Shortcut`
43 /// entry with a default handler is added to the shortcut menu that is
44 /// displayed during drag-and-drop operations. The extension can also
45 /// implement its own handler for the `link` verb in place of the default.
46 /// If the extension does so, it is responsible for creating the shortcut.
47 ///
48 /// A `Create Shortcut` item is also added to the Windows Explorer `File`
49 /// menu and to normal shortcut menus. If the item is selected, your
50 /// application's `IContextMenu::InvokeCommand` method is invoked with
51 /// the `lpVerb` member of the `CMINVOKECOMMANDINFO` structure set to
52 /// `link`. Your application is responsible for creating the link.
53 const CanLink = 0x00000004;
54
55 /// The specified items can be bound to an `IStorage` object through
56 /// `IShellFolder::BindToObject`. For more information about namespace
57 /// manipulation capabilities, see `IStorage`.
58 const Storage = 0x00000008;
59
60 /// The specified items can be renamed.
61 ///
62 /// Note that this value is essentially a suggestion; not all namespace
63 /// clients allow items to be renamed. However, those that do must have
64 /// this attribute set.
65 const CanRename = 0x00000010;
66
67 /// The specified items can be deleted.
68 const CanDelete = 0x00000020;
69
70 /// The specified items have property sheets.
71 const HasPropSheet = 0x00000040;
72
73 /// The specified items are drop targets.
74 const DropTarget = 0x00000100;
75
76 /// This flag is a mask for the capability attributes: [`ItemAttributes::CanCopy`],
77 /// [`ItemAttributes::CanMove`], [`ItemAttributes::CanLink`], [`ItemAttributes::CanRename`], [`ItemAttributes::CanDelete`],
78 /// [`ItemAttributes::HasPropSheet`], and [`ItemAttributes::DropTarget`].
79 ///
80 /// Callers normally do not use this value.
81 const CapabilityMask = 0x00000177;
82
83 /// Windows 7 and later. The specified items are system items.
84 const System = 0x00001000;
85
86 /// The specified items are encrypted and might require special presentation.
87 const Encrypted = 0x00002000;
88
89 /// Accessing the item (through `IStream` or other storage interfaces) is
90 /// expected to be a slow operation.
91 /// Applications should avoid accessing items flagged with [`ItemAttributes::IsSlow`].
92 ///
93 /// Note: Opening a stream for an item is generally a slow operation at
94 /// all times. [`ItemAttributes::IsSlow`] indicates that it is expected to be
95 /// especially slow, for example in the case of slow network connections
96 /// or offline (`FILE_ATTRIBUTE_OFFLINE`) files. However, querying
97 /// [`ItemAttributes::IsSlow`] is itself a slow operation. Applications should query
98 /// [`ItemAttributes::IsSlow`] only on a background thread. An alternate method,
99 /// such as retrieving the `PKEY_FileAttributes` property and testing for
100 /// `FILE_ATTRIBUTE_OFFLINE`, could be used in place of a method call
101 /// that involves [`ItemAttributes::IsSlow`].
102 const IsSlow = 0x00004000;
103
104 /// The specified items are shown as dimmed and unavailable to the user.
105 const Ghosted = 0x00008000;
106
107 /// The specified items are shortcuts.
108 const Link = 0x00010000;
109
110 /// The specified objects are shared.
111 const Share = 0x00020000;
112
113 /// The specified items are read-only.
114 ///
115 /// In the case of folders, this means that new items cannot be created
116 /// in those folders. This should not be confused with the behavior
117 /// specified by the `FILE_ATTRIBUTE_READONLY` flag retrieved by
118 /// `IColumnProvider::GetItemData` in a `SHCOLUMNDATA` structure.
119 /// `FILE_ATTRIBUTE_READONLY` has no meaning for Win32 file system folders.
120 const ReadOnly = 0x00040000;
121
122 /// The item is hidden and should not be displayed unless the `Show hidden
123 /// files and folders` option is enabled in `Folder Settings`.
124 const Hidden = 0x00080000;
125
126 /// Do not use.
127 const DisplayAttrMask = 0x000FC000;
128
129 /// The items are nonenumerated items and should be hidden.
130 ///
131 /// They are not returned through an enumerator such as that created by
132 /// the `IShellFolder::EnumObjects` method.
133 const NonEnumerated = 0x00100000;
134
135 /// The items contain new content, as defined by the particular application.
136 const NewContent = 0x00200000;
137
138 /// Not supported.
139 ///
140 /// [`ItemAttributes::CanMoniker`], [`ItemAttributes::HasStorage`] and [`ItemAttributes::Stream`]
141 /// have the same value.
142 const CanMoniker = SFGAO_CANMONIKER.0;
143
144 /// Not supported.
145 ///
146 /// [`ItemAttributes::CanMoniker`], [`ItemAttributes::HasStorage`] and [`ItemAttributes::Stream`]
147 /// have the same value.
148 const HasStorage = SFGAO_HASSTORAGE.0;
149
150 /// Indicates that the item has a stream associated with it.
151 ///
152 /// That stream can be accessed through a call to
153 /// `IShellFolder::BindToObject` or `IShellItem::BindToHandler` with
154 /// `IID_IStream` in the `riid` parameter.
155 ///
156 /// [`ItemAttributes::CanMoniker`], [`ItemAttributes::HasStorage`] and [`ItemAttributes::Stream`]
157 /// have the same value.
158 const Stream = SFGAO_STREAM.0;
159
160 /// Children of this item are accessible through `IStream` or `IStorage`.
161 ///
162 /// Those children are flagged with [`ItemAttributes::Storage`] or [`ItemAttributes::Stream`].
163 const StorageAncestor = SFGAO_STORAGEANCESTOR.0;
164
165 /// When specified as input, [`ItemAttributes::Validate`] instructs the folder to
166 /// validate that the items contained in a folder or Shell item array
167 /// exist.
168 ///
169 /// If one or more of those items do not exist,
170 /// `IShellFolder::GetAttributesOf` and `IShellItemArray::GetAttributes`
171 /// return a failure code. This flag is never returned as an `out` value.
172 /// When used with the file system folder, [`ItemAttributes::Validate`] instructs the
173 /// folder to discard cached properties retrieved by clients of
174 /// `IShellFolder2::GetDetailsEx` that might have accumulated for the
175 /// specified items.
176 const Validate = SFGAO_VALIDATE.0;
177
178 /// The specified items are on removable media or are themselves removable devices.
179 const Removable = SFGAO_REMOVABLE.0;
180
181 /// The specified items are compressed.
182 const Compressed = SFGAO_COMPRESSED.0;
183
184 /// The specified items can be hosted inside a web browser or Windows Explorer frame.
185 const Browsable = SFGAO_BROWSABLE.0;
186
187 /// The specified folders are either file system folders or contain at
188 /// least one descendant (child, grandchild, or later) that is a file
189 /// system ([`ItemAttributes::FileSystem`]) folder.
190 const FileSysAncestor = SFGAO_FILESYSANCESTOR.0;
191
192 /// The specified items are folders.
193 ///
194 /// Some items can be flagged with both [`ItemAttributes::Stream`] and [`ItemAttributes::Folder`],
195 /// such as a compressed file with a `.zip` file name extension. Some
196 /// applications might include this flag when testing for items that are
197 /// both files and containers.
198 const Folder = SFGAO_FOLDER.0;
199
200 /// The specified folders or files are part of the file system (that is,
201 /// they are files, directories, or root directories).
202 ///
203 /// The parsed names of the items can be assumed to be valid Win32 file
204 /// system paths. These paths can be either UNC or drive-letter based.
205 const FileSystem = SFGAO_FILESYSTEM.0;
206
207 /// This flag is a mask for the storage capability attributes:
208 /// [`ItemAttributes::Storage`], [`ItemAttributes::Link`], [`ItemAttributes::ReadOnly`], [`ItemAttributes::Stream`],
209 /// [`ItemAttributes::StorageAncestor`], [`ItemAttributes::FileSysAncestor`], [`ItemAttributes::Folder`],
210 /// and [`ItemAttributes::FileSystem`].
211 ///
212 /// Callers normally do not use this value.
213 const StorageCapMask = SFGAO_STORAGECAPMASK.0;
214
215 /// The specified folders have subfolders.
216 ///
217 /// The [`ItemAttributes::HasSubFolder`] attribute is only advisory and might be
218 /// returned by Shell folder implementations even if they do not contain
219 /// subfolders. Note, however, that the converse—failing to return
220 /// [`ItemAttributes::HasSubFolder`]—definitively states that the folder objects do
221 /// not have subfolders. Returning [`ItemAttributes::HasSubFolder`] is recommended
222 /// whenever a significant amount of time is required to determine whether
223 /// any subfolders exist. For example, the Shell always returns
224 /// [`ItemAttributes::HasSubFolder`] when a folder is located on a network drive.
225 ///
226 /// ## Remark
227 /// [`ItemAttributes::HasSubFolder`] may access local drives,
228 /// which can be slow.
229 const HasSubFolder = SFGAO_HASSUBFOLDER.0;
230
231 /// This flag is a mask for content attributes, at present only
232 /// [`ItemAttributes::HasSubFolder`].
233 ///
234 /// Callers normally do not use this value.
235 const ContentMask = SFGAO_CONTENTSMASK.0;
236
237 /// Mask used by the `PKEY_SFGAOFlags` property to determine attributes
238 /// that are considered to cause slow calculations or lack context:
239 /// [`ItemAttributes::IsSlow`], [`ItemAttributes::ReadOnly`], [`ItemAttributes::HasSubFolder`], and
240 /// [`ItemAttributes::Validate`].
241 ///
242 /// Callers normally do not use this value.
243 const PkeySfgaoMask = SFGAO_PKEYSFGAOMASK.0;
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 use windows::{
252 Win32::{
253 Foundation::HWND,
254 UI::Shell::{IShellFolder, IShellItem},
255 },
256 core::w,
257 };
258
259 use crate::{ShellItem, folder::ShellFolder, init};
260
261 #[test]
262 fn item() {
263 _ = init();
264
265 let windows = IShellItem::from_path_w(w!(r"C:\Windows")).unwrap();
266 assert_eq!(
267 windows.get_attributes(ItemAttributes::Folder),
268 Ok(ItemAttributes::Folder)
269 );
270 assert!(windows.is_folder());
271
272 let explorer = IShellItem::from_path_w(w!(r"C:\Windows\explorer.exe")).unwrap();
273 assert_eq!(
274 explorer.get_attributes(ItemAttributes::Folder),
275 Ok(ItemAttributes::empty())
276 );
277 assert!(!explorer.is_folder());
278 }
279
280 #[allow(deprecated)]
281 #[test]
282 fn folder() {
283 // Get the C:\Windows folder
284 let windows_folder = IShellFolder::from_path_w(HWND::default(), w!(r"C:\Windows")).unwrap();
285
286 // Get child PIDs relative to the Windows folder
287 // Use "system32" as a subfolder and "notepad.exe" as a file
288 let system32_pidl = windows_folder
289 .parse_display_name_to_id_list(HWND::default(), w!(r"system32"))
290 .unwrap()
291 .into_child();
292 let notepad_pidl = windows_folder
293 .parse_display_name_to_id_list(HWND::default(), w!(r"notepad.exe"))
294 .unwrap()
295 .into_child();
296
297 // Query folder attribute for system32 folder - should have Folder bit
298 let system32_attrs = windows_folder
299 .get_attributes_of(&[system32_pidl.to_ref()], ItemAttributes::Folder)
300 .unwrap();
301 // system32 is a folder, so it must have the Folder bit set
302 assert!(system32_attrs.contains(ItemAttributes::Folder));
303
304 // Query folder attribute for notepad.exe - should not have Folder bit
305 let notepad_attrs = windows_folder
306 .get_attributes_of(&[notepad_pidl.to_ref()], ItemAttributes::Folder)
307 .unwrap();
308 assert!(!notepad_attrs.contains(ItemAttributes::Folder));
309
310 // Query multiple items - folder and file - Folder bit should not be common
311 let children = &[system32_pidl.to_ref(), notepad_pidl.to_ref()];
312 let multi_attrs = windows_folder
313 .get_attributes_of(children, ItemAttributes::Folder)
314 .unwrap();
315 // Since notepad.exe is not a folder, the common attributes should not include Folder
316 assert!(!multi_attrs.contains(ItemAttributes::Folder));
317 assert!(!windows_folder.are_children_folders(children));
318
319 // Not supported
320 let children = &[system32_pidl.to_ref(), system32_pidl.to_ref()];
321 let multi_attrs = windows_folder
322 .get_attributes_of(children, ItemAttributes::Folder)
323 .unwrap();
324 assert!(!multi_attrs.contains(ItemAttributes::Folder));
325 assert!(!windows_folder.are_children_folders(children));
326
327 // Not supported
328 let system_pidl = windows_folder
329 .parse_display_name_to_id_list(HWND::default(), w!(r"System"))
330 .unwrap()
331 .into_child();
332 let children = &[system32_pidl.to_ref(), system_pidl.to_ref()];
333 let multi_attrs = windows_folder
334 .get_attributes_of(children, ItemAttributes::Folder)
335 .unwrap();
336 assert!(!multi_attrs.contains(ItemAttributes::Folder));
337 assert!(!windows_folder.are_children_folders(children));
338 }
339
340 #[test]
341 fn folder_nosub() {
342 let folder = IShellFolder::from_path_w(HWND::default(), w!(r"C:\Windows")).unwrap();
343
344 let inboxapps_pidl = folder
345 .parse_display_name_to_id_list(HWND::default(), w!(r"InboxApps"))
346 .unwrap()
347 .into_child();
348 let inboxapps_attrs = folder
349 .get_attributes_of(
350 &[inboxapps_pidl.to_ref()],
351 ItemAttributes::Folder | ItemAttributes::FileSystem,
352 )
353 .unwrap();
354 println!("{:X}", inboxapps_attrs);
355 assert!(inboxapps_attrs.contains(ItemAttributes::Folder | ItemAttributes::FileSystem));
356 assert!(folder.is_child_fs_folder(inboxapps_pidl.to_ref()));
357 }
358}