3

If I create a directory that contains "Hello.txt" then the following code will output "Hello.txt":

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

int _tmain(int argc, _TCHAR* argv[])
{
    WIN32_FIND_DATA findData = {};
    HANDLE hFind = ::FindFirstFile(L"<.txt", &findData);
    if (hFind != INVALID_HANDLE_VALUE)
    {
        std::wcout << findData.cFileName << std::endl;
        while (::FindNextFile(hFind, &findData))
        {
            std::wcout << findData.cFileName << std::endl;
        }
        ::CloseHandle(hFind);
    }
    else
    {
        std::wcout << "FindFirstFile: " << ::GetLastError() << std::endl;
    }
    return 0;
}

But why does "<" act like "*" in the call to FindFirstFile?

MSDN says that only wild card charterers are valid in this call (i.e ? or *).

https://msdn.microsoft.com/en-us/library/windows/desktop/aa364418%28v=vs.85%29.aspx

Edit:

Seems to be very related FindFirstFile undocumented wildcard or bug?

Community
  • 1
  • 1
paulm
  • 5,629
  • 7
  • 47
  • 70
  • It is a wildcard character. Probably the best place to see the `<`, `>` and `"` wildcards documented (copied from the internal Windows Name.c source file) is the .NET Reference Source, [PatternMatcher.cs](http://referencesource.microsoft.com/#System/services/io/system/io/PatternMatcher.cs) source file. – Hans Passant Mar 13 '15 at 11:34
  • So the behavior isn't quite the same as *, since it says "DOS_QM matches any single character, or upon encountering a period or end of name string, advances the expression to the end of the set of contiguous DOS_QMs." – paulm Mar 13 '15 at 12:12

1 Answers1

6

< is an invalid character in a file name. It is a reserved character, as documented here: Naming Files, Paths, and Namespaces – Naming Conventions.

My guess is that the implementation of FindFirstFile simply treats all reserved characters as wild cards. Perhaps there is a backwards compatibility reason for this. All the same, since < is officially invalid here, and since the behaviour is undocumented, you should not rely on it.

Update

Thanks to @eryksun for providing some low-level details in comments. The low-level layer that actually performs the search uses < and > as wild cards. The Win32 layer translates * and ? to < and > before passing the search string on to the lower layer. Which explains why * and < are interchangeable from the high level.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • interestingly Microsoft Outlook is doing this – paulm Mar 13 '15 at 10:20
  • 1
    It calls `NtQueryDirectoryFile`, which has to implement DOS semantics here. For this the filesystem runtime library defines `DOS_DOT` (`L'"'`), `DOS_QM` (`L'>'`) and `DOS_STAR` (`L'<'`). See [`FsRtlIsNameInExpression`](https://msdn.microsoft.com/en-us/library/windows/hardware/ff546850%28v=vs.85%29.aspx) for more details. – Eryk Sun Mar 13 '15 at 10:58
  • I'm not recommending that people use these characters instead of the normal DOS wildcards. This is just for anyone curious about how `FindFirstFileEx` translates DOS wildcards into the NT domain, and to explain why `"`, `<`, and `>` are reserved. There of special significance to the shell, which means they were available for use as NT wildcard characters. – Eryk Sun Mar 13 '15 at 11:16
  • @eryksun So, the Win32 layer translates `*` into `<` before passing the string on to the lower layers. Is that how it goes? – David Heffernan Mar 13 '15 at 11:22
  • Yes, that's how it goes. – Eryk Sun Mar 13 '15 at 11:22
  • I guess FsRtlIsNameInExpression info seals the deal :) – paulm Mar 13 '15 at 12:09
  • An qualified affirmative was really oversimplifying. It's not a simple translation. If you're curious you can use the console debugger to see where the translation occurs. Here's a `wildshow` console command for 64-bit Windows with cdb.exe on the `PATH`: `doskey wildshow=cdb -c "bp ntdll!NtQueryDirectoryFile;g;g;dS poi(@rsp + a*8);q" cmd /c dir $*`. For example `wildshow *.?` shows the filename pattern is translated to `<">`. – Eryk Sun Mar 13 '15 at 12:19