2
auto path = std::filesystem::path("c:") / "PosteClient.log";

results in c:PosteClient.log instead of c:\PosteClient.log

This is a strange behavior for me as the result on windows can't be used e.g.

CreateFile("c:PosteClient.log")

fails with ERROR_FILE_NOT_FOUND.

I can't find the reason for this behaviour in the docu but from the example it looks like this is the expected behaviour. https://en.cppreference.com/w/cpp/filesystem/path/append

I want to understand why the behaviour is like this to find a proper solution for my code which works for all scenarios of using this operator

Ok lets be more precise, let's assume i have the following code:

HANDLE CreateHandleFromPath(const std::filesystem::path& path, const std::string& fileName)
{
    auto pathComplete = path / fileName;

    auto* hFile = CreateFileW(
        pathComplete.wstring().c_str(),
        GENERIC_READ,
        FILE_SHARE_READ, nullptr, OPEN_EXISTING,
        0,
        nullptr);

    if (hFile == INVALID_HANDLE_VALUE || hFile == nullptr)
    {
        throw std::filesystem::filesystem_error(
            "Can't get handle",
            std::error_code(::GetLastError(), std::system_category()));
    }

    return hFile;
}

The working directory is

C:\SRC\ConsoleApplication3\

When i call now this code with

CreateHandleFromPath(std::filesystem::path("c:\\"), "test.log")

The function succeed because the result path is "c:\test.log"

CreateHandleFromPath(std::filesystem::path("c:"), "test.log")

The function fails because the result path is "c:test.log"

I don't have control over the caller. For sure it is easy to make here a check and add the separator by hand but this would mean i can do it all the time by myself and don't need the operator or even more precise it is more secure to add it by myself because this will work in all situations, the operator/ works for all except if someone calls with the drive letter without the backslash. I only want to understand why the function is working like that because i think there is a reason behind which i don't see currently

usurpbrain
  • 21
  • 2
  • I think the behaviour is limit to the drive letters on Windows. But as you know it exists, you can manually add the file-separator: `std::filesystem::path::preferred_separator` – Mansoor Aug 19 '20 at 11:38
  • 2
    thats `operator/` and `operator=`, not `operator/=` – 463035818_is_not_an_ai Aug 19 '20 at 11:39
  • Does it matter if you use relative path `std::filesystem::path("c:")` versus absolute path `std::filesystem::path("c:/")`? – Eljay Aug 19 '20 at 11:49
  • 4
    "C:" is a drive-relative path. It resolves to the working directory on the drive. Joining with "PosteClient.log" yields "C:PosteClient.log". If the working directory on the drive is "C:\Spam", then the resolved path will be "C:\Spam\PosteClient.log". – Eryk Sun Aug 19 '20 at 11:49
  • 1
    If drive "C:" isn't the drive of the process working directory, then the system gets the per-drive working directory from a hidden environment variable named "=C:". If that variable isn't defined, or references a non-existent path on the drive, then the system uses the root directory on the drive. – Eryk Sun Aug 19 '20 at 11:50
  • ok this means c: is processed as a relative path which is what i wanted to understand. I never said it is a bug i said i want to understand why it is implemented like that :D thanks @ErykSun – usurpbrain Aug 19 '20 at 12:33

1 Answers1

0

This is because C:PosteClient.log is in fact a valid path, and can very well be different from C:\PosteClient.log.

The key point is that Windows processes maintain per-drive working directories.

Let's say:

  • C:\THING\PosteClient.log and D: exists
  • cd C:\THING
  • D:

Now, type C:PosteClient.log can find your file correctly, whereas type C:\PosteClient.log cannot (and shouldn't).

Sz.
  • 3,342
  • 1
  • 30
  • 43