2009-08-23 9 views
3

J'essaie d'obtenir l'emplacement du dossier Local AppData de Windows de manière agnostique en utilisant Haskell, et j'ai un peu de mal à le faire. J'ai essayé d'utiliser la bibliothèque System.Win32.Registry, et j'ai été en mesure d'obtenir le code ci-dessous (après quelques essais et erreurs), mais je n'ai pas réussi à comprendre comment utiliser le regQueryValueEx ou toute autre fonction pour obtenir la valeur dont j'ai besoin.Obtention du dossier Local AppData dans Haskell

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 

J'ai essayé aussi regarder le code source de la fonction getAppUserDataDirectory dans le module System.Directory, mais cela ne m'a pas aidé non plus.

Peut-être qu'il y a un moyen plus facile de le faire que je suis juste manquant.

Répondre

11

Si vous voulez la portabilité, vous ne devrait pas directement accéder au registre. Il existe une fonction API pour obtenir des dossiers spéciaux: SHGetFolderPath. Vous pouvez l'appeler ainsi:

{-# 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

Pour lire les valeurs du registre dans un format utile, un peu de code est nécessaire pour convertir entre les types Haskell et C. Et que les valeurs en question sont généralement de type REG_EXPAND_SZ n'aide pas non plus. Donc, ce n'est pas assez, mais cela fonctionne pour moi:

{-# 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 

Je ne sais pas si tous les cas d'erreur sont traités correctement (comme si le tampon passé était trop petite), de sorte que vous pouvez vérifier les fenêtres docs pour voir ce qui se passe dans ces cas.

+0

Merci beaucoup, cela fait exactement ce dont j'ai besoin. – Alasdair

+0

Pas la manière documentée de le faire, et pas clair sur quel Windows il fonctionne. C'est peut-être le plus proche de ce qu'Aasdairn avait déjà, mais la solution API est meilleure. – MSalters

+0

Je me suis contenté de ce qu'Alasdair avait déjà fait et je n'ai pas cherché d'autres appels Api. Quoi qu'il en soit, je ne vais pas supprimer cela car il pourrait être utile pour d'autres personnes qui essaient de lire différentes valeurs de registre qui n'ont pas accès à un API distinct. – sth

Questions connexes