Re: FindFirstFileW

From: Rob Kennedy (me3_at_privacy.net)
Date: 03/08/05


Date: Tue, 08 Mar 2005 11:09:14 -0600

Dave Turner wrote:
> Ive been using other languages all my life and im new to Delphi so I'd just
> love to see a unicode version of FindFirstFile/FindNextFile, as thatd be
> almost like a good Hello World ansi vs unicode comparison
>
> MessageBoxA/W would also be sufficient, just not as fun 8)

Mostly, you just just tack a W to the end of every API function and type.

I searched Google for "FindFirstFile FindNextFile Delphi" and found this
example. I'll comment on it to describe what changes are necessary to
make it use Unicode.

http://www.luckie-online.de/Delphi/Sonstiges/FindFirstFile.html

type
   TStringDynArray = array of string;

First change: string -> WideString

var
   FileCount: Cardinal = 0;
   Files : TStringDynArray = nil;

function FindAllFiles(RootFolder: string; Mask: string = '*.*'; Recurse:
Boolean = True): TStringDynArray;

Declare those string parameters as WideStrings, too. Also, declare Mask
as const. It should have been const in the non-Unicode version, too.
Without const, this function makes a copy of the parameter, and copying
a WideString is considerably more expensive than copying an AnsiString.

As long as you're changing the declaration, go ahead and turn this into
a procedure instead of a function. It never returns anything anyway.

var
   wfd : TWin32FindData;

Declare the above as TWin32FindDataW.

   hFile : THandle;
begin
   if AnsiLastChar(RootFolder)^ <> '\' then
     RootFolder := RootFolder + '\';

I don't know whether there is a WideLastChar function, but you don't
need it. Wide characters are always two bytes wide, whereas AnsiString
characters can have multiple widths. That's why this code calls
AnsiLastChar. You can do the WideString check with this:
RootFolder[Length(RootFolder)] <> '\'.

   if Recurse then
   begin
     hFile := FindFirstFile(PChar(RootFolder + '*.*'), wfd);

Tack a W on the end of that function, and cast to PWideChar instead of
PChar.

     if hFile <> INVALID_HANDLE_VALUE then
     try
       repeat
         if wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY =
           FILE_ATTRIBUTE_DIRECTORY then
           if (string(wfd.cFileName) <> '.') and (string(wfd.cFileName)
<> '..')

Don't cast those arrays to WideString. This code shouldn't cast to
AnsiString in the first place. It should instead use the function
designed for comparing character arrays, StrComp. For WideChar arrays,
you can use StrCompW from the JclWideString or JclUnicode units if you
have the JCL, or you can call the StrCmpW API function. (Delphi's
function name has an "o" in it; Windows' doesn't.)

             then
             FindAllFiles(RootFolder + wfd.cFileName, Mask, Recurse);
       until FindNextFile(hFile, wfd) = False;

Tack a W on the end of that function's name, too.

     finally
       windows.FindClose(hFile);

There is no FindCloseW; it isn't necessary. FindClose already knows how
the find handle was opened.

     end;
   end;
   hFile := FindFirstFile(PChar(RootFolder + '*.*'), wfd);

Add a W and cast to PWideChar.

   if hFile <> INVALID_HANDLE_VALUE then
   try
     repeat
       if wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <>
         FILE_ATTRIBUTE_DIRECTORY then
       begin
         FileCount := length(Files)+1;
         Setlength(Files, FileCount);
         Files[FileCount - 1] := RootFolder + String(wfd.cFileName);

This is a terrible example of how to use a dynamic array, but I didn't
choose this example for its dynarray use. Regardless, the type cast to
AnsiString isn't necessary, and neither is a cast to WideString. Just
append wfd.cFileName directly to RootFolder.

       end;
     until FindNextFile(hFile, wfd) = False;

Add a W to the name.

   finally
     Windows.FindClose(hFile);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
   i : Integer;
begin
   Files := nil;
   FindAllFiles('d:\Dokumente', '*.*', True);
   for i := 0 to length(Files) - 1 do
   begin
     Listbox1.Items.Add(Files[i]);

This last part is probably the most frustrating part about using Unicode
with Delphi. The VCL controls don't support Unicode at all, so adding
all your Unicode strings to a non-Unicode list box undoes all the
changes you made above. I urge you to go download Troy Wolbrink's Tnt
Unicode controls. They're all descendants of Borland's non-Unicode
controls, so they have the same functionality as their ancestors.
Replace the TListBox with a TTntListBox, and the code in this event
handler won't need to change at all. TTntListBox introduces a
Unicode-aware Items property.

   end;
end;

-- 
Rob

Quantcast