如何在Haskell中获取Windows系统的版本构建号?
获取Windows版本构建号的更清晰方法
嘿,你提到的用readProcess调用ver命令确实能拿到Windows版本信息,但确实有更清晰、更原生的方式——直接调用Windows系统API,借助Haskell的Win32库来实现,避免了解析命令输出字符串的麻烦。
方法一:调用RtlGetVersion(推荐,无版本欺骗问题)
这种方法直接调用Windows内核的RtlGetVersion函数,不受Windows版本兼容性限制(不会被系统“欺骗”返回旧版本号),能准确获取构建号。
步骤1:添加依赖
首先确保你的项目依赖Win32库,在你的cabal文件中添加:
build-depends: base >= 4.14 && < 5, Win32 >= 2.6.2.0
或者在stack.yaml的extra-deps中添加对应版本的Win32。
步骤2:实现代码
import System.Win32.Types import Foreign.C.Types import Foreign.Storable import Foreign.Ptr import Foreign.Marshal.Alloc -- 对应Windows的OSVERSIONINFOEXW结构体定义 data OSVERSIONINFOEXW = OSVERSIONINFOEXW { osvewOSVersionInfoSize :: DWORD , osvewMajorVersion :: DWORD , osvewMinorVersion :: DWORD , osvewBuildNumber :: DWORD , osvewPlatformId :: DWORD , osvewCSDVersion :: LPTSTR , osvewServicePackMajor :: WORD , osvewServicePackMinor :: WORD , osvewSuiteMask :: WORD , osvewProductType :: BYTE , osvewReserved :: BYTE } deriving (Show) instance Storable OSVERSIONINFOEXW where sizeOf _ = 284 -- 匹配Windows中OSVERSIONINFOEXW的字节大小 alignment _ = alignment (undefined :: DWORD) peek ptr = do size <- peek (castPtr ptr :: Ptr DWORD) major <- peekByteOff ptr 4 minor <- peekByteOff ptr 8 build <- peekByteOff ptr 12 platform <- peekByteOff ptr 16 csd <- peekByteOff ptr 20 spMajor <- peekByteOff ptr 264 spMinor <- peekByteOff ptr 266 suite <- peekByteOff ptr 268 productType <- peekByteOff ptr 270 reserved <- peekByteOff ptr 271 return OSVERSIONINFOEXW{ osvewOSVersionInfoSize = size , osvewMajorVersion = major , osvewMinorVersion = minor , osvewBuildNumber = build , osvewPlatformId = platform , osvewCSDVersion = csd , osvewServicePackMajor = spMajor , osvewServicePackMinor = spMinor , osvewSuiteMask = suite , osvewProductType = productType , osvewReserved = reserved } poke ptr info = do poke (castPtr ptr :: Ptr DWORD) (osvewOSVersionInfoSize info) pokeByteOff ptr 4 (osvewMajorVersion info) pokeByteOff ptr 8 (osvewMinorVersion info) pokeByteOff ptr 12 (osvewBuildNumber info) pokeByteOff ptr 16 (osvewPlatformId info) pokeByteOff ptr 20 (osvewCSDVersion info) pokeByteOff ptr 264 (osvewServicePackMajor info) pokeByteOff ptr 266 (osvewServicePackMinor info) pokeByteOff ptr 268 (osvewSuiteMask info) pokeByteOff ptr 270 (osvewProductType info) pokeByteOff ptr 271 (osvewReserved info) -- 声明外部调用RtlGetVersion函数 foreign import stdcall unsafe "ntdll.dll RtlGetVersion" c_RtlGetVersion :: Ptr OSVERSIONINFOEXW -> IO DWORD -- 获取构建号的核心函数 getWindowsBuildNumber :: IO (Maybe DWORD) getWindowsBuildNumber = alloca $ \ptr -> do let structSize = fromIntegral $ sizeOf (undefined :: OSVERSIONINFOEXW) -- 初始化结构体大小(Windows API要求必须设置) poke ptr (OSVERSIONINFOEXW structSize 0 0 0 0 nullPtr 0 0 0 0 0) status <- c_RtlGetVersion ptr if status == 0 -- STATUS_SUCCESS表示调用成功 then do osInfo <- peek ptr return $ Just $ osvewBuildNumber osInfo else return Nothing -- 测试用例 main :: IO () main = do maybeBuildNum <- getWindowsBuildNumber case maybeBuildNum of Just buildNum -> putStrLn $ "当前Windows构建号:" ++ show buildNum Nothing -> putStrLn "获取Windows构建号失败"
方法二:用Win32库封装好的getOSVersion(更简洁,但需注意版本欺骗)
如果你的程序有正确的应用程序manifest(声明支持Windows 10/11),可以用Win32库提供的封装函数,代码会更简洁:
import System.Win32.Info getWindowsBuildNumber :: IO DWORD getWindowsBuildNumber = dwBuildNumber <$> getOSVersion main :: IO () main = do buildNum <- getWindowsBuildNumber putStrLn $ "当前Windows构建号:" ++ show buildNum
注意事项
没有manifest的话,Windows 10及以上系统会返回模拟Windows 8的版本号(6.2),导致构建号不准确。所以如果用这种方法,需要在项目中添加manifest文件,声明支持目标Windows版本。
和readProcess+ver的对比
readProcess+ver的优点是不需要额外依赖,但需要手动解析ver命令的输出字符串(比如Microsoft Windows [版本 10.0.19045.3570]),容易受系统语言、输出格式变化影响。- 系统API调用的方法更可靠、原生,不需要解析字符串,是更清晰的解决方案。
内容的提问来源于stack exchange,提问作者Malyutin Egor
相关产品推荐
相关产品推荐

