【入門編】【バイナリデータの16進数ダンプ表示】ADODB.Streamで取得したバイト配列を16進表記(Hex Dump)に整形・出力する開発支援モジュール – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは!日々の業務自動化、お疲れ様です。一歩進んだシステム開発の世界へようこそ。優しく、かつ徹底的にVBScriptの深淵を案内する先輩エンジニアです。

日頃、Excelのマクロ(VBA)を記録して動かすところから一歩踏み出し、「もっとシステムの中身を理解したい」「ファイルを直接解析してみたい」と思っていませんか?

今回は、VBScriptを用いて「バイナリデータの16進数ダンプ(Hex Dump)」を行うツールを一緒に作りましょう。
一見難しそうに聞こえるかもしれませんが、ここをクリアすれば、VBScriptのファイル操作、オブジェクトの取り扱い、そしてデータ型の仕組みといった「VBScriptの基本と本質」が完全にマスターできますよ。

それでは、ワクワクする低レイヤの世界へ一緒に出発しましょう!

—

1. そもそも「16進数ダンプ(Hex Dump)」とは?

私たちが普段使っているテキストファイル(`.txt`や`.csv`)は、人間が読める文字で構成されています。しかし、画像(`.png`)、実行ファイル(`.exe`)、PDFファイル、あるいは壊れてしまった破損ファイルなどは、そのままメモ帳で開いても文字化けして読めませんよね。

これらはすべて「バイナリデータ(2進数の集まり)」でできています。

このバイナリデータを、デバッグや解析のために「16進数の文字」と「読める文字(ASCII文字)」に整形して並べたものが「16進数ダンプ」です。

16進数ダンプの出力イメージ

Address 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F Dump Text
———————————————————————-
00000000 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 .PNG……..IHDR
00000010 00 00 00 10 00 00 00 10 08 06 00 00 00 1F F3 FF …………….

  • Address: ファイルの先頭から何バイト目か(16進数表記)。
  • 00〜0F: 16バイト分のデータを16進数(2桁)で表現したもの。
  • Dump Text: そのバイトがアルファベットや数字ならそのまま表示し、制御文字や読めない文字なら「`.`(ドット)」で置き換えたもの。

これを作ることで、「ファイルがどんなフォーマットで始まっているか(マジックナンバーの確認)」や「改行コードが`CRLF`(`0D 0A`)なのか`LF`(`0A`)なのか」がひと目で分かるようになります。

—

2. VBScriptでバイナリを扱う「2つの壁」

VBScriptは非常に手軽で強力な言語ですが、実はバイナリデータを扱う際には「2つの大きな壁」が存在します。ここを理解することが、初心者からプロへ脱皮するための最大のポイントです。

壁①:VBScriptには「Byte型」の変数を直接宣言できない

VBScriptの変数は、すべて「Variant(万能型)型」です。C言語やVBAのように `Dim buf As Byte` といった厳密な型宣言ができません。
バイナリファイルを読み込むと、内部的には 「Byte型の配列(SafeArray)」 という特殊なコンテナに入って返されます。これに気づかずに通常の文字列のように扱おうとすると、型エラーでプログラムが止まってしまいます。

壁②:ループ処理による文字列結合は「激重」になる

1バイトずつデータを読み込んで `&` 演算子で文字列を結合していく処理(`str = str & newChar`)は、データ量が大きくなると恐ろしく動作が遅くなります。
これは、結合のたびにメモリ上で新しい文字列領域を確保し直すためです。

💡 解決策:

1. `ADODB.Stream` オブジェクト を使って、ファイルを「バイナリモード」で安全かつ高速にメモリに展開します。
2. 1バイトずつのループ処理を工夫し、バッファリングを行いながら美しく整形します。

—

3. 【実践】16進数ダンプ開発支援モジュール

では、実際のコードを見てみましょう。
以下のコードをコピーして、`HexDumper.vbs` という名前で保存してください。
解析したいファイルをこのスクリプトファイルにドラッグ&ドロップするだけで、デスクトップにダンプ結果のテキストファイルを出力する仕組みになっています。

‘ ==============================================================================
‘ VBScript バイナリデータ 16進数ダンプ(Hex Dump)ツール
‘ ==============================================================================
Option Explicit

‘ 1行に表示するバイト数(16バイトが世界標準です)
Const BYTES_PER_LINE = 16

Call Main()

Sub Main()
Dim objArgs, filePath, dumpText, outPath
Set objArgs = WScript.Arguments

‘ ドラッグ&ドロップされたファイルの存在チェック
If objArgs.Count = 0 Then
MsgBox “解析したいファイルをこのスクリプトにドラッグ&ドロップしてください。”, _
vbInformation, “ファイル未指定”
WScript.Quit
End If

filePath = objArgs(0)

‘ ダンプ処理の実行
On Error Resume Next
dumpText = GenerateHexDump(filePath)

If Err.Number <> 0 Then
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “エラー”
Err.Clear
WScript.Quit
End If
On Error GoTo 0

‘ 結果をデスクトップのファイルに出力
outPath = CreateDesktopPath(filePath)
Call WriteTextFile(outPath, dumpText)

MsgBox “解析が完了しました!” & vbCrLf & “結果を保存しました: ” & outPath, _
vbInformation, “完了”
End Sub

‘ ==============================================================================
‘ メインロジック: バイナリデータの読み込みとダンプ生成
‘ ==============================================================================
Function GenerateHexDump(filePath)
Dim stream, binData, totalBytes

‘ 1. ADODB.Streamオブジェクトの作成
Set stream = CreateObject(“ADODB.Stream”)

‘ 2. ストリームを開いてバイナリモードに設定
stream.Open
stream.Type = 1 ‘ adTypeBinary (バイナリデータを示す)
stream.LoadFromFile filePath

‘ 3. 全データを一括で読み込む(Byte配列が返る)
totalBytes = stream.Size
If totalBytes = 0 Then
GenerateHexDump = “ファイルサイズが0バイトです。”
stream.Close
Set stream = Nothing
Exit Function
End If

binData = stream.Read(-1) ‘ adReadAll (すべてのデータを読み込む)

‘ リソースの解放(使い終わったらすぐに閉じるのがプロの鉄則)
stream.Close
Set stream = Nothing

‘ 4. バイト配列を16進数表記に変換
GenerateHexDump = FormatBinaryToDump(binData, totalBytes)
End Function

‘ ==============================================================================
‘ バイナリ配列(SafeArray)をダンプフォーマットに整形する関数
‘ ==============================================================================
Function FormatBinaryToDump(binData, totalBytes)
Dim outputLines(), lineCount
Dim i, j, byteVal, hexPart, asciiPart, addressStr
Dim lineBytes

‘ 出力行配列の初期化(メモリ効率化のため、必要な行数をあらかじめ計算)
lineCount = Int((totalBytes – 1) / BYTES_PER_LINE) + 1
ReDim outputLines(lineCount + 3) ‘ ヘッダー分をプラスして確保

‘ ヘッダー部分の作成
outputLines(0) = “Address 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F Dump Text”
outputLines(1) = “———————————————————————-”

Dim lineIdx: lineIdx = 2

‘ 16バイトずつのループ処理
For i = 0 To totalBytes – 1 Step BYTES_PER_LINE
‘ アドレス部分の作成(8桁の16進数でゼロ埋め)
addressStr = Right(“00000000” & Hex(i), 8)

hexPart = “”
asciiPart = “”

‘ 1行(16バイト分)のデータを構築
For j = 0 To BYTES_PER_LINE – 1
If (i + j) < totalBytes Then ' AscB 関数を使って、バイト配列から直接1バイトの数値を取得 ' ※VBScriptのMidBはSafeArrayに対して有効です byteVal = AscB(MidB(binData, i + j + 1, 1)) ' 16進数文字列(2桁)に変換 hexPart = hexPart & Right("0" & Hex(byteVal), 2) & " " ' 8バイト目でスペースをもう1つ入れて見やすくする(可読性の向上) If j = 7 Then hexPart = hexPart & " " ' 表示可能なASCII文字(スペース[0x20]から~[0x7E]まで)か判定 If byteVal >= &H20 And byteVal <= &H7E Then asciiPart = asciiPart & Chr(byteVal) Else asciiPart = asciiPart & "." ' 表示できない制御文字はドットに置換 End If Else ' ファイル終端に達した場合のパディング(空白埋め) hexPart = hexPart & " " If j = 7 Then hexPart = hexPart & " " End If Next ' 1行のデータを組み立てて配列に格納 outputLines(lineIdx) = addressStr & " " & hexPart & " " & asciiPart lineIdx = lineIdx + 1 Next ' 配列を一括で改行コードで結合(超高速化テクニック!) FormatBinaryToDump = Join(outputLines, vbCrLf) End Function ' ============================================================================== ' ユーティリティ関数(ファイル保存・パス生成) ' ============================================================================== Function CreateDesktopPath(originalPath) Dim shell, desktopDir, fso, fileName Set shell = CreateObject("WScript.Shell") Set fso = CreateObject("Scripting.FileSystemObject") desktopDir = shell.SpecialFolders("Desktop") fileName = fso.GetFileName(originalPath) ' 「ファイル名_dump.txt」という名前でデスクトップのパスを作成 CreateDesktopPath = desktopDir & "\" & fileName & "_dump.txt" End Function Sub WriteTextFile(filePath, content) Dim fso, txtFile Set fso = CreateObject("Scripting.FileSystemObject") ' UTF-8やUnicodeではなく、標準的なANSIテキストファイルとして書き出し Set txtFile = fso.CreateTextFile(filePath, True) txtFile.Write content txtFile.Close Set txtFile = Nothing Set fso = Nothing End Sub ---

4. コードの核心!ここを理解すればVBScriptマスター

このスクリプトには、初心者が中級者へステップアップするための重要な技術が詰まっています。ポイントを絞って分かりやすく解説しますね。

① `ADODB.Stream` の魔法

通常、VBScriptでファイルを読むときは `FileSystemObject` (FSO) を使いますが、FSOは「テキスト専用」です。バイナリ(画像やZipファイルなど)を読み込むと、データが破損したり途中で読み込みが止まったりします。

そこで登場するのが `ADODB.Stream` オブジェクトです。

stream.Type = 1 ‘ adTypeBinary

この `Type = 1` を指定することで、ファイルを一切の変換なしに、生の「0と1のデータ(バイナリ)」としてメモリに読み込むことができます。

② なぜ `AscB` と `MidB` を使うのか?

VBScriptには通常の文字操作用関数(`Mid` や `Asc`)のほかに、バイナリ(バイト)専用の関数が用意されています。末尾に `B` がつくのが特徴です。

  • `MidB(binData, 開始位置, バイト数)`:バイト配列から指定した位置の生データを切り出します。
  • `AscB(…)`:切り出した1バイトのデータを、VBScriptで計算可能な「0〜255の数値」に変換します。

これらを使うことで、Variant型の中に入っている「Byte型の配列」を壊すことなく、安全に1つの数値として取り出すことができるのです。

③ 高速化の極意:`Join` 関数と配列の事前確保

もし、1行ずつ以下のように文字列を結合していたら、大きなファイルを処理したときにパソコンが固まってしまいます。

‘ やってはいけない悪い例(激重)
allText = allText & lineText & vbCrLf

今回のコードでは、あらかじめ計算して求めた行数で配列 `outputLines()` を作り(`ReDim`)、そこに1行ずつのデータを格納しています。
最後に `Join(outputLines, vbCrLf)` を使って、一瞬で1つの巨大な文字列に結合しています。これが、VBScriptで処理速度を100倍以上高速化するためのプロの常套手段です。

—

5. 陥りやすいエラーと対処法

開発中によく遭遇するエラーとその対策をまとめました。これを知っておくだけで、デバッグの時間が大幅に短縮されますよ!

エラー1:「書き込み禁止」や「ファイルが開けません」

  • 原因: `ADODB.Stream` で読み込もうとしているファイルが、別のアプリケーション(Excelなど)で排他モードで開かれている、またはアクセス権限がない場合に発生します。
  • 対策: ファイルが閉じているか確認しましょう。また、読み込み専用で開くようにコードを工夫することも有効です。

エラー2:メモリリーク(動作がだんだん重くなる)

  • 原因: `ADODB.Stream` などの外部オブジェクト(COMオブジェクト)を、`Close` せずに何度も呼び出すと、パソコンのメモリが解放されずに残り続けてしまいます。
  • 対策: ストリームの処理が終わったら、必ず以下の「お片付け」を徹底しましょう。

stream.Close
Set stream = Nothing

—

6. まとめ:基礎をクリアしたあなたへ

お疲れ様でした!
今回作成したツールは、ただの「便利なデバッグツール」ではありません。
この中には、

  • COMオブジェクト(ADODB.Stream)の制御
  • バイナリ関数(MidB/AscB)による型エラーの回避
  • 配列を活用した文字列結合の高速化アルゴリズム

という、VBScriptにおける最重要のエッセンスがすべて凝縮されています。

これが理解できれば、VBScriptの基本は完全にクリアしたと言っても過言ではありません。マクロの自動記録から脱却し、システムの裏側をコントロールできる本物のエンジニアへ大きな一歩を踏み出しましたね。

ぜひ、身近なファイル(小さな画像やテキストなど)をドラッグ&ドロップして、その中身を覗いてみてください。新しい発見があってとても面白いですよ!

何か分からないことがあれば、いつでも聞いてくださいね。あなたの挑戦をいつも応援しています!

タイトルとURLをコピーしました