Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Атрибуты поиска


Автор: m6a6g6 17.8.2009, 14:28
Привет всем. 
В общем решил написать программу-поисковик, которая бы искала файлы на компьютере пользователя. Написал работающий алгоритм, но вот когда стал пытаться организовать поиск с заданными атрибутами, то столкнулся с ошибкой "неадекватности" их определения. Использую функции FindFirst и FindNext, они работают со следующими атрибутами:
Код

{faReadOnly $00000001 Только чтение
faHidden $00000002 Скрытый
faSysFile $00000004 Системный
faVolumeID $00000008 Метка диска
faDirectory $00000010 Директория
faArchive $00000020 Обычный
faAnyFile $0000003F Любой файл}

Вся фишка в том, что эти атрибуты могут применяться совместно, то есть складываться, например, файл с атрибутами "только для четения" и "скрытый" имеет атрибут 1+2=3. 
Такая ситуация сильно запутывает, а касательно папок, то тут вообще скрытые игнорируются. 
Кто использовал эти функции может как-то смог это "обойти"?

Автор: CodeMonkey 17.8.2009, 15:00
http://www.delphikingdom.ru/asp/answer.asp?IDAnswer=61608.

Автор: m6a6g6 17.8.2009, 15:17
Цитата(CodeMonkey @ 17.8.2009,  15:00)
http://www.delphikingdom.ru/asp/answer.asp?IDAnswer=61608.

Видимо с этим ничего не поделать? Или все же может поддаться логике? 
У меня было что выдавло атрибут в 8122 это вообще не поддается объяснению.

Автор: CodeMonkey 17.8.2009, 15:50
Я, если честно, вообще не понял, что вы спросили - дал ссылку в тему, в надежде, что вы сами разберётесь.
Если возникли проблемы - попробуйте переформулировать свой вопрос. Потому что в вашей вопросе без миелофона сейчас не разобраться. Скажите чётко, что дано (напр.: "такой-то набор файлов"), что вы делаете (покажите код), что ожидаете ("найдёт/подсчитает то-то и сё-то"), что получаете ("а он находит вотто и вот это"). Тогда вам подскажут, http://gunsmoker.blogspot.com/2008/10/x-y-z.html.

Автор: Romikgy 17.8.2009, 15:51
Цитата(m6a6g6 @  17.8.2009,  13:28 Найти цитируемый пост)
Кто использовал эти функции может как-то смог это "обойти"? 

без вашего кода , ответы бессмыслены

Автор: WandG 17.8.2009, 16:06
Цитата

У меня было что выдавло атрибут в 8122 это вообще не поддается объяснению.


Кажется, на каком-то форуме писали, что большие значения атрибутов получаеются, например, если файл шифрованный или сжатый.

Автор: CodeMonkey 17.8.2009, 16:55
Ну, вообще-то поле Attr - это точная копия TWin32FindData.dwFileAttributes, т.е. комбинация флагов:
Код
  FILE_ATTRIBUTE_READONLY             = $00000001; // = faReadOnly
  FILE_ATTRIBUTE_HIDDEN               = $00000002; // = faHidden
  FILE_ATTRIBUTE_SYSTEM               = $00000004; // = faSysFile
  FILE_ATTRIBUTE_DIRECTORY            = $00000010; // = faDirectory
  FILE_ATTRIBUTE_ARCHIVE              = $00000020; // = faArchive
  FILE_ATTRIBUTE_DEVICE               = $00000040; // = faSymLink
  FILE_ATTRIBUTE_NORMAL               = $00000080; // = faNormal
  FILE_ATTRIBUTE_TEMPORARY            = $00000100; // = faTemporary
  FILE_ATTRIBUTE_SPARSE_FILE          = $00000200;
  FILE_ATTRIBUTE_REPARSE_POINT        = $00000400;
  FILE_ATTRIBUTE_COMPRESSED           = $00000800;
  FILE_ATTRIBUTE_OFFLINE              = $00001000;
  FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  = $00002000;
  FILE_ATTRIBUTE_ENCRYPTED            = $00004000;

(право же: это нетрудно увидеть самому).

Автор: m6a6g6 18.8.2009, 11:08
Ладно, постараюсь получше объяснить. Приведу код.
Код
procedure TfMain.FindFiles(StartFolder, Mask: string; List: TStrings;  //Процедура поиска файлов
  ScanSubFolders: Boolean = True);
var
  SearchRec: TSearchRec;
  FindResult: Integer;
begin
  Application.ProcessMessages;   
  //В этой глобальной переменной хранятся имена найденных файлов         
  List.BeginUpdate;
  //А в этой пути к ним
  Pathes.BeginUpdate;

  //Чтобы не допустить выход за границы массива, очищаем память
  if List.Count>50000 then EmptyMemory;

  try
    StartFolder := IncludeTrailingBackslash(StartFolder);
    //Ищем любой файл
    FindResult := FindFirst(StartFolder + '*.*', faAnyFile, SearchRec);
    try
      //Пока находим, делаем...
      while FindResult = 0 do
        with SearchRec do
        begin
          //Если директория

          //Вот тут возникает проблема, если обычная папка, то ищется нормально
          //Но если папка скрытая, то пропускается, не знаю что и думать
          if (Attr and faDirectory) { or (Attr and 12) or (Attr and 13) } <> 0 then
          begin
            //Если ищем во всех директориях, то...
            if ScanSubFolders and (Name <> '.') and (Name <> '..') then begin
                                                                          //Вызываем процедуру с новыми параметрами
                                                                          FindFiles(StartFolder + Name, Mask, List, ScanSubFolders);
                                                                          //Директорий просмотрено
                                                                          DirectoryCount:=DirectoryCount+1;
                                                                          lDirectoryCount.Caption:=InttoStr(DirectoryCount);
                                                                        end;
          end
          //Иначе
          else
          begin
            //Если соответствует маске, то...
            if MatchesMask(Name, Mask) then
            begin
                 //Здесь я проверял какой элемент выбран в списке ComboBox. В нем они расположены так: 0: Только чтение, 1: Скрытые файлы
                 case cmbAtrib.ItemIndex of
                   0: begin {ShowMessage ('0 '+cmbAtrib.Items[0]);} if Attr=faReadOnly then begin bCheck:=true; List.Add(Name); lFileCount.Caption:=InttoStr(pathcount); Pathes.Add(StartFolder); pathcount:=pathcount+1; end; end;
                   1: begin {ShowMessage ('1 '+cmbAtrib.Items[1]);} if Attr=2+32+1 then begin bCheck:=true; List.Add(Name); lFileCount.Caption:=InttoStr(pathcount); Pathes.Add(StartFolder); pathcount:=pathcount+1; end; end;
                   else begin

{faReadOnly $00000001 Только чтение
faHidden $00000002 Скрытый
faSysFile $00000004 Системный
faVolumeID $00000008 Метка диска
faDirectory $00000010 Директория
faArchive $00000020 Обычный
faAnyFile $0000003F Любой файл}


              //Флаг, указывающий на то, что что-то нашли
              bCheck:=true;
            //Добавляем к списку файлов
              List.Add(Name);
              //Файлов найдено
              lFileCount.Caption:=InttoStr(pathcount);
             //Добавляем запись с папкой, где был найден файл в массив строк
             Pathes.Add(StartFolder);

            //Так я проверял какой атрибут имеет файл, что бы хоть немного понять логику работы функций
            // ShowMessage(InttoStr(Attr));

             //Увеличиваем счетчик директорий
             pathcount:=pathcount+1;
               end; end; //case
            end;
          end;
          //Продложаем поиск
          FindResult := FindNext(SearchRec);
        end;
    //При ошибке освобождаем память
    finally
      FindClose(SearchRec);
    end;
  //При ошибке "закрываем" переменные   
  finally
    List.EndUpdate;
    Pathes.EndUpdate;
  end;
end;


Собственно есть у этого алгоритма один большой минус: при такой проверке:
Код
  if (Attr and faDirectory) <> 0 then

игнорируются скрытые папки, так как имеют атрибут отличный от faDirectory (от 10).
И второе, при запуске поиска по всем дискам компьютера, находит не все файлы, которые по идее надо найти. Допустим я заранее знаю что у меня на диске лежит файл только для чтения, а прога его не находит. Поразмышляв, мне стало понятно что конечный (после сложения) атрибут имеет совсем другое число, и понять из всех атрибутов является ли он скрытым или только для чтения очень трудно, это можно сделать только если файл имеет единичный атрибут (легко проверить в условии). 

Может кто-нибудь наведет на мысль как улучшить алгоритм...

Автор: CodeMonkey 18.8.2009, 11:26
Цитата(m6a6g6 @  18.8.2009,  11:08 Найти цитируемый пост)
Собственно есть у этого алгоритма один большой минус: при такой проверке:
код Pascal/Delphi
1:  if (Attr and faDirectory) <> 0 then

игнорируются скрытые папки, так как имеют атрибут отличный от faDirectory (от 10).

Вы ошиблись. Вы видите что-то другое.

Вот пример:
Код
procedure TForm1.FormCreate(Sender: TObject);

  function FormatAttrs(Attr: Cardinal): String;
  begin
    Result := ' (';
    if (Attr and FILE_ATTRIBUTE_READONLY) <> 0 then
      Result := Result + 'ReadOnly, ';
    if (Attr and FILE_ATTRIBUTE_HIDDEN) <> 0 then
      Result := Result + 'Hidden, ';
    if (Attr and FILE_ATTRIBUTE_SYSTEM) <> 0 then
      Result := Result + 'System, ';
    if (Attr and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
      Result := Result + 'Directory, ';
    if (Attr and FILE_ATTRIBUTE_ARCHIVE) <> 0 then
      Result := Result + 'Archive, ';
    if (Attr and FILE_ATTRIBUTE_DEVICE) <> 0 then
      Result := Result + 'Device, ';
    if (Attr and FILE_ATTRIBUTE_NORMAL) <> 0 then
      Result := Result + 'Normal, ';
    if (Attr and FILE_ATTRIBUTE_TEMPORARY) <> 0 then
      Result := Result + 'Temp, ';
    if (Attr and FILE_ATTRIBUTE_SPARSE_FILE) <> 0 then
      Result := Result + 'Sparse, ';
    if (Attr and FILE_ATTRIBUTE_REPARSE_POINT) <> 0 then
      Result := Result + 'Reparse, ';
    if (Attr and FILE_ATTRIBUTE_COMPRESSED) <> 0 then
      Result := Result + 'Compressed, ';
    if (Attr and FILE_ATTRIBUTE_OFFLINE) <> 0 then
      Result := Result + 'Offline, ';
    if (Attr and FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) <> 0 then
      Result := Result + 'NotIndexed, ';
    if (Attr and FILE_ATTRIBUTE_ENCRYPTED) <> 0 then
      Result := Result + 'Encrypted, ';
    if Length(Result) > 2 then
      SetLength(Result, Length(Result) - 2);
    Result := Result + ')';
  end;

var
  SR: TSearchRec;
begin
  Memo1.Clear;
  Memo2.Clear;
  if FindFirst('C:\*.*', faAnyFile, SR) = 0 then
  try
    repeat
      if (SR.Attr and faDirectory) <> 0 then
        Memo1.Lines.Add(SR.Name + FormatAttrs(SR.Attr))
      else
        Memo2.Lines.Add(SR.Name + FormatAttrs(SR.Attr));
    until FindNext(SR) <> 0;
  finally
    FindClose(SR);
  end;
end;


Он выводит:
Цитата
Папки:

Documents and Settings (Directory)
Inetpub (Directory)
OpenSSL (Directory)
Program Files (ReadOnly, Directory)
Projects (Directory)
RECYCLER (Hidden, System, Directory)
System Volume Information (Hidden, System, Directory)
TotalCmd (Directory)
UnicodeTest (Directory)
WINDOWS (Directory)
zzzz (Directory)

Файлы:

.rnd (Archive)
AUTOEXEC.BAT (Archive)
BOOT.BAK (Hidden, System)
boot.ini (Hidden, System)
Bootfont.bin (ReadOnly, Hidden, System, Archive)
CheckDLL.zip (Archive)
CONFIG.SYS (Archive)
DLL_bestprac.doc (Archive)
dump.bin ()
hiberfil.sys (Hidden, System, Archive)
IO.SYS (ReadOnly, Hidden, System, Archive)
MapDrives.bat (Archive)
MSDOS.SYS (ReadOnly, Hidden, System, Archive)
NTDETECT.COM (ReadOnly, Hidden, System, Archive)
ntldr (ReadOnly, Hidden, System, Archive)
Passwords.kdbx (Archive)
PDOXUSRS.NET (Archive)
RestartJVCS.bat (Archive)
trail.txt.1 (Archive)
Сборка в ProE - ProE.zip (Archive)


Добавлено через 3 минуты и 12 секунд
Вопрос: вы не под Vista работаете?
Быть может, у вас http://forum.vingrad.ru/index.php?showtopic=222240&view=findpost&p=1606140?

Автор: m6a6g6 18.8.2009, 12:59
Цитата(CodeMonkey @ 18.8.2009,  11:26)
Вопрос: вы не под Vista работаете?

Да, так и есть, забыл про это написать  smile 

Решил тоже выполнить Ваш код.
Вот результат:
Цитата

Папки: 
Boot (Hidden, System, Directory)
Config.Msi (Hidden, Directory)
Documents and Settings (Hidden, System, Directory)
Downloads (Directory)
Games (Directory)
Intel (Directory)
MASM611 (Directory)
MSOCache (ReadOnly, Hidden, Directory)
My Documents (ReadOnly, Directory)
PerfLogs (Directory)
Program Files (ReadOnly, Directory)
ProgramData (Hidden, Directory)
publish (Directory)
STALSTCS (Directory)
System Volume Information (Hidden, System, Directory)
TASM (Directory)
Users (ReadOnly, Directory)
vbnet03sbs (Directory)
Windows (Directory)
Музыка (Directory)

Файлы:
autoexec.bat (Archive)
bootmgr (ReadOnly, Hidden, System, Archive)
BOOTSECT.BAK (ReadOnly, System, Archive)
config.sys (Archive)
csb.log (Archive)
eula.1028.txt (Archive)
eula.1031.txt (Archive)
eula.1033.txt (Archive)
eula.1036.txt (Archive)
eula.1040.txt (Archive)
eula.1041.txt (Archive)
eula.1042.txt (Archive)
eula.2052.txt (Archive)
eula.3082.txt (Archive)
globdata.ini (Archive)
grldr (ReadOnly, Hidden, System, Archive)
hiberfil.sys (Hidden, System, Archive)
install.exe (Archive)
install.ini (Archive)
install.res.1028.dll (Archive)
install.res.1031.dll (Archive)
install.res.1033.dll (Archive)


При чем компилятор ругался на:
  • FILE_ATTRIBUTE_DEVICE
  • FILE_ATTRIBUTE_SPARSE_FILE
  • FILE_ATTRIBUTE_REPARSE_POINT
  • FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
  • FILE_ATTRIBUTE_ENCRYPTED

Сейчас добавил пару строк чтобы просматривать атрибуты всех файлов, так вот в "проблемной папке" лежит файл у которого все атрибуты сняты (ну я в его свойствах галочки снял), в моей программе атрибут возвращаемый числом равен 8192, а функция FormatAttrs возвращает пустую скобку (). Этот загадочный файл простой фильм .avi
Добавленная строка:
Код
FindResult := FindFirst(StartFolder + '*.*', faAnyFile, SearchRec);

         while FindResult = 0 do begin  ShowMessage(InttoStr(SearchRec.Attr)); Memo1.Lines.Add(SearchRec.Name+' '+ FormatAttrs(SearchRec.Attr));

Автор: CodeMonkey 18.8.2009, 13:37
Цитата(m6a6g6 @  18.8.2009,  12:59 Найти цитируемый пост)
Да, так и есть, забыл про это написать

В таком случае, проверьте, что в вашей программе есть Вистовский манифест. Это обычно актуально для старых Delphi (до 2007-й).
Подключение манифеста - обычно самый простой способ отключить костыли для старых программ. Только учтите, что ваша программа при этом действительно должна быть совместима с Вистой. Т.е. если раньше вы могли делать такие плохие вещи, как запись в Program Files, то теперь, с манифестом и отключенными костылями, вам это уже не удастся.
Настоятельно не рекомендую отключать UAC для тестов.

Добавлено через 2 минуты и 33 секунды
Ну, чисто для теста, можете ничего не менять: запустите программу и стартаните поиск. Посмотрите, сколько нашла. Потом в Диспетчере Задач переключите виртуализацию для своего процесса и повторите поиск.

Автор: m6a6g6 18.8.2009, 13:59
Хорошо, сейчас попробую. Использую Delphi 7UAC отключен.

Пункт меню Виртуализация в диспетчере задач для моего процесса недоступен...

Автор: CodeMonkey 18.8.2009, 14:21
Цитата(m6a6g6 @  18.8.2009,  12:59 Найти цитируемый пост)
а функция FormatAttrs возвращает пустую скобку ()

Наверное, она всё же должна возвращать '(NotIndexed)'?

Добавлено через 3 минуты и 30 секунд
Цитата(m6a6g6 @  18.8.2009,  13:59 Найти цитируемый пост)
Пункт меню Виртуализация в диспетчере задач для моего процесса недоступен...

Тогда добавляйте манифест. У меня сейчас под рукой нет Висты, чтобы поиграться.

Ссылки в тему:
http://deldev.blogspot.com/2009/05/uac-dlja-razrabotchika-2.html.
http://blogs.msdn.com/uac/archive/2006/02/22/537129.aspx.

Автор: m6a6g6 19.8.2009, 17:43
Ну я вообще в полном замешательстве. 
Запустил программу от имени администратора, так процесс поиска прервался с ошибкой Access violation at address ..., хотя под Delphi все нормально было  smile. Впрочем это уже моя ошибка буду искать, надо теперь как-то ее отловить, в общем разбираться со всем этим мне предстоит долго...

А разве эти функции восприимчивы к уровню привилегий?

Автор: CodeMonkey 21.8.2009, 11:19
Вы по ссылкам вообще ходите? Я их не просто так даю.

Цитата
Виртуализация не включается:
...
для процессов администратора
...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)