Быстрее DirectoryExists функция? - PullRequest
14 голосов
/ 17 сентября 2009

Я использую

DirectoryExists (const PathName : String);

чтобы проверить, доступен ли каталог с компьютера или нет. Но если каталог не существует, а путь является сетевым, то есть

\\computer1\Data

метод возвращается очень долго.

Должен быть более быстрый способ определить, что сетевая папка недоступна. Или я могу настроить некоторый параметр времени ожидания, который DirectoryExists использует внутренне (я посмотрел на исходный код, но он просто делегирует GetFileAttributes, который определен в kernel32)?

Есть идеи?

Ответы [ 8 ]

19 голосов
/ 17 сентября 2009

Нет более быстрого пути:

Любая функция, получающая доступ к чему-либо на удаленном общем ресурсе, будет отключена, когда этот общий ресурс недоступен.

Если причиной вашего тайм-аута является автоматическое отключение акций, то вам может помочь эта ссылка:

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

Имейте в виду, что при попытке использования многопоточности вам придется опровергать свой код без каких-либо условий гонки и утечек памяти. Тайм-ауты в сочетании с исключениями обычно усложняют задачу.

6 голосов
/ 17 сентября 2009

Был такой же вопрос для C #: Как избежать сетевых сбоев в GetFileAttributes?

Как сказал codymanix, используйте темы. Приведенная выше ссылка покажет вам, как вы можете сделать это с делегатами в C #. Не знаю Delphi, но, может быть, вы знаете, как конвертировать код?

5 голосов
/ 17 сентября 2009

Если вы тестируете множество каталогов, вы должны использовать потоки для параллельного выполнения всех запросов, потому что для сетевых ресурсов обычно большие тайм-ауты.

3 голосов
/ 17 сентября 2009

Это лучший способ. Вы можете добавить некоторый код для проверки связи с машиной, чтобы убедиться, что она существует, но это по-прежнему может привести к сбою процедуры, поскольку сегодня на многих компьютерах установлены программные брандмауэры, игнорирующие запросы на проверку связи, а также вероятность того, что запрошенный ресурс не есть.

Кроме того, на некоторых компьютерах, если путь UNC находится на локальном компьютере, а на локальном компьютере нет активной сетевой карты (ноутбук с отключенным Wi-Fi, например, в режиме «В самолете»), запросы UNC также не будут выполняться.

2 голосов
/ 17 ноября 2010

Я использую следующий код ...

private delegate bool DirectoryExistsDelegate(string folder);

bool DirectoryExistsTimeout(string path, int millisecondsTimeout)
{
    try
    {
        DirectoryExistsDelegate callback = new DirectoryExistsDelegate(Directory.Exists);
        IAsyncResult result = callback.BeginInvoke(path, null, null);

        if (result.AsyncWaitHandle.WaitOne(millisecondsTimeout, false))
        {
            return callback.EndInvoke(result);
        }
        else
        {
            callback.EndInvoke(result);  // Needed to terminate thread?

            return false;
        }
    }

    catch (Exception)
    {
        return false;
    }
}

... что позволяет мне иметь версию Directory.Exist с тайм-аутом. Я называю это чем-то вроде ...

bool a = DirectoryExistsTimeout("\\\\machine\\folder", 5000);

Будет ли это нормально для вас?


Чтобы быть безопасным / легальным, вам нужно вызвать "callback.EndInvoke (result);" но его вызов блокируется до тех пор, пока не завершится асинхронность, поэтому это побеждает объект кода. Возможно, это нужно сделать в конце кода - возможно, завершиться?

2 голосов
/ 18 сентября 2009

В аналогичной ситуации, как вы прописали, я сначала добавил ICMP-пинг на сервер. Если сервер не отвечает на пинг, я предполагаю, что он не работает. Вы можете сами решить, какой тайм-аут использовать для проверки связи, поэтому вы можете установить его намного короче, чем тайм-аут, используемый при попытке открыть общий файловый ресурс.

0 голосов
/ 07 февраля 2019

Эта функция сработала для меня очень хорошо: NetDirectoryExists(Path, Timeout)
Он использует Threading и является идеальной альтернативой для TDirectory.Exists(Path)

Использование:
if NetDirectoryExists('\\computer1\Data', 1000) then ...
if NetDirectoryExists('C:\Folder', 500) then ...

Если папка существует, функции требуется всего несколько миллисекунд, то же самое с несуществующими папками (C:\NotExisting). Если это недоступный сетевой путь (\\ServerNotReady\C$), он будет использовать количество миллисекунд, указанное вторым параметром.

Function NetDirectoryExists( const dirname: String; 
                              timeoutMSecs: Dword ): Boolean; 

implementation 


uses 
   Classes, Sysutils, Windows; 


type 
   ExceptionClass = Class Of Exception; 
   TTestResult = (trNoDirectory, trDirectoryExists, trTimeout ); 
   TNetDirThread = class(TThread) 
   private 
     FDirname: String; 
     FErr    : String; 
     FErrclass: ExceptionClass; 
     FResult : Boolean; 
   protected 
     procedure Execute; override; 
   public 
     Function TestForDir( const dirname: String; 
                timeoutMSecs: Dword ):TTestResult; 
   end; 


Function NetDirectoryExists( 
            const dirname: String; timeoutMSecs: Dword ): Boolean; 
 Var 
   res: TTestResult; 
   thread: TNetDirThread; 
 Begin 
   Assert( dirname <> '', 'NetDirectoryExists: dirname cannot be empty.' ); 
   Assert( timeoutMSecs > 0, 'NetDirectoryExists: timeout cannot be 0.' ); 
   thread:= TNetDirThread.Create( true ); 
   try 
     res:= thread.TestForDir( dirname, timeoutMSecs ); 
     Result := res = trDirectoryExists; 
     If res <> trTimeout Then 
       thread.Free; 
     {Note: if the thread timed out it will free itself when it finally 
      terminates on its own. } 
   except 
     thread.free; 
     raise 
   end; 
 End; 


procedure TNetDirThread.Execute; 
 begin 
   try 
     FResult := DirectoryExists( FDirname ); 
   except 
     On E: Exception Do Begin 
       FErr := E.Message; 
       FErrclass := ExceptionClass( E.Classtype ); 
     End; 
   end; 
 end; 


function TNetDirThread.TestForDir(const dirname: String; 
   timeoutMSecs: Dword): TTestResult; 
 begin 
   FDirname := dirname; 
   Resume; 
   If WaitForSingleObject( Handle, timeoutMSecs ) = WAIT_TIMEOUT 
   Then Begin 
     Result := trTimeout; 
     FreeOnTerminate := true; 
   End 
   Else Begin 
     If Assigned( FErrclass ) Then 
       raise FErrClass.Create( FErr ); 
     If FResult Then 
       Result := trDirectoryExists 
     Else 
       Result := trNoDirectory; 
   End; 
 end; 
0 голосов
/ 17 сентября 2009

Если оба компьютера находятся в одном домене, это ускорит файловые операции при работе с общими папками.

...