Skip to main content

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}