Many callers of RaiseLastOsErrorEx build the message in two steps: they first put the file name (or command line) into the format string with Format, and RaiseLastOsErrorEx then uses the result as the format string for EOSError.CreateFmt:
RaiseLastOSErrorEx(LastError, Format(_('Error %%1:s (%%0:d) deleting file "%s"'), [_Filename]));
A % in the file name, which is legal, is then read as a format specifier. Measured with Delphi 12, TFileSystem.DeleteFile on a file that does not exist in a directory that does not exist:
dz100.txt: EOSError: Error The system cannot find the path specified (3) deleting file "C:\dzDoesNotExist\dz100.txt"dz100%.txt: EConvertError: Format 'Error %1:s (%0:d) deleting file' invalid or incompatible with argumentdz%s.txt: EOSError: ... deleting file "C:\dzDoesNotExist\dzThe system cannot find the path specified.txt"So the caller gets the wrong exception class, without the error code, or a garbled message.
Affected calls (all pass a file name, DLL name or command line):
u_dzFileUtils: 12 calls (CreateDir, ForceDir, TouchFileTimes, MoveFile, MoveFileEx, SetReadonly, CopyFile, CopyFileWithProgress, MoveFileWithProgress, DeleteFile, RemoveDir)u_dzWinApi: TWinApi.CreateFile, CreateFileMapping, GetFileSizeExu_dzFileStreams: TdzFile.Openu_dzAsyncExec: TAsyncExec.doExecute (the command line), and the same in the deprecated u_dzExecutoru_dzDllLoader: TdzDllLoader.LoadDllu_dzResourceDllLoader: TdzResourceDllLoader.GetProcAddressExPossible fixes: an overload of RaiseLastOsErrorEx that takes the additional arguments, so the inserted strings never become part of a format string, or doubling every % in them before the first Format. The first one fits [features:#27], which proposes consolidating these messages anyway.
Fixed in revision #2406