@@ -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
A3E2
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