2008-02-22

All users

Yesterday I needed my app to read and write data from a folder to which all users have access. Having the data in the current user's data folder was unacceptible as this would have resulted in duplicate data storages, the MSI installer even generates a compiler warning telling me I shouldn’t use this folder! So I went for Environment.GetFolderPath(SpecialFolder.CommonApplicationData);

This seemed to work fine until I tested on Vista, at which point my app would "stop responding" and quit. With a bit of investigation I discovered that CommonApplicationData maps to c:\ProgramData on Vista, which to me looked good until I tried creating a read/write FileStream in that path and received an access denied exception. So, where was I supposed to store my data? Checking each of the values in the SpecialFolder enum I was surprised to see that there doesn’'t seem to be a suitable value.

So, I reflected over Environment.GetFolderPath and copied the code. I then started at 0 and worked up until I hit a path with the word "public" in it (running on Vista). The value I required was 24! I looked through the SpecialFolders enum and there was no entry for 24, what"s worse is that the GetFolderPath wont allow integer values. So, I had to reimplement it myself.

public static class SpecialFolders
{
public static string GetFolderPath(SpecialFolderEx folder)
  {
    if (!Enum.IsDefined(typeof(SpecialFolderEx), folder))
      throw new ArgumentException("Unknown folder: " + folder.ToString());

    StringBuilder lpszPath = new StringBuilder(260);
    SHGetFolderPath(IntPtr.Zero, (int)folder, IntPtr.Zero, 0, lpszPath);
    string path = lpszPath.ToString();
    new FileIOPermission(FileIOPermissionAccess.PathDiscovery, path).Demand();
    return path;
  }

  [DllImport("shfolder.dll", CharSet = CharSet.Auto)]
  private static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, StringBuilder lpszPath);
}

public enum SpecialFolderEx
{
  ApplicationData = 0x1a,
  CommonApplicationData = 0x23,
  CommonDocuments = 0x2e,
  CommonProgramFiles = 0x2b,
  Cookies = 0x21,
  Desktop = 0,
  DesktopDirectory = 0x10,
  Favorites = 6,
  History = 0x22,
  InternetCache = 0x20,
  LocalApplicationData = 0x1c,
  MyComputer = 0x11,
  MyDocuments = 5,
  MyMusic = 13,
  MyPictures = 0x27,
  Personal = 5,
  ProgramFiles = 0x26,
  Programs = 2,
  Recent = 8,
  SendTo = 9,
  StartMenu = 11,
  Startup = 7,
  System = 0x25,
  Templates = 0x15,
  Windows = 0x24
}


If in future like me you need to store data for all users to access you should use SpecialFoldersEx.CommonDocuments, because now it works a treat!

No comments: