Skip to content

Commit

Permalink
windows-sys: bump & replace SHGetFolderPathW with SHGetKnownFolderPath
Browse files Browse the repository at this point in the history
  • Loading branch information
utkarshgupta137 committed Feb 27, 2025
1 parent 6cc955a commit 7452817
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ home = "0.5"

# We should keep this in sync with the `home` crate.
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.48", features = ["Win32_Foundation", "Win32_UI_Shell"] }
windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_System_Com", "Win32_UI_Shell"] }
2 changes: 1 addition & 1 deletion src/app_strategy/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
/// This strategy follows Windows’ conventions. It seems that all Windows GUI apps, and some command-line ones follow this pattern. The specification is available [here](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
///
/// This initial example removes all the relevant environment variables to show the strategy’s use of the:
/// - (on Windows) SHGetFolderPathW API.
/// - (on Windows) SHGetKnownFolderPath API.
/// - (on non-Windows) Windows default directories.
///
/// ```
Expand Down
53 changes: 34 additions & 19 deletions src/base_strategy/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::HomeDirError;
/// This strategy follows Windows’ conventions. It seems that all Windows GUI apps, and some command-line ones follow this pattern. The specification is available [here](https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid).
///
/// This initial example removes all the relevant environment variables to show the strategy’s use of the:
/// - (on Windows) SHGetFolderPathW API.
/// - (on Windows) SHGetKnownFolderPath API.
/// - (on non-Windows) Windows default directories.
///
/// ```
Expand Down Expand Up @@ -108,8 +108,6 @@ pub struct Windows {
home_dir: PathBuf,
}

// Ref: /~https://github.com/rust-lang/cargo/blob/home-0.5.5/crates/home/src/windows.rs
// We should keep this code in sync with the above.
impl Windows {
/// Create a new Windows BaseStrategy
pub fn new() -> Result<Self, HomeDirError> {
Expand All @@ -125,39 +123,56 @@ impl Windows {
.or_else(|| Self::dir_crt(env))
}

#[cfg(all(windows, target_vendor = "uwp"))]
// Ref: /~https://github.com/rust-lang/cargo/blob/home-0.5.11/crates/home/src/windows.rs
// We should keep this code in sync with the above.
#[cfg(all(windows, not(target_vendor = "uwp")))]
fn dir_crt(env: &'static str) -> Option<PathBuf> {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;

use windows_sys::Win32::Foundation::{MAX_PATH, S_OK};
use windows_sys::Win32::UI::Shell::{SHGetFolderPathW, CSIDL_APPDATA, CSIDL_LOCAL_APPDATA};

let csidl = match env {
"APPDATA" => CSIDL_APPDATA,
"LOCALAPPDATA" => CSIDL_LOCAL_APPDATA,
_ => return None,
use std::ptr;
use std::slice;

use windows_sys::Win32::Foundation::S_OK;
use windows_sys::Win32::System::Com::CoTaskMemFree;
use windows_sys::Win32::UI::Shell::{
FOLDERID_LocalAppData, FOLDERID_RoamingAppData, SHGetKnownFolderPath,
KF_FLAG_DONT_VERIFY,
};

extern "C" {
fn wcslen(buf: *const u16) -> usize;
}

let folder_id = match env {
"APPDATA" => FOLDERID_RoamingAppData,
"LOCALAPPDATA" => FOLDERID_LocalAppData,
_ => return None,
};

unsafe {
let mut path: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
match SHGetFolderPathW(0, csidl, 0, 0, path.as_mut_ptr()) {
let mut path = ptr::null_mut();
match SHGetKnownFolderPath(
&folder_id,
KF_FLAG_DONT_VERIFY as u32,
std::ptr::null_mut(),
&mut path,
) {
S_OK => {
let len = wcslen(path.as_ptr());
path.set_len(len);
let s = OsString::from_wide(&path);
let path_slice = slice::from_raw_parts(path, wcslen(path));
let s = OsString::from_wide(path_slice);
CoTaskMemFree(path.cast());
Some(PathBuf::from(s))
}
_ => None,
_ => {
// Free any allocated memory even on failure. A null ptr is a no-op for `CoTaskMemFree`.
CoTaskMemFree(path.cast());
None
}
}
}
}

#[cfg(not(all(windows, target_vendor = "uwp")))]
#[cfg(not(all(windows, not(target_vendor = "uwp"))))]
fn dir_crt(_env: &'static str) -> Option<PathBuf> {
None
}
Expand Down

0 comments on commit 7452817

Please sign in to comment.