2009-08-23 54 views
3

我試圖讓使用哈斯克爾版本無關的方式窗口的本地應用程序數據文件夾的位置,而我有一點無法做到的。我已經使用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功能的源代碼,但沒有幫助我滿意。

也許有可以做到這一點,我只是缺少一個更簡單的方法。

回答

11

如果你想可移植性,你不應該訪問註冊表直接。有一個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

要讀取註冊表中的值在一個有用的格式相當長的一段代碼是必要的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 

我不知道如果所有的錯誤的情況下正確處理(比如,如果傳遞的緩衝區是小),所以你可能要檢查窗戶docs看看在這些情況下會發生什麼。

+0

非常感謝你,這不正是我需要的。 – Alasdair 2009-08-23 13:24:44

+0

不是記錄的方式來做到這一點,並不清楚它在哪個Windows上工作。它可能與Alasdair已經最接近,但API解決方案更好。 – MSalters 2009-08-24 11:09:04

+0

我剛剛通過Alasdair已經擁有的內容,並沒有尋找任何其他的Api調用。無論如何,我不會刪除它,因爲它可能對嘗試讀取不具有單獨Api訪問的不同註冊表值的其他人有用。 – sth 2009-08-24 11:17:10