はじめに
VBAでは、ファイル操作やデータ処理を自動化することが多いですが、ZIPファイルを解凍せずに中身を確認したい場面もあります。
たとえば、定期的に届くZIPファイルの中にどんなファイルが入っているかを一覧で確認したい場合や、展開前にファイル名をチェックしたい場合などです。
この記事では、VBAからPowerShellを使ってZIPファイル内のファイル一覧を取得し、テキストファイルに出力する方法を解説します。解凍不要で高速に処理できます。
ZIPの中身を確認する3つの方法
VBAでZIPファイルの内容を取得する方法は主に3つあります。
- PowerShellで一覧取得
メリット:Windows標準機能で追加インストール不要
デメリット:階層構造の取得がやや複雑 - Shell.Application.NameSpaceを使用
メリット:VBAのみで完結
デメリット:処理が遅い、大きなZIPで不安定、Windows 10以降では非推奨 - 7-Zipのコマンドラインで一覧取得
メリット:高速で詳細情報も取得可能、パスワード付きZIPにも対応
デメリット:7-Zipのインストールが必要
この記事では、最も汎用性の高いPowerShell方式を中心に解説します。Windows 7以降であれば標準で動作します。
事前準備:参照設定
VBAからPowerShellを呼び出すには、Windows Script Host Object Modelの参照設定が必要です。なお以下のサンプルコードではFileSystemObjectも利用しているため、同じ参照設定でまとめて有効にします。
設定手順:
- VBAエディタを開く(Alt + F11)
- メニューバーから「ツール」→「参照設定」を選択
- 「Windows Script Host Object Model」にチェックを入れる
- 「Microsoft Scripting Runtime」にチェックを入れる(FileSystemObject用)
- OKをクリック
なお、参照設定を行わず、CreateObject関数を使う場合は以下のように記述します。
1 2 3 4 5 |
Dim obj Set obj = CreateObject("WScript.Shell") Dim fso Set fso = CreateObject("Scripting.FileSystemObject") |
参照設定を使う方が、コード補完(IntelliSense)が効くため開発効率が良くなります。
ZIP内の全てのファイルパスを取得する関数(GetZipFileList)
以下の関数は引数で渡されたZIPファイルパスに格納されている全てのファイルパスを取得して配列で返します。
ZIPファイル内のファイル取得には、PowerShellの.NET Frameworkクラスを利用しています。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
Function GetZipFileList(a_sZipPath As String) As Variant Dim sh As New IWshRuntimeLibrary.WshShell '// WshShell(コマンド実行用) Dim ex As WshExec '// WshExec(コマンド実行用) Dim sCmd As String '// コマンド文字列 Dim sOutput As String '// 出力結果(CRLFでコマンド結果が改行されて格納される) Dim arrLines As Variant '// 出力結果(ファイルパスの配列) Dim arrResult() As String '// 出力結果(空行を除いたファイルパスの配列) Dim i As Long '// ループカウンタ Dim iCount As Long '// データ数カウンタ '// 半角スペースをバッククォートでエスケープ a_sZipPath = Replace(a_sZipPath, " ", "` ") '// PowerShellコマンド作成 '// Add-Type:.NET Frameworkのクラスを読み込み '// ZipFile::OpenRead:ZIPファイルを開く '// Entries.FullName:各エントリのフルパスを取得 sCmd = "Add-Type -AssemblyName System.IO.Compression.FileSystem; " & "[System.IO.Compression.ZipFile]::OpenRead('" & Replace(a_sZipPath, "` ", " ") & "').Entries.FullName" '// PowerShellコマンド実行 Set ex = sh.Exec("powershell -NoLogo -ExecutionPolicy RemoteSigned -Command """ & sCmd & """") '// コマンド実行完了まで待機 Do While ex.Status = WshRunning DoEvents Loop '// 実行失敗時 If ex.ExitCode <> 0 Then GetZipFileList = Array() Exit Function End If '// 出力結果を取得 sOutput = ex.StdOut.ReadAll '// 結果が空の場合 If Trim(sOutput) = "" Then GetZipFileList = Array() Exit Function End If '// 改行で分割 arrLines = Split(sOutput, vbCrLf) '// 空行を除いてカウント iCount = 0 For i = LBound(arrLines) To UBound(arrLines) If Trim(arrLines(i)) <> "" Then iCount = iCount + 1 End If Next i '// 配列を準備 ReDim arrResult(1 To iCount) '// 結果を格納 iCount = 0 For i = LBound(arrLines) To UBound(arrLines) If Trim(arrLines(i)) <> "" Then iCount = iCount + 1 arrResult(iCount) = Trim(arrLines(i)) End If Next i '// 配列を返す GetZipFileList = arrResult End Function |
コードの解説
PowerShellコマンドの内容:
- Add-Type -AssemblyName System.IO.Compression.FileSystem:.NET FrameworkのZIP操作クラスを読み込み
- ZipFile::OpenRead():ZIPファイルを読み取り専用で開く
- Entries.FullName:ZIP内の各ファイルのフルパス(フォルダ構造を含む)を取得
戻り値:ファイル名の配列を返します。ZIP内にファイルがない場合は空の配列を返します。
ZIP内のフォルダについて:
ZIP内にフォルダが含まれる場合、フォルダ自体もパスの一部として返されます(末尾が/付与)。
使用例
ZIPファイルパスをGetZipFileList関数に渡し、その結果のファイルパスリストをZIPファイルと同じフォルダのresult.txtに出力するサンプルです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
Sub GetZipFileListTest() Dim path As String '// ZIPファイルパス Dim arrFiles As Variant '// ZIPファイル内の全ファイル Dim fso As New FileSystemObject '// FileSystemObject(出力ファイル用) Dim ts As TextStream '// TextStream(出力ファイル用) Dim outputPath As String '// 出力ファイルパス Dim filePath As Variant '// ZIPファイル内のファイルパス '// ZIPファイルパスを指定 path = "D:\test\test3.zip" '// ZIPファイル内の全ファイルパスを取得 arrFiles = GetZipFileList(path) '// 出力先(ZIPと同じフォルダにresult.txtを作成) outputPath = fso.BuildPath(fso.GetParentFolderName(path), "result.txt") '// テキストファイルにZIP内のファイルパスを出力 Set ts = fso.CreateTextFile(outputPath, True, False) For Each filePath In arrFiles ts.WriteLine filePath Next ts.Close End Sub |
出力例(result.txt の内容)
data/subfolder/test.xlsx
sample1.csv
sample2.txt
文字コードはANSI(Shift-JIS)で出力しています。Unicodeにしたい場合はfso.CreateTextFileの第3引数をTrueに変更します。
Set ts = fso.CreateTextFile(outputPath, True, False)
↓
Set ts = fso.CreateTextFile(outputPath, True, True)
既に result.txt が存在する場合は上書きされます。
追記にしたい場合は CreateTextFile の代わりに OpenTextFile(…, 8) を使用してください。
よくあるエラーと対処法
エラー1:「ZipFile型が見つかりません」
原因:
- .NET Framework 4.5以降が必要です。古いWindowsではSystem.IO.Compression.FileSystemが利用できません。
対処法:
- Windows 7の場合:.NET Framework 4.5以降をインストール
- Windows 10/11:標準で対応済み
エラー2:日本語ファイル名が文字化け
原因:
- PowerShellの出力エンコーディングがデフォルトでShift-JISのため、UTF-8のZIPでは文字化けする可能性があります。
対処法:
- PowerShellコマンドの冒頭にエンコーディング設定を追加します。
1 2 3 4 |
sCmd = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; " & _ "Add-Type -AssemblyName System.IO.Compression.FileSystem; " & _ "[System.IO.Compression.ZipFile]::OpenRead('" & _ Replace(a_sZipPath, "` ", " ") & "').Entries.FullName" |
エラー3:「パスが見つかりません」
原因:
- 指定したZIPファイルが存在しないか、パスに誤りがあります。
対処法:
- 事前にFileSystemObjectのFileExistsメソッドで存在確認を行います(使用例で実装済み)。
エラー4:空の配列が返される
原因:
- ZIPファイルが破損している
- 中身が実際に空
- パスワード保護されている(この方法では開けない)
対処法:
- 手動でZIPを開けるか確認
- パスワード付きZIPの場合は7-Zipを使用(別記事参照)