【テクニカル・上級編】【動的HTMLダイアログ】MSHTA を一時作成・呼び出しして実装するプログレスバー付き進捗表示ウィンドウ – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【動的HTMLダイアログ】MSHTA を一時作成・呼び出しして実装するプログレスバー付き進捗表示ウィンドウ

レガシーシステムの裏側で、未だにインフラの動脈として稼働し続けるVBScript(Visual Basic Scripting Edition)およびWSH(Windows Script Host)。
モダンなGUIフレームワーク全盛の今、VBScriptにおける画面出力といえば、時代遅れの `WScript.Echo` か、極めてプリミティブな `MsgBox` しか選択肢がないと思い込んでいないか?

数万件のレコードを処理するバッチ、あるいはネットワーク越しの重厚なファイル転送。
黒いコンソール画面が沈黙し、ユーザーが「フリーズしたのでは」と不安に駆られてタスクマネージャーから強制終了ボタンを押す――。この悪夢を幾度となく経験してきたはずだ。

本稿では、外部DLLのインポートやサードパーティ製コンポーネントのインストールを一切行わず、Windows標準機能(MSHTA.exe)のみを用いて、モダンで滑らかなプログレスバー付き進捗表示ウィンドウを動的に生成・制御する極限のテクニックを授ける。

—

1. アーキテクチャの核心:なぜ「MSHTA」なのか

VBScript単体では、ウィンドウの描画スレッドと処理スレッドを非同期に分離することが極めて困難である。標準のモーダルダイアログを表示した瞬間、背後で走るメインのループ処理は完全にブロックされる。

このジレンマを打破するのが、Windowsに標準搭載されているHTMLアプリケーション実行エンジン `MSHTA.exe` である。

[VBScript (WSH)] ──(動的ファイル生成)──> [%TEMP%\progress.hta]
│ │
(非同期実行) (描画・DOM操作)
▼ ▼
[メインバッチ処理] ──────(ポーリング/ファイル連携)──> [HTML/CSS/JS GUI]

このアプローチの優位性

1. 環境依存の排除: 追加のランタイムやCOMコンポーネントの登録(`regsvr32`など)が不要であり、Windows 7から最新のWindows 11まで(環境制限はあるものの)同一のコードベースで動作する。
2. 完全な非同期UI: MSHTAは独立したプロセスとして起動するため、VBScript側の重いループ処理とは完全に切り離された滑らかなCSSアニメーションやプログレスバー描画を実現できる。
3. ゼロ・クリーンアップ: スクリプト終了時に一時ファイルを確実に消去する設計にすれば、ターゲット端末に一切の汚染を残さない。

—

2. 実装コード:動的HTA生成と非同期制御の全貌

以下のコードは、一時的にHTML Applicationをディスク上に生成し、VBScript側から進捗状況(パーセンテージ)をリアルタイムに伝達しながら処理を実行する完全版スクリプトである。

実務への導入を想定し、エラーハンドリング、オブジェクトの明示的解放、そしてプロセス間通信の堅牢性を担保している。

‘ ==============================================================================
‘ Script Name : DynamicProgressHTA.vbs
‘ Description : MSHTAを用いたプログレスバー付き非同期進捗ダイアログの実装
‘ Architecture: Chief Architect Edition
‘ ==============================================================================
Option Explicit

Const HTA_FILE_NAME = “progress_dialog.hta”
Const POLL_INTERVAL = 200 ‘ プロセス生存確認の間隔 (ミリ秒)

Dim objFSO, objShell, strTempPath, strHtaPath, objExec
Dim i, maxLoop

‘ 1. オブジェクトの初期化
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objShell = CreateObject(“WScript.Shell”)

‘ 2. 一時フォルダパスの取得とHTAファイルの動的生成
strTempPath = objFSO.GetSpecialFolder(2) ‘ 2 = TemporaryFolder
strHtaPath = objFSO.BuildPath(strTempPath, HTA_FILE_NAME)

Call CreateHtaFile(objFSO, strHtaPath)

‘ 3. MSHTAプロセスの非同期起動 (ウィンドウを非表示・ポップアップとして制御)
‘ ※ WScript.ShellのRunメソッドを使用 (第2引数:0=非表示, 第3引数:False=非同期)
Set objExec = objShell.Exec(“mshta.exe “”” & strHtaPath & “”””)

On Error Resume Next
‘ — メイン処理ループの模擬 (例: 100ステップの重負荷処理) —
maxLoop = 100
For i = 1 to maxLoop
‘ ここに実際の業務ロジック(DB処理、ファイルコピー等)が入る
WScript.Sleep 50

‘ 進捗状況をテキストファイル経由、またはウィンドウタイトル経由でHTAに伝達
‘ 今回はシンプルにファイル経由のIPC(プロセス間通信)を採用
Call UpdateProgress(objFSO, strTempPath, i, “処理中… (” & i & “/” & maxLoop & “完了)”)

‘ ユーザーがHTAウィンドウを×ボタンで閉じた場合の検出
If objExec.Status <> 0 Then
WScript.Echo “ユーザーによって処理がキャンセルされました。”
Exit For
End If
Next
On Error GoTo 0

‘ 4. 終了処理とクリーンアップ
Call Cleanup(objFSO, strHtaPath, strTempPath)

‘ 5. オブジェクトの完全解放 (メモリリーク防止の鉄則)
Set objExec = Nothing
Set objShell = Nothing
Set objFSO = Nothing

WScript.Echo “すべての処理が正常に完了しました。”
WScript.Quit(0)

‘ ==============================================================================
‘ 内部関数: 動的HTAファイルの生成
‘ ==============================================================================
Sub CreateHtaFile(fso, path)
Dim objFile
Set objFile = fso.CreateTextFile(path, True, False) ‘ ASCII形式で出力

objFile.WriteLine “<" & "!DOCTYPE html>”
objFile.WriteLine “”
objFile.WriteLine “”
objFile.WriteLine “”
objFile.WriteLine “”
objFile.WriteLine “システム処理実行中”
objFile.WriteLine “

”
objFile.WriteLine “”
objFile.WriteLine “”
objFile.WriteLine “

”
objFile.WriteLine ”

バックグラウンド処理を実行中です…

”
objFile.WriteLine ”

”
objFile.WriteLine ”

”
objFile.WriteLine ”

”
objFile.WriteLine ”

初期化中…

”
objFile.WriteLine “

”
objFile.WriteLine “”
objFile.WriteLine “”
objFile.WriteLine “”

objFile.Close
Set objFile = Nothing
End Sub

‘ ==============================================================================
‘ 内部関数: 進捗データの書き込み (IPC)
‘ ==============================================================================
Sub UpdateProgress(fso, tempPath, current, message)
Dim dataFile, dataPath
dataPath = fso.BuildPath(tempPath, “progress_dialog.dat”)
On Error Resume Next ‘ ファイルロック競合の防衛策
Set dataFile = fso.CreateTextFile(dataPath, True, False)
dataFile.WriteLine current & “|” & message
dataFile.Close
Set dataFile = Nothing
On Error GoTo 0
End Sub

‘ ==============================================================================
‘ 内部関数: クリーンアップ
‘ ==============================================================================
Sub Cleanup(fso, htaPath, tempPath)
Dim dataPath
dataPath = fso.BuildPath(tempPath, “progress_dialog.dat”)

On Error Resume Next
If fso.FileExists(htaPath) Then fso.DeleteFile htaPath, True
If fso.FileExists(dataPath) Then fso.DeleteFile dataPath, True
On Error GoTo 0
End Sub

—

3. シニアエンジニアが知るべき「罠」と極限の最適化

レガシー環境や特殊なセキュリティポリシーを持つ企業システムにおいて、上記のコードを実戦投入する際、いくつかの強烈な「落とし穴」が存在する。チーフアーキテクトとしての知見から、その対策を明記しておく。

A. プロセス間通信(IPC)におけるファイルロック競合

VBScript側が `progress_dialog.dat` を書き込んでいるまさにその瞬間、HTML側のJavaScript(JScript)がそのファイルを読み込もうとすると、Windowsのファイルシステム排他制御により 「権限がありません (Permission Denied)」エラー が発生する。

  • 極限の対策: VBScript側・JavaScript側の双方で `On Error Resume Next` によるトランザクションの弾力性(Fail-Soft)を担保し、一瞬の競合によるスクリプト異常終了を完全に殺している。

B. COMオブジェクトのメモリリークとゾンビプロセス

WSHにおける最大の悪習は、COMオブジェクトの解放漏れによるメモリリークや、スクリプト強制終了時の `mshta.exe` のゾンビ化である。

  • 極限の対策: スクリプトのライフサイクル終了時には必ず `Set obj = Nothing` を明示的に行い、さらに強制終了対策としてタスクキルコマンド(`taskkill /f /im mshta.exe`)をバッチのラッパー層で担保する設計が望ましい。

C. ブラウザエンジンのモダン化(IEモードの呪縛)

標準の `MSHTA.exe` は内部的にInternet Explorerの Trident エンジンをレンダリングに利用している。しかし、現代のWindows環境ではIEの廃止が進んでおり、グループポリシーやセキュリティ設定によってはHTAの実行がブロックされるケースがある。

  • 極限の対策: 社内インフラのポリシーでMSHTAが完全に塞がれている環境では、無理にHTMLに固執せず、PowerShellのWindows Forms(`System.Windows.Forms.ProgressBar`)をWSHから呼び出すブリッジパターンへとアーキテクチャをシフトするべきである。だが、依然として「レガシーのまま追加モジュールなしで動かす」という要件においては、本稿のMSHTA動的生成手法が最もコストパフォーマンスが高い。

—

総括

VBScriptは「古い言語」ではない。正しくその制約を理解し、OSのプリミティブな機能(この場合はMSHTAとファイルシステム)を組み合わせることで、モダンなUI体験すらもレガシー環境に降臨させることが可能だ。

保守性を担保しつつ、ユーザーの「フリーズしたのか?」というストレスをゼロにする。このプログレスバー実装をあなたの現場の自動化スクリプトに組み込み、レガシーシステムの体験価値を極限まで引き上げてほしい。

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