こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

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の処理は元々なかったのですが、
この現象が出たため試しにつけてみたものです。)

どなたかアドバイスをお願い致します。

投稿日時 - 2002-11-12 22:27:19

QNo.403181

すぐに回答ほしいです

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

かなり自信がありませんが・・・・勝手な推測です・・・

おそらく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

投稿日時 - 2002-11-13 02:44:08

お礼

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

投稿日時 - 2002-11-13 10:12:46

このQ&Aは役に立ちましたか?

2人が「このQ&Aが役に立った」と投票しています

回答(9)

ANo.9

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

題意から異なります。

投稿日時 - 2002-11-13 23:06:54

お礼

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

投稿日時 - 2002-11-14 23:48:21

ANo.8

ですので・・・しつこいようですが・・・

機能分割という言葉をご存知ないのでしょうか?

モジュール分割以外に機能分割というものがあります。

サンプルで載せている関数をよくみてください。
出口は一つです。エラー時の処理はフラグを戻すだけです。
オブジェクトの操作を操作をしているわけではありません。
落ちる?このプログラムで落ちようがないのは明白です。

クリアの意味を履き違えてはいませんか?
on error ステートメントのヘルプもよく見てください。
「プロシージャ内部で有効」とあります。

プロシージャの意味がわかってますか?
機能分割の意味がわかってますか?
このサンプルで落ちる場合を知りたいです。
出口が複数とありますが、全てPGMENDラベルを通って終了しますが、どのように思っているのでしょうか?

一つの命令がどこからどこまでが有効で、どのような動きをしているかを理解してから発言することを望みます。

投稿日時 - 2002-11-13 21:35:08

ANo.7

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

投稿日時 - 2002-11-13 18:19:18

ANo.6

》エラーを引っ掛けたい最小のコードにOnErrorをかけないと予期せぬ別のエラー
》でもエラー処理を行うバグを生むと以前聞いたのでコピー処理だけ挟み込んでいる
》のですが、これは良くないことなのでしょうか?

 「予期せぬ別のエラー」の事も考えたエラー処理のコーディングをすべきでしょう。「その他のエラーの場合はエラーメッセージが出てアプリケーションが終了」でも良いのですか?という事です。
 「最小のコードに On Errorを」というのは、コーディングする人の好みも入ってきますが、僕は、関数ごとに1つの On Errorで、関数1つをある程度短く、ですね。

》>CopyErr処理部での resume ????が無い?
》OnErrorで飛んだ先からはどこかにResumeで戻るべきなのでしょうか?
》大体関数自体を抜けてしまう事が多いんですが・・・

 あくまでも僕の考えですが、
 関数自体を抜ける場所(出口)をいくつも作らない方が良いと思います。
 関数は、入り口と出口があります。出来れば、入り口が1つなので、出口も1つにするべきだと思います。
 経験上、関数の出口が複数ある場合ほど、バグが発生する可能性も大きくなります。
 ですので、Resumeで終了処理部分に戻るようにすると。#2の方へ言った事はこういう理由からです。
 オブジェクトの破棄(Nothing)は明示的にやっておいた方が良いですしね。

投稿日時 - 2002-11-13 17:55:59

お礼

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

投稿日時 - 2002-11-14 23:50:46

ANo.5

コーディングの手ほどきを受けるとは思わなかった。。。

KojiSさんへ
On Error ステートメントはプロシージャ内部で有効です。
ですのでerrオブジェクトはプロシージャ内部だけで有効です。
サンプルのfuncCopy関数を抜けると、Errオブジェクトは自動的に初期化されます。

今回のOn Error ステートメントを使用した関数は単機能のため、わざわざ戻す必要はありません。
「戻す必要がない=機能分割ができている」
(他のメーリングリストでも話題になったことがありますよ)


一行追加するかどうかは、お好み次第だと思います。

投稿日時 - 2002-11-13 04:53:06

ANo.4

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

投稿日時 - 2002-11-13 03:08:46

ANo.3

>Call objFs.CopyFile(objFile.Path, "コピー先名")
の下の
>GoTo PGMERR
いらないです・・・

テスト用のロジックが残ってしまった^^;

投稿日時 - 2002-11-13 02:46:04

ANo.1

 どうせなら、変数の宣言部分から全部書き込みましょう。そうでないと正しい判断は下せません。

 とりあえず、
・For Eachの中で objFolderと objSubFolderの Nothingの処理は必要ない。
・on error goto 0でエラー処理を無効にしているのは何故か?
・on error goto CopyErrはループ外に書いた方が良いと思う。
・CopyErr処理部での resume ????が無い?
 という事ぐらいでしょうか。

 なんならエラーフラグでも設けて、エラー処理部でエラーフラグをセットし、For Eachでエラーフラグが立っていたら exit forをしてやるような処理に書き換えてみてはどうですか?その方がすっきりしそうです。もちろん、エラー処理部からは resume nextで戻ると。

投稿日時 - 2002-11-13 02:39:17

補足

早速のご回答ありがとうございます。
アバウトな質問ですみません。
補足いたします。

'宣言
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で戻るべきなのでしょうか?
大体関数自体を抜けてしまう事が多いんですが・・・

よろしくお願いいたします。

投稿日時 - 2002-11-13 09:15:48

あなたにオススメの質問