Home > Mobile >  Why does passing C:* to the Windows API function FindFirstFileA list the files in the current direct
Why does passing C:* to the Windows API function FindFirstFileA list the files in the current direct

Time:01-31

While debugging some seemingly unexpected behaviour with Rust's std::fs::read_dir function, I made a small example of it using the underlying FindFirstFileA Windows API function.

When listing the files using C:\\* as the parameter, files in the root of the C drive are listed, however when C:* is passed, the files in the current directory are listed, identical to the behaviour seen when .\\* is passed.

The demo code:

// FindFirstFileACPPTest.cpp

#include <windows.h>
#include <iostream>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> directories = {
      "C:\\",
      "C:",
      "NonExistentDirectory"
    };

    for (const auto& directory : directories) {
        std::string searchPath = directory   "*";

        WIN32_FIND_DATAA fileData;
        HANDLE searchHandle = FindFirstFileA(searchPath.c_str(), &fileData);

        std::cout << std::endl;

        if (searchHandle == INVALID_HANDLE_VALUE) {
            std::cout << "Error: Could not find files in directory " << directory << std::endl;
            continue;
        }

        std::cout << "Directory: " << directory << std::endl;
        do {
            if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                std::cout << "[DIR]  " << fileData.cFileName << std::endl;
            }
            else {
                std::cout << "[FILE] " << fileData.cFileName << std::endl;
            }
        } while (FindNextFileA(searchHandle, &fileData));

        FindClose(searchHandle);
    }

    return 0;
}

The output:

Directory: C:\
[DIR]  $Recycle.Bin
[DIR]  AMD
[DIR]  Documents and Settings
[DIR]  Drivers
[FILE] DumpStack.log.tmp
[DIR]  Licenses
[FILE] pagefile.sys
[DIR]  PerfLogs
[DIR]  Program Files
[DIR]  Program Files (x86)
[DIR]  ProgramData
[DIR]  Recovery
[FILE] swapfile.sys
[DIR]  System Volume Information
[DIR]  Users
[DIR]  Windows

Directory: C:
[DIR]  .
[DIR]  ..
[FILE] FindFirstFileACPPTest.cpp
[FILE] FindFirstFileACPPTest.vcxproj
[FILE] FindFirstFileACPPTest.vcxproj.filters
[FILE] FindFirstFileACPPTest.vcxproj.user
[DIR]  x64

Error: Could not find files in directory NonExistentDirectory

If C:* was an invalid path in the eyes of FindFirstFileA, I would expect an ERROR_FILE_NOT_FOUND, like is done with the obviously invalid path NonExistentDirectory. Is passing C:* resulting in undefined behaviour? Or is this intentional? I don't see any comments about this in the remarks section.

Any hints as to what is going on?

Cheers

CodePudding user response:

This is by design.

Per Fully Qualified vs. Relative Paths on MSDN:

For Windows API functions that manipulate files, file names can often be relative to the current directory, while some APIs require a fully qualified path. A file name is relative to the current directory if it does not begin with one of the following:

  • A UNC name of any format, which always start with two backslash characters ("\\"). For more information, see the next section.
  • A disk designator with a backslash, for example "C:\" or "d:\".
  • A single backslash, for example, "\directory" or "\file.txt". This is also referred to as an absolute path.

If a file name begins with only a disk designator but not the backslash after the colon, it is interpreted as a relative path to the current directory on the drive with the specified letter. Note that the current directory may or may not be the root directory depending on what it was set to during the most recent "change directory" operation on that disk. Examples of this format are as follows:

  • "C:tmp.txt" refers to a file named "tmp.txt" in the current directory on drive C.
  • "C:tempdir\tmp.txt" refers to a file in a subdirectory to the current directory on drive C.

Also see: Why does each drive have its own current directory?

  • Related