ユーザーの不安を「信頼」に変える設計:HTAを用いた動的プログレスバーの実装極意
業務自動化の現場において、プロフェッショナルとアマチュアを分かつ決定的な差は何か?
それは、ロジックの正しさだけではない。「実行中のプロセスが、ユーザーに対してどのような状態を提示できているか」という、UX(ユーザーエクスペリエンス)に対する設計思想の有無である。
VBScriptを用いた自動化ツールを作成する際、多くの開発者が陥る罠がある。それは、重たい処理(大量のファイル操作、巨大なデータベースへのクエリ、ネットワーク越しの一括処理など)を、単なるコンソール出力や、あるいは「何も表示しない」状態で実行させてしまうことだ。
5分間、画面が固まったままのスクリプト。ユーザーはこう思う。「フリーズしたのではないか?」「エラーで止まったのではないか?」「強制終了しても大丈夫か?」
この疑念は、ツールの信頼性を根底から破壊する。
本稿では、VBScriptの限界を超え、MSHTA(HTML Application)をフロントエンドとして活用することで、「残り時間(ETR: Estimated Time Remaining)」を動的に表示する、プロ仕様のプログレスバー実装手法を伝授する。
1. 設計思想:なぜ「VBScript単体」では不可能なのか
VBScriptは、Windows Script Host (WSH) 上で動作する優れたスクリプト言語だが、UI(ユーザーインターフェース)の構築能力は皆無に等しい。`MsgBox` はユーザーの入力を待機してプロセスを止めてしまうため、動的な進捗表示には適さない。
ここで登場するのが HTA (HTML Application) だ。
HTAは、Internet Explorerのレンダリングエンジンを利用して、HTML/CSS/JavaScript(およびVBScript)を「アプリケーション」として動作させる仕組みである。
アーキテクチャの要諦
プロフェッショナルな設計では、以下の役割分担を明確にする。
1. View (HTML/CSS): プログレスバーの描画、残り時間のテキスト表示、ステータスメッセージの表示。
2. Controller (HTA/JavaScript or VBScript): ユーザーの操作(開始ボタン等)を受け取り、ロジックを呼び出す。
3. Model/Logic (VBScript): ファイル操作、DB連携、計算などの「重たい実務」を遂行する。
ここで重要なテクニックは、「ロジックの進行度をUI側にフィードバックする」という双方向の通信設計である。
2. プロダクション・コード:動的プログレスバーの実装例
以下に、そのまま実務に投入可能なレベルのHTA実装コードを示す。
このコードは、単にバーを伸ばすだけでなく、経過時間に基づいた「残り時間の予測計算」を組み込んでいる。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Professional Task Runner</title>
<hta:application
id="oTaskApp"
applicationname="TaskRunner"
border="thin"
caption="yes"
showintaskbar="yes"
singleinstance="yes"
sysmenu="yes"
windowstate="normal"
>
<style>
body { font-family: 'Segoe UI', Tahoma, sans-serif; background-color: #f4f4f9; color: #333; margin: 20px; }
.container { width: 100%; }
.progress-container { width: 100%; background-color: #ddd; border-radius: 5px; height: 25px; margin: 20px 0; overflow: hidden; }
#progressBar { width: 0%; height: 100%; background-color: #2ecc71; transition: width 0.2s ease; }
.status-text { font-size: 0.9em; color: #666; }
.timer-text { font-weight: bold; color: #2c3e50; }
.btn-start { padding: 10px 20px; background-color: #3498db; color: white; border: none; border-radius: 3px; cursor: pointer; }
.btn-start:hover { background-color: #2980b9; }
</style>
<script language="vbscript">
' --- グローバル変数 ---
Dim startTime, totalSteps, currentStep
Sub Window_OnLoad
' ウィンドウサイズの初期設定
window.resizeTo 500, 300
End Sub
' --- メインロジック ---
Sub StartProcess()
' 初期化
currentStep = 0
totalSteps = 100 ' 今回は例として100ステップとする
startTime = Timer
' UIの初期化
UpdateUI(0, "準備中...", "計算中...")
' 実際の業務処理をシミュレート
' ※ 実際の実装では、ここでファイルループなどの重い処理を呼び出す
ExecuteHeavyTask
End Sub
Sub ExecuteHeavyTask()
On Error Resume Next ' エラーハンドリングの基本
For i = 1 To totalSteps
' --- 【重要】ここが実務の肝:重い処理の代わり ---
' 実際には FileSystemObject による操作や SQL 実行が入る
WScript.Sleep 100 ' 処理負荷のシミュレーション (100ms)
' エラーチェック
If Err.Number <> 0 Then
MsgBox "エラーが発生しました: " & Err.Description, vbCritical, "致命的エラー"
Exit Sub
End If
' ----------------------------------------------
currentStep = i
UpdateProgress
Next
' 完了通知
UpdateUI(100, "完了しました", "---")
MsgBox "全ての工程が正常に終了しました。", vbInformation, "完了"
End Sub
' --- UI更新ロジック ---
Sub UpdateProgress()
Dim percent, elapsed, estRemaining, remainingSec
' 進捗率の計算
percent = (currentStep / totalSteps) * 100
' 経過時間の計算
elapsed = Timer - startTime
' 残り時間の予測計算 (ETR)
' 式: (経過時間 / 現在の進捗) * (1 - 現在の進捗)
If currentStep > 0 Then
estRemaining = (elapsed / (currentStep / totalSteps)) - elapsed
remainingSec = Int(estRemaining)
' 秒数を HH:MM:SS 形式に変換
Dim h, m, s
h = remainingSec \ 3600
m = (remainingSec Mod 3600) \ 60
s = remainingSec Mod 60
Dim timerStr
timerStr = Right("0" & h, 2) & ":" & Right("0" & m, 2) & ":" & Right("0" & s, 2)
Else
timerStr = "計算中..."
End If
' HTML要素への反映
' document.getElementById を使用してDOMを操作
document.getElementById("progressBar").style.width = percent & "%"
document.getElementById("statusText").innerText = "処理中: " & currentStep & " / " & totalSteps & " ステップ"
document.getElementById("timerText").innerText = "残り予測: " & timerStr
End Sub
Sub UpdateUI(percent, status, timer)
document.getElementById("progressBar").style.width = percent & "%"
document.getElementById("statusText").innerText = status
document.getElementById("timerText").innerText = "残り予測: " & timer
End Sub
</script>
</head>
<body>
<div class="container">
<h3>業務自動化タスク実行</h3>
<div class="status-text" id="statusText">待機中...</div>
<div class="progress-container">
<div id="progressBar"></div>
</div>
<div class="status-text">
残り予測: <span class="timer-text" id="timerText">---</span>
</div>
<br>
<input type="button" class="btn-start" value="処理を開始する" onclick="StartProcess()">
</div>
</body>
</html>
3. アーキテクトの視点:堅牢な設計のための「3つの鉄則」
このコードを単にコピーして終わりにしてはいけない。実務で運用する際には、以下の設計上の注意点を遵守せよ。
① シングルスレッドの壁とUIの更新
HTA(MSHTA)は基本的にシングルスレッドで動作する。つまり、`ExecuteHeavyTask` 内で非常に重い処理(例えば、数秒間応答のないネットワーク通信)が行われると、その間、プログレスバーの描画自体が止まってしまう。
- 対策: 処理を「小さなチャンク(塊)」に分割せよ。一気に1万件のファイルを処理するのではなく、100件ごとにUI更新のタイミングを作る、あるいは処理の合間にイベントループに制御を戻す設計が必要だ。
② ファイル・データベース連携における例外処理
自動化ツールが最も死ぬのは、I/O(入出力)エラーだ。
- ファイルが使用中(Lock)である。
- ネットワークドライブが切断された。
- DBの接続タイムアウトが発生した。
これらを無視してループを回し続けるのは、プロの仕事ではない。`On Error Resume Next` を使う場合でも、必ず `Err.Number` をチェックし、「どこまで処理が進み、どこで失敗したか」をログに吐き出し、ユーザーに明示的に伝える設計にせよ。
③ ETR(残り時間予測)の数学的信頼性
上記のコードで使用した `(elapsed / progressRatio) – elapsed` という計算式は、処理速度が一定であることを前提としている。
実務では、処理の重さが一定ではない(例:ファイルサイズがバラバラ)場合、残り時間は激しく変動する。
- 高度な設計: 直近10ステップの平均速度を算出して予測に用いる「移動平均」の手法を導入すると、より滑らかで信頼性の高い残り時間表示が可能になる。
結論
「動くスクリプト」を作るのは、エンジニアの基礎体力だ。
しかし、「使われるツール」を作るには、ユーザーの心理を読み、不確実性をコントロールする設計能力が求められる。
HTAを用いた動的な進捗表示は、決して贅沢な装飾ではない。それは、システムと人間の間の「信頼のプロトコル」なのだ。この知見を、貴殿の次なるプロジェクトに実装することを強く推奨する。
