@@ -457,6 +457,69 @@ pub fn expand_environment_path(src: &Path, environment: &HashMap<String, String>
457457 ) ) ?)
458458}
459459
460+ use windows:: Win32 :: Foundation :: { BOOL , HMODULE } ;
461+ use windows:: Win32 :: Storage :: FileSystem :: {
462+ GetFileVersionInfoSizeW , GetFileVersionInfoW , VerQueryValueW , VS_FIXEDFILEINFO ,
463+ } ;
464+ use windows:: Win32 :: System :: LibraryLoader :: { GetModuleFileNameW , GetModuleHandleW } ;
465+
466+ pub fn get_exe_version ( ) -> Result < String , anyhow:: Error > {
467+ // Passing None to GetModuleHandleW requests the handle to the current process main executable module
468+ let h_module: HMODULE = unsafe { GetModuleHandleW ( None ) ? } ;
469+
470+ let mut path_buf = [ 0u16 ; MAX_PATH as usize ] ;
471+
472+ // SAFETY: We're passing a valid mutable buffer to GetModuleFileNameW of large enough size (MAX_PATH WCHARs)
473+ let len = unsafe { GetModuleFileNameW ( h_module, & mut path_buf) } ;
474+
475+ if len == 0 {
476+ anyhow:: bail!( "GetModuleFileNameW failed: {}" , windows:: core:: Error :: from_win32( ) ) ;
477+ }
478+
479+ let exe_path = & path_buf[ ..len as usize ] ;
480+ let exe_path_w = PCWSTR ( exe_path. as_ptr ( ) ) ;
481+
482+ // SAFETY: `exe_path_w` is a valid pointer to a null-terminated UTF-16 string from the OS.
483+ let size = unsafe { GetFileVersionInfoSizeW ( exe_path_w, None ) } ;
484+ if size == 0 {
485+ anyhow:: bail!( "GetFileVersionInfoSizeW failed: {}" , windows:: core:: Error :: from_win32( ) ) ;
486+ }
487+
488+ let mut buffer = vec ! [ 0u8 ; size as usize ] ;
489+
490+ // SAFETY: `buffer` is allocated with the correct size.
491+ // `exe_path_w` is a valid pointer to a null-terminated UTF-16 string from the OS.
492+ unsafe { GetFileVersionInfoW ( exe_path_w, 0 , size, buffer. as_mut_ptr ( ) as * mut _ ) ? } ;
493+
494+ let mut lp_buffer: * mut c_void = ptr:: null_mut ( ) ;
495+ let mut len = 0u32 ;
496+ let path = "\\ \0 " . encode_utf16 ( ) . collect :: < Vec < u16 > > ( ) ;
497+ let path_ptr = PCWSTR ( path. as_ptr ( ) ) ;
498+
499+ // SAFETY: The version info buffer is valid and comes from GetFileVersionInfoW.
500+ // The query string is valid null-terminated UTF-16.
501+ // `lp_buffer` and `len` are valid out parameters.
502+ let ok = unsafe { VerQueryValueW ( buffer. as_ptr ( ) as * const _ , path_ptr, & mut lp_buffer, & mut len) } ;
503+
504+ if !ok. as_bool ( ) {
505+ anyhow:: bail!( "VerQueryValueW failed" ) ;
506+ }
507+
508+ // SAFETY: ff VerQueryValueW succeeded, `lp_buffer` points to a valid VS_FIXEDFILEINFO.
509+ let info = unsafe { & * ( lp_buffer as * const VS_FIXEDFILEINFO ) } ;
510+
511+ if info. dwSignature != 0xFEEF04BD {
512+ bail ! ( "invalid VS_FIXEDFILEINFO signature" ) ;
513+ }
514+
515+ let major = ( info. dwFileVersionMS >> 16 ) & 0xffff ;
516+ let minor = ( info. dwFileVersionMS >> 0 ) & 0xffff ;
517+ let build = ( info. dwFileVersionLS >> 16 ) & 0xffff ;
518+ let revision = ( info. dwFileVersionLS >> 0 ) & 0xffff ;
519+
520+ Ok ( format ! ( "{}.{}.{}.{}" , major, minor, build, revision) )
521+ }
522+
460523pub struct Snapshot {
461524 handle : OwnedHandle ,
462525}
0 commit comments