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