パスワード付きZipファイルメールを自動解凍する方法

こんにちは。カナメです。

 

2020年11月に、日本の内閣府、内閣官房がパスワード付きZipファイルメールを廃止すると発表しました。

 

とはいえ、現在でも完全になくなっているわけではありません。

私の業界ではいまだにZipファイルメールが飛び交っています。

 

PPAPと呼ばれるこのメール送信方法ですが、セキュリティ上の利点はあまりないといわれています。

何が大変かというと、このZipファイルメールの添付ファイルを見るためには、

・一度ファイルを保存して、

・拡張子を.zipに変えて(メールの受信セキュリティをくぐりぬけるため、拡張子が「.zi_」のように一文字変えてあるため)

・別メールからパスワードをコピペして解凍しないといけません。

 

私のデスクトップは一時置きファイルだらけになってしまいました。

 

そこで、何かいい方法はないかとインターネットで検索したら、

何名かのエンジニアの方々が、この問題に取り組んでいましたので

インターネットの情報を参考に、自分で作ってみることにしました。

 

できたものは、Outlookに組み込む形のVBAで作成したプログラムです。

 

例えば、パスワード付きZipファイルとパスワードのメールが届いたら、zipファイル付きのメールを別ウィンドウで開き、プログラムを起動すると・・・

こんな感じでメッセージが出て、

元のメールに解凍されたファイルが自動で添付されます。

 

環境条件

このプログラムが動く条件は、

・Windows10 64bit であること

・Dドライブがあること

です。

 

Dドライブがない場合は、Cドライブの容量を一部切り離してDドライブを作ることができます。

(Dドライブ 作成 などで検索すればやり方が出てきます)

 

必要なフリーソフト

このプログラムは2つのフリーソフトを連携しているため、事前にインストールが必要です

以下のURLからダウンロード可能です。

・7-Zip

圧縮・解凍ソフト 7-Zip (osdn.jp)

・7-Zip.DLL (個人で製作されているソフトです)

http://akky.xrea.jp/

・今回自作したモジュール(3個) ※以下のリンクからダウンロードできます。

パスワード付zipファイルメール自動解凍VBAのモジュールはこちらからダウンロードできます。 Outlook 2013,2016で動作...

7-zipをインストールする

ダウンロードしたexeファイルをダブルクリックしてProgram Fileにインストールします

7zip DLLを配置する

ファイル「7-zip32.dll」を Cドライブ → Windows →SysWOW64のフォルダ内にコピーします

Outlookにモジュールを組み込む

まずOutlookからメールを1通なんでもいいので別ウィンドウで開きます

タブの部分で右クリック→リボンのユーザー設定を選択

右のウィンドウの「開発」にチェックを入れて、OKを押す

開発タブをクリックして、VisualBasicのボタンをクリックします

VisualBasicEditer画面が開いたら、左上のProjectを右クリック

「ファイルのインポート」をクリック

今回作成したモジュール「AutoExtract1.bas」を選択し、OKを押す

こんな感じでインポートされます

同様の手順で、ファイル「DLL.bas」もインポートします

同様の手順で、ファイル「Progress.frm」もインポートします

※ファイル「Progress.frx」は、インポート作業をしなくても「Progress.frm」をインポートすれば一緒にインポートされます

保存ボタンを押します

マクロの無効化を解除する

Outlookでマクロが無効にされていると動かせませんので、マクロを有効化します。

Outlookの左上「ファイル」タブをクリック

「オプション」をクリック

セキュリティセンターをクリック

「セキュリティセンターの設定」をクリック

マクロの設定をクリック

→すべてのマクロに対して警告を表示する のラジオボタンを選択

→ウィンドウ右下のOKを押す

マクロ起動ボタンを作る

メールの表示ウィンドウで作成します(受信トレイのウィンドウではありません)

なんでもいいので1通メールを別ウィンドウで開きます。

タブのあたりを右クリック → リボンのユーザー設定 をクリック

右ウィンドウで右クリック

新しいタブの追加 をクリック

新しいタブ(ユーザー設定)ができます(右クリック→名前の変更で「新しいタブ」の名前は変更できます)

(ここではとりあえずタブ名を”マクロ”としておきます)

右ウィンドウで「新しいグループ(ユーザー設定)」を選択した状態で

左ウィンドウのコマンドの選択のプルダウンをクリック

「マクロ」を選択する

先程組み込んだマクロ「自動解凍」が出てきますので、クリックして

「追加」ボタンを押す

これでマクロボタンができましたので、OKを押す

メールのウィンドウに新しいタブができて、そこをクリックするとマクロ起動ボタンができています

これで設定は完了です。

一度Outlookを閉じて、再起動します。

 

使ってみよう

Outlookを立ち上げます

zipファイルが添付されているメールを別ウィンドウで開きます

(このとき、同じ差出人で、メール受信前後1時間以内にパスワードメールが来ていることが前提です)

メールのタブに、先程作成した新しいタブができているはずです

自動解凍のマクロ起動ボタンを押します

最初の1回目だけ、マクロを有効にするか確認するメッセージが出るので

「マクロを有効にする」をクリック

解凍に成功すると「解凍されました」のメッセージが出て、解凍されたファイルが添付ファイルに追加されます

これでもう、めんどくさいパスワード解凍作業から解放されますね!

 

こういう場合は解凍できない

このプログラムは、残念ながらすべてのzipファイルが解凍できるわけではありません。

  • パスワードに「”」ダブルコーテーションが入っていると、解凍できません
  • パスワードが全角だと解凍できません
  • ”パスワードはabcです”のようにパスワードの文字列が本文につなげられている場合は解凍できません(ただし全角の”「” 、”【” のカギかっこでパスワードが区切られている場合は解凍できます
  • パスワードメールの差出人がzipファイルのメールの差出人と違うと、解凍できません

 

私の力では今のところこれ以上のコードが書けません・・・

こうしたら解けるようになるよ!という達人の方がいらっしゃいましたら、ぜひ教えてください。

 

進捗バーが最後まで行って「解凍できませんでした」というメッセージが出たら

残念ですが手動で解凍するしかありません

パスワードの候補が多すぎると時間がかかるときがあります。

その時は「中止」ボタンを押すと中断できます。

どうやってパスワードを解除しているの?

今回作成したプログラムがどういう動きをしているかといいますと、こんな感じです

1.メールの添付ファイルが、拡張子が “.zip” “.ZIP” “.zi_” “.zi”のいずれかであるかどうか確認する

2.zipファイルが見つかったら、Dドライブに一時的に保存する

3.メールの受信時間の前後1時間に、同じ差出人のメールがあるか確認する

4.同じ差出人のメールが見つかったら、メールの本文の中に、”パスワード” “Password” “password” “PASS” の文字があるか探す

5.もし見つかったら、その行に「,【 カギかっこがあるか探して、かっこ内の文字列をパスワード候補にする

6.見つからなかったら、本文内の文字列で4~24文字の半角英数字の文字列をランダムに抽出する

7.複数のパスワード候補を、パスワードが長くて、複雑なものから順番にパスワードを入れて、解凍を試みる

8.解凍に成功したらファイルをメールに添付する

最も難しいのは、パスワードを解凍するのにかかる時間を短縮にするために、パスワードの複雑なものから順番に解凍を試みるところでした。

 

これはインターネットにYoshinari Nomuraさんという方が「パスワードは別途お送りします」をなんとかしたい

というブログで書かれており、パスワード別送添付メールの問題点と受信側での対策についてという論文を出されていましたので、こちらのロジックを使用させていただきました。

「glima」という自動解凍ソフトをGitHubで発表されていましたが、私はRubyが理解できなかったので、仕事で使うOutlookで何とか自動解凍ができないかと思い、今回VBAで作成しました。

  

その他、Outlook VBAの自動解凍については、タナイさんのブログthanaism.com

「パスワード別送メールのzipを自動で解凍したい」のエントリを参考にさせていただきました。

 

プログレスバーの作成は エクセルの神髄 第26回.プログレスバーを自作する から作成

 

当初はコマンドラインで7-zipを動かしていたのですが、仕様なのか3割くらい(パスワードが正しくても)解凍できないzipファイルがあり、困り果てていたところ、

7-zipでDLLを作成されていたAkkyWareHOUSE にたどり着き、hatenachips というサイトでDLLを使った7-zipの動かし方を使うことで、解決することができました。

 

いやあすごい人がいるもんですね(私はプログラマではなく、素人です)

2023年7月更新

Office365(Microsoft365)の方はこちらを使ってください

 

それでは、また。

 

ソースコードはこちら

1.AutoExtract1.bas

  1. Option Explicit
  2. Sub 自動解凍() 'パスワード付zipメール自動解凍(メインモジュール)
  3. Dim objIns As Inspector
  4. Dim myMail As Object
  5. Dim passMail As Object
  6. Dim myPassword() As Variant
  7. Dim cPass As String
  8. Dim f As Integer
  9. Dim next_time As Date
  10. Dim ii As Integer
  11. Dim ath As Integer
  12. Dim athf As Integer
  13. Set objIns = Application.ActiveInspector
  14. 'メールIDを取得
  15. Set myMail = objIns.CurrentItem
  16. '添付ファイル有無
  17. If myMail.Attachments.Count > 0 Then
  18.     
  19.     'ファイルの添付がいくつあるか確認
  20.     ath = myMail.Attachments.Count
  21.     
  22.     '何番目のファイルがzipファイルか確認
  23.     For ii = 1 To ath
  24.         '拡張子が.zi_,.zip,.zi_p であったらメッセージを出す
  25.         If Right(myMail.Attachments(ii).FileName, 4) = ".zi_" Or _
  26.            Right(myMail.Attachments(ii).FileName, 4) = ".zip" Or _
  27.            Right(myMail.Attachments(ii).FileName, 5) = ".ZIP" Or _
  28.            Right(myMail.Attachments(ii).FileName, 3) = ".zi" Then
  29.         athf = ii
  30.         End If
  31.     Next
  32.     
  33.         If athf <> 0 Then
  34.         'パスワードの記載されているメールを探す→Function findPassMailへ
  35.         Set passMail = findPassMail(myMail)
  36.         
  37.             If passMail Is Nothing Or passMail.Count = 0 Then
  38.                     MsgBox "パスワードメールが見つかりません"
  39.                     Exit Sub
  40.             End If
  41.         'パスワード候補の配列を入れる→Function getRegExpへ
  42.         myPassword() = getRegExp(passMail)
  43.          
  44.         'もしDドライブにtmp000フォルダがあったら削除する (エラー回避)
  45.             Dim wt As String
  46.             wt = Dir("D:\tmp000", vbDirectory)
  47.             If wt <> "" Then
  48.                 Dim FSO As Object
  49.                 Set FSO = CreateObject("Scripting.FileSystemObject")
  50.                 FSO.DeleteFolder "D:\tmp000"
  51.             End If
  52.         
  53.         'Dドライブに一時フォルダ作成
  54.         CreateObject("Scripting.FilesystemObject").createFolder "D:\tmp000"
  55.         
  56.         '7-Zipを動かす
  57.         Const ZIP_PATH As String = "D:\tmp000.zip"
  58.         Const TGT_PATH As String = "D:\tmp000\"
  59.         Const EXE_7ZIP As String = "C:\Program Files\7-Zip\7z.exe"
  60.         
  61.         myMail.Attachments(athf).SaveAsFile ZIP_PATH
  62.         
  63.         Dim myWsh As Object
  64.         
  65.         Set myWsh = CreateObject("WScript.Shell")
  66.         
  67.         Dim myExec As String
  68.         Dim myCmd As String
  69.         Dim result As String
  70.         Dim ss As String
  71.         Dim flg As Integer
  72.         Dim C As Integer
  73.                         
  74.         If Dir(EXE_7ZIP) <> "" Then
  75.             myCmd = """ & EXE_7ZIP & """
  76.         Else
  77.             Exit Sub
  78.         End If
  79.         
  80.         'パスワード候補を一つずつ入れる
  81.         C = UBound(myPassword)
  82.         
  83.         '1個もパスワードが抽出できなかった場合、終了する
  84.         If C = 0 Then
  85.         MsgBox "パスワードが見つかりませんでした"
  86.         Exit Sub
  87.         End If
  88.         
  89.     'プログレスバーを準備
  90.     Dim Progress As New Progress
  91.     
  92.     With Progress
  93.         .MaxValue = C
  94.         .BackColor = RGB(222, 222, 222)
  95.         .Interactive = True
  96.         .ShowModeless "開始します"
  97.     End With
  98.     
  99.     
  100.         On Error GoTo myError: '中断したらエラーを回避して終了
  101.         For f = 0 To C
  102.         'プログレスバーの表記
  103.         
  104.         Progress.Value f, f & "件試行中‥/パスワード候補" & Progress.MaxValue & "件中"
  105.         cPass = myPassword(f, 0)
  106.         
  107.         If ExtractZIP(TGT_PATH, ZIP_PATH, cPass) Then
  108.         
  109.     
  110.         End If
  111.         
  112.             '一時フォルダにファイルがあり、かつファイルサイズが0でない場合、解凍できたと判断し修了する
  113.             Dim ext As String
  114.             
  115.             ext = Dir(TGT_PATH & "\*.*")
  116.             
  117.             If ext = "" Then 'ファイルが無かったら、フォルダがないか調べる 解凍したらフォルダだった場合
  118.                         
  119.              With CreateObject("Scripting.FileSystemObject")
  120.                     If .GetFolder(TGT_PATH).SubFolders.Count <> 0 Then
  121.         
  122.                             Dim fd As Object
  123.                             Dim exb As String
  124.                             Dim FlgT As Boolean
  125.         
  126.                             'フォルダ内のファイルをTmpフォルダにコピー
  127.                             For Each fd In .GetFolder(TGT_PATH).SubFolders
  128.         
  129.                              exb = Dir(fd.Path & "\*.*")
  130.                             Do While exb <> ""
  131.                                  If FileLen(fd.Path & "\" & exb) <> 0 Then 'ファイルサイズが0じゃないファイルがあったらフラグをTrue
  132.                                     FlgT = True
  133.                                      .CopyFile fd.Path & "\" & exb, TGT_PATH & exb 'Tmpフォルダにファイルをコピーする
  134.                                  End If
  135.                                  exb = Dir()
  136.                              Loop
  137.         
  138.                             Next fd
  139.                        
  140.                       End If
  141.                 End With
  142.            Else 'ファイルがあった場合
  143.             
  144.             
  145.                 If FileLen(TGT_PATH & "\" & ext) <> 0 Then 'ファイルサイズが0じゃないファイルがあったらフラグをTrue
  146.                     FlgT = True
  147.                 End If
  148.            End If
  149.            
  150.            
  151.            If FlgT = True Then
  152.         
  153.             MsgBox "解凍できました"
  154.             
  155.             '作成中 元のメールに、解凍したファイルを添付する(元のメールは削除しない 念のため)
  156.             Dim tgtFilename As String
  157.             
  158.             tgtFilename = Dir(TGT_PATH & "\*.*", vbNormal)
  159.             
  160.             Do While tgtFilename <> ""
  161.                   Name TGT_PATH & "\" & tgtFilename As TGT_PATH & "\" & tgtFilename
  162.                   myMail.Attachments.Add TGT_PATH & "\" & tgtFilename
  163.                 tgtFilename = Dir()
  164.             Loop
  165.             
  166.             'フラグを付ける
  167.             myMail.FlagStatus = 1
  168.             myMail.FlagRequest = "自動解凍しました"
  169.             myMail.Save
  170.             
  171.             ' Dドライブの一時フォルダ、ファイルを削除する
  172.                 'tmpフォルダ削除
  173.                 Set FSO = CreateObject("Scripting.FileSystemObject")
  174.                 FSO.DeleteFolder "D:\tmp000"
  175.                 Kill "D:\tmp000.zip"
  176.                 Set FSO = Nothing
  177.                 
  178.                 Set myMail = Nothing
  179.                 Set myWsh = Nothing
  180.                 
  181.                 'プログレスバーを閉じる
  182.                 Progress.SelfClose
  183.             Exit Sub
  184.                 End If
  185.             
  186.         
  187.         '次のパスワードを試す
  188.         Next f
  189.         flg = 1
  190.         End If
  191. End If
  192. If flg = 1 Then
  193.             ' Dドライブの一時フォルダ、ファイルを削除する
  194.                 'tmpフォルダ削除
  195.                 
  196.                 Set FSO = CreateObject("Scripting.FileSystemObject")
  197.                 FSO.DeleteFolder "D:\tmp000"
  198.                 Kill "D:\tmp000.zip"
  199.                 Set FSO = Nothing
  200.                 
  201.                 'プログレスバーを閉じる
  202.                 Progress.SelfClose
  203. MsgBox "解凍できませんでした"
  204.             'フラグを付ける
  205.             myMail.FlagStatus = 2
  206.             myMail.FlagRequest = "解凍できませんでした"
  207.             myMail.Save
  208. End If
  209. myError:
  210. Set myMail = Nothing
  211. Set myWsh = Nothing
  212. End Sub
  213. 'パスワードのメールを特定する
  214. Private Function findPassMail(ByVal myMail) As Items
  215. Dim myItems As Items
  216. Dim tgtItem As Object
  217. Dim myFolder As Folder
  218. Dim bb As Items
  219. Dim cd As Items
  220. Dim sdr As String
  221. Dim canD As Items
  222. '対象メールの差出人を取得
  223. 'sdr = myMail.SenderEmailAddress
  224. 'メールの親フォルダを特定
  225. Set myFolder = myMail.Parent
  226. Set myItems = myFolder.Items
  227. '受信フォルダを絞り込み
  228. Set bb = minimizeSearchRange(myMail, myItems)
  229. Set findPassMail = bb
  230. End Function
  231. '前後1時間のメールから送信者が同じメールを検索する
  232. Private Function minimizeSearchRange(ByVal myMail, myItems) As Items
  233.     Dim myDateFrom As Date
  234.     Dim myDateTo As Date
  235.     Dim strFilter As String
  236.     Dim Str As String
  237.     Dim zzz As Items
  238.         
  239.    
  240.     myDateFrom = DateAdd("h", -1, myMail.ReceivedTime)
  241.     myDateTo = DateAdd("h", 1, myMail.ReceivedTime)
  242.      strFilter = "[SenderEmailAddress] = " & "'" & myMail.SenderEmailAddress & "'" & " AND [受信日時] >= '" & Format(myDateFrom, "yyyy/mm/dd hh:mm") & "' AND [受信日時] < '" & Format(myDateTo, "yyyy/mm/dd hh:mm") & "'"
  243.    
  244.     Set zzz = myItems.Restrict(strFilter)
  245.     
  246.     '添付ファイルのついているメールは除く
  247.      strFilter = "@SQL=urn:schemas:httpmail:hasattachment= False "
  248.     
  249.     Set minimizeSearchRange = zzz.Restrict(strFilter)
  250.     
  251. End Function
  252. '
  253. 'パスワードメールの本文からパスワード候補を検索
  254. Private Function getRegExp(ByVal passMail As Object) As Variant()
  255.     Dim myBody As String
  256.     Dim myRE As Object
  257.     Dim myMatches As Object
  258.     Dim myMatch As Object
  259.     Dim C As Integer
  260.     Dim passN() As Variant 'パスワード候補の配列
  261.     Dim i As Integer
  262.     Dim ln As Integer
  263.     Dim g As String
  264.     Dim h As String
  265.     Dim entP As Long
  266.     Dim b As Integer
  267.     Dim lg As Long
  268.     Dim t As Long
  269.     Dim tgtItem As MailItem
  270.     Dim q As Integer
  271.     Dim ps As String
  272.     Dim v As Integer
  273.     Dim passF() As Variant 'パスワード候補の配列2
  274.     
  275.     
  276.     
  277. For Each tgtItem In passMail
  278.    
  279.     
  280.     'メール本文を取得
  281.     myBody = tgtItem.Body
  282.     
  283.     
  284. 'パスワード候補文字列切り出し
  285. '---------------------------------------------------
  286. Dim lines() As String
  287. Dim ww As Integer
  288. Dim cn As Integer
  289. Dim pw As String
  290. Dim L As Integer
  291. Dim Psc As Integer
  292. Dim ed As Integer
  293. Dim mojs As Integer
  294. Dim lnn As Integer
  295. Dim lstr As String
  296. '本文を1行ずつ配列に入れる
  297. lines = Split(myBody, vbCrLf)
  298. 'パスワード,もしくはPassword,もしくはpassword を含む行を特定する
  299. For ww = 0 To UBound(lines)
  300. If InStr(lines(ww), "パスワード") <> 0 Or _
  301.    InStr(lines(ww), "Password") <> 0 Or _
  302.    InStr(lines(ww), "PASS") <> 0 Or _
  303.    InStr(lines(ww), "password") <> 0 Then
  304.    
  305.    
  306.    
  307.    ''パスワード'という文字以降の文字列を対象にする
  308.    If InStr(lines(ww), "パスワード") <> 0 Then
  309.     lnn = InStr(lines(ww), "パスワード")
  310.    ElseIf InStr(lines(ww), "Password") <> 0 Then
  311.     lnn = InStr(lines(ww), "Password")
  312.    ElseIf InStr(lines(ww), "password") <> 0 Then
  313.     lnn = InStr(lines(ww), "password")
  314.    ElseIf InStr(lines(ww), "PASS") <> 0 Then
  315.     lnn = InStr(lines(ww), "PASS")
  316.    End If
  317.    
  318.    lstr = Mid(lines(ww), lnn)
  319.    
  320.    
  321.    
  322.    'パスワードとの区切り":"を特定
  323.    cn = InStr(lstr, ":")
  324.    
  325.    '見つからなかったら全角の:も探す
  326.     If cn = 0 Then
  327.         cn = InStr(lstr, ":")
  328.     End If
  329.     
  330.     'それでも見つからなかったら「カッコを探す かっこの場合は終わりの」カッコとじを除く
  331.     If cn = 0 Then
  332.         cn = InStr(lstr, "「")
  333.         
  334.             If cn <> 0 Then
  335.                 ed = InStr(lstr, "」")
  336.                 mojs = ed - cn - 1
  337.             End If
  338.         
  339.     End If
  340.     
  341.     'それでも見つからなかったら【カッコを探す かっこの場合は終わりの】カッコとじを除く
  342.     If cn = 0 Then
  343.         cn = InStr(lstr, "【")
  344.         
  345.             If cn <> 0 Then
  346.                 ed = InStr(lstr, "】")
  347.                 mojs = ed - cn - 1
  348.             End If
  349.         
  350.     End If
  351.    
  352.    ':以降の文字列をパスワード候補とする
  353.    If mojs <> 0 Then
  354.         pw = Mid(lstr, cn + 1, mojs)
  355.         Else
  356.         pw = Mid(lstr, cn + 1)
  357.    End If
  358.    
  359.    
  360.    '空白を削除
  361.    pw = Replace(pw, " ", "")
  362.    pw = Replace(pw, " ", "")
  363.    
  364.    'nullの場合は適当な文字を入れる(DLLでパスワード入力ダイアログを出さないため)
  365.    If pw = "" Then
  366.    pw = "n"
  367.    End If
  368.    
  369.    'パスワード候補のカウンタ
  370.    L = L + 1
  371.    
  372.    '配列に入れる
  373.    ReDim Preserve passN(Psc + L)
  374.    passN(Psc + L) = pw
  375.    
  376. End If
  377. mojs = 0 '文字数リセット
  378. Next ww
  379. 'カウンタの保存
  380. Psc = Psc + L
  381.     
  382. '---------------------------------------------------
  383.     
  384.     
  385. '他のパスワード候補の切り出し
  386.     '正規表現で検索
  387.     Set myRE = CreateObject("VBScript.RegExp")
  388.         
  389.         With myRE
  390.             .Pattern = "\S[!-~]{3,24}" '空白、改行等を含まない半角英数字で4〜24桁の文字列を検索
  391.             .IgnoreCase = False
  392.             .Global = True
  393.         End With
  394.         
  395.     Set myMatches = myRE.Execute(myBody)
  396.         C = myRE.Execute(myBody).Count
  397. ' MsgBox c
  398.         
  399.         C = C + t
  400.         
  401.         ReDim Preserve passF(C)
  402.         
  403.         passF(0) = "n" 'Empty値を入れないように適当な値を入れる(DLLでパスワード入力ダイアログを出さないため)
  404.         
  405.         For Each myMatch In myMatches
  406.         
  407.                     
  408.             
  409.             '配列に入れる
  410.             passF(i + t + 1) = myMatch.Value
  411.             
  412.             passF(i + t + 1) = Replace(passF(i + t + 1), " ", "") '全角空白があったら削除
  413.         
  414.             i = i + 1
  415.         
  416.         Next myMatch
  417.         
  418.         t = UBound(passF)
  419.         
  420.         L = 0
  421.         i = 0
  422.         
  423. Next tgtItem
  424.         
  425. '2次元配列を作成し、データを移行 passNとpassF →passD
  426. Dim passD() As Variant
  427. Dim kz As Integer
  428. ReDim passD(Psc + t, 1)
  429. passD(0, 0) = "n" 'Empty値を入れないように適当な値を入れる(DLLでパスワード入力ダイアログを出さないため)
  430. 'パスワード候補2を入れる
  431. '本文からパスワード候補として切り出した文字列は正しいパスワードである確率が高いのでエントロピーを高スコアにする
  432. For v = 1 To Psc
  433.     passD(v, 0) = passN(v)
  434.     passD(v, 1) = 100
  435. Next v
  436. 'パスワード候補1を入れる
  437. For v = Psc + 1 To Psc + t
  438.     passD(v, 0) = passF(kz + 1)
  439.     kz = kz + 1
  440. Next v
  441. 'それ以外の文字列はエントロピーを計算する
  442. For q = Psc + 1 To Psc + t
  443.     ps = passD(q, 0)
  444.     
  445.             '文字列のエントロピー計算
  446.             ln = Len(ps) 'パスワード候補の文字長さ
  447.             
  448.             '対数で取得
  449.             lg = Log(ln + 1) / Log(2)
  450.             
  451.             '隣り合う文字が違っているほどエントロピーは高いとする
  452.             For b = 1 To ln
  453.                 g = Mid(ps, b, 1)
  454.                     If g Like "[a-z]" Then
  455.                         g = "a"
  456.                         ElseIf g Like "[A-Z]" Then
  457.                         g = "A"
  458.                         ElseIf g Like "[0-9]" Then
  459.                         g = "1"
  460.                         Else
  461.                         g = "other"
  462.                     End If
  463.                 
  464.                 h = Mid(ps, b + 1, 1)
  465.                     If h Like "[a-z]" Then
  466.                         h = "a"
  467.                         ElseIf h Like "[A-Z]" Then
  468.                         h = "A"
  469.                         ElseIf h Like "[0-9]" Then
  470.                         h = "1"
  471.                         Else
  472.                         h = "other"
  473.                     End If
  474.                     
  475.                 If g <> h Then
  476.                 entP = entP + 1
  477.                 End If
  478.                 
  479.             Next b
  480.             '計算結果を配列2次元目に入れる
  481.             entP = entP + lg
  482.             passD(q, 1) = entP
  483.             entP = 0
  484.             
  485.         Next q
  486.         
  487. 'エントロピーの高い順に配列をソート(バブルソート)
  488. Dim vSwap
  489. Dim m As Integer
  490. Dim j As Integer
  491. Dim k As Integer
  492. For m = LBound(passD, 1) To UBound(passD, 1)
  493.     For j = LBound(passD) To UBound(passD) - 1
  494.         
  495.         If passD(j, 1) < passD(j + 1, 1) Then
  496.                 
  497.             For k = LBound(passD, 2) To UBound(passD, 2)
  498.             
  499.                 vSwap = passD(j, k)
  500.                 
  501.                 passD(j, k) = passD(j + 1, k)
  502.                 passD(j + 1, k) = vSwap
  503.             
  504.             Next k
  505.         End If
  506.     Next j
  507. Next m
  508.         
  509.         '戻り値(2次元配列)を入れる
  510.         getRegExp = passD()
  511.         
  512.         
  513. Set myRE = Nothing
  514. Set myMatches = Nothing
  515.         
  516. End Function

2.DLL.bas

  1. Option Explicit
  2.  'パスワード付zipメール自動解凍(DLL用モジュール)
  3. Private Declare Function SevenZip Lib "7-zip32.dll" ( _
  4.     ByVal hWnd As Long, _
  5.     ByVal szCmdLine As String, _
  6.     ByVal szOutput As String, _
  7.     ByVal dwSize As Long) As Long
  8. 'ZIPファイルを解凍
  9. '引数 sDstPath:解凍先のフォルダーのパス
  10. ' sZIPFile:ZIPファイルのパス
  11. ' sPassWord:パスワード 省略可
  12. '返り値 成功したら True、失敗したらFalse
  13. Public Function ExtractZIP( _
  14.     sDstPath As String, sZIPFile As String, Optional sPassWord As String = "") As Boolean
  15. Dim sCmd As String
  16.     sCmd = "X -hide -aoa "
  17.     If sPassWord <> "" Then sCmd = sCmd & "-P" & sPassWord & " "
  18.     sCmd = sCmd & Q2(sZIPFile) & " -o" & Q2(sDstPath)
  19.     ExtractZIP = DoSevenZip(sCmd) = 0
  20. End Function
  21. Private Function DoSevenZip(sCmd As String) As Long
  22. Dim sRet As String * 1024
  23.     DoSevenZip = SevenZip(0, sCmd, sRet, 1024)
  24. ' If DoSevenZip <> 0 Then MsgBox (Left(sRet, InStr(sRet, vbNullChar) - 1))
  25. End Function
  26. Public Function Q2(ByVal Text As String) As String
  27.     Q2 = """" & Replace(Text, """", """""") & """"
  28. End Function

3.Progress.frm

  1. Option Explicit
  2. 'パスワード付zipメール自動解凍(プログレスバー用フォーム)
  3. Public isCancel As Boolean '中断時にTrueにする
  4. Private pProgressBar As MSForms.Label 'ラベル:動的追加
  5. Private pMaxValue As Long 'プログレスバー最大値
  6. Private pBarColor As Long 'プログレスバー色
  7. Private pCurValue As Double 'プログレスバー現在値
  8. Private pInteractive As Long '割り込み
  9. '最大値プロパティ
  10. Public Property Let MaxValue(aMaxValue As Long)
  11.     pMaxValue = aMaxValue
  12. End Property
  13. Public Property Get MaxValue() As Long
  14.     MaxValue = pMaxValue
  15. End Property
  16. 'プログレスバー色プロパティ
  17. Public Property Let BarColor(aBarColor As Long)
  18.     pBarColor = aBarColor
  19. End Property
  20. '割り込み拒否プロパティ
  21. Public Property Let Interactive(aInteractive As Boolean)
  22.     pInteractive = aInteractive
  23. End Property
  24. 'フォーム表示入り口
  25. Public Sub ShowModeless(Optional ByVal strTitle As String = "")
  26.     'ラベルコントロール追加
  27.     Set pProgressBar = Me.FrameProgress.Controls.Add("Forms.Label.1", "lblProgress")
  28.         If pBarColor = 0 Then
  29.             pBarColor = RGB(0, 0, 128)
  30.             pProgressBar.Width = 0
  31.             pProgressBar.Height = Me.FrameProgress.Height
  32.             pProgressBar.BackColor = pBarColor
  33.         End If
  34.         'プログレスバーの背景をへこます
  35.         Me.FrameProgress.SpecialEffect = fmSpecialEffectSunken
  36.         
  37.         '割り込み拒否の設定
  38.         If pInteractive = False Then
  39.             Me.Enabled = False
  40. ' Application.Interactive = False
  41. ' Application.EnableCancelKey = xlDisabled
  42.         End If
  43.         
  44.         'フォームをモードレスで表示
  45.         Me.Caption = ""
  46.         Me.Show vbModeless
  47.         
  48.         
  49. End Sub
  50. 'プログレス進捗:指定値
  51. Public Sub Value(ByVal aValue As Double, Optional ByVal strTitle As String = "")
  52.     'プログレスバー値変更
  53.     pCurValue = aValue
  54.     
  55.     '最大値判定
  56.     If pCurValue > pMaxValue Then
  57.         pCurValue = pMaxValue
  58.     End If
  59.     
  60.     'プログレスバー描画
  61.     pProgressBar.Width = pCurValue * Me.FrameProgress.Width / pMaxValue
  62.         If Me.Caption <> strTitle Then
  63.             Me.Caption = strTitle
  64.         End If
  65.         
  66.     DoEvents
  67. End Sub
  68. 'フォーム終了
  69. Public Sub SelfClose()
  70.     Unload Me
  71. End Sub
  72. '中止ボタン
  73. Private Sub CommandButton1_Click()
  74. If MsgBox("処理を中断しますか?", vbYesNo, "中断確認") = vbYes Then
  75.     Unload Me
  76. End If
  77. End Sub
  78. '正規終了以外をキャンセル
  79. Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
  80.     If CloseMode = vbFormControlMenu Then
  81.         If pInteractive Then
  82.             If MsgBox("処理を中断しますか?", vbYesNo, "中断確認") = vbYes Then
  83.                 isCancel = True
  84.             Else
  85.                 Cancel = True
  86.             End If
  87.         Else
  88.             Cancel = True
  89.         End If
  90.     End If
  91. End Sub
  92. 'フォーム終了時に割り込み拒否を戻す
  93. Private Sub UserForm_Terminate()
  94.     If pInteractive = False Then
  95.     End If
  96.       
  97. End Sub

マージナルソフト srctohtml ソースをHTMLで見やすく出力するツール で作成