《コンペ》 VBAマクロでサムチェックツール


職場でちょっとしたプログラムを作っているのですが、機能追加などで改修することになり、バージョン管理の一環として、サムチェック値の計算ツールを作りたいと考えています。
要は、作成した実行ファイル(バイナリ)について、サム値を計算すればいいので、FCIVとか巷のフリーツールでもいいのですが、ExcelVBAの勉強を兼ねて色々アレンジしたいのでマクロ文でお願いします。

要求を満たすものが複数挙がった場合は、私の主観で点数分配します。
評価ポイントは以下の通りですが、質問あれば随時受け付けます。
よろしくお願い致します。

評価ポイント:
・動作が軽い事。
・可読性。自分で色々アレンジしたいので。コメント等も多ければ良いとは思いませんがわかりやすいコメントや構文は好評価。
・機能別にサブルーチン化されていること。たぶん個別にボタン割り付けて使うので。

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2015/09/11 16:25:03
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。
id:gm91

質問者から

GM912015/09/06 16:53:01

仕様は以下の通りです。

★:必須

※:できれば

★ファイルの読み書きはWindows7上で動作すればOK。その他のOS対応は不要。

★Excel2010のVBAマクロ記述だけで動作が完結すること。

他のツールを呼び出すとかはNG。

★対象は バイナリファイル×1 複数ファイルとかフォルダ毎とかは不要。

★対象ファイルは、シートの特定箇所にファイル名を記載して指定する。

※そのExcelファイルが置かれたパスを検出してくれるか、選択用のダイアログが出ると便利。

★サムチェック値は32bitのHEX表現で、特定のシートに出力される。

※マクロ実行する毎に次の行へ書き込んでくれると嬉しい。

★サム値の計算アルゴリズムは任意。

改ざん防止や保安度については考慮しなくてよい。

★処理速度優先。ファイルの中身が同じなら毎回同じサム値を出す。

これができれば複雑な演算は不要。

※最悪、単純加算でもOKだが、データを2箇所変更するとサム値が同じになってもうたとかそういうのが回避されるとちょっとだけ嬉しい。

★複数のアルゴリズムを切り替える機能は不要。

(どれでやったかわからなくなるので実装禁止)

★対象となるファイルのサイズは変動あり。固定長で処理を中断しないこと。

ベストアンサー

id:cx20 No.1

回答回数607ベストアンサー獲得回数108

ポイント50pt

<Sheet1 の内容>

 A B
1C:\Data\Tools\FCIV\fciv.exe

[コマンドボタン]
<Sheet1 / コマンドボタンクリック時処理>

' ボタンクリック時にシートに記載したファイルのハッシュ値を取得する
Private Sub CommandButton1_Click()
    Dim strFileName
    strFileName = Range("A1").Value
    Dim strMD5
    strMD5 = GetFileHashMD5(strFileName)
    ' MD5 ハッシュ値128bit中32bitを表示する
    Range("B1").Value = Mid(strMD5, 24, 8)
End Sub

<標準モジュール>

Option Explicit

' 指定したファイルの MD5 ハッシュ値を取得する
Function GetFileHashMD5(strFileName)
    Dim md5
    Set md5 = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
    md5.ComputeHash_2 (ReadBinaryFile(strFileName))
    Dim msxml
    Set msxml = CreateObject("MSXML2.DOMDocument")
    Dim el
    Set el = msxml.CreateElement("tmp")
    el.DataType = "bin.hex"
    el.NodeTypedValue = md5.Hash
    GetFileHashMD5 = el.Text
End Function

' 指定したファイルのバイト配列を取得する
Function ReadBinaryFile(strFileName)
    Const adTypeBinary = 1
    Dim stm
    Set stm = CreateObject("ADODB.Stream")
    stm.Type = adTypeBinary
    stm.Open
    stm.LoadFromFile strFileName
    ReadBinaryFile = stm.Read
End Function

<実行結果>

 A B
1C:\Data\Tools\FCIV\fciv.exe 4e9c07c6

<参考サイト>

■ WindowsでSHA1とMD5を調べる
http://d.hatena.ne.jp/papaking_ken/20110224/1298564016


対応要否要求仕様 実装状況コメント
ファイルの読み書きはWindows7上で動作すればOK。その他のOS対応は不要。Windows10にて開発。バージョン依存する機能は未使用の為、Windows7でも動作すると思われる。
Excel2010のVBAマクロ記述だけで動作が完結すること。 Excel2013にて開発。バージョン依存する機能は未使用の為、Excel2010でも動作すると思われる。
対象は バイナリファイル×1 複数ファイルとかフォルダ毎とかは不要。バイナリファイルにて確認済み
対象ファイルは、シートの特定箇所にファイル名を記載して指定する。 セルA1にファイル名を指定。ボタン押下にてマクロ起動
サムチェック値は32bitのHEX表現で、特定のシートに出力される。 MD5ハッシュ値128bitのうち32bitを出力。妥当性については未確認
サム値の計算アルゴリズムは任意。 MD5
処理速度優先。ファイルの中身が同じなら毎回同じサム値を出す。 Crypto APIを使用している為、ネイティブ並みの処理速度を実現
複数のアルゴリズムを切り替える機能は不要。 MD5のみを使用
対象となるファイルのサイズは変動あり。固定長で処理を中断しないこと。任意のファイルサイズに対応。数百MBでも数秒でチェック可能
他1件のコメントを見る
id:cx20

必須項目について可能な範囲で対応しました。
実機による動作テストについては、手元に、Windows7+Excel2010環境が無い為、出来ていません。
お手数ですが、お手持ちの環境にて確認お願いします。

2015/09/07 23:46:46
id:gm91

ありがとうございます。確認してみます。

2015/09/08 00:33:23

その他の回答1件)

id:cx20 No.1

回答回数607ベストアンサー獲得回数108ここでベストアンサー

ポイント50pt

<Sheet1 の内容>

 A B
1C:\Data\Tools\FCIV\fciv.exe

[コマンドボタン]
<Sheet1 / コマンドボタンクリック時処理>

' ボタンクリック時にシートに記載したファイルのハッシュ値を取得する
Private Sub CommandButton1_Click()
    Dim strFileName
    strFileName = Range("A1").Value
    Dim strMD5
    strMD5 = GetFileHashMD5(strFileName)
    ' MD5 ハッシュ値128bit中32bitを表示する
    Range("B1").Value = Mid(strMD5, 24, 8)
End Sub

<標準モジュール>

Option Explicit

' 指定したファイルの MD5 ハッシュ値を取得する
Function GetFileHashMD5(strFileName)
    Dim md5
    Set md5 = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
    md5.ComputeHash_2 (ReadBinaryFile(strFileName))
    Dim msxml
    Set msxml = CreateObject("MSXML2.DOMDocument")
    Dim el
    Set el = msxml.CreateElement("tmp")
    el.DataType = "bin.hex"
    el.NodeTypedValue = md5.Hash
    GetFileHashMD5 = el.Text
End Function

' 指定したファイルのバイト配列を取得する
Function ReadBinaryFile(strFileName)
    Const adTypeBinary = 1
    Dim stm
    Set stm = CreateObject("ADODB.Stream")
    stm.Type = adTypeBinary
    stm.Open
    stm.LoadFromFile strFileName
    ReadBinaryFile = stm.Read
End Function

<実行結果>

 A B
1C:\Data\Tools\FCIV\fciv.exe 4e9c07c6

<参考サイト>

■ WindowsでSHA1とMD5を調べる
http://d.hatena.ne.jp/papaking_ken/20110224/1298564016


対応要否要求仕様 実装状況コメント
ファイルの読み書きはWindows7上で動作すればOK。その他のOS対応は不要。Windows10にて開発。バージョン依存する機能は未使用の為、Windows7でも動作すると思われる。
Excel2010のVBAマクロ記述だけで動作が完結すること。 Excel2013にて開発。バージョン依存する機能は未使用の為、Excel2010でも動作すると思われる。
対象は バイナリファイル×1 複数ファイルとかフォルダ毎とかは不要。バイナリファイルにて確認済み
対象ファイルは、シートの特定箇所にファイル名を記載して指定する。 セルA1にファイル名を指定。ボタン押下にてマクロ起動
サムチェック値は32bitのHEX表現で、特定のシートに出力される。 MD5ハッシュ値128bitのうち32bitを出力。妥当性については未確認
サム値の計算アルゴリズムは任意。 MD5
処理速度優先。ファイルの中身が同じなら毎回同じサム値を出す。 Crypto APIを使用している為、ネイティブ並みの処理速度を実現
複数のアルゴリズムを切り替える機能は不要。 MD5のみを使用
対象となるファイルのサイズは変動あり。固定長で処理を中断しないこと。任意のファイルサイズに対応。数百MBでも数秒でチェック可能
他1件のコメントを見る
id:cx20

必須項目について可能な範囲で対応しました。
実機による動作テストについては、手元に、Windows7+Excel2010環境が無い為、出来ていません。
お手数ですが、お手持ちの環境にて確認お願いします。

2015/09/07 23:46:46
id:gm91

ありがとうございます。確認してみます。

2015/09/08 00:33:23
id:TransFreeBSD No.2

回答回数668ベストアンサー獲得回数268

ポイント50pt

チェックサムと言えばCRC32なので探し出しましたがスピードは遅いです。
たぶんライブラリ使ったid:cx20さんのにかなうのはないと思います。

※そのExcelファイルが置かれたパスを検出してくれるか、選択用のダイアログが出ると便利。
※マクロ実行する毎に次の行へ書き込んでくれると嬉しい。

この辺実装しました。
id:cx20さんのと組み合わせていただければ良いかと思います。

Option Explicit
Const FileNameSheet = "ファイル名"
Const FileNameCell = "A1"
Const HashStoreSheet = "サム値"
Const HashStoreColumn = "B"

Dim Crc32Table&(255)

' Mainマクロを実行する
Sub Main()
    Dim strFileName As String
    strFileName = GetFileName()
    Dim hash
    hash = GetCrc32FromFile(strFileName)
    hash = Hex(hash)
    'hash = GetFileHashMD5(strFileName)
    'hash = Mid(strHash, 24, 8)
    StoreResult hash
End Sub

Function GetFileName()
    ' カレントディレクトリをブックのあるフォルダにすることで絶対パス・相対パスどちらでも対応可能にする
    ' 参考: http://www.asahi-net.or.jp/~ef2o-inue/vba_o/sub05_110_140.html
    ChDrive ThisWorkbook.Path
    ChDir ThisWorkbook.Path
    ' 以下三種の取得方法
    ' GetFileName = Sheets(FileNameSheet).Range(FileNameCell).Value ' 特定シートの特定セルから得る
    GetFileName = Range(FileNameCell).Value ' 現在のシートの特定位置のセルから得る
    ' GetFileName = Selection.Value ' 選択中のセルから得る
End Function

Sub StoreResult(strResult)
    Sheets(HashStoreSheet).Cells(Rows.Count, HashStoreColumn).End(xlUp).Item(2).Value = strResult
    ' Rows.Count 行の最大値
    ' Cells(Rows.Count, "B") B列最終行のセル
    ' End(xlUp) 上方向(xlUp)でデータのある最初のセル(Ctrl+↑)
    ' 最終行のセル + Ctrl+↑ = データのある最後のセル
    ' Item(2) その下のセル(現在位置を(1)として上下に2番の位置)
End Sub

' 以下コピペ
' http://d.hatena.ne.jp/n7shi/20100910/1284112553
' https://code.msdn.microsoft.com/office/VBACRC-32-dad7d087
' Dim I%, J%, R&, R1&等参考
' http://www.atmarkit.co.jp/fdotnet/vb6tonet/vb6tonet30/vb6tonet30_01.html

Private Sub InitCrc32Table()
    Dim I%, J%, R&, R1&
    For I = 0 To 255
        R = I
        For J = 0 To 7
            R1 = R And 1
            R = (R - R1) / 2
            If R < 0 Then R = R - &H80000000
            If R1 Then R = R Xor &HEDB88320
        Next J
        Crc32Table(I) = R
    Next I
End Sub

Public Function GetCrc32FromFile&(Path$)
    Dim R&, I&, B As Byte, FL&
    If Crc32Table(255) = 0 Then InitCrc32Table
    FL = FileLen(Path)
    Open Path For Binary Lock Read As #2
    R = Not 0
    For I = 1 To FL
        Get #2, , B
        R = (Int(R / 256) And &HFFFFFF) Xor Crc32Table((R Xor B) And &HFF)
    Next I
    Close #2
    GetCrc32FromFile = Not R
End Function
他1件のコメントを見る
id:TransFreeBSD

書いておいて何ですが、そういえば勉強用でしたね……
前半は数行で済む所を無駄に関数化してたり、後半は古い書き方だったりで、あまり向かない気もしてきました。
まあ、知識として知っておいた方が良い部分はあると思いますけども。

2015/09/10 00:09:48
id:gm91

いえ、関数は小分けにしてもらえるほうがありがたいです。
つけたし、流用、はその方がやりやすいので。

2015/09/10 00:53:11
id:gm91

質問者から

GM912015/09/13 17:41:35

ありがとうございました。

  • id:cx20
    VBA でバイナリファイルを高速に読み込む方法が知りたいということでしたら、参考にならないかも知れませんが、
    高速にファイルハッシュを求めたいということであれば、Crypto API を用いるのが手軽で高速かと思います。

    ■ WindowsでSHA1とMD5を調べる
    http://d.hatena.ne.jp/papaking_ken/20110224/1298564016

    試したところ、数百MBでも数秒でハッシュ値を算出でき、fciv ユーティリティの MD5 や SHA1 の出力結果と同じ値になることを確認しました。
  • id:gm91
    ハッシュ演算の高速さそのものについては、できあがったものの「またされ感」が無ければそれでOKです。

    ※ファイルサイズは大きくても数百kBオーダーです。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません