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
//! This crate provides access to Windows APIs for querying the location of standard
//! [standard Windows folders](https://msdn.microsoft.com/en-us/library/windows/desktop/bb776911.aspx)
//! on the current system.
//!
//! # Example
//!
//! ```
//! extern crate winfolder;
//!
//! use winfolder::Folder;
//!
//! # fn main() {
//! # let _ =
//! Folder::ProgramFilesX86.path()
//! # ;
//! # }
//! ```

#![cfg(windows)]

extern crate winapi;
extern crate shell32;
extern crate ole32;

#[macro_use]
extern crate guid;

pub mod id;

use id::*;

use std::ptr::null_mut;
use std::mem;
use std::ffi::OsString;
use std::slice;
use std::os::windows::ffi::OsStringExt;
use std::path::{Path, PathBuf};

use winapi::winnt::PWSTR;
use winapi::minwindef::MAX_PATH;
use shell32::SHGetKnownFolderPath;
use ole32::CoTaskMemFree;

/// Construct an `OsString` from a pointer to a wide-character string.
/// This is marked as unsafe because it can only be safely used on a
/// string that is known to come from a trusted API.
unsafe fn os_string_from_trusted_api(mut p: PWSTR) -> OsString {
    let mut s: OsString = OsString::with_capacity(MAX_PATH + 1);
    while *p != 0 {
        s.push(&OsString::from_wide(slice::from_raw_parts(p, 1)));
        p = p.offset(1);
    }
    s
}

/// Returns the path for a Windows
/// [known folder](https://msdn.microsoft.com/en-us/library/windows/desktop/bb776911.aspx)
/// based on that folder's GUID. Some standard known folder GUIDs can be found in
/// the `winfolder::id` module.
///
/// If the GUID does not represent a standard folder, this function
/// produces `None`.
///
/// This function provides the functionality of the standard Windows
/// [SHGetKnownFolderPath](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762188.aspx)
/// API.
pub fn known_path(guid: &guid::GUID) -> Option<PathBuf> {
    let string: OsString;
    unsafe {
        let mut path: PWSTR = null_mut();
        if SHGetKnownFolderPath(guid, 0, null_mut(), mem::transmute(&mut path)) != 0 {
            return None;
        }
        string = os_string_from_trusted_api(path);
        CoTaskMemFree(mem::transmute(path));
    }
    Some(Path::new(&string).to_path_buf())
}

/// Represents a standard Windows [known folder](https://msdn.microsoft.com/en-us/library/windows/desktop/bb776911.aspx).
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum Folder {
    /// The [`FOLDERID_LocalAppData`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx#folderid_localappdata)
    /// known folder.
    LocalAppData,

    /// The [`FOLDERID_ProgramData`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx#folderid_programdata)
    /// known folder.
    ProgramData,

    /// The [`FOLDERID_ProgramFiles`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx#folderid_programfiles)
    /// known folder.
    ProgramFiles,

    /// The [`FOLDERID_ProgramFilesX64`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx#folderid_programfilesx64)
    /// known folder.
    ProgramFilesX64,

    /// The [`FOLDERID_ProgramFilesX86`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx#folderid_programfilesx86)
    /// known folder.
    ProgramFilesX86
}

impl Folder {

    /// Returns the Windows GUID associated with this known folder.
    pub fn id(self) -> guid::GUID {
        match self {
            Folder::LocalAppData    => LOCAL_APP_DATA,
            Folder::ProgramData     => PROGRAM_DATA,
            Folder::ProgramFiles    => PROGRAM_FILES,
            Folder::ProgramFilesX64 => PROGRAM_FILES_X64,
            Folder::ProgramFilesX86 => PROGRAM_FILES_X86
        }
    }

    /// Returns the path for this known folder on this system.
    ///
    /// This function provides the functionality of the standard Windows
    /// [SHGetKnownFolderPath](https://msdn.microsoft.com/en-us/library/windows/desktop/bb762188.aspx)
    /// API.
    pub fn path(self) -> PathBuf {
        known_path(&self.id()).expect("Folder::path")
    }

}

#[cfg(test)]
mod tests {
    use super::{known_path, Folder};
    use super::id;
    use std::path::Path;

    #[test]
    fn it_works() {
        assert_eq!(known_path(&id::PROGRAM_FILES_X86), Some(Path::new(r"C:\Program Files (x86)").to_path_buf()));
        assert_eq!(Folder::ProgramFilesX86.path(), Path::new(r"C:\Program Files (x86)").to_path_buf());
    }
}