15

I'm trying to locate the path for the AppData\LocalLow folder.

I have found an example which uses:

string folder = "c:\users\" + Environment.UserName + @"\appdata\LocalLow";

which for one is tied to c: and to users which seems a bit fragile.

I tried to use

Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)

but this gives me AppData\Local, and I need LocalLow due to the security constraints the application is running under. It returned blank for my service user as well (at least when attaching to the process).

Any other suggestions?

Mikael Svenson
  • 39,181
  • 7
  • 73
  • 79
  • Is there a reason you can't appent a `Low` to the returned string? – Oded Dec 20 '10 at 21:55
  • or `Path.Combine(localData, @"..\LocalLow")` – H H Dec 20 '10 at 21:57
  • Of course I could append low or use path combine, but I think @Thomas solution is the best one. Since it's already an OS call, I would rather use that. – Mikael Svenson Dec 22 '10 at 08:59
  • 4
    Unless Microsoft has promised to leave 'LocalLow' as the name in non-English versions of Windows, it seems like a good idea to prefer the OS call... – Spike0xff Sep 25 '12 at 14:03

3 Answers3

26

The Environment.SpecialFolder enumeration maps to CSIDL, but there is no CSIDL for the LocalLow folder. So you have to use the KNOWNFOLDERID instead, with the SHGetKnownFolderPath API:

void Main()
{
    Guid localLowId = new Guid("A520A1A4-1780-4FF6-BD18-167343C5AF16");
    GetKnownFolderPath(localLowId).Dump();
}

string GetKnownFolderPath(Guid knownFolderId)
{
    IntPtr pszPath = IntPtr.Zero;
    try
    {
        int hr = SHGetKnownFolderPath(knownFolderId, 0, IntPtr.Zero, out pszPath);
        if (hr >= 0)
            return Marshal.PtrToStringAuto(pszPath);
        throw Marshal.GetExceptionForHR(hr);
    }
    finally
    {
        if (pszPath != IntPtr.Zero)
            Marshal.FreeCoTaskMem(pszPath);
    }
}

[DllImport("shell32.dll")]
static extern int SHGetKnownFolderPath( [MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr pszPath);
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
1

Thomas's answer is effective yet needlessly complex for some use cases.

A quick solution is:

string LocalLowPath = 
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).Replace("Roaming","LocalLow");
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 03 '22 at 06:47
  • 1
    What happens if the current user's name is `Roaming`? – GSerg Jan 15 '23 at 17:37
  • See https://xkcd.com/327/ – David Steinberg Jun 05 '23 at 14:56
1

Indeed, the suggestion you found is fragile. I didn't know the method from Thomas, but if you still consider keeping it simple, here's a way to detect the drive letter as well as the path separator character. This will still be windows specific, but all three options below work the same outputting "C:\Users\MyUser\AppData\LocalLow", where 'MyUser' is the username on the machine you run this on. There are other special folders here, but below are the ones you need.

// UserProfile = C:\Users\MyUser
string folderFromUserProfile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "AppData", "LocalLow");
string folderAppendingFromLocalAppData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)) + "Low";
// GetFullPath expands from relative to absolute, removing the ".."
string folderRelativeFromLocalAppData = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..", "LocalLow"));

This assumes you have

using System;
using System.IO;