Use the windows crate facade consistently (#15737)

The initial motivation for this change was that we were using both the
`windows`, the `window_sys` and the `windows_core` crate in various
places. These crates have slightly unconventional versioning scheme
where there is a large workspace with the same version in general, but
only some crates get breaking releases when a new breaking release
happens, the others stay on the previous breaking version. The `windows`
crate is a shim for all three of them, with a single version. This
simplifies handling the versions.

Using `windows` over `windows_sys` has the advantage of a higher level
error interface, we now get a `Result` for all windows API calls instead
of C-style int-returns and get-last-error calls. This makes the
uv-keyring crate more resilient.

We keep using the `windows_registry` crate, which provides a higher
level interface to windows registry access.
This commit is contained in:
konsti 2025-09-09 17:07:14 +02:00 committed by GitHub
parent 12764df8b2
commit cd49e1d11f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 123 additions and 130 deletions

View file

@ -37,7 +37,6 @@ rustix = { workspace = true }
backon = { workspace = true }
junction = { workspace = true }
windows = { workspace = true }
windows-core = { workspace = true }
[features]
default = []

View file

@ -5,7 +5,7 @@ use std::path::Path;
fn get_binary_type(path: &Path) -> windows::core::Result<u32> {
use std::os::windows::ffi::OsStrExt;
use windows::Win32::Storage::FileSystem::GetBinaryTypeW;
use windows_core::PCWSTR;
use windows::core::PCWSTR;
// References:
// https://github.com/denoland/deno/blob/01a6379505712be34ebf2cdc874fa7f54a6e9408/runtime/permissions/which.rs#L131-L154

View file

@ -18,7 +18,7 @@ apple-native = ["dep:security-framework"]
## Use the secret-service on *nix.
secret-service = ["dep:secret-service"]
## Use the built-in credential store on Windows
windows-native = ["dep:windows-sys", "dep:byteorder"]
windows-native = ["dep:windows", "dep:byteorder"]
[dependencies]
async-trait = { workspace = true }
@ -33,7 +33,7 @@ secret-service = { workspace = true, features = ["rt-tokio-crypto-rust"], option
[target.'cfg(target_os = "windows")'.dependencies]
byteorder = { workspace = true, optional = true }
windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_Security_Credentials"], optional = true }
windows = { workspace = true, features = ["Win32_Foundation", "Win32_Security_Credentials"], optional = true }
zeroize = { workspace = true }
[dev-dependencies]

View file

@ -44,15 +44,16 @@ use byteorder::{ByteOrder, LittleEndian};
use std::collections::HashMap;
use std::iter::once;
use std::str;
use windows_sys::Win32::Foundation::{
use windows::Win32::Foundation::{
ERROR_BAD_USERNAME, ERROR_INVALID_FLAGS, ERROR_INVALID_PARAMETER, ERROR_NO_SUCH_LOGON_SESSION,
ERROR_NOT_FOUND, FILETIME, GetLastError,
ERROR_NOT_FOUND, FILETIME, WIN32_ERROR,
};
use windows_sys::Win32::Security::Credentials::{
use windows::Win32::Security::Credentials::{
CRED_FLAGS, CRED_MAX_CREDENTIAL_BLOB_SIZE, CRED_MAX_GENERIC_TARGET_NAME_LENGTH,
CRED_MAX_STRING_LENGTH, CRED_MAX_USERNAME_LENGTH, CRED_PERSIST_ENTERPRISE, CRED_TYPE_GENERIC,
CREDENTIAL_ATTRIBUTEW, CREDENTIALW, CredDeleteW, CredFree, CredReadW, CredWriteW,
};
use windows::core::PWSTR;
use zeroize::Zeroize;
/// The representation of a Windows Generic credential.
@ -160,15 +161,13 @@ impl CredentialApi for WinCredential {
/// credential in the store.
async fn delete_credential(&self) -> Result<()> {
self.validate_attributes(None, None)?;
let target_name = to_wstr(&self.target_name);
let mut target_name = to_wstr(&self.target_name);
let cred_type = CRED_TYPE_GENERIC;
crate::blocking::spawn_blocking(move || {
// SAFETY: Calling Windows API
if unsafe { CredDeleteW(target_name.as_ptr(), cred_type, 0) } != 0 {
Ok(())
} else {
// SAFETY: Calling Windows API
Err(Error::from_win32_code(unsafe { GetLastError() }).into())
unsafe {
CredDeleteW(PWSTR(target_name.as_mut_ptr()), cred_type, None)
.map_err(|err| Error(err).into())
}
})
.await
@ -264,22 +263,20 @@ impl WinCredential {
let credential = CREDENTIALW {
Flags: flags,
Type: cred_type,
TargetName: target_name.as_mut_ptr(),
Comment: comment.as_mut_ptr(),
TargetName: PWSTR(target_name.as_mut_ptr()),
Comment: PWSTR(comment.as_mut_ptr()),
LastWritten: last_written,
CredentialBlobSize: blob_len,
CredentialBlob: blob.as_mut_ptr(),
Persist: persist,
AttributeCount: attribute_count,
Attributes: attributes,
TargetAlias: target_alias.as_mut_ptr(),
UserName: username.as_mut_ptr(),
TargetAlias: PWSTR(target_alias.as_mut_ptr()),
UserName: PWSTR(username.as_mut_ptr()),
};
// SAFETY: Calling Windows API
let result = match unsafe { CredWriteW(&raw const credential, 0) } {
0 => Err(Error::from_win32_code(unsafe { GetLastError() }).into()),
_ => Ok(()),
};
let result =
unsafe { CredWriteW(&raw const credential, 0) }.map_err(|err| Error(err).into());
// erase the copy of the secret
blob.zeroize();
result
@ -300,31 +297,33 @@ impl WinCredential {
T: Send + 'static,
{
self.validate_attributes(None, None)?;
let target_name = to_wstr(&self.target_name);
let mut target_name = to_wstr(&self.target_name);
crate::blocking::spawn_blocking(move || {
let mut p_credential = std::ptr::null_mut();
// at this point, p_credential is just a pointer to nowhere.
// The allocation happens in the `CredReadW` call below.
let cred_type = CRED_TYPE_GENERIC;
// SAFETY: Calling windows API
let result =
unsafe { CredReadW(target_name.as_ptr(), cred_type, 0, &raw mut p_credential) };
if result == 0 {
// `CredReadW` failed, so no allocation has been done, so no free needs to be done
Err(Error::from_win32_code(unsafe { GetLastError() }).into())
} else {
// SAFETY: `CredReadW` succeeded, so p_credential points at an allocated credential. Apply
// the passed extractor function to it.
let ref_cred: &mut CREDENTIALW = unsafe { &mut *p_credential };
let result = f(ref_cred);
// Finally, we erase the secret and free the allocated credential.
erase_secret(ref_cred);
let p_credential = p_credential;
// SAFETY: `CredReadW` succeeded, so p_credential points at an allocated credential.
// Free the allocation.
unsafe { CredFree(p_credential.cast()) }
result
unsafe {
CredReadW(
PWSTR(target_name.as_mut_ptr()),
cred_type,
None,
&raw mut p_credential,
)
}
.map_err(Error)?;
// SAFETY: `CredReadW` succeeded, so p_credential points at an allocated credential. Apply
// the passed extractor function to it.
let ref_cred: &mut CREDENTIALW = unsafe { &mut *p_credential };
let result = f(ref_cred);
// Finally, we erase the secret and free the allocated credential.
erase_secret(ref_cred);
let p_credential = p_credential;
// SAFETY: `CredReadW` succeeded, so p_credential points at an allocated credential.
// Free the allocation.
unsafe { CredFree(p_credential.cast()) }
result
})
.await
}
@ -332,10 +331,10 @@ impl WinCredential {
#[allow(clippy::unnecessary_wraps)]
fn extract_credential(w_credential: &CREDENTIALW) -> Result<Self> {
Ok(Self {
username: unsafe { from_wstr(w_credential.UserName) },
target_name: unsafe { from_wstr(w_credential.TargetName) },
target_alias: unsafe { from_wstr(w_credential.TargetAlias) },
comment: unsafe { from_wstr(w_credential.Comment) },
username: unsafe { from_wstr(w_credential.UserName.as_ptr()) },
target_name: unsafe { from_wstr(w_credential.TargetName.as_ptr()) },
target_alias: unsafe { from_wstr(w_credential.TargetAlias.as_ptr()) },
comment: unsafe { from_wstr(w_credential.Comment.as_ptr()) },
})
}
@ -475,34 +474,40 @@ unsafe fn from_wstr(ws: *const u16) -> String {
/// Windows error codes are `DWORDS` which are 32-bit unsigned ints.
#[derive(Debug)]
pub struct Error(pub u32);
pub struct Error(windows::core::Error);
impl Error {
/// Create a Windows error from a Win32 error code.
pub fn from_win32_code(code: u32) -> Self {
Self(code)
impl From<WIN32_ERROR> for Error {
fn from(error: WIN32_ERROR) -> Self {
Self(windows::core::Error::from(error))
}
}
impl From<Error> for ErrorCode {
fn from(err: Error) -> Self {
match err.0 {
ERROR_NOT_FOUND => Self::NoEntry,
ERROR_NO_SUCH_LOGON_SESSION => Self::NoStorageAccess(Box::new(err)),
_ => Self::PlatformFailure(Box::new(err)),
if err.0 == ERROR_NOT_FOUND.into() {
Self::NoEntry
} else if err.0 == ERROR_NO_SUCH_LOGON_SESSION.into() {
Self::NoStorageAccess(Box::new(err))
} else {
Self::PlatformFailure(Box::new(err))
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.0 {
ERROR_NO_SUCH_LOGON_SESSION => write!(f, "Windows ERROR_NO_SUCH_LOGON_SESSION"),
ERROR_NOT_FOUND => write!(f, "Windows ERROR_NOT_FOUND"),
ERROR_BAD_USERNAME => write!(f, "Windows ERROR_BAD_USERNAME"),
ERROR_INVALID_FLAGS => write!(f, "Windows ERROR_INVALID_FLAGS"),
ERROR_INVALID_PARAMETER => write!(f, "Windows ERROR_INVALID_PARAMETER"),
err => write!(f, "Windows error code {err}"),
if self.0 == ERROR_NO_SUCH_LOGON_SESSION.into() {
write!(f, "Windows ERROR_NO_SUCH_LOGON_SESSION")
} else if self.0 == ERROR_NOT_FOUND.into() {
write!(f, "Windows ERROR_NOT_FOUND")
} else if self.0 == ERROR_BAD_USERNAME.into() {
write!(f, "Windows ERROR_BAD_USERNAME")
} else if self.0 == ERROR_INVALID_FLAGS.into() {
write!(f, "Windows ERROR_INVALID_FLAGS")
} else if self.0 == ERROR_INVALID_PARAMETER.into() {
write!(f, "Windows ERROR_INVALID_PARAMETER")
} else {
write!(f, "Windows error code {}", self.0)
}
}
}
@ -545,18 +550,18 @@ mod tests {
let attribute_count = 0;
let attributes: *mut CREDENTIAL_ATTRIBUTEW = std::ptr::null_mut();
CREDENTIALW {
Flags: 0,
Flags: CRED_FLAGS(0),
Type: CRED_TYPE_GENERIC,
TargetName: std::ptr::null_mut(),
Comment: std::ptr::null_mut(),
TargetName: PWSTR::null(),
Comment: PWSTR::null(),
LastWritten: last_written,
CredentialBlobSize: password.len() as u32,
CredentialBlob: password.as_mut_ptr(),
Persist: CRED_PERSIST_ENTERPRISE,
AttributeCount: attribute_count,
Attributes: attributes,
TargetAlias: std::ptr::null_mut(),
UserName: std::ptr::null_mut(),
TargetAlias: PWSTR::null(),
UserName: PWSTR::null(),
}
}
// the first malformed sequence can't be UTF-16 because it has an odd number of bytes.

View file

@ -70,8 +70,7 @@ once_cell = { workspace = true }
[target.'cfg(target_os = "windows")'.dependencies]
windows-registry = { workspace = true }
windows-result = { workspace = true }
windows-sys = { workspace = true }
windows = { workspace = true }
[dev-dependencies]
anyhow = { workspace = true }

View file

@ -8,9 +8,6 @@ use std::{env, io, iter};
use std::{path::Path, path::PathBuf, str::FromStr};
use thiserror::Error;
use tracing::{debug, instrument, trace};
use uv_preview::Preview;
use which::{which, which_all};
use uv_cache::Cache;
use uv_fs::Simplified;
use uv_fs::which::is_executable;
@ -18,8 +15,10 @@ use uv_pep440::{
LowerBound, Prerelease, UpperBound, Version, VersionSpecifier, VersionSpecifiers,
release_specifiers_to_ranges,
};
use uv_preview::Preview;
use uv_static::EnvVars;
use uv_warnings::warn_user_once;
use which::{which, which_all};
use crate::downloads::{PlatformRequest, PythonDownloadRequest};
use crate::implementation::ImplementationName;
@ -251,7 +250,7 @@ pub enum Error {
#[cfg(windows)]
#[error("Failed to query installed Python versions from the Windows registry")]
RegistryError(#[from] windows_result::Error),
RegistryError(#[from] windows::core::Error),
/// An invalid version request was given
#[error("Invalid version request: {0}")]
@ -1502,13 +1501,15 @@ fn warn_on_unsupported_python(interpreter: &Interpreter) {
pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
use std::os::windows::fs::MetadataExt;
use std::os::windows::prelude::OsStrExt;
use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Storage::FileSystem::{
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::Storage::FileSystem::{
CreateFileW, FILE_ATTRIBUTE_REPARSE_POINT, FILE_FLAG_BACKUP_SEMANTICS,
FILE_FLAG_OPEN_REPARSE_POINT, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_MODE, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
OPEN_EXISTING,
};
use windows_sys::Win32::System::IO::DeviceIoControl;
use windows_sys::Win32::System::Ioctl::FSCTL_GET_REPARSE_POINT;
use windows::Win32::System::IO::DeviceIoControl;
use windows::Win32::System::Ioctl::FSCTL_GET_REPARSE_POINT;
use windows::core::PCWSTR;
// The path must be absolute.
if !path.is_absolute() {
@ -1553,7 +1554,7 @@ pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
let Ok(md) = fs_err::symlink_metadata(path) else {
return false;
};
if md.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT == 0 {
if md.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT.0 == 0 {
return false;
}
@ -1567,19 +1568,19 @@ pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
#[allow(unsafe_code)]
let reparse_handle = unsafe {
CreateFileW(
path_encoded.as_mut_ptr(),
PCWSTR(path_encoded.as_mut_ptr()),
0,
0,
std::ptr::null_mut(),
FILE_SHARE_MODE(0),
None,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
std::ptr::null_mut(),
None,
)
};
if reparse_handle == INVALID_HANDLE_VALUE {
let Ok(reparse_handle) = reparse_handle else {
return false;
}
};
let mut buf = [0u16; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize];
let mut bytes_returned = 0;
@ -1590,19 +1591,20 @@ pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
DeviceIoControl(
reparse_handle,
FSCTL_GET_REPARSE_POINT,
std::ptr::null_mut(),
None,
0,
buf.as_mut_ptr().cast(),
Some(buf.as_mut_ptr().cast()),
buf.len() as u32 * 2,
&raw mut bytes_returned,
std::ptr::null_mut(),
) != 0
Some(&raw mut bytes_returned),
None,
)
.is_ok()
};
// SAFETY: The handle is valid.
#[allow(unsafe_code)]
unsafe {
CloseHandle(reparse_handle);
let _ = CloseHandle(reparse_handle);
}
// If the operation failed, assume it's not a reparse point.

View file

@ -34,7 +34,7 @@ use crate::{
};
#[cfg(windows)]
use windows_sys::Win32::Foundation::{APPMODEL_ERROR_NO_PACKAGE, ERROR_CANT_ACCESS_FILE};
use windows::Win32::Foundation::{APPMODEL_ERROR_NO_PACKAGE, ERROR_CANT_ACCESS_FILE, WIN32_ERROR};
/// A Python executable and its associated platform markers.
#[derive(Debug, Clone)]
@ -928,8 +928,10 @@ impl InterpreterInfo {
_ => {}
}
#[cfg(windows)]
if let Some(APPMODEL_ERROR_NO_PACKAGE | ERROR_CANT_ACCESS_FILE) =
err.raw_os_error().and_then(|code| u32::try_from(code).ok())
if let Some(APPMODEL_ERROR_NO_PACKAGE | ERROR_CANT_ACCESS_FILE) = err
.raw_os_error()
.and_then(|code| u32::try_from(code).ok())
.map(WIN32_ERROR)
{
// These error codes are returned if the Python interpreter is a corrupt MSIX
// package, which we want to differentiate from a typical spawn failure.

View file

@ -15,7 +15,7 @@ use thiserror::Error;
use tracing::{debug, warn};
use uv_preview::{Preview, PreviewFeatures};
#[cfg(windows)]
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
use windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
use uv_fs::{LockedFile, Simplified, replace_symlink, symlink_or_copy_file};
use uv_platform::{Error as PlatformError, Os};
@ -857,7 +857,7 @@ impl PythonMinorVersionLink {
.is_ok_and(|metadata| {
// Check that this is a reparse point, which indicates this
// is a symlink or junction.
(metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT) != 0
(metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT.0) != 0
})
}
}

View file

@ -12,13 +12,10 @@ use thiserror::Error;
use tracing::debug;
use uv_platform::Arch;
use uv_warnings::{warn_user, warn_user_once};
use windows::Win32::Foundation::ERROR_FILE_NOT_FOUND;
use windows::Win32::System::Registry::{KEY_WOW64_32KEY, KEY_WOW64_64KEY};
use windows::core::HRESULT;
use windows_registry::{CURRENT_USER, HSTRING, Key, LOCAL_MACHINE, Value};
use windows_result::HRESULT;
use windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND;
use windows_sys::Win32::System::Registry::{KEY_WOW64_32KEY, KEY_WOW64_64KEY};
/// Code returned when the registry key doesn't exist.
const ERROR_NOT_FOUND: HRESULT = HRESULT::from_win32(ERROR_FILE_NOT_FOUND);
/// A Python interpreter found in the Windows registry through PEP 514 or from a known Microsoft
/// Store path.
@ -32,7 +29,7 @@ pub(crate) struct WindowsPython {
}
/// Find all Pythons registered in the Windows registry following PEP 514.
pub(crate) fn registry_pythons() -> Result<Vec<WindowsPython>, windows_result::Error> {
pub(crate) fn registry_pythons() -> Result<Vec<WindowsPython>, windows::core::Error> {
let mut registry_pythons = Vec::new();
// Prefer `HKEY_CURRENT_USER` over `HKEY_LOCAL_MACHINE`.
// By default, a 64-bit program does not see a 32-bit global (HKLM) installation of Python in
@ -47,7 +44,7 @@ pub(crate) fn registry_pythons() -> Result<Vec<WindowsPython>, windows_result::E
let mut open_options = root_key.options();
open_options.read();
if let Some(access_modifier) = access_modifier {
open_options.access(access_modifier);
open_options.access(access_modifier.0);
}
let Ok(key_python) = open_options.open(r"Software\Python") else {
continue;
@ -131,7 +128,7 @@ pub enum ManagedPep514Error {
#[error("Windows has an unknown pointer width for arch: `{_0}`")]
InvalidPointerSize(Arch),
#[error("Failed to write registry entry: {0}")]
WriteError(#[from] windows_result::Error),
WriteError(#[from] windows::core::Error),
}
/// Register a managed Python installation in the Windows registry following PEP 514.
@ -216,7 +213,7 @@ pub fn remove_registry_entry<'a>(
if all {
debug!("Removing registry key HKCU:\\{}", astral_key);
if let Err(err) = CURRENT_USER.remove_tree(&astral_key) {
if err.code() == ERROR_NOT_FOUND {
if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) {
debug!("No registry entries to remove, no registry key {astral_key}");
} else {
warn_user!("Failed to clear registry entries under {astral_key}: {err}");
@ -230,7 +227,7 @@ pub fn remove_registry_entry<'a>(
let python_entry = format!("{astral_key}\\{python_tag}");
debug!("Removing registry key HKCU:\\{}", python_entry);
if let Err(err) = CURRENT_USER.remove_tree(&python_entry) {
if err.code() == ERROR_NOT_FOUND {
if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) {
debug!(
"No registry entries to remove for {}, no registry key {}",
installation.key(),
@ -256,7 +253,7 @@ pub fn remove_orphan_registry_entries(installations: &[ManagedPythonInstallation
let astral_key = format!("Software\\Python\\{COMPANY_KEY}");
let key = match CURRENT_USER.open(&astral_key) {
Ok(subkeys) => subkeys,
Err(err) if err.code() == ERROR_NOT_FOUND => {
Err(err) if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) => {
return;
}
Err(err) => {
@ -268,7 +265,7 @@ pub fn remove_orphan_registry_entries(installations: &[ManagedPythonInstallation
// Separate assignment since `keys()` creates a borrow.
let subkeys = match key.keys() {
Ok(subkeys) => subkeys,
Err(err) if err.code() == ERROR_NOT_FOUND => {
Err(err) if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) => {
return;
}
Err(err) => {
@ -284,7 +281,7 @@ pub fn remove_orphan_registry_entries(installations: &[ManagedPythonInstallation
let python_entry = format!("{astral_key}\\{subkey}");
debug!("Removing orphan registry key HKCU:\\{}", python_entry);
if let Err(err) = CURRENT_USER.remove_tree(&python_entry) {
if err.code() == ERROR_NOT_FOUND {
if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) {
continue;
}
// TODO(konsti): We don't have an installation key here.

View file

@ -24,9 +24,8 @@ tracing = { workspace = true }
nix = { workspace = true }
[target.'cfg(windows)'.dependencies]
windows = { workspace = true }
windows-registry = { workspace = true }
windows-result = { workspace = true }
windows-sys = { workspace = true }
[dev-dependencies]
fs-err = { workspace = true }

View file

@ -8,9 +8,9 @@ use std::path::Path;
use anyhow::Context;
use tracing::warn;
use windows::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_INVALID_DATA};
use windows::core::HRESULT;
use windows_registry::{CURRENT_USER, HSTRING};
use windows_result::HRESULT;
use windows_sys::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_INVALID_DATA};
use uv_static::EnvVars;
@ -60,13 +60,11 @@ fn get_windows_path_var() -> anyhow::Result<Option<HSTRING>> {
let reg_value = environment.get_hstring(EnvVars::PATH);
match reg_value {
Ok(reg_value) => Ok(Some(reg_value)),
Err(err) if err.code() == HRESULT::from_win32(ERROR_INVALID_DATA) => {
Err(err) if err.code() == HRESULT::from(ERROR_INVALID_DATA) => {
warn!("`HKEY_CURRENT_USER\\Environment\\PATH` is a non-string");
Ok(None)
}
Err(err) if err.code() == HRESULT::from_win32(ERROR_FILE_NOT_FOUND) => {
Ok(Some(HSTRING::new()))
}
Err(err) if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) => Ok(Some(HSTRING::new())),
Err(err) => Err(err.into()),
}
}

View file

@ -119,7 +119,6 @@ zip = { workspace = true }
arrayvec = { workspace = true }
self-replace = { workspace = true }
windows = { workspace = true }
windows-result = { workspace = true }
[dev-dependencies]
assert_cmd = { workspace = true }

View file

@ -54,11 +54,11 @@ enum ExceptionSafeStderr {
}
impl ExceptionSafeStderr {
fn new() -> Result<Self, windows_result::Error> {
fn new() -> Result<Self, windows::core::Error> {
// SAFETY: winapi call, no interesting parameters
let handle = unsafe { GetStdHandle(STD_ERROR_HANDLE) }?;
if handle.is_invalid() {
return Err(windows_result::Error::empty());
return Err(windows::core::Error::empty());
}
let mut mode = CONSOLE_MODE::default();
// SAFETY: winapi calls, no interesting parameters
@ -73,7 +73,7 @@ impl ExceptionSafeStderr {
}
}
fn write_winerror(&mut self, s: &str) -> Result<(), windows_result::Error> {
fn write_winerror(&mut self, s: &str) -> Result<(), windows::core::Error> {
match self {
Self::WriteConsole(handle) => {
// According to comments in the ReactOS source, NT's behavior is that writes of 80