Home > other >  Is there any way to get Environment.SpecialFolder from file path?
Is there any way to get Environment.SpecialFolder from file path?

Time:04-29

I have a requirement where I want to get the Environment.SpecialFolder value from a file path.

Eg -

string filePath = @"C:\Program Files (x86)\text.txt"
//SpecialFolder sf = GetSpecialFolderAssociatedWithPath(filePath); // sf will be ProgramFilesX86
//Need something similar

I want to further use the sf to generate another path, so if we get the path corresponding to that particular SpecialFolder, that will work as well.

CodePudding user response:

You could do it something like this (assuming you want to get the actual enum value for the special folder):

public static Environment.SpecialFolder? FindSpecialFolder(string filePath)
{
    filePath = Path.GetFullPath(filePath);

    foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
    {
        string directory = Environment.GetFolderPath(folder);

        if (directory.Length > 0 && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
            return folder;
    }

    return null;
}

Note that I had to return a nullable because Microsoft failed to follow their own guidelines and didn't include a special "None" zero value in the Environment.SpecialFolder enum that I could return to indicate "not found".

The usage would be something like this:

string filePath = @"C:\Program Files (x86)\text.txt";

var folder = FindSpecialFolder(filePath);

if (folder == null)
    Console.WriteLine("No folder found");
else
    Console.WriteLine(folder.Value);

If you want both the path and the enum value, you could return them both in a tuple:

public static (Environment.SpecialFolder? specialFolder, string? directory) FindSpecialFolder(string filePath)
{
    filePath = Path.GetFullPath(filePath);

    foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
    {
        string directory = Environment.GetFolderPath(folder);

        if (directory.Length > 0 && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
            return (folder, directory);
    }

    return default;
}

Which you could use like:

var folder = FindSpecialFolder(filePath);

if (folder.specialFolder == null)
    Console.WriteLine("No folder found");
else
    Console.WriteLine($"{folder.specialFolder.Value}: {folder.directory}");

Actually, it's possible that some of the special folders may be nested underneath other special folders, for example you might have:

C:\Path1\Path2
C:\Path1\Path2\Path3

In that event, the code above will return the first path that it matches, rather than the longest; i.e. looking for "C:\Path1\Path2\Path3\SomeFile.txt" might return the special folder for "C:\Path1\Path2" rather than the one for "C:\Path1\Path2\Path3".

If you want to handle that possibility, you'll have to find the longest matching path, for example:

public static (Environment.SpecialFolder? specialFolder, string? directory) FindSpecialFolder(string filePath)
{
    filePath = Path.GetFullPath(filePath);

    int longest = 0;
    Environment.SpecialFolder? longestFolder = null;
    string? longestDir = null;

    foreach (var folder in Enum.GetValues<Environment.SpecialFolder>())
    {
        string directory = Environment.GetFolderPath(folder);

        if (directory.Length > longest && filePath.StartsWith(directory, StringComparison.OrdinalIgnoreCase))
        {
            longestDir    = directory;
            longestFolder = folder;
            longest       = directory.Length;
        }
    }

    return (longestFolder, longestDir);
}

And use it like:

var folder = FindSpecialFolder(filePath);

if (folder.specialFolder == null)
    Console.WriteLine("No folder found");
else
    Console.WriteLine($"{folder.specialFolder.Value}: {folder.directory}");

Another thing to be aware of is that multiple special folders might have the same path. In this case it's not possible to differentiate them, so the code will just return the first match it finds.

Also note the use of filePath = Path.GetFullPath(filePath); to ensure that relative paths are converted to absolute paths, otherwise the matching likely wouldn't work.

CodePudding user response:

I'm not aware of any existing function to do this, but rolling your own isn't too difficult. For example:

private static bool TryGetSpecialFolderAssociatedWithPath(string filePath, out Environment.SpecialFolder sf)
{
    foreach (Environment.SpecialFolder value in Enum.GetValues(typeof(Environment.SpecialFolder)))
    {
        string path = Environment.GetFolderPath(value);
        if (!string.IsNullOrEmpty(path) && filePath.StartsWith(path   "\\"))
        {
            sf = value;
            return true;
        }
    }

    sf = default; // Actually the same as Desktop
    return false;
}

Usage:

string filePath = @"C:\Program Files (x86)\text.txt";
if (TryGetSpecialFolderAssociatedWithPath(filePath, out Environment.SpecialFolder sf))
{
    Console.WriteLine("Special folder is "   sf);
}

This produces the following output:

Special folder is ProgramFilesX86

One fine point to note is that I append a backslash to the returned path. If I didn't, the code snippet would've hit ProgramFiles before ProgramFilesX86.

  • Related