ネットワークファイルのタイムアウトについて

解決


ラーク  2008-02-05 01:10:38  No: 29642

いつも参考にさせて頂いています。

アプリ上で、LAN上の別PCの共有フォルダへアクセスする必要があり、「\\abc\hoge\1234.txt」といった具合にファイルパスを保存して運用しているものがあるのですが、「\\abc」のPCの電源が切れている時にこのファイルへのFileExistsコマンドを打つと、特定の環境でアプリケーションがフリーズしてしまう現象が発生しています。
ネットワークのタイムアウトの問題ではありますが、できれば、FileExistsを打つ前に、ネットワークフォルダであるか否かを判断し、そのPC・フォルダへアクセスできるかを、できるだけ短時間で行えるような形を取りたいと思っています。この場合、どのようにすればよろしいでしょうか??

すいませんが、宜しくご教授下さい。


KHE00221  2008-02-05 18:15:29  No: 29643

NetShareEnum を使用すれば共有されているフォルダをの一覧を取得する事は出来ます。レベル0で共有名は取得出来ます。

当然接続先に接続しにいくのでフリーズの可能性は残るかもしれません。

PCさえ起動していれば共有されていなくてもフリーズはしないと思うので

PING をして接続先 PCが起動しているか確認するのもいいでしょう?

また FileExists の部分をスレッドにしてしまうのが良いかもしれません


KHE00221  2008-02-05 19:31:44  No: 29644

NetShareEnum よりも NetShareGetInfo のほうがいいですね


Fusa  2008-02-06 01:55:04  No: 29645

俺もとても興味があるんですが、
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;


KHE00221  2008-02-06 02:16:50  No: 29646

ほれ

http://khe00221.image.coocan.jp/index.php?FrontPage%2FComponent%2F%A5%E6%A5%CB%A5%C3%A5%C8%2FNetShareUnit.pas


ラーク  2008-02-06 02:29:33  No: 29647

ご回答ありがとうございます。

私の環境で、存在しないPC名・共有名でNetShareGetInfoコマンドをを試してみたところ、概ねFileExistsでの時間と同じ時間でのタイムアウトが発生しました。

やはり、このタイムアウトはWindows依存になるところが大きいのかもしれませんね・・・。

FileExistsコマンドを別スレッドにして、タイムアウトを測る方向も探ってみたいと思います。引き続き何か良い方法がありましたらよろしくお願いします。


KHE00221  2008-02-06 03:19:03  No: 29648

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;


KHE00221  2008-02-06 03:19:32  No: 29649

FileExistsThread.Result := -2;
いらない


KHE00221  2008-02-06 03:29:20  No: 29650

こうすれば 

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;


Fusa  2008-02-06 11:08:20  No: 29651

すげー、勉強になりますな・・・


KHE00221  2008-02-06 19:51:47  No: 29652

FreeOnTerminate := True; にして

最後のFileExistsThread.Free;を無くさないと駄目みたい。


ラーク  2008-02-06 20:06:13  No: 29653

別スレッド化するのは経験が無かったので凄く勉強になりますw

早速試してみたいと思います。


ラーク  2008-02-06 20:36:51  No: 29654

サンプルソースをテストしてみました。上手くタイムアウトが取れました。ありがとうございます。

なお、コンパイル時に警告が出るようでしたので少しアレンジしてみました。

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;


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加