[fpc-pascal] DoDirSeparators and special filenames on Windows

Jürgen Hestermann juergen.hestermann at gmx.de
Sat Sep 7 16:19:17 CEST 2013


Am 2013-09-07 12:52, schrieb Bart:

 > Note that '//?/C:/foo/bar' in contrast happily opens 'C:\foo\bar'
 > (nice touch from M$).

Why is this a "nice touch"? IMO it is crap. This kind of inconsistencies in MS programs lead to false assumptions and mislead the user (programmer). If characters are forbitten then they should be forbidden and not *sometimes* be allowed and *sometimes* be converted to something different. Being consequent in this respect would have saved a lot of headaches for generations of programmers.


 > My question is: should DoDirSeparators handle this case and leave the
 > filename untouched if it starts with '\\?\', or should we leave it up
 > to the programmer not calling functions like ExpandFilename if a
 > filename starts with '\\?\'

In general I would prefer if this '\\?\' prefix is hidden away from the programmer at all. File names should be just that: File names. On windows *every* API function that expects file names should be prepended with '\\?\' internally while for Linux this should be omitted. But the programmer should not need to know about this. Internal functions that use WinAPI calls should add the prefix theirself in all cases(!) (if supported) and omit it for Linux. For all situations where the file name is moved/converted from one function to another (when no OS-API functions is involved) it should just leave the name as it is.

I have written my own functions which do this because it is a nightmare to find out which kind of string encoding is expected/required by what function within FPC/Lazarus. The documentation just says "string" but never says whether it is UTF8, UTF16 or whatever. So when I want to be sure what happens I have to reinvent the wheel and write my own OS independend functions which in my case *always* expect UTF8 (which is my standard encoding in all situations) and convert to UTF16 and add the prefix '\\?\' when feeding it to a WinAPI function:

function WinAPIPathName(const Pfad : UTF8String) : UTF8String;
begin
Result := Pfad;
if (length(Result)<=3) or
    (Result[1]='\') and
    (Result[2]='\') and
    (Result[3]='?') and
    (Result[4]='\') then exit;
if (length(Result)>=2) and
    (Result[1]='\') and
    (Result[2]='\') then      // UNC-path, i.e. "\\server\share"
    begin
    Result := '\\?\UN'+Result;
    Result[7] := 'C';
    end
else
    Result := '\\?\'+Result;
end;

which I then call like this:

H  := FindFirstFileW(pwidechar(UTF8Decode(WinAPIPathName(Pathname))),FW);

This way I don't need to bother about path lengths and unicode issues. I just use UTF8 in my programs. And when I need other encodings I know that I have to convert them before calling functions that need UTF8.

What I would expect as a programming interface when writing programs for multiple operating systems would be functions like
RenameFileUTF8 and RenameFileUTF16 where I know that I have to use UTF8 (Ansi-)Strings or UTF16 (Ansi-)Strings as parameter without the need to prepend '\\?\' because it is done (if needed) by the internal OS-dependend function.





More information about the fpc-pascal mailing list