partially fix sys.getwindowsversion() (#5595)

This commit is contained in:
Ashwin Naren
2025-03-13 19:38:35 -07:00
committed by GitHub
parent 8484bfa2e0
commit 8e22c399df
2 changed files with 91 additions and 11 deletions

View File

@@ -81,7 +81,7 @@ if sys.platform.startswith("win"):
0x00000100 | 0x00000001 | 0x00000020 | 0x00002000 | 0x00000010 | 0x00008000 | 0x00020000 0x00000100 | 0x00000001 | 0x00000020 | 0x00002000 | 0x00000010 | 0x00008000 | 0x00020000
# We really can't test if the results are correct, so it just checks for meaningful value # We really can't test if the results are correct, so it just checks for meaningful value
assert winver.major > 0 assert winver.major > 6
assert winver.minor >= 0 assert winver.minor >= 0
assert winver.build > 0 assert winver.build > 0
assert winver.platform == 2 assert winver.platform == 2
@@ -91,8 +91,8 @@ if sys.platform.startswith("win"):
# XXX if platform_version is implemented correctly, this'll break on compatiblity mode or a build without manifest # XXX if platform_version is implemented correctly, this'll break on compatiblity mode or a build without manifest
# these fields can mismatch in CPython # these fields can mismatch in CPython
# assert winver.major == winver.platform_version[0] assert winver.major == winver.platform_version[0]
# assert winver.minor == winver.platform_version[1] assert winver.minor == winver.platform_version[1]
# assert winver.build == winver.platform_version[2] # assert winver.build == winver.platform_version[2]
# test int_max_str_digits getter and setter # test int_max_str_digits getter and setter

View File

@@ -22,12 +22,23 @@ mod sys {
vm::{Settings, VirtualMachine}, vm::{Settings, VirtualMachine},
}; };
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
#[cfg(windows)]
use std::os::windows::ffi::OsStrExt;
use std::{ use std::{
env::{self, VarError}, env::{self, VarError},
path, path,
sync::atomic::Ordering, sync::atomic::Ordering,
}; };
#[cfg(windows)]
use windows_sys::Win32::{
Foundation::MAX_PATH,
Storage::FileSystem::{
GetFileVersionInfoSizeW, GetFileVersionInfoW, VS_FIXEDFILEINFO, VerQueryValueW,
},
System::LibraryLoader::{GetModuleFileNameW, GetModuleHandleW},
};
// not the same as CPython (e.g. rust's x86_x64-unknown-linux-gnu is just x86_64-linux-gnu) // not the same as CPython (e.g. rust's x86_x64-unknown-linux-gnu is just x86_64-linux-gnu)
// but hopefully that's just an implementation detail? TODO: copy CPython's multiarch exactly, // but hopefully that's just an implementation detail? TODO: copy CPython's multiarch exactly,
// https://github.com/python/cpython/blob/3.8/configure.ac#L725 // https://github.com/python/cpython/blob/3.8/configure.ac#L725
@@ -485,6 +496,78 @@ mod sys {
vm.trace_func.borrow().clone() vm.trace_func.borrow().clone()
} }
#[cfg(windows)]
fn get_kernel32_version() -> std::io::Result<(u32, u32, u32)> {
unsafe {
// Create a wide string for "kernel32.dll"
let module_name: Vec<u16> = std::ffi::OsStr::new("kernel32.dll")
.encode_wide()
.chain(Some(0))
.collect();
let h_kernel32 = GetModuleHandleW(module_name.as_ptr());
if h_kernel32.is_null() {
return Err(std::io::Error::last_os_error());
}
// Prepare a buffer for the module file path
let mut kernel32_path = [0u16; MAX_PATH as usize];
let len = GetModuleFileNameW(
h_kernel32,
kernel32_path.as_mut_ptr(),
kernel32_path.len() as u32,
);
if len == 0 {
return Err(std::io::Error::last_os_error());
}
// Get the size of the version information block
let verblock_size =
GetFileVersionInfoSizeW(kernel32_path.as_ptr(), std::ptr::null_mut());
if verblock_size == 0 {
return Err(std::io::Error::last_os_error());
}
// Allocate a buffer to hold the version information
let mut verblock = vec![0u8; verblock_size as usize];
if GetFileVersionInfoW(
kernel32_path.as_ptr(),
0,
verblock_size,
verblock.as_mut_ptr() as *mut _,
) == 0
{
return Err(std::io::Error::last_os_error());
}
// Prepare an empty sub-block string (L"") as required by VerQueryValueW
let sub_block: Vec<u16> = std::ffi::OsStr::new("")
.encode_wide()
.chain(Some(0))
.collect();
let mut ffi_ptr: *mut VS_FIXEDFILEINFO = std::ptr::null_mut();
let mut ffi_len: u32 = 0;
if VerQueryValueW(
verblock.as_ptr() as *const _,
sub_block.as_ptr(),
&mut ffi_ptr as *mut *mut VS_FIXEDFILEINFO as *mut *mut _,
&mut ffi_len as *mut u32,
) == 0
|| ffi_ptr.is_null()
{
return Err(std::io::Error::last_os_error());
}
// Extract the version numbers from the VS_FIXEDFILEINFO structure.
let ffi = *ffi_ptr;
let real_major = (ffi.dwProductVersionMS >> 16) & 0xFFFF;
let real_minor = ffi.dwProductVersionMS & 0xFFFF;
let real_build = (ffi.dwProductVersionLS >> 16) & 0xFFFF;
Ok((real_major, real_minor, real_build))
}
}
#[cfg(windows)] #[cfg(windows)]
#[pyfunction] #[pyfunction]
fn getwindowsversion(vm: &VirtualMachine) -> PyResult<crate::builtins::tuple::PyTupleRef> { fn getwindowsversion(vm: &VirtualMachine) -> PyResult<crate::builtins::tuple::PyTupleRef> {
@@ -519,21 +602,18 @@ mod sys {
sp.into_string() sp.into_string()
.map_err(|_| vm.new_os_error("service pack is not ASCII".to_owned()))? .map_err(|_| vm.new_os_error("service pack is not ASCII".to_owned()))?
}; };
let real_version = get_kernel32_version().map_err(|e| vm.new_os_error(e.to_string()))?;
Ok(WindowsVersion { Ok(WindowsVersion {
major: version.dwMajorVersion, major: real_version.0,
minor: version.dwMinorVersion, minor: real_version.1,
build: version.dwBuildNumber, build: real_version.2,
platform: version.dwPlatformId, platform: version.dwPlatformId,
service_pack, service_pack,
service_pack_major: version.wServicePackMajor, service_pack_major: version.wServicePackMajor,
service_pack_minor: version.wServicePackMinor, service_pack_minor: version.wServicePackMinor,
suite_mask: version.wSuiteMask, suite_mask: version.wSuiteMask,
product_type: version.wProductType, product_type: version.wProductType,
platform_version: ( platform_version: (real_version.0, real_version.1, real_version.2), // TODO Provide accurate version, like CPython impl
version.dwMajorVersion,
version.dwMinorVersion,
version.dwBuildNumber,
), // TODO Provide accurate version, like CPython impl
} }
.into_struct_sequence(vm)) .into_struct_sequence(vm))
} }