4

greetings from Iceland!

To begin with, I have searched both with google and here with no result.

I'm writing in Delphi and have couple of years experience in Delphi (over 30 years in Pascal)

I have build over these years variety of files utility's programs and used the functions FindFirst and FindNext in almost every such app. Today I was using a old file-utility program I wrote and found out it didn't work 100%

It was ok until the directory name got somehow strange, i.e. name with couple of periods inside. The story is I was installing Wamp and some folders there was named with this strange method.

As: "c:\wamp\apps\phpmyadmin3.4.5"
    "c:\wamp\apps\sqlbuddy1.3.3"
    "c:\wamp\apps\webgrind1.0 etc"

When I did debugging, I found out Findnext just returned error 18 which is same error as FindNext return when no more files is to find.

I have tried FindFirstFile and FindNextFile with same result. I'm thinking to try API FindNextFileEx, if this has to do with long file names, but not so optimistic..

Also I notice the attribute in these folders wasn't 16 (hex10) instead it was 8208 (8192+16), but it doesn't have anything to do with this problem as I could for example mask (AND) the attr with $00FF etc.

PROCEDURE TForm_Leit.Finna_Dir (Str_InnDir : STRING);
  VAR
    S_Rec1           : TSearchRec;
    Bo_Buid          : BOOLEAN;
BEGIN
  .
  .
  .
  //Find Dir part
  IF (FindFirst (Str_Inndir+'\*.', faDirectory, S_Rec1) = 0) THEN
  REPEAT
    Bo_Buid := FALSE;
    IF ((S_Rec1.Name = '.') OR (S_Rec1.Name = '..')) THEN
    REPEAT
      Bo_Buid := FindNext (S_Rec1) <> 0;
    UNTIL NOT((S_Rec1.Name = '.') OR (S_Rec1.Name = '..')) OR (Bo_BUid);

    IF NOT(Bo_Buid) THEN
      Finna_Dir (Str_Inndir+'\'+S_Rec1.Name); //Recursion

  UNTIL (FindNext (S_Rec1) <> 0);
END;
4

3 回答 3

8

The pattern you're using ('\*.') only matches with file and directory names without a period in it (which also means no extension for files). Try with:

IF (FindFirst (Str_Inndir+'\*', ...


To eloborate a little, VCL's 'Find..' functions are wrappers around the underlying API's 'Find..' functions which does not really care if you want to search for files or folders (the 'Attr' parameter you can pass to sysutils.FindFirst is a facility provided by VCL, and the VCL can only effect the search outcome by filtering it). So the result of using a '*.' pattern will not be different for a file or a folder. If the pattern cannot return a file name with a period in it, then it also cannot return a folder with a period in it.

The usage of the parameter is the same with the dir command. Navigate to your '..\apps' folder from a command prompt, and if you issue a >dir *. you won't see the folders in your question listed.

You can have a look at and try with different 'Pattern's in the following sample code ,which only uses the API to enumerate files, to see how different wild cards effect the search outcome.

var
  Data: TWIN32FindData;

procedure ListFiles(const Path, Pattern: string; List: TStrings);

  function IsDot: Boolean;
  begin
    Result := (string(Data.cFileName) = '.') or (Data.cFileName = '..');
  end;

  function IsDirectory: Boolean;
  begin
    Result := Bool(Data.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY);
  end;

  procedure PutFileName;
  begin
    if not IsDot then begin
      if IsDirectory then
        List.Add(Path + '\' + Data.cFileName + ' <dir>')
      else
        List.Add(Path + '\' + Data.cFileName);                                  //'
    end;
  end;

var
  FindHandle: THandle;
begin
  FindHandle := FindFirstFile(PChar(Path + Pattern), Data);
  if FindHandle <> INVALID_HANDLE_VALUE then
    try
      PutFileName;

      while FindNextFile(FindHandle, Data) do begin
        PutFileName;
        if (not IsDot) and IsDirectory then
          ListFiles(Path + '\' + Data.cFileName, Pattern, List);                //'
      end;

    finally
      windows.FindClose(FindHandle);
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  Pattern = '\*';
begin
  Memo1.Clear;
  ListFiles('..sometestdirectory..', Pattern, Memo1.Lines);
end;
于 2012-04-23T11:25:20.687 回答
0

There are different method to avoid this issue:

1/ Use complete filter (update is for folders)

  FA_ALL_FILES_EX = faNormalFile +
    faReadOnly + faHidden + faSysFile + faArchive + faTemporary + faSparseFile
    + faReparsePoint + faCompressed + faOffline + faNotContentIndexed + faEncrypted;  

  // $80 must be added because if the file's archive attribute is not set,
  // then FindFirst return [FindoInfo.Attr = 128]  ...

2/ Use the latest function from Delphi2009+ unit IOUtils : IOUtils.TDirectory.GetDirectories() http://docwiki.embarcadero.com/VCL/XE2/en/IOUtils.TDirectory.GetDirectories

于 2012-04-23T07:11:33.363 回答
0

Do you use the faAnyFile constant? It is wrong and doesn't contain all necessary values. Define (and use) a new constant with a value of $FFFF.

于 2012-04-23T07:14:34.163 回答