views:

1441

answers:

6

I'm trying to write a static member function in C# or find one in the .NET Framework that will re-case a file path to what the filesystem specifies.

Example:

string filepath = @"C:\temp.txt";
filepath = FileUtility.RecaseFilepath(filepath);

// filepath = C:\Temp.TXT
// Where the real fully qualified filepath in the NTFS volume is C:\Temp.TXT

I've tried the following code below and many variants of it and it still doesn't work. I know Windows is case-insensitive in general but I need to pass these file paths to ClearCase which considers file path casing since it's a Unix and Windows application.

public static string GetProperFilePathCapitalization(string filepath)
{
    string result = "";

    try
    {
        result = Path.GetFullPath(filepath);
        DirectoryInfo dir = new DirectoryInfo(Path.GetDirectoryName(result));
        FileInfo[] fi = dir.GetFiles(Path.GetFileName(result));
        if (fi.Length > 0)
        {
            result = fi[0].FullName;
        }
    }
    catch (Exception)
    {
        result = filepath;
    }

    return result;
}
+1  A: 

You can search for the file you want to get the case on and return the results of your search (you want to check the casing of a file that exists, right?). Something like this:

public static string GetProperFilePathCapitalization(string filepath) {
   string directoryPath = Path.GetDirectoryName(filepath);
   string[] files = Directory.GetFiles(directoryPath, Path.GetFileName(filepath));
   return files[0];
}

Is this what you're looking for?

Jay Riggs
A: 

No this doesn't solve the answer completely. I was getting the proper capitalization for the file name but the rest of the path was all lowercase. The paths I'd send to this function are all lowercased.

Jeremy Edwards
+3  A: 

This is a pretty simple implementation that assumes that the file and directories all exist and are accessible:

static string GetProperDirectoryCapitalization(DirectoryInfo dirInfo)
{
    DirectoryInfo parentDirInfo = dirInfo.Parent;
    if (null == parentDirInfo)
        return dirInfo.Name;
    return Path.Combine(GetProperDirectoryCapitalization(parentDirInfo),
                        parentDirInfo.GetDirectories(dirInfo.Name)[0].Name);
}

static string GetProperFilePathCapitalization(string filename)
{
    FileInfo fileInfo = new FileInfo(filename);
    DirectoryInfo dirInfo = fileInfo.Directory;
    return Path.Combine(GetProperDirectoryCapitalization(dirInfo),
                        dirInfo.GetFiles(fileInfo.Name)[0].Name);
}

There is a bug with this, though: Relative paths are converted to absolute paths. Your original code above did the same, so I'm assuming that you do want this behavior.

Ants
This breaks on UNC paths unfortunately. I need it to work on UNC paths. Like \\SERVERNAME\Hostdir\MyDocument.docx
Jeremy Edwards
I'm going to put this as the answer. For my needs it works the best. I can add additional hacks on top of this hack to make it work but it turns out I'm leaving the feature out because of complications.
Jeremy Edwards
I found `dirInfo.Name` was just plain wrong for UNC shares, because it drops the server name. For my purposes, `dirInfo.FullName.ToUpperInvariant()` is good enough - it doesn't recover the correct case for the share name, but it does construct a valid path.
Weeble
A: 

This code works but it's inefficient. Is there a better way to do this?

Jeremy Edwards
A: 

I have something more efficient but:

1) It doesn't seem to work for all cases. (I've not figured out the pattern of which files and directories it correctly gets the casing, and which ones it does not.)

2) It's Windows specific.

static string GetProperFilePathCapitalization1(string filename)
{
    StringBuilder sb = new StringBuilder(260);
    int length = GetLongPathName(filename, sb, sb.Capacity);

    if (length > sb.Capacity)
    {
        sb.Capacity = length;
        length = GetLongPathName(filename, sb, sb.Capacity);
    }

    if (0 == length)
        throw new Win32Exception("GetLongPathName");

    return sb.ToString();
}

[DllImport("kernel32.dll")]
static extern int GetLongPathName(string path, StringBuilder pszPath, int cchPath);
Ants
+1  A: 

The below works fine to the extent I tested... only catch is that the API used is available only in Vista.

static void Main(string[] args)
{
    using (FileStream fs = File.OpenRead(@"D:\temp\case\mytest.txt"))
    {
     StringBuilder path = new StringBuilder(512);
     GetFinalPathNameByHandle(fs.SafeFileHandle.DangerousGetHandle(), path, path.Capacity, 0);
     Console.WriteLine(path.ToString());
    }
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen, int flags);
Shiva
Nice solution for Vista. Beware that this resolves symbolic links. See remarks at:http://msdn.microsoft.com/en-us/library/aa364962(VS.85).aspx
Ants