Best way to determine if two path reference to same file in Windows?

How would I compare 2 strings to determine if they refer to the same path in Win32 using C/C++?

While this will handle a lot of cases it misses some things:

_tcsicmp(szPath1, szPath2) == 0

For example:

  • forward slashes / backslashes

  • relative / absolute paths.

[Edit] Title changed to match an existing C# question.


Solution 1:

Open both files with CreateFile, call GetFileInformationByHandle for both, and compare dwVolumeSerialNumber, nFileIndexLow, nFileIndexHigh. If all three are equal they both point to the same file:

GetFileInformationByHandle function

BY_HANDLE_FILE_INFORMATION Structure

Solution 2:

Filesystem library

Since C++17 you can use the standard filesystem library. Include it using #include <filesystem>. You can access it even in older versions of C++, see footnote.

The function you are looking for is equivalent, under namespace std::filesystem:

bool std::filesystem::equivalent(const std::filesystem::path& p1, const filesystem::path& p2 );

To summarize from the documentation: this function takes two paths as parameters and returns true if they reference the same file or directory, false otherwise. There is also a noexcept overload that takes a third parameter: an std::error_code in which to save any possible error.

Example

#include <filesystem>
#include <iostream>
//...

int main() {
    std::filesystem::path p1 = ".";
    std::filesystem::path p2 = fs::current_path();
    std::cout << std::filesystem::equivalent(p1, p2);
    //...
}

Output:

1

Using filesystem before C++17

To use this library in versions prior to C++17 you have to enable experimental language features in your compiler and include the library in this way: #include <experimental/filesystem>. You can then use its functions under the namespace std::experimental::filesystem. Please note that the experimental filesystem library may differ from the C++17 one. See the documentation here.
For example:

#include <experimental/filesystem>
//...
std::experimental::filesystem::equivalent(p1, p2);

Solution 3:

See this question: Best way to determine if two path reference to same file in C#

The question is about C#, but the answer is just the Win32 API call GetFileInformationByHandle.

Solution 4:

use the GetFullPathName from kernel32.dll, this will give you the absolute path of the file. Then compare it against the other path that you have using a simple string compare

edit: code

TCHAR buffer1[1000];
TCHAR buffer2[1000];
TCHAR buffer3[1000];
TCHAR buffer4[1000];

GetFullPathName(TEXT("C:\\Temp\\..\\autoexec.bat"),1000,buffer1,NULL);
GetFullPathName(TEXT("C:\\autoexec.bat"),1000,buffer2,NULL);
GetFullPathName(TEXT("\\autoexec.bat"),1000,buffer3,NULL);
GetFullPathName(TEXT("C:/autoexec.bat"),1000,buffer4,NULL);
_tprintf(TEXT("Path1: %s\n"), buffer1);
_tprintf(TEXT("Path2: %s\n"), buffer2);
_tprintf(TEXT("Path3: %s\n"), buffer3);
_tprintf(TEXT("Path4: %s\n"), buffer4);

the code above will print the same path for all three path representations.. you might want to do a case insensitive search after that