【実務・中級編】Windows API(P/Invoke)の活用:VB.NETからOSの標準機能(タスクバーのプログレス表示やウィンドウの点滅)を制御する実践テクニック – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

Windows API(P/Invoke)が解き放つ極限のUI制御:VB.NETでタスクバーを完全掌握する実践テクニック

開発現場で業務効率化ツールを設計していると、Windows Forms標準のコントロールだけでは「物足りない」壁にぶ当たる。
例えば、数万件のレコードを処理するバッチ処理や、巨大なファイルのインポート。ユーザーは画面の前で「今、全体の何パーセントが進んでいるのか」「処理が完了して他の作業に移っていいのか」を視覚的に一目で知りたい。

メッセージボックスやステータスバーのテキスト変化?
非力なインフォメーションは、ユーザーがブラウザを開いた瞬間に視界から消え去る。

ここでプロのアーキテクトが取るべきアプローチは一つだ。「OSのシェル(Shell32)を直接叩き、タスクバーそのものをプログレスバーに変える」。
今回は、VB.NETからP/Invoke(Platform Invocation Services)を駆使し、Windowsタスクバーの進捗表示とウィンドウ点滅を完全に制御する、プロダクション品質の設計と実装を伝授する。

—

1. なぜ標準機能ではなく「P/Invoke」なのか?

Windows Formsの `ProgressBar` コントロールは、フォーム内部の描画に閉じこんでいる。ユーザーが別ウィンドウで作業している時、あるいはアプリケーションが最小化されている時、その進捗は完全に視界から消える。

Windows 7以降、OSのタスクバーボタンは単なる「最小化アイコンの置き場所」ではない。アプリケーションの状態を伝える最前線のダッシュボードへと進化している。
これを制御するのが、COMインターフェイスである `ITaskbarList3` だ。

しかし、VB.NETからこれを直接扱うには、マネージドとアンマネージドの境界(Interop)を正しく理解し、メモリリークやCOMオブジェクトの解放漏れを防ぐ厳格な設計が求められる。単にネットのサンプルをコピペしただけのコードは、長時間稼働する業務ツールにおいて必ずメモリリークやクラッシュを引き起こす。

—

2. 堅牢なアーキテクチャ設計

P/Invokeを実装するにあたり、以下の3点を鉄則とする。

1. DllImportの適切な定義: 文字コードや呼び出し規約(CallingConvention)を明示し、環境差異によるクラッシュを防ぐ。
2. COMオブジェクトのライフサイクル管理: `Marshal.ReleaseComObject` を用いた確実な解放、またはガベージコレクタに依存しないusingパターンの適用。
3. UIスレッドの保護: バックグラウンドワーカー(`Task` や `BackgroundWorker`)から安全にAPIを叩くためのスレッドセーフな設計。

—

3. 実装:タスクバー制御クラス(Production Ready)

以下のコードは、そのままプロジェクトに組み込めるプロダクション品質のクラスモジュールである。タスクバーのプログレス状態(通常、一時停止、エラー、indeterminate=不明)と、進捗率を制御する。

Imports System.Runtime.InteropServices
Imports System.Windows.Forms

”’

”’ Windowsタスクバーの高度な制御を行うヘルパークラス
”’

Public NotInheritable Class TaskbarProgress

Private Sub New()
‘ 静的クラスのためインスタンス化させない
End Sub

‘ ITaskbarList3インターフェイスのGUID



Private Interface ITaskbarList3
Sub HrInit()
Sub AddTab(hwnd As IntPtr)
Sub DeleteTab(hwnd As IntPtr)
Sub ActivateTab(hwnd As IntPtr)
Sub SetActiveAlt(hwnd As IntPtr)
Sub MarkFullscreenWindow(hwnd As IntPtr, fFullscreen As Boolean)
Sub SetProgressValue(hwnd As IntPtr, ullCompleted As ULong, ullTotal As ULong)
Sub SetProgressState(hwnd As IntPtr, state As TaskbarProgressBarState)
‘ その他のメソッドは省略(今回は進捗関連のみ使用)
End Interface



Private Class TaskbarInstance
End Class

”’

”’ タスクバーの進捗状態を表す列挙体
”’

Public Enum TaskbarProgressBarState As Integer
NoProgress = 0
Indeterminate = &H1 // 処理中アニメーション(不確定)
Normal = &H2 // 緑色(通常)
ErrorState = &H4 // 赤色(エラー)
Paused = &H8 // 黄色(一時停止)
End Enum

”’

”’ 指定されたウィンドウのタスクバーに進捗状況を設定します。
”’

Public Shared Sub SetProgress(form As Form, current As ULong, maximum As ULong, state As TaskbarProgressBarState)
If form Is Nothing OrElse form.IsDisposed Then Return

Dim taskbarList As ITaskbarList3 = Nothing
Try
‘ COMオブジェクトのインスタンス生成
taskbarList = DirectCast(New TaskbarInstance(), ITaskbarList3)
taskbarList.HrInit()

‘ 状態の設定
taskbarList.SetProgressState(form.Handle, state)

‘ 値の設定(NormalまたはPaused等の場合のみ有効)
If state = TaskbarProgressBarState.Normal OrElse
state = TaskbarProgressBarState.ErrorState OrElse
state = TaskbarProgressBarState.Paused Then
taskbarList.SetProgressValue(form.Handle, current, maximum)
End If

Catch ex As Exception
‘ ログ出力やフォールバック処理(OSが対応していない環境等への配慮)
System.Diagnostics.Debug.WriteLine($”Taskbar API Error: {ex.Message}”)
Finally
‘ COMオブジェクトの参照カウントを確実にデクリメントし、メモリリークを防ぐ
If taskbarList IsNot Nothing Then
Marshal.ReleaseComObject(taskbarList)
End If
End Try
End Sub

”’

”’ 注意を促すためにウィンドウ(タスクバーアイコン)を点滅させます。
”’


Private Shared Function FlashWindow(hwnd As IntPtr, bInvert As Boolean) As Boolean
End Function

Public Shared Sub Flash(form As Form)
If form IsNot Nothing AndAlso Not form.IsDisposed Then
FlashWindow(form.Handle, True)
End If
End Sub

End Class

—

4. 実務での活用シナリオ:データベース連携バッチ処理

では、このクラスを実際の業務アプリケーション(例:数万件の受注データをDBからCSV出力・インポートする処理)でどう組み込むか。

ここで重要なのは、UIスレッドをブロックしない非同期処理(Async/Await)との統合である。

Public Class MainForm

Private Async Sub btnStartImport_Click(sender As Object, e As EventArgs) Handles btnStartImport.Click
‘ 多重実行防止
btnStartImport.Enabled = False

Try
Dim totalRecords As ULong = 50000 ‘ 仮の総件数

‘ 1. 処理開始:タスクバーを「不確定(処理中アニメーション)」に設定
TaskbarProgress.SetProgress(Me, 0, totalRecords, TaskbarProgress.TaskbarProgressBarState.Indeterminate)

‘ 2. バックグラウンドでの重い処理を実行
Await Task.Run(
Sub()
For i As ULong = 1 To totalRecords
‘ — ここに実際のDB処理やファイルI/Oを記述 —
System.Threading.Thread.Sleep(1) ‘ 処理の模擬

‘ 定期的に進捗を更新(例: 100件ごと)
If i Mod 100 = 0 Then
‘ UIスレッド側へ進捗を通知してタスクバーを更新
Me.Invoke(Sub()
TaskbarProgress.SetProgress(Me, i, totalRecords, TaskbarProgress.TaskbarProgressBarState.Normal)
ProgressBar1.Value = CInt(i)
End Sub)
End If
Next
End Sub)

‘ 3. 正常終了時の処理
TaskbarProgress.SetProgress(Me, 0, 0, TaskbarProgress.TaskbarProgressBarState.NoProgress)
MessageBox.Show(“インポートが完了しました。”, “完了”, MessageBoxButtons.OK, MessageBoxIcon.Information)

Catch ex As Exception
‘ 4. エラー発生時の処理:タスクバーを赤色にし、ウィンドウを点滅させてユーザーに異常を通知
TaskbarProgress.SetProgress(Me, 0, 0, TaskbarProgress.TaskbarProgressBarState.ErrorState)
TaskbarProgress.Flash(Me)

MessageBox.Show($”エラーが発生しました: {ex.Message}”, “致命的エラー”, MessageBoxButtons.OK, MessageBoxIcon.Error)

Finally
‘ UIの復元
btnStartImport.Enabled = True
TaskbarProgress.SetProgress(Me, 0, 0, TaskbarProgress.TaskbarProgressBarState.NoProgress)
ProgressBar1.Value = 0
End Try
End Sub

End Class

—

5. チーフアーキテクトからの実践アドバイス

1. OSバージョンのフォールバック:
`ITaskbarList3` は Windows 7 以降でサポートされている。もしレガシーなWindows環境(Server 2008等)で動作する可能性が少しでもあるなら、`Environment.OSVersion.Version` をチェックするか、API呼び出しを `Try-Catch` で完全に囲み、失敗してもアプリ本体がクラッシュしないフェイルセーフ構造を徹底すること。
2. COM解放の徹底:
P/InvokeやCOM相互運用において、`Marshal.ReleaseComObject` の省略は「時限爆弾」を仕込むようなものだ。ガベージコレクタ(GC)の気まぐれにメモリ管理を委ねてはならない。必ず `Finally` ブロックで明示的に解放せよ。

総括

VB.NETは「古い言語」などではない。こうしたWindows APIの深部まで手懐けることで、C#製アプリケーションと何ら遜色のない、いや、それ以上に堅牢で洗練されたネイティブUI体験を構築できる。

現場のエンジニアよ、画面の中だけのプログレスバーに別れを告げよ。タスクバーを支配し、真にプロフェッショナルな業務ツールをその手で完成させろ。

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