Getting AppData\Local folder

Need to read a file that’s in AppData\Local.

SpecialFolder.ApplicationData returns the Roaming variant of AppData.

I realize I could use SpecialFolder.ApplicationData.Parent.Child(“Local”) but this is not the guaranteed path on every system.

Is there another SpecialFolder method that yields the Local AppData folder?

So apparently the Local AppData path is stored in the registry under \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\Local AppData

But then the problem becomes that the paths can contain any environment variable (for example %USERPROFILE%) so now you need to resolve every one of those, make the string replacement, then cast it into a FolderItem. This is also complicated by the fact that something like “%USERPROFILE%” is both a possible environment variable or a valid folder name.

There has to be a simpler way as this seems as if it would be basic functionality.

For now, I’m using:

dim AppDataLocal as new folderitem(system.EnvironmentVariable("LocalAppData"))

But if someone knows a better way, please chime in, because that’s reliant on an Environment Variable that should be present, but doesn’t have to be.

You could grab the registry value and use a shell to cd to it then pwd to get the resolved path.

1 Like

Thanks for that! This got me thinking. I ended up putting this batch file together:

@for /f "tokens=4" %%a in ('reg query "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"  /V "Local AppData"  ^|findstr /ri "REG_EXPAND_SZ"') do @call echo %%a

And turned it into a Xojo function.

Private Function AppDataLocal() As FolderItem
  #If TargetWindows
    
    dim s as new shell
    s.execute("@for /f " + Chr(34) + "tokens=4" + Chr(34) + " %a in ('reg query " + Chr(34) + "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" + Chr(34) + "  /V " + Chr(34) + "Local AppData" + Chr(34) + "  ^|findstr /ri " + Chr(34) + "REG_EXPAND_SZ" + Chr(34) + "') do @call echo %a")
    return new folderItem(s.result.trim)
    
  #Else
    
    return SpecialFolder.ApplicationData
    
  #EndIf
End Function

This works! Thanks again for the inspiration.

It is documented by Microsoft that you should not rely on those values in the registry. Your approach reading the environment variable it probably the more reliable approach.

A better approach would be to use the Win32 API SHGetKnownFolderPath function with the corresponding KNOWNFOLDERID using a declare in Xojo.

Maybe this code snippet will help:

    Const CSIDL_LOCAL_APPDATA = &h1c
  
    Declare Function SHGetSpecialFolderPathA Lib "Shell32" (hwnd As Integer, pszPath As Ptr, csidl As Integer, fCreate As Boolean) As Boolean
    
    Var oSpace As New MemoryBlock(1024)
    Var pSpacePtr As Ptr = oSpace
    
    If SHGetSpecialFolderPathA(0, pSpacePtr, CSIDL_LOCAL_APPDATA, False) Then
      Var sPath As String
      sPath = oSpace.CString(0)
      Return New FolderItem(sPath, FolderItem.PathModes.Native)
    End If

This should work:

Var localAppData As FolderItem = SpecialFolder.Temporary.Parent
1 Like

Just by curiosity; why do you need to read that file ?

@Stefan_Roth1 Thank you for that! I believe you are correct that this is the “correct” way to retrieve it. @Rick_A’s workaround is absolutely brilliant too, after a bit of research it turns out that the Temporary folder is always part of the local Appdata.

@Michel_Bujardet I am interfacing with another piece of software that writes its configuration file to a subfolder of LocalAppData.

Ask Stefan to rewrite that solution using the current SHGetKnownFolderPath instead of the legacy SHGetSpecialFolderPathA so it can be safer/proper for international users with unicode string paths.

That’s why I opted for using the shorter Xojo native option.

1 Like