Locating special folders in cross-platform .NET applications

Locating special folders in cross-platform .NET applications

.NET has APIs for locating special folders that can be used for application and user configuration and data storage. They provide a convenient, portable way to make cross-platform applications find the appropriate folders on different operating systems. We’ll look at how Environment.GetFolderPath, Path.GetTempPath, and Path.GetTempFileName behave on Linux.

Environment.GetFolderPath

The System.Environment class has two GetFolderPath overloads:

public static string GetFolderPath (SpecialFolder folder);
public static string GetFolderPath (SpecialFolder folder, SpecialFolderOption option);

SpecialFolder is an enum with values like ApplicationData, MyDocuments, and ProgramFiles. The SpecialFolderOption enum has three values: None, Create, and DoNotVerify. These control the return value when the folder does not exist. Specifying None causes an empty string to be returned. Specifying Create causes the folder to be created. And DoNotVerify causes the path to be returned even when the folder does not exist.

Develop using Red Hat's most valuable products

Your membership unlocks Red Hat products and technical training on enterprise cloud application development.

JOIN RED HAT DEVELOPER

Note that SpecialFolder and SpecialFolderOption are nested in the Environment class, and to use them directly you should add a using static System.Environment; statement to your code.

To make this API work cross-platform, .NET Core needs to map the SpecialFolder values to some locations. For Linux, this mapping is based on file-hierarcy and basedir-spec.

 

SpecialFolder Environment Variable Config File Linux Default
CommonApplicationData /usr/share
CommonTemplates /usr/share/templates
MyDocuments (home) HOME passwd /
UserProfile home
ApplicationData XDG_CONFIG_HOME home/.config
LocalApplicationData XDG_DATA_HOME home/.local/share
Fonts home/.fonts
Desktop, DesktopDirectory XDG_DESKTOP_DIR user-dirs.dirs home/Desktop
Templates XDG_TEMPLATES_DIR user-dirs.dirs home/Templates
MyVideos XDG_VIDEOS_DIR user-dirs.dirs home/Videos
MyMusic XDG_MUSIC_DIR user-dirs.dirs home/Music
MyPictures XDG_PICTURES_DIR user-dirs.dirs home/Pictures

The table lists all the mapped values in .NET Core 2.1. Other values are not mapped and return an empty string. The returned value is determined from left to right: first checking the environment variable, then falling back to the config file, and finally falling back to a default.

Cross-platform applications should be limited to using the mapped values, or they should be able to fall back to another location when GetFolderPath returns an empty string.

The user home folder is read from the HOME environment variable. When that is unset, the home directory is read from the system user database. It’s safe to assume that for known users, .NET Core will be able to determine the home directory. A number of other locations are based on the user home folder. Some can be overridden using environment variables and some others by using a file at ApplicationData/users-dirs.dirs.

On Windows, most of the special folders will exist by default. This may not be the case on Linux. It is the application’s responsibility to create the folder when it doesn’t exist. This may require some changes to your code to use the overload with a SpecialFolderOption.

For example, the following code ensures the LocalApplicationData folder will be created if it doesn’t exist.

// Use DoNotVerify in case LocalApplicationData doesn’t exist.
string appData = Path.Combine(Environment.GetFolderPath(SpecialFolder.LocalApplicationData, SpecialFolderOption.DoNotVerify), "myapp");
// Ensure the directory and all its parents exist.
Directory.CreateDirectory(appData);

Path.GetTempPath and Temp.GetTempFileName

The System.IO.Path class has a method that returns the path of the current user’s temporary folder:

public static string GetTempPath ();

Windows applications may assume the path returned here is user-specific. This is because the implementation picks up the USERPROFILE environment variable. When the variable is unset, the API returns the Windows temp folder.

On Linux, the implementation returns /tmp. This folder is shared with other users. As a consequence, applications should use unique names to avoid conflicts with other applications. Furthermore, because the location is shared, other users will be able to read the files created here, so you should not store sensitive data in this folder. The first user to create a file or directory will own it. This can cause your application to fail when trying to create a file or directory that is already owned by another user.

The Temp.GetTempFileName method solves these issues for creating files. It creates a unique file under GetTempPath that is only readable and writable by the current user.

On Windows, the value returned by GetTempPath can be controlled using the TMP/TEMP environment variables. On Linux, this can be done using TMPDIR.

On systems with systemd, like Fedora and Red Hat Enterprise Linux (RHEL), a user-private temporary directory is available and can be located using the XDG_RUNTIME_DIR environment variable.

Conclusion

In this article, you’ve seen the features and limitations of using Environment.GetFolderPath, Temp.GetTempPath and Temp.GetTempFileName in your cross-platform .NET Core applications.

Here are some additional .NET Core articles that might be helpful:

 

Take advantage of your Red Hat Developers membership and download RHEL today at no cost.

Share