9

If I have a string that resolves to a file path in Windows, is there an accepted way to get a canonical form of the file name?

For example, I'd like to know whether

C:\stuff\things\etc\misc\whatever.txt

and

C:\stuff\things\etc\misc\other\..\whatever.txt

actually point to the same file or not, and store the canonical form of the path in my application.

Note that simple string comparisons won't work, nor will any RegEx magic. Remember that we have things like NTFS reparse points to deal with since Windows 2000 and the new Libraries structure in Windows 7.

dthrasher
  • 36,940
  • 32
  • 104
  • 137

8 Answers8

10

Short answer: not really.

There is no simple way to get the canonical name of a file on Windows. Local files can be available via reparse points, via SUBST. Do you want to deal with NTFS junctions? Windows shortcuts? What about \\?\-escaped filenames

Remote files can be available via mapped drive letter or via UNC. Is that the UNC to the origin server? Are you using DFS? Is the server using reparse points, etc.? Is the server available by more than one name? What about the IP address? Does it have more than one IP address?

So, if you're looking for something like the inode number on Windows, it ain't there. See, for example, this page.

Roger Lipscombe
  • 81,986
  • 49
  • 214
  • 348
  • +1 for the link to the nice blog post 'PathCanonicalize Versus What It Says On The Tin' – sean e Nov 29 '09 at 21:13
  • 4
    Wow. What a mess. Why is nothing easy in Windowsland? – dthrasher Dec 01 '09 at 01:59
  • -1: [GetFileInformationByHandle](http://msdn.microsoft.com/en-us/library/aa364952%28VS.85%29.aspx) is *exactly* the "something like the inode number" that you say ain't there. It's there alright. – Roman Starkov Apr 04 '12 at 17:24
  • 4
    From that very page: "Depending on the underlying network features of the operating system and the type of server connected to, the GetFileInformationByHandle function may *fail*, return *partial information*, or full information for the given file." – Roger Lipscombe Apr 05 '12 at 12:38
  • You can check the inode: https://stackoverflow.com/questions/7162164/does-windows-have-inode-numbers-like-linux . – Erik Aronesty Dec 08 '20 at 16:27
4

GetFinalPathNameByHandle appears to do what your asking for, which is available starting with Windows Vista.

user75810
  • 859
  • 7
  • 13
4

Roger is correct, there is no simple way. If the volume supports file a unique file index, you can open the file and call GetFileInformationByHandle, but this will not work on all volumes.

The Windows API call GetFullPathName may be the best simple approach.

Stephen Nutt
  • 3,188
  • 1
  • 19
  • 20
  • +1... also the file index is only unique w.r.t. the volume, so you need the volume serial number as well, assuming that it's available remotely. – Roger Lipscombe Nov 30 '09 at 07:46
3

Using FileInfo (example in C#):

FileInfo info1 = new FileInfo(@"C:\stuff\things\etc\misc\whatever.txt");
FileInfo info2 = new FileInfo(@"C:\stuff\things\etc\misc\other\..\whatever.txt");
if (info1.FullName.Equals(info2.FullName)) {
    Console.WriteLine("yep, they're equal");
}
Console.WriteLine(info1.FullName);
Console.WriteLine(info2.FullName);

Output is:

yep, they're equal
C:\stuff\things\etc\misc\whatever.txt
C:\stuff\things\etc\misc\whatever.txt

jheddings
  • 24,331
  • 7
  • 47
  • 62
  • I'm not sure that Equals check will be useful -- as far as I can tell FileInfo doesn't override Equals so this will just give you reference equality, not file path equivalence. Thus, in your example, info1.Equals(info2) returns false. – itowlson Nov 29 '09 at 20:54
  • @itowlson: yeah, I noticed too that after I read the docs... I changed my answer and actually tested it this time :) – jheddings Nov 29 '09 at 20:56
  • It's not a complete solution, as @Roger Lipscombe pointed out in his answer. We might have to invoke the 80/20 rule on this one. – dthrasher Dec 01 '09 at 01:57
1

jheddings has a nice answer, but since you didn't indicate which language you are using, I thought I'd give a Python way to do it that also works from the command line, using os.path.abspath:

> python -c "import os.path; print os.path.abspath('C:\stuff\things\etc\misc\other\..\whatever.txt')"
C:\stuff\things\etc\misc\whatever.txt
John Paulett
  • 14,852
  • 4
  • 43
  • 38
0

I would use System.IO.Path.GetFullPath. It takes a string as an input (C:\stuff\things\etc\misc\other..\whatever.txt in your case) and will output a string (C:\stuff\things\etc\misc\whatever.txt).

0

I guess I'm a little late, but you can use System.IO.Path.GetFullPath("C:\stuff\things\etc\misc\other..\whatever.txt") and it will return "C:\stuff\things\etc\misc\whatever.txt"

Hellon
  • 76
  • 4
-2

To get canonical path you should use PathCanonicalize function.

Nex
  • 303
  • 3
  • 9
  • No, the function does not compute a canonical path despite its name. See "Remarks" section of its documentation: the function does only simple string corrections. E.g. for "..\someFile.txt" it will return "someFile.txt" when programmers expect something like "C:\someDir\someFile.txt" as a canonical path. – Serge Rogatch Jul 06 '15 at 14:40