Getting the Local AppData folder in Haskell

haskell, registry, windows

Solution

If you want portability, you shouldn't access registry directly. There is an API function to get special folders: SHGetFolderPath. You can call it thus:

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

Problem

I'm trying to get the location of Window's Local AppData folder in a version-agnostic manner using Haskell, and I'm having a bit of trouble doing so. I've tried using the System.Win32.Registry library, and I was able to get the code below (after some trial and error), but I wasn't able to figure out how to use the `regQueryValueEx` or any other function to get the value I need. ``` 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 ``` I also tried looking at the source code for the `getAppUserDataDirectory` function in the System.Directory module, but that didn't help me either. Maybe there's an easier way to do this that I'm just missing.

Original source