So, we just discovered another non-sensual Windows behavior... With ancient CRTs on ancient systems `setlocale (LC_ALL, "")` will set locale to OEM code page, but FileAPIs will use ANSI code pages.
I still wonder how Win9x system would behave (aren't they DOS-based? I'm sorry if I'm mistaken), but I guess there's nearly zero practical value in testing them, or is it? I actually followed behavior of using OEM code pages by default with crtdll.dll and msvcrt10.dll in my library. Now I question whether I should use ANSI code pages by default with all CRTs... - Kirill Makurin ________________________________ From: Pali Rohár <[email protected]> Sent: Thursday, January 1, 2026 4:09 AM To: Kirill Makurin <[email protected]> Cc: mingw-w64-public <[email protected]> Subject: Re: [Mingw-w64-public] [PATCH 1/3] crt: Improve __mingw_filename_cp() to work on systems without AreFileApisANSI() function Ok, you give me an interesting idea, so I decided to test it today. I prepared the oldest Windows NT 3.1 version which is available here: https://winworldpc.com/product/windows-nt-3x/31 configured system to ANSI CP 1250 and OEM CP 852 and then compiled and run following test code against the system crtdll.dll: #include <stdio.h> #include <io.h> #include <fcntl.h> #include <locale.h> #include <windows.h> /* * UNICODE code point U+ED = LATIN SMALL LETTER I WITH ACUTE (í) * UNICODE code point U+DD = LATIN CAPITAL LETTER Y WITH ACUTE (Ý) * CP1250 0xED = LATIN SMALL LETTER I WITH ACUTE (í) * CP1250 0xDD = LATIN CAPITAL LETTER Y WITH ACUTE (Ý) * CP852 0xA1 = LATIN SMALL LETTER I WITH ACUTE (í) * CP852 0xED = LATIN CAPITAL LETTER Y WITH ACUTE (Ý) */ int main() { HANDLE handle; int fd; printf("WinAPI ANSI codepage: %u\n", GetACP()); printf("WinAPI OEM codepage: %u\n", GetOEMCP()); if (GetACP() != 1250 || GetOEMCP() != 852) { printf("This test is specially for ANSI codepage 1250 and OEM codepage 852\n"); return 1; } handle = CreateFileW(L"C:\\\xED", 0, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { printf("Cannot create file \\xED: %lu\n", GetLastError()); return 1; } CloseHandle(handle); handle = CreateFileW(L"C:\\\xED", 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { printf("Cannot open created file \\xED: %lu\n", GetLastError()); DeleteFileW(L"C:\\\xED"); return 1; } CloseHandle(handle); handle = CreateFileA("C:\\\xDD", 0, 0, NULL, OPEN_EXISTING, 0, NULL); printf("WinAPI CreateFileA \\xDD is mapped to UNICODE \\xED: %s\n", handle != INVALID_HANDLE_VALUE ? "yes" : "no"); if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); handle = CreateFileA("C:\\\xED", 0, 0, NULL, OPEN_EXISTING, 0, NULL); printf("WinAPI CreateFileA \\xED is mapped to UNICODE \\xED: %s\n", handle != INVALID_HANDLE_VALUE ? "yes" : "no"); if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); handle = CreateFileA("C:\\\xA1", 0, 0, NULL, OPEN_EXISTING, 0, NULL); printf("WinAPI CreateFileA \\xA1 is mapped to UNICODE \\xED: %s\n", handle != INVALID_HANDLE_VALUE ? "yes" : "no"); if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); fd = _open("C:\\\xDD", _O_RDONLY); printf("CRT _open \\xDD is mapped to UNICODE \\xED: %s\n", fd >= 0 ? "yes" : "no"); if (fd >= 0) _close(fd); fd = _open("C:\\\xED", _O_RDONLY); printf("CRT _open \\xED is mapped to UNICODE \\xED: %s\n", fd >= 0 ? "yes" : "no"); if (fd >= 0) _close(fd); fd = _open("C:\\\xA1", _O_RDONLY); printf("CRT _open \\xA1 is mapped to UNICODE \\xED: %s\n", fd >= 0 ? "yes" : "no"); if (fd >= 0) _close(fd); printf("Calling setlocale\n"); setlocale(LC_ALL, ""); printf("CRT codepage: %s\n", strchr(setlocale(LC_CTYPE, NULL), '.')); fd = _open("C:\\\xDD", _O_RDONLY); printf("CRT _open \\xDD is mapped to UNICODE \\xED: %s\n", fd >= 0 ? "yes" : "no"); if (fd >= 0) _close(fd); fd = _open("C:\\\xED", _O_RDONLY); printf("CRT _open \\xED is mapped to UNICODE \\xED: %s\n", fd >= 0 ? "yes" : "no"); if (fd >= 0) _close(fd); fd = _open("C:\\\xA1", _O_RDONLY); printf("CRT _open \\xA1 is mapped to UNICODE \\xED: %s\n", fd >= 0 ? "yes" : "no"); if (fd >= 0) _close(fd); DeleteFileW(L"C:\\\xED"); return 0; } The output is: WinAPI ANSI codepage: 1250 WinAPI OEM codepage: 852 WinAPI CreateFileA \xDD is mapped to UNICODE \xED: no WinAPI CreateFileA \xED is mapped to UNICODE \xED: yes WinAPI CreateFileA \xA1 is mapped to UNICODE \xED: no CRT _open \xDD is mapped to UNICODE \xED: no CRT _open \xED is mapped to UNICODE \xED: yes CRT _open \xA1 is mapped to UNICODE \xED: no Calling setlocale CRT codepage: .852 CRT _open \xDD is mapped to UNICODE \xED: no CRT _open \xED is mapped to UNICODE \xED: yes CRT _open \xA1 is mapped to UNICODE \xED: no So from this test it can be seen that the CRT code page after setlocale is the OEM one, but both WinAPI -A and CRT _open functions for filenames are using ANSI codepage. So it matches the __mingw_filename_cp() implementation. Hence filenames are another exception in CRT which do not follow CRT codepage, but rather the codepage returned by our helper __mingw_filename_cp() function. I have found old MS documentation for SetFileApisToOEM() which says: https://web.archive.org/web/20001206100700/http://msdn.microsoft.com/library/psdk/winbase/filesio_5xv1.htm "The 8-bit console functions use the OEM code page by default. All other functions use the ANSI code page by default." I was searching a bit more and I have found another documentation, not sure what is the source, but says the same thing and contains old note: http://winapi.freetechsecrets.com/win32/WIN32SetFileApisToOEM.htm "On Win32s all file APIs are ANSI." So it looks like that for file names are ancient systems using only ANSI, but for console functions they are using only OEM. On Wednesday 31 December 2025 09:53:38 Kirill Makurin wrote: > I have a funny feeling that ancient systems which do not have AreFileApisANSI > may use OEM code page for filenames, but I do not have a way to verify it. > > Remember that crtdll.dll and msvcrt10.dll use OEM code pages by default, this > makes me think that such ancient system would use OEM code pages in general. > > - Kirill Makurin > ________________________________ > From: Pali Rohár <[email protected]> > Sent: Wednesday, December 31, 2025 3:29 AM > To: [email protected] > <[email protected]> > Subject: [Mingw-w64-public] [PATCH 1/3] crt: Improve __mingw_filename_cp() to > work on systems without AreFileApisANSI() function > > It the AreFileApisANSI() function is not available then fallback to the > default value that ANSI (ACP) encoding for filenames is used. > --- > mingw-w64-crt/misc/__mingw_filename_cp.c | 17 ++++++++++++++--- > 1 file changed, 14 insertions(+), 3 deletions(-) > > diff --git a/mingw-w64-crt/misc/__mingw_filename_cp.c > b/mingw-w64-crt/misc/__mingw_filename_cp.c > index f6de486e983b..4fc92fcb269f 100644 > --- a/mingw-w64-crt/misc/__mingw_filename_cp.c > +++ b/mingw-w64-crt/misc/__mingw_filename_cp.c > @@ -10,9 +10,20 @@ > #include <windows.h> > #include <locale.h> > > +/* By default the ANSI (ACP) is used, fallack to default ANSI when function > AreFileApisANSI() is not available */ > +static BOOL WINAPI fallbackAreFileApisANSI(VOID) { return TRUE; } > + > unsigned int __cdecl __mingw_filename_cp(void) > { > - return (___lc_codepage_func() == CP_UTF8) > - ? CP_UTF8 > - : AreFileApisANSI() ? CP_ACP : CP_OEMCP; > + if (___lc_codepage_func() == CP_UTF8) > + return CP_UTF8; > + > + /* Function AreFileApisANSI() is not available in older Windows versions, > so resolve it at runtime */ > + static BOOL (WINAPI *myAreFileApisANSI)(VOID) = NULL; > + if (!myAreFileApisANSI) { > + HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); > + FARPROC farproc = kernel32 ? GetProcAddress(kernel32, "AreFileApisANSI") > : NULL; > + (void)InterlockedExchangePointer((PVOID*)&myAreFileApisANSI, farproc ?: > fallbackAreFileApisANSI); > + } > + return myAreFileApisANSI() ? CP_ACP : CP_OEMCP; > } > -- > 2.20.1 > > > > _______________________________________________ > Mingw-w64-public mailing list > [email protected] > https://lists.sourceforge.net/lists/listinfo/mingw-w64-public _______________________________________________ Mingw-w64-public mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mingw-w64-public
