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