こんにちは!Windows Formsアプリケーションの開発、日夜お疲れ様です。
ボタンを配置して、クリックイベントを書いて……という標準的なUI構築に慣れてくると、ふとこんな欲求が湧き上がってきませんか?
「処理の進捗状況を、タスクバーのアイコン自体にプログレスバーとして表示したい!」
「バックグラウンド処理が終わったことを、ウィンドウをピカピカ点滅させてユーザーに知らせたい!」
Windows Formsの標準コントロールだけでは、実はこの「OSのシェルと深く連携したリッチな演出」は少し荷が重いです。しかし、ここで登場するのが Windows API(P/Invoke) という強力なカード。
今回は、VB.NETからOSのネイティブ機能を直接叩き、デスクトップアプリの質感を一気に「プロ仕様」へと引き上げる極限のテクニックを伝授します。ここをクリアすれば、あなたのVB.NETスキルはマクロの域を完全に脱却し、ワンランク上のアーキテクトへと進化しますよ。さあ、一緒に扉を開きましょう!
—
1. なぜ「Windows API (P/Invoke)」が必要なのか?
私たちが普段使っているWindows Formsは、.NET Frameworkという安全な「管理された世界(マネージドコード)」で動いています。非常に扱いやすい反面、Windows OS(C++などで書かれたネイティブの世界)の奥深くにある機能へは、直接アクセスできない壁があります。
そこで使うのが P/Invoke (Platform Invoke) です。
これは、VB.NETからWindowsのシステムDLL(`user32.dll` や `shell32.dll` など)に直接ダイブし、OSの機能を直接呼び出すための魔法の呪文のような仕組みです。
今回は、このP/Invokeを使って以下の2つを実現します。
1. タスクバーのプログレスバー表示(Shell32.dll / ITaskbarList3インターフェイス)
2. ウィンドウの点滅通知(User32.dll / FlashWindowEx)
—
2. 実装の全体像とコード
百聞は一見にしかず。まずは、そのままコピペして動かせる実用的なフォームクラスのコードを提示します。
Visual Studioで新規のWindows Formsプロジェクト(VB.NET)を作成し、フォームにボタンを1つ配置して、以下のコードをフォームのコード全体と置き換えてみてください。
Imports System.Runtime.InteropServices
Imports System.Text
Public Class Form1
‘ ==========================================
‘ 1. ウィンドウ点滅(FlashWindowEx)の定義
‘ ==========================================
Private Shared Function FlashWindowEx(ByRef pwfi As FLASHWINFO) As Boolean
End Function
Private Structure FLASHWINFO
Public cbSize As UInteger
Public hwnd As IntPtr
Public dwFlags As UInteger
Public uCount As UInteger
Public dwTimeout As UInteger
End Structure
‘ 点滅フラグの定数
Private Const FLASHW_STOP As UInteger = 0 ‘ 点滅停止
Private Const FLASHW_ALL As UInteger = 3 ‘ ウィンドウとタスクバーボタンの両方を点滅
Private Const FLASHW_TIMERNOFG As UInteger = 12 ‘ 前面に来るまでずっと点滅
‘ ==========================================
‘ 2. タスクバー進捗(ITaskbarList3)の定義
‘ ==========================================
‘ GUIDとインターフェイスID
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,
Sub SetProgressValue(hwnd As IntPtr, ullCompleted As ULong, ullTotal As ULong)
Sub SetProgressState(hwnd As IntPtr, state As TaskbarProgressBarState)
End Interface
‘ プログレス状態の列挙体
Public Enum TaskbarProgressBarState As UInteger
NoProgress = 0
Indeterminate = 1 ‘ 処理中アニメーション(緑の流れるバー)
Normal = 2 ‘ 通常(緑色)
Error_State = 3 ‘ エラー(赤色)
Paused = 4 ‘ 一時停止(黄色)
End Enum
‘ CoClassの定義
Private Class TaskbarList
End Class
‘ ==========================================
‘ 3. 実処理とイベントハンドラ
‘ ==========================================
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
‘ 非同期風に重い処理をシミュレート(実際はBackgroundWorkerやAsync/Await推奨)
RunHeavyProcessWithTaskbarEffect()
End Sub
Private Sub RunHeavyProcessWithTaskbarEffect()
Dim tbarList As ITaskbarList3 = CType(New TaskbarList(), ITaskbarList3)
tbarList.HrInit()
Dim hwnd As IntPtr = Me.Handle
Dim maxProgress As ULong = 100
Try
‘ 1. プログレスバーを「通常(Normal)」状態にする
tbarList.SetProgressState(hwnd, TaskbarProgressBarState.Normal)
For i As ULong = 0 To maxProgress
‘ 進捗値を設定
tbarList.SetProgressValue(hwnd, i, maxProgress)
‘ UIのフリーズを防ぎつつ進捗を実感させるためのウェイト
System.Threading.Thread.Sleep(30)
Application.DoEvents() ‘ ※実務ではAsync/Awaitを推奨
ForNext:
Next
‘ 2. 処理完了後にプログレスバーを非表示にする
tbarList.SetProgressState(hwnd, TaskbarProgressBarState.NoProgress)
‘ 3. ユーザーに完了を知らせるためにウィンドウを点滅させる
FlashWindowNotification(hwnd)
Catch ex As Exception
‘ エラー時はタスクバーを赤色にする
tbarList.SetProgressState(hwnd, TaskbarProgressBarState.Error_State)
MessageBox.Show(“エラーが発生しました: ” & ex.Message)
End Try
End Sub
Private Sub FlashWindowNotification(targetHwnd As IntPtr)
Dim fwi As New FLASHWINFO()
fwi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fwi))
fwi.hwnd = targetHwnd
fwi.dwFlags = FLASHW_ALL Or FLASHW_TIMERNOFG
fwi.uCount = 3 ‘ 3回点滅
fwi.dwTimeout = 0 ‘ 標準の点滅速度
FlashWindowEx(fwi)
End Sub
End Class
—
3. コードの急所を解説!ここが分かれば怖くない
初めてこのコードを見たとき、「なんだか呪文のような型定義がたくさんあるぞ…」と圧倒されたかもしれません。しかし、押さえるべき本質はたったの3つです。
① `` 属性でOSの関数を釣る
Private Shared Function FlashWindowEx(ByRef pwfi As FLASHWINFO) As Boolean
End Function
これは、「Windowsのシステムフォルダにある `user32.dll` の中から、`FlashWindowEx` という名前の関数を引っ張ってきて、VB.NETの関数として使えるようにしてね」というコンパイラへの命令(属性)です。C++の関数シグネチャとVB.NETのデータ構造(Structure)のメモリレイアウトを完全に一致させる(`StructLayout(LayoutKind.Sequential)`)のが、API連携における最大のキモです。
② COMインターフェイスの活用(ITaskbarList3)
Windows 7以降のタスクバープログレス表示は、COM(Component Object Model)という仕組みを介して提供されています。
「ちょっと難解だな」と感じるかもしれませんが、やることはシンプルです。
1. `TaskbarList` クラスをインスタンス化し、`ITaskbarList3` インターフェイスにキャストする。
2. 自分のウィンドウハンドル(`Me.Handle`)を渡して、`SetProgressState`(状態の指定)や `SetProgressValue`(進捗率の指定)を呼ぶ。
これだけで、OS標準のエクスプローラーがタスクバーのアイコンを緑色に染め上げてくれます。
③ 完了を知らせる `FlashWindowEx`
処理が終わったあとに `FlashWindowEx` を呼び出すと、タスクバーのアイコンやウィンドウのタイトルバーがオレンジ色に点滅します。ユーザーが別のブラウザ作業などに没頭していても、「あ、処理終わったんだな」と直感的に気づかせることができます。この「ユーザー体験(UX)への配慮」こそが、プロのアプリケーションエンジニアの仕事です。
—
4. 陥りやすい罠とエンジニアの知見
ここで、現場でありがちな「ハマりポイント」を先回りして共有しておきます。これを知っておくだけで、無駄なデバッグ時間を何時間も節約できます。
- ウィンドウハンドル(`Me.Handle`)のタイミングに注意!
フォームが完全にロードされる前(`Form_Load` の最中など)にこれらのAPIを呼ぶと、まだ `Handle` が生成されておらず、うまく動作しないか例外が発生します。必ずフォームが表示された後(ボタンクリック時など)に呼び出してください。
- UIスレッドのブロック(`Application.DoEvents()` について)
上記のサンプルコードでは、初心者でも動きが視覚的にわかりやすいように `Application.DoEvents()` を使っていますが、実務の現場では、UIスレッドがブロックされてウィンドウが操作不能になるリスクがあります。本格的なアプリケーションを構築する際は、`Async` / `Await` キーワードと `Task` を組み合わせた非同期処理の中でAPIを叩くのが現代のベストプラクティスです。
—
5. おわりに:ここをクリアすれば基本はバッチリ!
今回は、Windows API(P/Invoke)を活用して、タスクバーのプログレス表示とウィンドウの点滅をVB.NETから制御する実践テクニックを解説しました。
「たかがボタンの処理、されど演出」です。
標準機能の枠を飛び出し、OSのネイティブ機能に直接アクセスできるようになると、作れるアプリケーションの幅が何倍にも広がります。マクロの記録や、決まりきったテンプレート通りのコードから脱却し、自分の手でシステムを自在に操る感覚を掴めたなら、あなたはもう立派なデスクトップアプリ・エンジニアです。
ここをクリアしたあなたなら、どんな高度なWindows機能の連携も怖くないはず。
ぜひ、日々の開発にこのテクニックを取り入れて、まわりをアッと言わせる洗練されたアプリケーションを作ってみてくださいね!
