windirs/
lib.rs

1#![cfg(windows)]
2
3use std::{
4    ffi::OsString, fmt::Display, os::windows::ffi::OsStringExt, path::PathBuf, ptr::null_mut,
5};
6
7use winapi::{
8    shared::guiddef::GUID,
9    shared::winerror::{E_FAIL, E_INVALIDARG, HRESULT, S_OK},
10    um::{
11        combaseapi::CoTaskMemFree, knownfolders::*, shlobj::SHGetKnownFolderPath,
12        shtypes::REFKNOWNFOLDERID, winbase::lstrlenW, winnt::PWSTR,
13    },
14};
15
16/// Error cases when resolving folder identifiers to paths.
17#[derive(Debug)]
18pub enum Error {
19    Virtual,
20    NotFound,
21    InvalidArg,
22    Other(std::io::Error, HRESULT),
23}
24
25impl Display for Error {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.write_str(match self {
28            Error::Virtual => "virtual folders have no path",
29            Error::NotFound => "not found",
30            Error::InvalidArg => "invalid arg",
31            Error::Other(_, _) => "other",
32        })
33    }
34}
35
36impl std::error::Error for Error {}
37
38const NOT_FOUND: HRESULT = 0x80070002u32 as i32;
39const CANNOT_FIND_PATH: HRESULT = 0x80070003u32 as i32;
40
41fn raw_known_folder_path(id: REFKNOWNFOLDERID) -> Result<PathBuf, Error> {
42    let mut ptr: PWSTR = null_mut();
43    let ret = unsafe { SHGetKnownFolderPath(id, 0, null_mut(), &mut ptr) };
44    let result = match ret {
45        S_OK => {
46            let len = unsafe { lstrlenW(ptr) } as usize;
47            let path = unsafe { std::slice::from_raw_parts(ptr, len) };
48            let os_str: OsString = OsString::from_wide(path);
49            Ok(PathBuf::from(os_str))
50        }
51        E_FAIL => Err(Error::Virtual),
52        E_INVALIDARG => Err(Error::InvalidArg),
53        NOT_FOUND | CANNOT_FIND_PATH => Err(Error::NotFound),
54        e => Err(Error::Other(std::io::Error::last_os_error(), e)),
55    };
56
57    // Docs say that even if the function fails, you need to free this.
58    unsafe { CoTaskMemFree(ptr as *mut _) };
59
60    result
61}
62
63/// Get a path for a given folder identifier.
64#[inline(always)]
65pub fn known_folder_path(id: FolderId) -> Result<PathBuf, Error> {
66    raw_known_folder_path(&FOLDER_IDS[id as usize])
67}
68
69/// Folder identifiers.
70#[non_exhaustive]
71pub enum FolderId {
72    NetworkFolder = 0,
73    ComputerFolder,
74    InternetFolder,
75    ControlPanelFolder,
76    PrintersFolder,
77    SyncManagerFolder,
78    SyncSetupFolder,
79    ConflictFolder,
80    SyncResultsFolder,
81    RecycleBinFolder,
82    ConnectionsFolder,
83    Fonts,
84    Desktop,
85    Startup,
86    Programs,
87    StartMenu,
88    Recent,
89    SendTo,
90    Documents,
91    Favorites,
92    NetHood,
93    PrintHood,
94    Templates,
95    CommonStartup,
96    CommonPrograms,
97    CommonStartMenu,
98    PublicDesktop,
99    ProgramData,
100    CommonTemplates,
101    PublicDocuments,
102    RoamingAppData,
103    LocalAppData,
104    LocalAppDataLow,
105    InternetCache,
106    Cookies,
107    History,
108    System,
109    SystemX86,
110    Windows,
111    Profile,
112    Pictures,
113    ProgramFilesX86,
114    ProgramFilesCommonX86,
115    ProgramFilesX64,
116    ProgramFilesCommonX64,
117    ProgramFiles,
118    ProgramFilesCommon,
119    UserProgramFiles,
120    UserProgramFilesCommon,
121    AdminTools,
122    CommonAdminTools,
123    Music,
124    Videos,
125    Ringtones,
126    PublicPictures,
127    PublicMusic,
128    PublicVideos,
129    PublicRingtones,
130    ResourceDir,
131    LocalizedResourcesDir,
132    CommonOEMLinks,
133    CDBurning,
134    UserProfiles,
135    Playlists,
136    SamplePlaylists,
137    SampleMusic,
138    SamplePictures,
139    SampleVideos,
140    PhotoAlbums,
141    Public,
142    ChangeRemovePrograms,
143    AppUpdates,
144    AddNewPrograms,
145    Downloads,
146    PublicDownloads,
147    SavedSearches,
148    QuickLaunch,
149    Contacts,
150    SidebarParts,
151    SidebarDefaultParts,
152    PublicGameTasks,
153    GameTasks,
154    SavedGames,
155    Games,
156    SearchMapi,
157    SearchCsc,
158    Links,
159    UsersFiles,
160    UsersLibraries,
161    SearchHome,
162    OriginalImages,
163    DocumentsLibrary,
164    MusicLibrary,
165    PicturesLibrary,
166    VideosLibrary,
167    RecordedTVLibrary,
168    HomeGroup,
169    HomeGroupCurrentUser,
170    DeviceMetadataStore,
171    Libraries,
172    PublicLibraries,
173    UserPinned,
174    ImplicitAppShortcuts,
175    AccountPictures,
176    PublicUserTiles,
177    AppsFolder,
178    StartMenuAllPrograms,
179    CommonStartMenuPlaces,
180    ApplicationShortcuts,
181    RoamingTiles,
182    RoamedTileImages,
183    Screenshots,
184    CameraRoll,
185    SkyDrive,
186    OneDrive,
187    SkyDriveDocuments,
188    SkyDrivePictures,
189    SkyDriveMusic,
190    SkyDriveCameraRoll,
191    SearchHistory,
192    SearchTemplates,
193    CameraRollLibrary,
194    SavedPictures,
195    SavedPicturesLibrary,
196    RetailDemo,
197    Device,
198    DevelopmentFiles,
199    Objects3D,
200    AppCaptures,
201    LocalDocuments,
202    LocalPictures,
203    LocalVideos,
204    LocalMusic,
205    LocalDownloads,
206    RecordedCalls,
207    AllAppMods,
208    CurrentAppMods,
209    AppDataDesktop,
210    AppDataDocuments,
211    AppDataFavorites,
212    AppDataProgramData,
213}
214
215static FOLDER_IDS: &[GUID] = &[
216    FOLDERID_NetworkFolder,
217    FOLDERID_ComputerFolder,
218    FOLDERID_InternetFolder,
219    FOLDERID_ControlPanelFolder,
220    FOLDERID_PrintersFolder,
221    FOLDERID_SyncManagerFolder,
222    FOLDERID_SyncSetupFolder,
223    FOLDERID_ConflictFolder,
224    FOLDERID_SyncResultsFolder,
225    FOLDERID_RecycleBinFolder,
226    FOLDERID_ConnectionsFolder,
227    FOLDERID_Fonts,
228    FOLDERID_Desktop,
229    FOLDERID_Startup,
230    FOLDERID_Programs,
231    FOLDERID_StartMenu,
232    FOLDERID_Recent,
233    FOLDERID_SendTo,
234    FOLDERID_Documents,
235    FOLDERID_Favorites,
236    FOLDERID_NetHood,
237    FOLDERID_PrintHood,
238    FOLDERID_Templates,
239    FOLDERID_CommonStartup,
240    FOLDERID_CommonPrograms,
241    FOLDERID_CommonStartMenu,
242    FOLDERID_PublicDesktop,
243    FOLDERID_ProgramData,
244    FOLDERID_CommonTemplates,
245    FOLDERID_PublicDocuments,
246    FOLDERID_RoamingAppData,
247    FOLDERID_LocalAppData,
248    FOLDERID_LocalAppDataLow,
249    FOLDERID_InternetCache,
250    FOLDERID_Cookies,
251    FOLDERID_History,
252    FOLDERID_System,
253    FOLDERID_SystemX86,
254    FOLDERID_Windows,
255    FOLDERID_Profile,
256    FOLDERID_Pictures,
257    FOLDERID_ProgramFilesX86,
258    FOLDERID_ProgramFilesCommonX86,
259    FOLDERID_ProgramFilesX64,
260    FOLDERID_ProgramFilesCommonX64,
261    FOLDERID_ProgramFiles,
262    FOLDERID_ProgramFilesCommon,
263    FOLDERID_UserProgramFiles,
264    FOLDERID_UserProgramFilesCommon,
265    FOLDERID_AdminTools,
266    FOLDERID_CommonAdminTools,
267    FOLDERID_Music,
268    FOLDERID_Videos,
269    FOLDERID_Ringtones,
270    FOLDERID_PublicPictures,
271    FOLDERID_PublicMusic,
272    FOLDERID_PublicVideos,
273    FOLDERID_PublicRingtones,
274    FOLDERID_ResourceDir,
275    FOLDERID_LocalizedResourcesDir,
276    FOLDERID_CommonOEMLinks,
277    FOLDERID_CDBurning,
278    FOLDERID_UserProfiles,
279    FOLDERID_Playlists,
280    FOLDERID_SamplePlaylists,
281    FOLDERID_SampleMusic,
282    FOLDERID_SamplePictures,
283    FOLDERID_SampleVideos,
284    FOLDERID_PhotoAlbums,
285    FOLDERID_Public,
286    FOLDERID_ChangeRemovePrograms,
287    FOLDERID_AppUpdates,
288    FOLDERID_AddNewPrograms,
289    FOLDERID_Downloads,
290    FOLDERID_PublicDownloads,
291    FOLDERID_SavedSearches,
292    FOLDERID_QuickLaunch,
293    FOLDERID_Contacts,
294    FOLDERID_SidebarParts,
295    FOLDERID_SidebarDefaultParts,
296    FOLDERID_PublicGameTasks,
297    FOLDERID_GameTasks,
298    FOLDERID_SavedGames,
299    FOLDERID_Games,
300    FOLDERID_SEARCH_MAPI,
301    FOLDERID_SEARCH_CSC,
302    FOLDERID_Links,
303    FOLDERID_UsersFiles,
304    FOLDERID_UsersLibraries,
305    FOLDERID_SearchHome,
306    FOLDERID_OriginalImages,
307    FOLDERID_DocumentsLibrary,
308    FOLDERID_MusicLibrary,
309    FOLDERID_PicturesLibrary,
310    FOLDERID_VideosLibrary,
311    FOLDERID_RecordedTVLibrary,
312    FOLDERID_HomeGroup,
313    FOLDERID_HomeGroupCurrentUser,
314    FOLDERID_DeviceMetadataStore,
315    FOLDERID_Libraries,
316    FOLDERID_PublicLibraries,
317    FOLDERID_UserPinned,
318    FOLDERID_ImplicitAppShortcuts,
319    FOLDERID_AccountPictures,
320    FOLDERID_PublicUserTiles,
321    FOLDERID_AppsFolder,
322    FOLDERID_StartMenuAllPrograms,
323    FOLDERID_CommonStartMenuPlaces,
324    FOLDERID_ApplicationShortcuts,
325    FOLDERID_RoamingTiles,
326    FOLDERID_RoamedTileImages,
327    FOLDERID_Screenshots,
328    FOLDERID_CameraRoll,
329    FOLDERID_SkyDrive,
330    FOLDERID_OneDrive,
331    FOLDERID_SkyDriveDocuments,
332    FOLDERID_SkyDrivePictures,
333    FOLDERID_SkyDriveMusic,
334    FOLDERID_SkyDriveCameraRoll,
335    FOLDERID_SearchHistory,
336    FOLDERID_SearchTemplates,
337    FOLDERID_CameraRollLibrary,
338    FOLDERID_SavedPictures,
339    FOLDERID_SavedPicturesLibrary,
340    FOLDERID_RetailDemo,
341    FOLDERID_Device,
342    FOLDERID_DevelopmentFiles,
343    FOLDERID_Objects3D,
344    FOLDERID_AppCaptures,
345    FOLDERID_LocalDocuments,
346    FOLDERID_LocalPictures,
347    FOLDERID_LocalVideos,
348    FOLDERID_LocalMusic,
349    FOLDERID_LocalDownloads,
350    FOLDERID_RecordedCalls,
351    FOLDERID_AllAppMods,
352    FOLDERID_CurrentAppMods,
353    FOLDERID_AppDataDesktop,
354    FOLDERID_AppDataDocuments,
355    FOLDERID_AppDataFavorites,
356    FOLDERID_AppDataProgramData,
357];
358
359#[cfg(test)]
360mod tests {
361    #[test]
362    fn all_ids() {
363        for (i, id) in super::FOLDER_IDS.iter().enumerate() {
364            let path = super::raw_known_folder_path(id);
365            match path {
366                Ok(path) => println!("{}: {}", i, path.display()),
367                Err(err) => println!("{}: {}", i, err),
368            }
369        }
370    }
371}