いつも参考にさせて頂いています。
アプリ上で、LAN上の別PCの共有フォルダへアクセスする必要があり、「\\abc\hoge\1234.txt」といった具合にファイルパスを保存して運用しているものがあるのですが、「\\abc」のPCの電源が切れている時にこのファイルへのFileExistsコマンドを打つと、特定の環境でアプリケーションがフリーズしてしまう現象が発生しています。
ネットワークのタイムアウトの問題ではありますが、できれば、FileExistsを打つ前に、ネットワークフォルダであるか否かを判断し、そのPC・フォルダへアクセスできるかを、できるだけ短時間で行えるような形を取りたいと思っています。この場合、どのようにすればよろしいでしょうか??
すいませんが、宜しくご教授下さい。
NetShareEnum を使用すれば共有されているフォルダをの一覧を取得する事は出来ます。レベル0で共有名は取得出来ます。
当然接続先に接続しにいくのでフリーズの可能性は残るかもしれません。
PCさえ起動していれば共有されていなくてもフリーズはしないと思うので
PING をして接続先 PCが起動しているか確認するのもいいでしょう?
また FileExists の部分をスレッドにしてしまうのが良いかもしれません
NetShareEnum よりも NetShareGetInfo のほうがいいですね
俺もとても興味があるんですが、
NetShareGetInfo というのは
どのようにコードを書くのでしょうか。
if FileExistTimeOut() then
begin
end else
なんて、かけるコードがあるとすごいうれしいのですが。
ちなみにIndyでpingを打つのはこんな感じ?
確か、IndyのDEMOに付いてきた記憶が。
procedure TForm1.Button1Click(Sender: TObject);
begin
Self.Caption := IdIcmpClient1.Version;
IdIcmpClient1.ReceiveTimeout := 10;
IdIcmpClient1.Host := '127.0.0.1';
IdIcmpClient1.Ping;
end;
procedure TForm1.IdIcmpClient1Reply(ASender: TComponent;
const AReplyStatus: TReplyStatus);
var
Output: String;
begin
//出力形式は
//Reply from 192.168.33.5: bytes=32 time<10ms TTL=128
//Request timed out.
if AReplyStatus.FromIpAddress = '0.0.0.0' then
begin
Output := 'Request timed out.';
end else
begin
Output := '';
Output := Output + 'Reply from ';
Output := Output + AReplyStatus.FromIpAddress;
Output := Output + ': bytes=';
Output := Output + IntToStr( AReplyStatus.BytesReceived );
if (AReplyStatus.MsRoundTripTime = 0) then
begin
Output := Output + ' time<1';
end else
begin
Output := Output + ' time=';
Output := Output + IntToStr( AReplyStatus.MsRoundTripTime );
end;
Output := Output + ' TTL=';
Output := Output + IntToStr( AReplyStatus.TimeToLive );
//Output := Output + ' icmp_seq=';
//Output := Output + AReplyStatus.SequenceId;
end;
Memo1.Lines.Add(Output);
end;
ほれ
ご回答ありがとうございます。
私の環境で、存在しないPC名・共有名でNetShareGetInfoコマンドをを試してみたところ、概ねFileExistsでの時間と同じ時間でのタイムアウトが発生しました。
やはり、このタイムアウトはWindows依存になるところが大きいのかもしれませんね・・・。
FileExistsコマンドを別スレッドにして、タイムアウトを測る方向も探ってみたいと思います。引き続き何か良い方法がありましたらよろしくお願いします。
FileExists スレッド化
TFileExistsThread = class(TThread)
private
FFileName : String;
FResult : Integer;
protected
procedure Execute;override;
published
property FileName : String read FFilename write FFilename;
property Result : Integer read FResult write FResult;
end;
procedure TFileExistsThread.Execute;
begin
Result := -1;
if FileExists(FileName) = True then Result := 0 else Result := 1;
Terminate;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
FileExistsThread : TFileExistsThread;
TC : Cardinal;
begin
FileExistsThread := TFileExistsThread.Create(True);
FileExistsThread.Result := -2;
FileExistsThread.FreeOnTerminate := False;
FileExistsThread.FileName := '\\P4-240-1024\AA\AA.TXT';
FileExistsThread.Resume;
TC := GetTickCount;
while (FileExistsThread.Terminated = False) and (GetTickCount - TC < 5000) do
begin
Application.ProcessMessages;
end;
case FileExistsThread.Result of
-1://タイムアウト
begin
TerminateThread(FileExistsThread.Handle,0);
end;
0://見付かった
begin
end;
1://見付からない
begin
end;
end;
FileExistsThread.Free;
end;
FileExistsThread.Result := -2;
いらない
こうすれば
if FileExistTimeOut(FileName,TimeOut) = True then
begin
end;
みたくできるかな
Application.ProcessMessages;
は 無い方がいいのかな?
function FileExistTimeOut(const FileName:String;TimeOut:Integer=3000):Boolean;
var
FileExistsThread : TFileExistsThread;
TC : Cardinal;
begin
FileExistsThread := TFileExistsThread.Create(True);
FileExistsThread.FreeOnTerminate := False;
FileExistsThread.FileName := FileName;
FileExistsThread.Resume;
TC := GetTickCount;
while (FileExistsThread.Terminated = False) and (GetTickCount - TC < TimeOut) do
begin
//Application.ProcessMessages;
end;
case FileExistsThread.Result of
-1://タイムアウト
begin
TerminateThread(FileExistsThread.Handle,0);
Result := False;
end;
0:Result := True;
1:Result := False;
end;
FileExistsThread.Free;
end;
すげー、勉強になりますな・・・
FreeOnTerminate := True; にして
最後のFileExistsThread.Free;を無くさないと駄目みたい。
別スレッド化するのは経験が無かったので凄く勉強になりますw
早速試してみたいと思います。
サンプルソースをテストしてみました。上手くタイムアウトが取れました。ありがとうございます。
なお、コンパイル時に警告が出るようでしたので少しアレンジしてみました。
function FileExistTimeOut(const FileName:String;TimeOut:Cardinal=3000):Boolean;
var
FileExistsThread : TFileExistsThread;
TC : Cardinal;
begin
FileExistsThread := TFileExistsThread.Create(True);
FileExistsThread.FreeOnTerminate := True;
FileExistsThread.FileName := FileName;
FileExistsThread.Resume;
TC := GetTickCount;
while (FileExistsThread.Terminated = False) and (GetTickCount - TC < TimeOut) do
begin
;
end;
case FileExistsThread.Result of
-1://タイムアウト
begin
TerminateThread(FileExistsThread.Handle,0);
Result := False;
end;
0:Result := True;
1:Result := False;
else
Result := False;
end;
end;
ツイート | ![]() |