diff --git a/Cargo.toml b/Cargo.toml index f25132857..07c6e4a2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -181,7 +181,7 @@ walkdir = { version = "2.5.0" } which = { version = "7.0.0", features = ["regex"] } windows-registry = { version = "0.5.0" } windows-result = { version = "0.3.0" } -windows-sys = { version = "0.59.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Ioctl", "Win32_System_IO"] } +windows-sys = { version = "0.59.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Ioctl", "Win32_System_IO", "Win32_System_Registry"] } winsafe = { version = "0.0.23", features = ["kernel"] } wiremock = { version = "0.6.2" } xz2 = { version = "0.1.7" } diff --git a/crates/uv-python/src/windows_registry.rs b/crates/uv-python/src/windows_registry.rs index 7589c9e47..7b6ea2dd9 100644 --- a/crates/uv-python/src/windows_registry.rs +++ b/crates/uv-python/src/windows_registry.rs @@ -14,6 +14,7 @@ use uv_warnings::{warn_user, warn_user_once}; use windows_registry::{Key, Value, CURRENT_USER, HSTRING, LOCAL_MACHINE}; 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); @@ -32,9 +33,22 @@ pub(crate) struct WindowsPython { /// Find all Pythons registered in the Windows registry following PEP 514. pub(crate) fn registry_pythons() -> Result, windows_result::Error> { let mut registry_pythons = Vec::new(); - // Prefer `HKEY_CURRENT_USER` over `HKEY_LOCAL_MACHINE` - for root_key in [CURRENT_USER, LOCAL_MACHINE] { - let Ok(key_python) = root_key.open(r"Software\Python") else { + // 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 + // the registry (https://github.com/astral-sh/uv/issues/11217). To work around this, we manually + // request both 32-bit and 64-bit access. The flags have no effect on 32-bit + // (https://stackoverflow.com/a/12796797/3549270). + for (root_key, access_modifier) in [ + (CURRENT_USER, None), + (LOCAL_MACHINE, Some(KEY_WOW64_64KEY)), + (LOCAL_MACHINE, Some(KEY_WOW64_32KEY)), + ] { + let mut open_options = root_key.options(); + open_options.read(); + if let Some(access_modifier) = access_modifier { + open_options.access(access_modifier); + } + let Ok(key_python) = open_options.open(r"Software\Python") else { continue; }; for company in key_python.keys()? {