1

Description

I have a C program that needs to check if a file exists using stat. That file can have a path longer than MAX_PATH (260 characters) and is therefore prefixed with \\?\, see Windows documentation. Also special characters are allowed, so it is using wide characters wchar_t* instead of char*.

When compiling the C program with MSVC 19 _wstat can find the file \\?\D:\path\to\test.txt (an empty text file). But when compiling the same program with MING64 _wstat is failing and the file can't be found.

How to reproduce

main.c

#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <wchar.h>

int main(void) {
  //wchar_t* unicodeLongFileName = L"D:\\path\\to\\test.txt";  // Works fine
  wchar_t* unicodeLongFileName = L"\\\\?\\D:\\path\\to\\test.txt";  // Broken in MSYS2

  struct _stat statbuf;
  int res = _wstat(unicodeLongFileName, &statbuf);

  if (res != 0) {
    perror( "Problem getting information" );
    switch (errno)
    {
    case ENOENT:
      wprintf(L"File \n%ls\n not found.\n", unicodeLongFileName);
      break;
    case EINVAL:
      printf("Invalid parameter to _stat.\n");
      break;
    default:
      /* Should never be reached. */
      printf("Unexpected error in _stat.\n");
    }
    return -1;
  }

  wprintf(L"Found file \n%ls\n.\n", unicodeLongFileName);
  return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)
project(Test_wstat)
add_executable(Test_wstat main.c)

Compile the MSVC version with:

cmake -S . -B build_msvc
cmake --build build_msvc/ --config Release

and the MSYS2 version with:

$ cmake -S . -B build_mingw -G "MSYS Makefiles" -DCMAKE_C_COMPILER=gcc -Wno-dev
$ make -C build_mingw/ all -Oline

This is using gcc 12.2 from MINGW64 (MINGW64_NT-10.0-22621 version 3.3.4-341.x86_64) on Windows 11.

Running the code

Create a new text file

$ touch /d/path/to/test.txt

and then run the programs

> ./build_msvc/Release/Test_wstat.exe
Found file 
\\?\D:\workspace\Testitesttest\issue-10898\test_c_program\test.txt
$ ./build_mingw/Test_wstat.exe
Problem getting information: No such file or directory
File
\\?\D:\path\to\test.txt
 not found.
AnHeuermann
  • 761
  • 3
  • 14
  • to check if a file exists use `GetFileAttributesW` or `RtlDoesFileExists_U` . '_wstat` implemented in several different libs and can have different implementation. – RbMm Jul 04 '23 at 10:49
  • Thanks @RbMm. With the info that there are multiple versions of `_wstat` is was able to find that the MSVC version links to universal C runtime `C:\WINDOWS\System32\ucrtbase.dll` while the MING64 version links to `C:\WINDOWS\System32\msvcrt.dll` which is there for backwards compatibility, I guess. For me switching from MSYS2/MINGW64 to MSYS2/UCRT64 is perfectly fine and solves the issue. Using GetFileAttributesW would work for this question, but my program needs to support stat as well and having the same interface for Windows and Linux simplifies things immensely. – AnHeuermann Jul 04 '23 at 13:44
  • Yes, different crt links to different dlls or static libs and implementation in msvsrt.dll and in ucrtbase.dll is different. Same exported function, but different code – RbMm Jul 04 '23 at 15:19

1 Answers1

0

The issue is that there are two variants of the C runtime on Windows. Both implement the_wstat function, but they differ in functionality.

While Visual Studio links to the newer UCRT (Universal C Runtime) the MINGW64 environment links to MSVCRT (Microsoft Visual C++ Runtime) which is older and missing the needed features to handle \\?\.

But I guess the answer to my original question is, that it's not possible to handle \\?\ using _wstat in MINGW64. One could use other functions to test if a file exists (@RbMm suggested GetFileAttributesW and RtlDoesFileExists_U). The solution for my specific case is to switch to the UCRT64 environment of MSYS2 which links to the UCRT.

AnHeuermann
  • 761
  • 3
  • 14