【问题标题】:Getting the Local AppData folder in Haskell在 Haskell 中获取本地 AppData 文件夹
【发布时间】:2009-08-23 00:18:03
【问题描述】:

我正在尝试使用 Haskell 以与版本无关的方式获取 Window 的本地 AppData 文件夹的位置,但这样做有点麻烦。我尝试使用 System.Win32.Registry 库,并且能够获得下面的代码(经过一些试验和错误),但我无法弄清楚如何使用 regQueryValueEx 或任何其他功能得到我需要的价值。

import System.Win32.Types
import System.Win32.Registry

userShellFolders :: IO HKEY
userShellFolders = regOpenKeyEx hKEY_CURRENT_USER "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\" kEY_QUERY_VALUE

我还尝试查看 System.Directory 模块中 getAppUserDataDirectory 函数的源代码,但这对我也没有帮助。

也许有一种更简单的方法可以做到这一点,我只是想念。

【问题讨论】:

    标签: windows haskell registry


    【解决方案1】:

    如果您想要可移植性,您不应该直接访问注册表。有一个API函数 获取特殊文件夹:SHGetFolderPath。你可以这样称呼它:

    {-# LANGUAGE ForeignFunctionInterface #-}
    import System.Win32.Types
    import Graphics.Win32.GDI.Types
    import Foreign.C.String
    import Foreign.Marshal.Array
    
    foreign import stdcall unsafe "SHGetFolderPathW"
        cSHGetFolderPathW :: HWND -> INT -> HANDLE -> DWORD -> CWString -> IO LONG
    
    maxPath = 260
    cSIDL_LOCAL_APPDATA = 0x001c -- //see file ShlObj.h in MS Platform SDK for other CSIDL constants
    
    getShellFolder :: INT -> IO String
    getShellFolder csidl = allocaArray0 maxPath $ \path -> do
        cSHGetFolderPathW nullHANDLE csidl nullHANDLE 0 path
        peekCWString path
    
    main = getShellFolder cSIDL_LOCAL_APPDATA >>= putStrLn
    

    【讨论】:

      【解决方案2】:

      要以有用的格式从注册表中读取值,需要一些代码在 Haskell 和 C 类型之间进行转换。并且有问题的值通常是REG_EXPAND_SZ 类型也无济于事。所以它并不漂亮,但这对我有用:

      {-# LANGUAGE ForeignFunctionInterface #-}
      
      import System.Win32.Types
      import System.Win32.Registry
      import Foreign.Ptr (castPtr)
      import Foreign.Marshal.Alloc (allocaBytes)
      import Foreign.C.String (peekCWString, withCWString)
      import Control.Exception (bracket, throwIO)
      
      -- // parse a string from a registry value of certain type
      parseRegString :: RegValueType -> LPBYTE -> IO String
      parseRegString ty mem
         | ty == rEG_SZ        = peekCWString (castPtr mem)
         | ty == rEG_EXPAND_SZ = peekCWString (castPtr mem) >>=
                                    expandEnvironmentStrings
         | otherwise           = ioError (userError "Invalid registry value type")
      
      -- // FFI import of the ExpandEnvironmentStrings function needed
      -- // to make use of the registry values
      expandEnvironmentStrings :: String -> IO String
      expandEnvironmentStrings toexpand =
         withCWString toexpand $ \input ->
         allocaBytes 512 $ \output ->
         do c_ExpandEnvironmentStrings input output 256
            peekCWString output
      foreign import stdcall unsafe "windows.h ExpandEnvironmentStringsW"
        c_ExpandEnvironmentStrings :: LPCTSTR -> LPTSTR -> DWORD -> IO DWORD
      
      -- // open the registry key
      userShellFolders :: IO HKEY
      userShellFolders = regOpenKeyEx hKEY_CURRENT_USER
         "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders"
         kEY_QUERY_VALUE
      
      -- // read the actual value
      localAppData :: IO String
      localAppData =
         bracket userShellFolders regCloseKey $ \usfkey ->
         allocaBytes 512 $ \mem ->
         do ty <- regQueryValueEx usfkey "Local AppData" mem 512
            parseRegString ty mem
      
      main = localAppData >>= print
      

      我不确定是否正确处理了所有错误情况(例如传递的缓冲区太小),因此您可能需要查看 Windows 文档以了解在这些情况下会发生什么。

      【讨论】:

      • 非常感谢,这正是我所需要的。
      • 不是记录在案的方法,也不清楚它在哪个 Windows 上工作。它可能是最接近 Alasdairn 的,但 API 解决方案更好。
      • 我只是通过了 Alasdair 已经拥有的东西,并没有寻找任何其他 Api 调用。无论如何,我不会删除它,因为它可能对其他尝试读取没有单独 Api 访问的不同注册表值的人有用。
      • “其他注册表值”位的公平点。肯定有一些注册表项记录在案。
      • 我尝试从注册表获取路径的原因是因为我正在交互操作的应用程序的文档说注册表是你应该如何做的,幸运的是它是 wiki 格式,所以我能够纠正它。
      猜你喜欢
      • 2021-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-09
      • 2013-10-05
      相关资源
      最近更新 更多