For Eachで実行時エラー424が発生する理由
まず最初に実行時エラー424の基本的な対応方法については「エラー424対応方法(オブジェクトが必要です)」に記載しています。ここではFor Eachを使ってエラーが発生した場合について対応方法を書いています。
ループ処理の書き方にはいくつかの方法がありますが、その1つがFor Eachです。
For Eachは配列や複数データを持つオブジェクトなどの「データの集まり」を1件ずつ取得しますが、その1件のデータを格納する変数が必要になります。このループごと変数に格納する際に実行時エラー424が発生することがあります。
これは、格納先のデータ型が格納元の「データの集まり」が持つデータ型と異なることが原因です。
以下のコードは実行時エラー424が発生します。詳細は後述します。
実行時エラー424の原因
エラーが発生するサンプルコードで、なぜ実行時エラー424が発生するのかを説明します。
なお、以下のコードを動かすにはDictionaryオブジェクトを利用しているため、事前にVBA画面→ツールメニュー→参照設定、を選択し、参照設定ダイアログで「Microsoft Scripting Runtime」にチェックを付ける必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Sub ForEach424Error() Dim dic As New Dictionary '// ファイルパスマップ '// テスト用にファイルパスをマップに追加 Call dic.Add("C:\a.txt", "111") Call dic.Add("C:\b.txt", "222") Call dic.Add("C:\c.txt", "333") Call dic.Add("C:\d.txt", "444") Dim oFile As File '// Fileオブジェクト '// マップを全件ループ For Each oFile In dic.Items '// ★ここで実行時エラー424が発生 '// ファイル名を出力 Debug.Print oFile.Name Next End Sub |
このコードは、格納元であるDictionaryオブジェクト(変数dic)に4件のデータを追加し、それをFor Eachループで回して、oFile変数にループ1回ごとに格納したい、という内容です。
DictionaryオブジェクトのItemsプロパティをFor Eachで回すと、Dictionaryオブジェクトのキーが返されます。このコードの場合、「”C:\a.txt”」「”C:\b.txt”」「”C:\c.txt”」「”C:\d.txt”」の4件が4回のループそれぞれで取得できます。
この「”C:\a.txt”」などはファイルパスを表していますが、ファイルパスである前に文字列のデータです。
そのため、VBAの実行時に「文字列をFileデータ型にコピーしようとしてもできないからエラーです。ファイルパスなのかもしれんけど、正しいファイルパスかどうかまだわからんしそれ以前の問題やし」という意味のエラーとして実行時エラー424が発生します。
「Fileオブジェクト = 文字列」という代入がダメ、というわけです。
「Set oFile = “abc”」と書いてOKか?と言われると、”abc”は全然ファイルじゃないしそりゃダメよね、って理屈です。
でも、言いたいことは分かります。「ファイルパスなんだからFileオブジェクトに格納してよ」と。これは別の書き方で解消しなければなりません。それを後述します。
ファイルパス文字列をFileオブジェクトに入れる方法
上のコードの場合の解消方法は、FileSystemObjectのGetFileメソッドです。GetFileメソッドは文字列をFileオブジェクトに変換します。
この方法で実行時エラー424を解消したコードが以下になります。
For EachのループではDictionaryのItemsプロパティではなくKeysプロパティを使って、キー文字列を取得します。このとき、キーが文字列かどうか分からないため、格納する変数のデータ型はVariant型でなければなりません。String型にしたいところですが、Dictionaryのキーが文字列かどうかはこの時点では確証が得られないためエラーになります。
あとは取得したキー文字列であるsPath変数のファイルパスが実際に存在するかをFileSystemObject.FileExistsメソッドで判定します。存在していれば、FileSystemObject.GetFileメソッドでFileオブジェクトに変換しています。
これでデータ型の不整合が解消され、実行時エラー424が出なくなります。
なお、GetFileメソッドについての詳細は「FileSystemObjectのGetFileメソッド」をご参照ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Sub ForEach424ErrorFix() Dim dic As New Dictionary '// ファイルパスマップ '// テスト用にファイルパスをマップに追加 Call dic.Add("C:\a.txt", "111") Call dic.Add("C:\b.txt", "222") Call dic.Add("C:\c.txt", "333") Call dic.Add("C:\d.txt", "444") Dim oFile As File '// Fileオブジェクト Dim fs As New FileSystemObject '// FileSystemObject(Fileオブジェクト取得用) Dim sPath As Variant '// ファイルパス文字列 '// マップを全件ループ For Each sPath In dic.Keys '// ファイルが存在する場合 If fs.FileExists(sPath) = True Then '// FileSystemObjectのGetFileメソッドでファイルパスをFileオブジェクトの変換 Set oFile = fs.GetFile(sPath) '// ファイル名を出力 Debug.Print oFile.Name End If Next End Sub |
実行時エラー424はコピー元と先のデータ型が違う
上のコードはFor Eachの行でFileオブジェクトに文字列を代入しようとして実行時エラー424になる例ですが、別のデータ型の場合も同様です。
違うデータ型同士の代入は、同じデータ型になるような変換をしてあげる必要があります。
Fileオブジェクトに文字列のファイルパスを設定したい場合はFileSystemObject.GetFileメソッド、というように、他のデータ型でも文字列から変換する関数が大抵用意されています。
例えば、日付を表すDate型に文字列の日付”yyyy/mm/dd”を格納したい場合はCDate関数を使う、などです。
ちなみ、Date型と日付文字列の変換については以下をご参照ください。
・「VBAで日付(Date型)から文字列に変換する」
・「VBAで文字列から日付(Date型)に変換する」
実行時エラー424が発生した場合は、そのような変換関数がないか探してみてください。