问题
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?
回答1:
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:
- How Autodisconnect Works in Windows NT and Windows 2000
- Mapped Drive Connection to Network Share May Be Lost
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.
回答2:
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?
回答3:
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.
回答4:
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.
回答5:
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.
回答6:
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?
回答7:
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;
回答8:
If both computers are on the same domain it will speed-up file operations when dealing with shares.
来源:https://stackoverflow.com/questions/1438923/faster-directoryexists-function