ファイルに変更を与える際、すでにファイルが掴まれていたら何度もチャレンジする

ファイルを書き込もうとしたら、他のプロセスなどにそのファイルが掴まれていて、書き込みに失敗することがある。このとき成功するまで何度もチャレンジしたい。

Dim fso As New FileSystemObject
Dim t As TextStream

Do
    On Error Resume Next
    Set t = fso.CreateTextFile("hoge.txt", True, False)
    On Error GoTo 0

    If Not t Is Nothing Then Exit Do

    Sleep 500
Loop

t.Write "hello, world!"
t.Close
Set t = Nothing

こんな感じでやった。ファイルが掴まれている場合、fso.CreateTextFileのところでエラーになるので、そこだけOn Error Resume Nextする。それで、あとでtにちゃんと結果が入ったかどうかを調べる。ダメだったらループしてもう一度やってみる。うまくいったらループを抜ける。

Sleep 500のところは、再チャレンジする前に0.5秒ほどお休みするために書いた。これがないとものすごい早さで再チャレンジして、全体が重くなる。Sleep関数は通常のVBには無いのでWindows APIを呼び出している。定義は以下。

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

再チャレンジの回数をカウントしておいて、20回試してダメだったらエラーにしてアプリを落とすとか、そういう感じにするとさらに良いと思う。

今回のはVB6だけど、FileSystemObjectやOn Error Resume Nextしか使ってないのでVBScriptでも書ける(Sleepは無理)。VB.NETなら、普通にTry〜Catchで書ける。

失敗例 : On Error GoToを使う

最初、On Error GoToを使おうとして、以下のようなコードを書いた。

    Dim fso As New FileSystemObject
    Dim t As TextStream

On Error GoTo catch
try:
    Set t = fso.CreateTextFile("hoge.txt", True, False)
    t.Write "hello, world!"
    t.Close
    Set t = Nothing
    Exit Sub

catch:
    Sleep 500
    GoTo try

これは失敗だった。On Error GoToでジャンプすると、そのプロシージャ内ではもう一度On Error GoToすることはできないらしい。

そこで最後のGoTo tryのところをResume tryって書けばいけそうだと思ったとき、だったら最初からOn Error Resume Nextすりゃいいやということに気付いて、冒頭のコードを書いたわけだ。