自動化厨のプログラミングメモブログ│CODE-LIFE

Python/ExcelVBA/JavaScript/Raspberry Piなどで色んなことを自動化

ExcelVBAからパスワード付き圧縮コマンド(7-zip/Lhaplus)を実行する

ExcelVBAからパスワード付き圧縮コマンド(7-zip/Lhaplus)を実行する

この記事でできるようになること

  • コマンドでファイルをパスワード付きZIP圧縮
  • ExcelVBAからcmd.exeにコマンドを渡す

毎日の作業を楽にするべくExcelVBAで作ったメール一括送信ツール。しかし、仕事で実用化するには添付ファイルにまつわる悪しき習慣"パスワード付きZIP"が立ちはだかる。

 

添付ファイルは何故パスワード付きzip?

ファイル送信直後にパスワードを記載したメールをもう一通送る。メールを不正に傍受する攻撃者がいたとしたら2通目も傍受しているはずであり、結局はパスワードを付けて暗号化していたとしてもパスワードを手に入れられたら無防備と変わらない。

しかし、調べてみると全く意味がないわけではないらしい。

 

一つ目はヒューマンエラー対策。メーラーのTo入力時に入力候補が出るような時、うっかり見た目が似ているアドレスに誤送信してしまうパターン。この場合にzipファイルを暗号化していれば受信者が悪意を持って解読しない限り、体裁上は暗号化していたので一安心となるわけである。

二つ目はPマークやISMS認証のためというもの。社外とデータをやり取りする場合は暗号化を施していないと審査に影響があるらしい。

 

以上のように全く意味がないわけではないが、送る方も受け取る方も手間のかかる作業であることに変わりはない。

参考:https://www.lrm.jp/mailzipper/attachment-encryption/

せめて送信者側の負担を軽減しましょうということでExcelVBAからパスワード付きZIP圧縮を実行する方法を紹介する。

 

【前準備】解凍・圧縮ソフトをインストール

定番のフリーソフトのいずれかがインストールされている必要があります。

Lhaplus

https://forest.watch.impress.co.jp/library/software/lhaplus/

7-Zip

https://sevenzip.osdn.jp/

インストールしていない場合は事前にインストールしてください。

 

それぞれの圧縮解凍ソフトのコマンド使用例

実行ファイルパスは環境により異なるのでインストール先を要確認。

Lhaplusは圧縮後のファイルパスに同名のファイルがある場合「名前をつけて保存」のダイアログが表示されるが7-zipは確認なしで上書きされるので注意が必要。

Lhaplusでのパスワード付きZIP化コマンド

64bitOS・・・ "C:\Program Files (x86)\Lhaplus\Lhaplus.exe"

32bitOS・・・ "C:\Program Files\Lhaplus\Lhaplus.exe"

 

パスワードなしの場合

実行ファイルパス /c:圧縮形式 /n:圧縮後ファイルパス 圧縮したいファイルもしくはフォルダパスのようにコマンドを実行する。

    "C:\Program Files (x86)\Lhaplus\Lhaplus.exe" /c:zip /n:"c:\zip\sample.zip" "c:\zip\sample.txt"

 

パスワードありの場合

実行ファイルパス /c:圧縮形式 /p:パスワード /n:圧縮後ファイルパス 圧縮したいファイルもしくはフォルダパスのようにコマンドを実行する。

    "C:\Program Files (x86)\Lhaplus\Lhaplus.exe" /c:zip /p:abc123 /n:"c:\zip\sample.zip" "c:\zip\sample.txt"

7-Zipでのパスワード付きZIP化コマンド

共通・・・ "C:\Program Files\7-Zip\7z.exe"

※64bit用のインストーラも用意されているが間違って32bit版をインストールしていた場合は"C:\Program Files (x86)\7-Zip\7z.exe"かもしれません。

 

パスワードなしの場合

実行ファイルパス a -t圧縮形式 圧縮後ファイルパス 圧縮したいファイルもしくはフォルダパスのようにコマンドを実行する。

    "C:\Program Files\7-Zip\7z.exe" a -tzip "c:\zip\sample.zip" "c:\zip\sample.txt"

 

パスワードありの場合

実行ファイルパス /c:圧縮形式 /p:パスワード /n:圧縮後ファイルパス 圧縮したいファイルもしくはフォルダパスのようにコマンドを実行する。

    "C:\Program Files\7-Zip\7z.exe" a -tzip -pabc123 "c:\zip\sample.zip" "c:\zip\sample.txt"

多分使わないと思うけど7z形式も可能でした

    "C:\Program Files\7-Zip\7z.exe" a -t7z -pabc123 "c:\zip\sample.7z" "c:\zip\sample.txt"

 

ExcelVBAからコマンドラインを叩く方法と用例

ここからはいよいよExcelVBAからこれらのコマンドを実行する方法。

最初はバッチファイルを用意して引数のみ渡す方向で考えていたが、その場合非同期の処理になるためVBAがメールにzipファイルを添付しようとするタイミングで圧縮が完了している保証が出来ない。

そのためWshShellオブジェクトにコマンドをまるごと渡すことにした。

参考:https://vbabeginner.net/vbaでコマンドプロンプトの起動とコマンドの実行/

 

ざっくり仕様

  • Lhaplus/7-zip両方に対応
  • パスワードはオプションとして設定するか選べる
  • 同期処理(圧縮コマンド実行中はVBA側の処理を待機させる)

 

zipCompression プロシージャ

    Sub zipCompression(targetPath As String, zipPath As String, Optional password As String = "NOT_SET_PASSWORD")
    
        Dim sh  As New IWshRuntimeLibrary.WshShell  ' WshShellクラスオブジェクト
        Dim ex  As WshExec                          ' Execメソッド戻り値
        Dim cmd As String                           ' 実行コマンド
        
        '圧縮ソフト実行ファイルパス
        Const EXE_7ZIP        As String = "C:\Program Files\7-Zip\7z.exe"
        Const EXE_7ZIP_X86    As String = "C:\Program Files (x86)\7-Zip\7z.exe"
        Const EXE_LHAPLUS     As String = "C:\Program Files\Lhaplus\Lhaplus.exe"
        Const EXE_LHAPLUS_X86 As String = "C:\Program Files (x86)\Lhaplus\Lhaplus.exe"
        
        
        ' 圧縮解凍ソフト存在チェック
        If Dir(EXE_7ZIP) <> "" Then
    
            cmd = """" & EXE_7ZIP & """"
        
        ElseIf Dir(EXE_7ZIP_X86) <> "" Then
        
            cmd = """" & EXE_7ZIP_X86 & """"
            
        ElseIf Dir(EXE_LHAPLUS) <> "" Then
        
            cmd = """" & EXE_LHAPLUS & """"
            
        ElseIf Dir(EXE_LHAPLUS_X86) <> "" Then
        
            cmd = """" & EXE_LHAPLUS_X86 & """"
        
        Else
        
            MsgBox "圧縮に必要なソフトウェアがありません"
            Exit Sub
        
        End If
        
        
        ' コマンド生成
        If InStr(cmd, "Lhaplus") <> 0 _
        Then ' Lhaplus
        
            cmd = cmd & " /c:zip"
        
            If password <> "NOT_SET_PASSWORD" Then cmd = cmd & " /p:" & password ' パスワード指定
            
            cmd = cmd & " /n:""" & zipPath & """ """ & targetPath & """"
            
        Else ' 7-zip
            
            cmd = cmd & " a"
            
            If password <> "NOT_SET_PASSWORD" Then cmd = cmd & " -p" & password ' パスワード指定
            
            cmd = cmd & " -tzip """ & zipPath & """ """ & targetPath & """"
            
        End If
        
        
        ' コマンド実行
        Set ex = sh.Exec("cmd.exe /c """ & cmd & "")
        
        
        ' コマンド失敗時
        If (ex.Status = WshFailed) Then
            ' エラー発生時は処理を抜ける
            MsgBox "コマンド実行エラー"
            Exit Sub
        End If
        
        
        ' コマンドの処理完了を待機
        Do While (ex.Status = WshRunning)
            DoEvents
        Loop
        
    End Sub

やたら "(ダブルクォーテーション)が多いのはDOSの仕様上「半角スペースを含むパスが扱えない」のを回避するためです。

参考:https://jj-blues.com/cms/column-spaceinbatfile/

 

呼び出し方法

call zipCompression("圧縮対象のパス", "圧縮後のパス", "パスワード")

※第3引数のパスワードは省略可能

    call zipCompression("c:\zip\sample.txt", "c:\zip\sample.zip", "abc123")

 

まとめ

これでzipCompressionを呼び出すだけでファイルを圧縮可能に。メール送信ツールに組み込めばメール作成の直前に自動でパスワード付き圧縮させることができます。ヽ(=´▽`=)ノ

今後の課題
  • Lhaplus/7-zipを選べるように第4引数として設定したら便利かも
  • 強固なパスワード生成プロシージャも作ったら便利かも

 

今回学んだこと
  • DOSの半角スペースうざい
  • IsMissingはバリアント型にしか有効でない

 

引数が渡されたかチェックするIsMissing関数

IsMissing 関数では、バリアント型ではない、整数型や倍精度浮動小数点型などの通常のデータ型に対しては無効です。これは、整数型などのデータ型では、引数が指定されたかどうかを判断するためのフラグが提供できないためです。

引用元:https://www.vba-ie.net/function/ismissing.php

使えるのはバリアント型だけでしたあああああ。つまりString型のpasswordはこれが使えない。

仕方なく上記のサイトのサンプルを参考にOptional password As String = "NOT_SET_PASSWORD"のように引数が渡されなかった場合の初期値にNOT_SET_PASSWORDをセットしておき、引数が渡された場合のみパスワードの設定コマンドが追加されるように書いた次第です。

    If password <> "NOT_SET_PASSWORD" Then cmd = cmd & " -p" & password ' パスワード指定

 

やっぱりちゃんと仕様を理解して使わないと無用な検証に追われる羽目になりますね。