2009-08-23 29 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