• ベストアンサー

FileSystemObject & For Eachループで・・・

皆様こんにちは! VBAでプログラムを作っていて不明点があり困っています。 FileSystemObjectを使用してあるフォルダにあるサブフォルダ内のすべての ファイルをコピーしフォルダを削除する処理を作成しています。 Set objFolder = objFs.GetFolder("C:\TEST") For Each objSubFolder In objFolder.subfolders 'TESTフォルダ内にあるサブフォルダ獲得 For Each objFile In objSubFolder.Files 'サブフォルダ内のファイル獲得 ON Error Go To CopyErr objFs.CopyFile(objFile.Path,"コピー先名") 'ファイルコピー処理 ON Error Go To 0 Set objFile = Nothing Next Set objSubFolder = Nothing Next Set objFolder = Nothing objFs.DeleteFolder("C:\TEST") 'TESTフォルダ削除 Set ObjFs = Nothing exit sub CopyErr: Set objFile = Nothing Set objSubFolder = Nothing Set objFolder = Nothing objFs.DeleteFolder("C:\TEST") Set ObjFs = Nothing end sub 上の様な処理でファイルのコピーでエラーが発生し CopyErrへ飛んだ場合、TESTフォルダの削除時に ”書込みできません”とエラーが発生し TESTフォルダが削除できません(その中のサブフォルダは削除されます)。 正常にFor Each文を抜けた場合は問題なく削除するので解せません。 For EachからはGo To,Exit等で抜けるとまずいのでしょうか? 上の様な処理を作成しようと思えば、Dirを使用した方がいいのでしょうか? (Nothingの処理は元々なかったのですが、 この現象が出たため試しにつけてみたものです。) どなたかアドバイスをお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.2

かなり自信がありませんが・・・・勝手な推測です・・・ おそらくVBのバグだと思います。 FSOは内部でファイルの存在チェックを行っておりますが、きっとうまく開放してないのだと思います。 それ以前に気になったのですが、エラー処理で削除を走らせていますよね? これはコーディング的にあまりよくないと思います。 特に現在のロジックでエラーに飛ぶ処理は、「コピーの失敗」という状況であり、コピーもできないのに削除というアクションをおこそうとしています。これはVBのバグをおこしやすいプログラムに見えます。 このようなエラーで飛んだ場合は、あくまでオブジェクトの初期化や、フラグの制御など以外の処理をしないように努めたほうが健全だと思います。それを一つの関数として、そのステータス次第で処理を分岐したほうがよいと思います。 サンプルを載せておきます。 ちなみに私の環境(WIN2000SP3/VB6SP5)ではステップ実行だと、なぜか失敗します。 Private Sub Command1_Click()   Const DEF_DIR As String = "C:\TEST"   Dim objFs    As FileSystemObject   Dim blnFlg   As Boolean      Set objFs = New FileSystemObject      blnFlg = funcCopy(objFs, DEF_DIR)   If Not blnFlg Then     Debug.Print "失敗"   End If   Call objFs.DeleteFolder(DEF_DIR) End Sub Private Function funcCopy(objFs As FileSystemObject, inFolderPath As String) As Boolean   On Error GoTo PGMERR      Dim objFolder    As Folder   Dim objSubFolder  As Folder   Dim objFile     As File      Set objFolder = objFs.GetFolder(inFolderPath)      For Each objSubFolder In objFolder.SubFolders     For Each objFile In objSubFolder.Files       Call objFs.CopyFile(objFile.Path, "コピー先名")       GoTo PGMERR     Next   Next      funcCopy = True PGMEND:   Set objFile = Nothing   Set objSubFolder = Nothing   Set objFolder = Nothing   Exit Function PGMERR:   funcCopy = False   GoTo PGMEND End Function

Plemo
質問者

お礼

ご指摘のように ループを含めたコピー関数を作成してその実行結果により 呼び元で処理を振り分ける様にすればエラーは発生しませんでした。 そもそも成功しようが、失敗しようがフォルダを削除する処理を行っていたので エラー処理を作成して削除する必要がありませんでした。お騒がせしてすみません。 大変ありがとうございました。

その他の回答 (8)

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.9

この件を引き続きされたいなら、 新たなスレを起こして、on error ステートメントについて問いてください。 題意から異なります。

Plemo
質問者

お礼

TAGOSAKU7さんのおっしゃるとおり内容がかわりつつある様ですね。 おかげさまで処理できる様になり非常に助かりました。 ありがとうございます。

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.8

ですので・・・しつこいようですが・・・ 機能分割という言葉をご存知ないのでしょうか? モジュール分割以外に機能分割というものがあります。 サンプルで載せている関数をよくみてください。 出口は一つです。エラー時の処理はフラグを戻すだけです。 オブジェクトの操作を操作をしているわけではありません。 落ちる?このプログラムで落ちようがないのは明白です。 クリアの意味を履き違えてはいませんか? on error ステートメントのヘルプもよく見てください。 「プロシージャ内部で有効」とあります。 プロシージャの意味がわかってますか? 機能分割の意味がわかってますか? このサンプルで落ちる場合を知りたいです。 出口が複数とありますが、全てPGMENDラベルを通って終了しますが、どのように思っているのでしょうか? 一つの命令がどこからどこまでが有効で、どのような動きをしているかを理解してから発言することを望みます。

  • KojiS
  • ベストアンサー率46% (145/312)
回答No.7

 そうそう Gotoを Resumeに変更した方が良いという考えの説明をしていませんでしたね。  Resumeする前までは「エラー処理中」なので、Resumeでエラー中をクリアした方が良いと思う、という事です。  後は、終了処理中にエラーが発生しないようなコーディングにすると。

  • KojiS
  • ベストアンサー率46% (145/312)
回答No.6

》エラーを引っ掛けたい最小のコードにOnErrorをかけないと予期せぬ別のエラー 》でもエラー処理を行うバグを生むと以前聞いたのでコピー処理だけ挟み込んでいる 》のですが、これは良くないことなのでしょうか? 》  「予期せぬ別のエラー」の事も考えたエラー処理のコーディングをすべきでしょう。「その他のエラーの場合はエラーメッセージが出てアプリケーションが終了」でも良いのですか?という事です。  「最小のコードに On Errorを」というのは、コーディングする人の好みも入ってきますが、僕は、関数ごとに1つの On Errorで、関数1つをある程度短く、ですね。 》>CopyErr処理部での resume ????が無い? 》OnErrorで飛んだ先からはどこかにResumeで戻るべきなのでしょうか? 》大体関数自体を抜けてしまう事が多いんですが・・・ 》  あくまでも僕の考えですが、  関数自体を抜ける場所(出口)をいくつも作らない方が良いと思います。  関数は、入り口と出口があります。出来れば、入り口が1つなので、出口も1つにするべきだと思います。  経験上、関数の出口が複数ある場合ほど、バグが発生する可能性も大きくなります。  ですので、Resumeで終了処理部分に戻るようにすると。#2の方へ言った事はこういう理由からです。  オブジェクトの破棄(Nothing)は明示的にやっておいた方が良いですしね。

Plemo
質問者

お礼

参考にします。ありがとうございました。

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.5

コーディングの手ほどきを受けるとは思わなかった。。。 KojiSさんへ On Error ステートメントはプロシージャ内部で有効です。 ですのでerrオブジェクトはプロシージャ内部だけで有効です。 サンプルのfuncCopy関数を抜けると、Errオブジェクトは自動的に初期化されます。 今回のOn Error ステートメントを使用した関数は単機能のため、わざわざ戻す必要はありません。 「戻す必要がない=機能分割ができている」 (他のメーリングリストでも話題になったことがありますよ) 一行追加するかどうかは、お好み次第だと思います。

  • KojiS
  • ベストアンサー率46% (145/312)
回答No.4

#2の方へですが、#2の回答を参考にする場合も考えて書いておきます。 エラー処理中の  GoTo PGMEND は、  Resume PGMEND にしましょう。  On Error Gotoによって飛んだ場合は、必ず Resumeで戻るように癖付けましょうね。  そうでないと、VB内部でエラー情報がクリアされません。

  • TAGOSAKU7
  • ベストアンサー率65% (276/422)
回答No.3

>Call objFs.CopyFile(objFile.Path, "コピー先名") の下の >GoTo PGMERR いらないです・・・ テスト用のロジックが残ってしまった^^;

  • KojiS
  • ベストアンサー率46% (145/312)
回答No.1

 どうせなら、変数の宣言部分から全部書き込みましょう。そうでないと正しい判断は下せません。  とりあえず、 ・For Eachの中で objFolderと objSubFolderの Nothingの処理は必要ない。 ・on error goto 0でエラー処理を無効にしているのは何故か? ・on error goto CopyErrはループ外に書いた方が良いと思う。 ・CopyErr処理部での resume ????が無い?  という事ぐらいでしょうか。  なんならエラーフラグでも設けて、エラー処理部でエラーフラグをセットし、For Eachでエラーフラグが立っていたら exit forをしてやるような処理に書き換えてみてはどうですか?その方がすっきりしそうです。もちろん、エラー処理部からは resume nextで戻ると。

Plemo
質問者

補足

早速のご回答ありがとうございます。 アバウトな質問ですみません。 補足いたします。 '宣言 dim objFs as FileSystemObject dim objFolder as FileSystemObject dim objSubFolder as FileSystemObject dim objFile as FileSystemObject Set objFs = CreateObject("Scripting.FileSystemObject") また、実際はコピーする前にobjFs.BuildPathでコピー先のパス名 を作っており、コピー処理自身はコピー元、コピー先を渡して 別の関数内にておこなっております。コピー関数を書きます。 Function fsoCopyFile(strSrcFile as string, strDistFile as string) as long dim objFs as FileSystemObject Set objFs = CreateObject("Scripting.FileSystemObject") ON ERROR GOTO CopyErr objFS.CopyFile(strSrcFile,strDistFile) ON ERROR GOTO 0 set objFS = Nothing fsoCopyFile=0 exit function CopyErr: fsoCopyFile=err.Number set objFS = Nothing end function ループ内で上記関数を呼び出しリターンが0以外ならGoto CopyErrを行っています。 全て書ききれなかったのと,質問に書いているソースで テストしても同様の現象が発生したのでいいかなって思ってしまいました。気をつけます。 >For Eachの中で objFolderと objSubFolderの Nothingの処理は必要ない。 やっぱりいりませんよね。 >on error goto 0でエラー処理を無効にしているのは何故か? >on error goto CopyErrはループ外に書いた方が良いと思う。 エラーを引っ掛けたい最小のコードにOnErrorをかけないと予期せぬ別のエラー でもエラー処理を行うバグを生むと以前聞いたのでコピー処理だけ挟み込んでいる のですが、これは良くないことなのでしょうか? >CopyErr処理部での resume ????が無い? OnErrorで飛んだ先からはどこかにResumeで戻るべきなのでしょうか? 大体関数自体を抜けてしまう事が多いんですが・・・ よろしくお願いいたします。

関連するQ&A