皆様こんにちは!
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の処理は元々なかったのですが、
この現象が出たため試しにつけてみたものです。)
どなたかアドバイスをお願い致します。
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
ご指摘のように
ループを含めたコピー関数を作成してその実行結果により
呼び元で処理を振り分ける様にすればエラーは発生しませんでした。
そもそも成功しようが、失敗しようがフォルダを削除する処理を行っていたので
エラー処理を作成して削除する必要がありませんでした。お騒がせしてすみません。
大変ありがとうございました。
No.8
- 回答日時:
ですので・・・しつこいようですが・・・
機能分割という言葉をご存知ないのでしょうか?
モジュール分割以外に機能分割というものがあります。
サンプルで載せている関数をよくみてください。
出口は一つです。エラー時の処理はフラグを戻すだけです。
オブジェクトの操作を操作をしているわけではありません。
落ちる?このプログラムで落ちようがないのは明白です。
クリアの意味を履き違えてはいませんか?
on error ステートメントのヘルプもよく見てください。
「プロシージャ内部で有効」とあります。
プロシージャの意味がわかってますか?
機能分割の意味がわかってますか?
このサンプルで落ちる場合を知りたいです。
出口が複数とありますが、全てPGMENDラベルを通って終了しますが、どのように思っているのでしょうか?
一つの命令がどこからどこまでが有効で、どのような動きをしているかを理解してから発言することを望みます。
No.7
- 回答日時:
そうそう Gotoを Resumeに変更した方が良いという考えの説明をしていませんでしたね。
Resumeする前までは「エラー処理中」なので、Resumeでエラー中をクリアした方が良いと思う、という事です。
後は、終了処理中にエラーが発生しないようなコーディングにすると。
No.6
- 回答日時:
》エラーを引っ掛けたい最小のコードにOnErrorをかけないと予期せぬ別のエラー
》でもエラー処理を行うバグを生むと以前聞いたのでコピー処理だけ挟み込んでいる
》のですが、これは良くないことなのでしょうか?
》
「予期せぬ別のエラー」の事も考えたエラー処理のコーディングをすべきでしょう。「その他のエラーの場合はエラーメッセージが出てアプリケーションが終了」でも良いのですか?という事です。
「最小のコードに On Errorを」というのは、コーディングする人の好みも入ってきますが、僕は、関数ごとに1つの On Errorで、関数1つをある程度短く、ですね。
》>CopyErr処理部での resume ????が無い?
》OnErrorで飛んだ先からはどこかにResumeで戻るべきなのでしょうか?
》大体関数自体を抜けてしまう事が多いんですが・・・
》
あくまでも僕の考えですが、
関数自体を抜ける場所(出口)をいくつも作らない方が良いと思います。
関数は、入り口と出口があります。出来れば、入り口が1つなので、出口も1つにするべきだと思います。
経験上、関数の出口が複数ある場合ほど、バグが発生する可能性も大きくなります。
ですので、Resumeで終了処理部分に戻るようにすると。#2の方へ言った事はこういう理由からです。
オブジェクトの破棄(Nothing)は明示的にやっておいた方が良いですしね。
No.5
- 回答日時:
コーディングの手ほどきを受けるとは思わなかった。
。。KojiSさんへ
On Error ステートメントはプロシージャ内部で有効です。
ですのでerrオブジェクトはプロシージャ内部だけで有効です。
サンプルのfuncCopy関数を抜けると、Errオブジェクトは自動的に初期化されます。
今回のOn Error ステートメントを使用した関数は単機能のため、わざわざ戻す必要はありません。
「戻す必要がない=機能分割ができている」
(他のメーリングリストでも話題になったことがありますよ)
一行追加するかどうかは、お好み次第だと思います。
No.4
- 回答日時:
#2の方へですが、#2の回答を参考にする場合も考えて書いておきます。
エラー処理中の
GoTo PGMEND
は、
Resume PGMEND
にしましょう。
On Error Gotoによって飛んだ場合は、必ず Resumeで戻るように癖付けましょうね。
そうでないと、VB内部でエラー情報がクリアされません。
No.3
- 回答日時:
>Call objFs.CopyFile(objFile.Path, "コピー先名")
の下の
>GoTo PGMERR
いらないです・・・
テスト用のロジックが残ってしまった^^;
No.1
- 回答日時:
どうせなら、変数の宣言部分から全部書き込みましょう。
そうでないと正しい判断は下せません。とりあえず、
・For Eachの中で objFolderと objSubFolderの Nothingの処理は必要ない。
・on error goto 0でエラー処理を無効にしているのは何故か?
・on error goto CopyErrはループ外に書いた方が良いと思う。
・CopyErr処理部での resume ????が無い?
という事ぐらいでしょうか。
なんならエラーフラグでも設けて、エラー処理部でエラーフラグをセットし、For Eachでエラーフラグが立っていたら exit forをしてやるような処理に書き換えてみてはどうですか?その方がすっきりしそうです。もちろん、エラー処理部からは resume nextで戻ると。
この回答への補足
早速のご回答ありがとうございます。
アバウトな質問ですみません。
補足いたします。
'宣言
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が見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) VBA This Workbookモジュールを別ファイルにコピーする方法 1 2022/09/14 01:51
- Visual Basic(VBA) VBAが止まります。 2 2022/09/02 14:02
- Excel(エクセル) VBA フォルダ見える化のコードについて 2 2023/06/19 15:04
- Visual Basic(VBA) 【VBAエラー】Nextに対するForがありません 対策について 5 2022/11/21 21:26
- Visual Basic(VBA) FileSearch2007に変わる構文について 1 2022/12/16 16:57
- Visual Basic(VBA) outlook マクロが終了しません。 1 2022/09/02 11:14
- Visual Basic(VBA) vbaの構文の修正相談(xmlファイルを順に開いてコピペ作業) 1 2023/04/22 01:18
- Visual Basic(VBA) vbaのエラー対応(実行時エラー7:メモリが不足しています) 4 2023/04/24 00:20
- Visual Basic(VBA) ExcelVBAに関する質問 3 2023/02/17 10:47
- Visual Basic(VBA) エクセルVBA(実行時エラー438)の対処法を教えてもらえないでしょうか 3 2023/04/22 13:43
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
UserForm1.Showでエラーになり...
-
文字列内で括弧を使うには
-
マクロで"#N/A"のエラー行を削...
-
「実行時エラー '3167' レコー...
-
実行時エラー 438 の解決策をお...
-
VBAでfunctionを利用しようとし...
-
インポート時のエラー「データ...
-
VBでSQL文のUPDATE構文を使った...
-
Python openpyxlを使用したセル...
-
String""から型'Double'への変...
-
【VBAエラー】Nextに対するFor...
-
TransferSpreadsheetのエラー処理
-
エクセル関数式=ABSで#VALUE!...
-
html5 エラー
-
VBA データ(特定値)のある最...
-
VB.net 重複チェックがしたいです
-
実行時エラー'-2147467259(8000...
-
お助けください!VBAのファイル...
-
Excel VBA 複数ブックシートご...
-
VBAで時間(00:00形式)を積算...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
UserForm1.Showでエラーになり...
-
String""から型'Double'への変...
-
VBAでfunctionを利用しようとし...
-
【VBA】ワークブックを開く時に...
-
マクロで"#N/A"のエラー行を削...
-
文字列内で括弧を使うには
-
実行時エラー 438 の解決策をお...
-
On ErrorでエラーNoが0
-
お助けください!VBAのファイル...
-
VBA データ(特定値)のある最...
-
インポート時のエラー「データ...
-
実行時エラー'-2147467259(8000...
-
ACCESSで値を代入できないとは?
-
VB.net 重複チェックがしたいです
-
ActiveCell.FormulaR1C1の変数
-
【VB.NET】 パワポ操作を非表示で
-
「実行時エラー '3167' レコー...
-
ApplicationとWorksheetFunctio...
-
フランスの生年月日(jj/mm/aaaa)
-
【マクロ】 IFERROR関数をマク...
おすすめ情報