It is fairly typical for immature software to have many dependencies on its local installation folder. After all, it is just so darn easy to read and write files using relative paths! This easy default causes configuration files, log files and data files to pile up in the application’s install directory. To make matters worse, the .NET framework encourages this behavior via the built-in app.config mechanism, which teaches developers that it is okay to store application configuration next to the executable. Unfortunately, the use of relative paths comes with many pitfalls. First and foremost, relative paths rely on the current working directory, which is very likely to be the install directory (containing your entry executable), but it is not guaranteed to be so. And it will come as no surprise to anyone that has used the “file open” dialog that the current directory can change while the application is running. But, perhaps the worst thing about reliance on relative paths from a DevOps perspective is that such behavior is probably the leading cause of dysfunctional, overly complex deployments.
When user configuration, application data, and audit logs all sit in the application installation directory, deployments become a terrifying dance of removing certain files while preserving and backing up others. This approach makes a simple xcopy deployment impossible, because the install directory cannot be removed without destroying the user’s data! To enable xcopy deployment, applications must scrupulously avoid accessing files in the current working directory or the installation directory. Instead, they should store files in one or more of the known folders that Windows provides for our use. In .NET, the known folders are accessed by calling the Environment.GetFolderPath method with a member of the Environment.SpecialFolders enumeration. There are many folders available, but there are a few you should definitely know about as they have the power to reduce your reliance on relative paths:
- Environment Variable : %appdata%
- Typical Path : C:\Users\username\AppData\Roaming
- Should be used for user-specific data that should roam to multiple machines as part of the user’s roaming profile. This is a good place to store user preferences like colors and fonts.
- Environment Variable : %localappdata%
- Typical Path : C:\Users\username\AppData\Local
- Should be used for user-specific data that should not roam with the user. If users run the application on multiple machines with different configurations, it is a good idea to use this folder instead of its roaming counterpart.
- Environment Variable : %programdata%
- Typical Path : C:\ProgramData
- Should be used for non-user-specific application data that is common to the machine. Depending on the circumstances, this is a good spot to write log files or configuration data that is not specific to or editable by the user.
- Environment Variable : %programfiles%
- Typical Path : C:\Program Files
- This directory should be used to store application installations (and should not be manipulated by apps at runtime). On a 32-bit system, this always corresponds to the “C:\Program Files” directory. On a 64-bit system, it corresponds to the “C:\Program Files” directory within a 64-bit process and the “C:\Program Files (x86)” directory within a 32-bit process. Deployment automation solutions should xcopy your applications to this directory.
When placing data in these special folders, your applications should follow the convention of using a subdirectory of the special folder named for the company, and a subdirectory of the that directory named for the application. It is useful to wrap up this convention in a helper class that ensures consistent application-specific access to the special folder:
Do yourself a favor and stop using relative paths in your applications. Write configuration files to absolute paths inside a special folder. This ensures that your application accesses the same files regardless of its install location or working directory and it will enable you to easily deploy and upgrade your applications via a simple xcopy-based process.