更快的 DirectoryExists 功能?

发布于 2024-08-05 21:26:34 字数 323 浏览 6 评论 0原文

我用来

DirectoryExists (const PathName : String);

检查目录是否可以从计算机访问。但如果该目录不存在并且路径名是网络路径,即

\\computer1\Data

该方法需要很长时间才能返回。

必须有一种更快的方法来确定网络文件夹不可访问。或者我可以配置 DirectoryExists 内部使用的一些超时参数(我查看了源代码,但它只是委托给 kernel32 中定义的 GetFileAttributes)?

有什么想法吗?

I use

DirectoryExists (const PathName : String);

to check if a directory is reachable from a computer or not. But if the directory does not exist and the path name is a network path, i.e.

\\computer1\Data

the method takes a very long time to return.

There must be a faster way to determine that a network folder is not reachable. Or can I configure some timeout parameter that DirectoryExists uses internally (I looked at the source code but it just delegates to GetFileAttributes which is defined in kernel32)?

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

节枝 2024-08-12 21:26:34

没有更快的方法:

当该共享不可用时,访问远程共享上任何内容的任何函数都会超时。

如果超时的原因是自动断开共享,那么这些链接可能会帮助您:

如果应用程序可以在未完成检查的情况下继续,那么您可以将在单独的线程中进行检查,检查完成后,您可以在 UI 中更新您的状态。

请注意,当您尝试多线程方式时,您必须证明您的代码不存在竞争条件和内存泄漏。超时与异常相结合通常会使这成为一项艰巨的任务。

There is no faster way:

any function accessing anything on a remote share will timeout when that share is not available.

If the cause of your timeouts is automatic disconnecting of shares, then these link may help you:

If the application can continue without the completion of the check, then you can put the check in a separate thread, and upon completion of the check, you can update your status in the UI.

Be aware that when you try a multi-threading way, that you have to disprove your code is free of race-conditions and memory leaks. Time-outs in combination with exceptions usually make that a hard task.

年少掌心 2024-08-12 21:26:34

C# 也有同样的问题:
如何避免 GetFileAttributes 中的网络停顿?

正如 codymanix 所说,使用线程。上面的链接将向您展示如何使用 C# 中的委托来完成此操作。不懂Delphi,但也许你知道如何转换代码?

There was the same question for C#:
How to avoid network stalls in GetFileAttributes?

As codymanix said, use threads. The above link will show you how you can do it with delegates in C#. Don't know Delphi, but maybe you know how to convert the code?

笑着哭最痛 2024-08-12 21:26:34

如果您测试大量目录,您应该使用线程并行执行所有查询,因为对于网络共享,通常会有很长的超时。

If you test for lots of directories you should use threads to do all the queries in parallel because for network shares ther are usually long timeouts.

枕梦 2024-08-12 21:26:34

这个函数对我来说非常有效:NetDirectoryExists(Path, Timeout)
它使用线程,是 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; 

This function worked for me very well: NetDirectoryExists(Path, Timeout)
It uses Threading and is the perfect alternative for TDirectory.Exists(Path)

Usage:
if NetDirectoryExists('\\computer1\Data', 1000) then ...
if NetDirectoryExists('C:\Folder', 500) then ...

If the Folder exists, the function needs only some milliseconds, same with not existing folders (C:\NotExisting). If it is a not reachable network path (\\ServerNotReady\C$) then it will consume the number of milliseconds given by the second parameter.

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; 
天邊彩虹 2024-08-12 21:26:34

这是最好的办法。您可以添加一些代码来 ping 机器以确保它存在,但这仍然会使例程失败,因为当今许多计算机都设置了软件防火墙来忽略 ping 请求,并且请求的共享也有可能不存在存在。

此外,在某些计算机上,如果 UNC 路径位于本地计算机上并且本地计算机没有活动网卡(例如处于“飞行”模式的 Wi-Fi 断开连接的笔记本电脑),则 UNC 请求也会失败。

This is the best way. You could add some code to ping the machine to insure it exists, but this would still leave the routine up to fail as many computers today have software firewalls set up to ignore ping requests, as well as the possibility that the share requested doesn't exist.

Also, on some machines if the UNC path is on the local machine and the local machine does not have an active network card (a wi-fi disconnected laptop for instance in "Airplane" mode) then UNC requests will also fail.

陌伤浅笑 2024-08-12 21:26:34

我使用以下代码...

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);”但调用它会锁定直到异步完成,因此这会破坏代码的对象。也许这需要在代码末尾完成 - 也许退出?

I use the following code...

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;
    }
}

...which allows me to have a timeout version of Directory.Exist. I call it with something like...

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

Would this be OK for you needs?


To be safe/legal then you need to call "callback.EndInvoke(result);" but calling it locks until the async finishes so this defeats the object of the code. Perhaps this needs to be done at the end of you code - exit maybe?

红玫瑰 2024-08-12 21:26:34

在与您规定的类似情况下,我首先向服务器添加了 ICMP ping。如果服务器没有响应 ping,我认为它已关闭。您可以自己决定在 ping 上使用哪个超时,因此可以将其设置为比尝试打开文件共享时内部使用的超时短得多。

In a similar situation like you prescribed, I've added an ICMP ping to the server first. If the server doesn't respond to the ping, I assume it is down. You can decide which timeout to use on the ping yourself, so you can set it much shorter than the timeout used internally when trying to open a file-share.

月隐月明月朦胧 2024-08-12 21:26:34

如果两台计算机位于同一域中,则在处理共享时将加快文件操作速度。

If both computers are on the same domain it will speed-up file operations when dealing with shares.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文