10000 partially fix sys.getwindowsversion() (#5595) · RustPython/RustPython@8e22c39 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8e22c39

Browse files
authored
partially fix sys.getwindowsversion() (#5595)
1 parent 8484bfa commit 8e22c39

File tree

2 files changed

+91
-11
lines changed

2 files changed

+91
-11
lines changed

extra_tests/snippets/stdlib_sys.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def recursive_call(n):
8181
0x00000100 | 0x00000001 | 0x00000020 | 0x00002000 | 0x00000010 | 0x00008000 | 0x00020000
8282

8383
# We really can't test if the results are correct, so it just checks for meaningful value
84-
assert winver.major > 0
84+
assert winver.major > 6
8585
assert winver.minor >= 0
8686
assert winver.build > 0
8787
assert winver.platform == 2
@@ -91,8 +91,8 @@ def recursive_call(n):
9191

9292
# XXX if platform_version is implemented correctly, this'll break on compatiblity mode or a build without manifest
9393
# these fields can mismatch in CPython
94-
# assert winver.major == winver.platform_version[0]
95-
# assert winver.minor == winver.platform_version[1]
94+
assert winver.major == winver.platform_version[0]
95+
assert winver.minor == winver.platform_version[1]
9696
# assert winver.build == winver.platform_version[2]
9797

9898
# test int_max_str_digits getter and setter

vm/src/stdlib/sys.rs

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,23 @@ mod sys {
2222
vm::{Settings, VirtualMachine},
2323
};
2424
use num_traits::ToPrimitive;
25+
#[cfg(windows)]
26+
use std::os::windows::ffi::OsStrExt;
2527
use std::{
2628
env::{self, VarError},
2729
path,
2830
sync::atomic::Ordering,
2931
};
3032

33+
#[cfg(windows)]
34+
use windows_sys::Win32::{
35+
Foundation::MAX_PATH,
36+
Storage::FileSystem::{
37+
GetFileVersionInfoSizeW, GetFileVersionInfoW, VS_FIXEDFILEINFO, VerQueryValueW,
38+
},
39+
System::LibraryLoader::{GetModuleFileNameW, GetModuleHandleW},
40+
};
41+
3142
// not the same as CPython (e.g. rust's x86_x64-unknown-linux-gnu is just x86_64-linux-gnu)
3243
// but hopefully that's just an implementation detail? TODO: copy CPython's multiarch exactly,
3344
// https://github.com/python/cpython/blob/3.8/configure.ac#L725
@@ -485,6 +496,78 @@ mod sys {
485496
vm.trace_func.borrow().clone()
486497
}
487498

499+
#[cfg(windows)]
500+
fn get_kernel32_version() -> std::io::Result<(u32, u32, u32)> {
501+
unsafe {
502+
// Create a wide string for "kernel32.dll"
503+
let module_name: Vec<u16> = std::ffi::OsStr::new("kernel32.dll")
504+
.encode_wide()
505+
.chain(Some(0))
506+
.collect();
507+
let h_kernel32 = GetModuleHandleW(module_name.as_ptr());
508+
if h_kernel32.is_null() {
509+
return Err(std::io::Error::last_os_error());
510+
}
511+
512+
// Prepare a buffer for the module file path
513+
let mut kernel32_path = [0u16; MAX_PATH as usize];
514+
let len = GetModuleFileNameW(
515+
h_kernel32,
516+
kernel32_path.as_mut_ptr(),
517+
kernel32_path.len() as u32,
518+
);
519+
if len == 0 {
520+
return Err(std::io::Error::last_os_error());
521+
}
522+
523+
// Get the size of the version information block
524+
let verblock_size =
525+
GetFileVersionInfoSizeW(kernel32_path.as_ptr(), std::ptr::null_mut());
526+
if verblock_size == 0 {
527+
return Err(std::io::Error::last_os_error());
528+
}
529+
530+
// Allocate a buffer to hold the version information
531+
let mut verblock = vec![0u8; verblock_size as usize];
532+
if GetFileVersionInfoW(
533+
kernel32_path.as_ptr(),
534+
0,
535+
verblock_size,
536+
verblock.as_mut_ptr() as *mut _,
537+
) == 0
538+
{
539+
return Err(std::io::Error::last_os_error());
540+
}
541+
542+
// Prepare an empty sub-block string (L"") as required by VerQueryValueW
543+
let sub_block: Vec<u16> = std::ffi::OsStr::new("")
544+
.encode_wide()
545+
.chain(Some(0))
546+
.collect();
547+
548+
let mut ffi_ptr: *mut VS_FIXEDFILEINFO = std::ptr::null_mut();
549+
let mut ffi_len: u32 = 0;
550+
if VerQueryValueW(
551+
verblock.as_ptr() as *const _,
552+
sub_block.as_ptr(),
553+
&mut ffi_ptr as *mut *mut VS_FIXEDFILEINFO as *mut *mut _,
554+
&mut ffi_len as *mut u32,
555+
) == 0
556+
|| ffi_ptr.is_null()
557+
{
558+
return Err(std::io::Error::last_os_error());
559+
}
560+
561+
// Extract the version numbers from the VS_FIXEDFILEINFO structure.
562+
let ffi = *ffi_ptr;
563+
let real_major = (ffi.dwProductVersionMS >> 16) & 0xFFFF;
564+
let real_minor = ffi.dwProductVersionMS & 0xFFFF;
565+
let real_build = (ffi.dwProductVersionLS >> 16) & 0xFFFF;
566+
567+
Ok((real_major, real_minor, real_build))
568+
}
569+
}
570+
488571
#[cfg(windows)]
489572
#[pyfunction]
490573
fn getwindowsversion(vm: &VirtualMachine) -> PyResult<crate::builtins::tuple::PyTupleRef> {
@@ -519,21 +602,18 @@ mod sys {
519602
sp.into_string()
520603
.map_err(|_| vm.new_os_error("service pack is not ASCII".to_owned()))?
521604
};
605+
let real_version = get_kernel32_version().map_err(|e| vm.new_os_error(e.to_string()))?;
522606
Ok(WindowsVersion {
523-
major: version.dwMajorVersion,
524-
minor: version.dwMinorVersion,
525-
build: version.dwBuildNumber,
607+
major: real_version.0,
608+
minor: real_version.1,
609+
build: real_version.2,
526610
platform: version.dwPlatformId,
527611
service_pack,
528612
service_pack_major: version.wServicePackMajor,
529613
service_pack_minor: version.wServicePackMinor,
530614
suite_mask: version.wSuiteMask,
531615
product_type: version.wProductType,
532-
platform_version: (
533-
version.dwMajorVersion,
534-
version.dwMinorVersion,
535-
version.dwBuildNumber,
536-
), // TODO Provide accurate version, like CPython impl
616+
platform_version: (real_version.0, real_version.1, real_version.2), // TODO Provide accurate version, like CPython impl
537617
}
538618
.into_struct_sequence(vm))
539619
}

0 commit comments

Comments
 (0)
0