Получение локальной папки AppData в Haskell - PullRequest
3 голосов
/ 23 августа 2009

Я пытаюсь получить местоположение папки Local AppData в Window в независимой от версии манере, используя Haskell, и у меня возникают некоторые проблемы с этим. Я пытался использовать библиотеку 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

Я также пытался посмотреть исходный код функции getAppUserDataDirectory в модуле System.Directory, но это мне тоже не помогло.

Может быть, есть более простой способ сделать это, что я просто скучаю.

Ответы [ 2 ]

11 голосов
/ 24 августа 2009

Если вы хотите переносимости, не должен обращаться к реестру напрямую. Есть функция 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
0 голосов
/ 23 августа 2009

Чтобы прочитать значения из реестра в полезном формате, требуется довольно много кода для преобразования между типами 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, чтобы увидеть, что происходит в этих случаях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...