In this tip, you will see how to store data files and avoid problems arising from choosing the wrong location. You will also see how to get the path.
Introduction
Recently, I have been answering questions regarding storing data files, and the problems that result from choosing the wrong location. So, where should you store data? And how do you get the path?
Background
Windows has a number of places that are free for data storage and which will not cause "Access Denied" exceptions when you try to write to them - unfortunately, the default directory is not one of them. If you use code such as:
File.WriteAllText("myFile.txt", "Hello World");
Then the system will try to write it to the current folder - which is normally the application executable directory, which in release programs is (or should be) under the "Program Files" or "Program Files(x86)" folder. It is not a good idea to write anything here - you may get an exception, or the user may not be able to locate it with Windows Explorer, or worse: it may well not be on a regular backup schedule folder list.
If you want to write data to a sensible location, Windows provides three Data folders:
User Data
Roaming User Data
All User Data
And these are accessible in C# via the Environment.SpecialFolder
enumeration:
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)
However, it is not a good idea to use these directly, as they are not specific to your application, but general to all.
One way to get an appropriate folder is just to use the Application Properties:
Application.UserAppDataPath
Application.LocalUserAppDataPath
Application.CommonAppDataPath
But I am not in favour of this as the path is specific to your application, and the version number of the assembly: If you issue a new version of your EXE, then it may not be able to find the data that worked with a previous version.
Instead, I prefer to use the Application GUID and the Environment.SpecialFolder
enumeration.
When you create an assembly with Visual Studio, it is assigned a Guid value, which does not change during the life of that assembly - be it an EXE file or a Class Library. You can use that Guid to absolutely identify your application, and thus it's data - there is a very, very small chance of any two applications being given the same GUID - which is not the case for application names! How many companies do you think have wanted to call an application "Word", or "Paint"?
The table at the bottom shows the problem with using the Application Properties too blindly.
The Code
Include the following in your application data storage class, or in an appropriate Class Library.
public static Guid AppGuid
{
get
{
Assembly asm = Assembly.GetEntryAssembly();
object[] attr = (asm.GetCustomAttributes(typeof(GuidAttribute), true));
return new Guid((attr[0] as GuidAttribute).Value);
}
}
public static Guid AssemblyGuid
{
get
{
Assembly asm = Assembly.GetExecutingAssembly();
object[] attr = (asm.GetCustomAttributes(typeof(GuidAttribute), true));
return new Guid((attr[0] as GuidAttribute).Value);
}
}
public static string UserDataFolder
{
get
{
Guid appGuid = AppGuid;
string folderBase = Environment.GetFolderPath
(Environment.SpecialFolder.LocalApplicationData);
string dir = string.Format(@"{0}\{1}\", folderBase, appGuid.ToString("B").ToUpper());
return CheckDir(dir);
}
}
public static string UserRoamingDataFolder
{
get
{
Guid appGuid = AppGuid;
string folderBase = Environment.GetFolderPath
(Environment.SpecialFolder.ApplicationData);
string dir = string.Format(@"{0}\{1}\",
folderBase, appGuid.ToString("B").ToUpper());
return CheckDir(dir);
}
}
public static string AllUsersDataFolder
{
get
{
Guid appGuid = AppGuid;
string folderBase = Environment.GetFolderPath
(Environment.SpecialFolder.CommonApplicationData);
string dir = string.Format(@"{0}\{1}\",
folderBase, appGuid.ToString("B").ToUpper());
return CheckDir(dir);
}
}
private static string CheckDir(string dir)
{
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
return dir;
}
Do note that if you include this code in a Class Library, then AppGuid
will not be the same as the AssemblyGuid
- a Class Library is a separate Assembly, and has its own Guid. This can be useful, if you want to store information which is to be used by multiple applications - User Login details for example if these are not related to the Windows Login details.
Sample Paths Generated
Common.AppGuid 158aa00d-332e-440b-9c2c-47e2fc11c078
Common.AssemblyGuid 8d14ff7e-8d39-4163-a1ba-4fae4a5c361d
Common.UserDataFolder C:\Users\griff\AppData\Local\
{158AA00D-332E-440B-9C2C-47E2FC11C078}\
Application.UserAppDataPath C:\Users\griff\AppData\Roaming\SetShares\SetShares\1.0.0.0
Common.UserRoamingDataFolder C:\Users\griff\AppData\Roaming\
{158AA00D-332E-440B-9C2C-47E2FC11C078}\
Application.LocalUserAppDataPath C:\Users\griff\AppData\Local\SetShares\SetShares\1.0.0.0
Common.AllUsersDataFolder C:\ProgramData\{158AA00D-332E-440B-9C2C-47E2FC11C078}\
Application.CommonAppDataPath C:\ProgramData\SetShares\SetShares\1.0.0.0
History
- 20th April, 2012: Original version