【実務・中級編】【HTA プログレスバー】長時間実行されるスクリプトに「残り時間」を表示する動的 HTML インターフェースの組み込み – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

ユーザーの不安を「信頼」に変える設計: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を用いた動的な進捗表示は、決して贅沢な装飾ではない。それは、システムと人間の間の「信頼のプロトコル」なのだ。この知見を、貴殿の次なるプロジェクトに実装することを強く推奨する。

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