はじめに
Delphi にはフレームワーク非依存の Taskbar ユニット System.Win.Taskbar があります 1。
ドキュメントはありませんが、なんとなく使い方は理解できます。
使い方
タスクバー関連の各機能の使い方です。System.Win.Taskbar には TWinTaskbar というクラスがあり、これを使ってタスクバー関連の処理を行います。
クラスなので、共通してこのようなコードを書く事になります。
uses
..., System.Win.Taskbar, Winapi.ShlObj{$IFDEF FRAMEWORK_FMX}, FMX.Platform.Win{$ENDIF};
...
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := Self.Handle; // VCL
// WinTaskbar.MainWindow := ApplicationHWND; // FMX
// 何かの処理
finally
WinTaskbar.Free;
end;
| プロパティ | 型 | 説明 |
|---|---|---|
| MainWindow | HWND | メインウインドウのハンドル |
古い TWinTaskbar には MainWindow プロパティが存在せず、メソッドのパラメータとしてウインドウハンドルをイチイチ渡してやらなければなりませんでした。
この古い書き方のメソッドは今でも互換性のために残されているのですが、現在のバージョンではウインドウハンドルのパラメータは無視されます。ウインドウハンドルを省略可能なオーバーロード関数が存在しない場合を除き、常に MainWindow プロパティの値が使われるようです。
なので、MainWindow プロパティの存在するバージョンでは TWinTaskbar を生成した直後に必ず MainWindow プロパティを設定するようにしてください。
Application.MainFormOnTaskbar が False の場合には、MainWindow プロパティに Self.Handle ではなく Application.Handle を渡してください。
FireMonkey の場合には FMX.Platform.Win で定義されている ApplicationHWND を指定します。
■ タスクバーボタン
タスクバーボタンのグループ化の制御を行うには Winapi.ShlObj を uses に入れて、SetCurrentProcessExplicitAppUserModelID() API を呼び出します。
詳しくは以下の記事を参照してください。
See also:
■ 進行状況バー (プログレスバー)
進行状況バーを表示するには次のようなコードになります。
uses
..., System.Win.Taskbar, Winapi.ShlObj;
procedure TForm1.Button1Click(Sender: TObject);
begin
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := Self.Handle;
WinTaskbar.SetProgressState(TBPF_NORMAL);
WinTaskbar.SetProgressValue(0, 100); // 進捗を初期化
for var i := 1 to 100 do
begin
WinTaskbar.SetProgressValue(i, 100); // 進捗を更新
Sleep(50);
Application.ProcessMessages;
end;
finally
WinTaskbar.SetProgressValue(0, 100); // 進捗を初期化 (元に戻す)
WinTaskbar.SetProgressState(TBPF_NOPROGRESS);
WinTaskbar.Free;
end;
end;
SetProgressState に設定できる状態は次の通りです。
| アイコン | 定数 | 説明 |
|---|---|---|
![]() |
TBPF_NOPROGRESS | 進捗表示なし (アクティブ) |
![]() |
TBPF_INDETERMINATE | 処理中&マーキー表示 (進捗不明) |
![]() |
TBPF_NORMAL | 処理中&進捗表示 |
![]() |
TBPF_PAUSED | 中断中&再開待ち |
![]() |
TBPF_ERROR | 中断中&再開不能 |
See also:
■ オーバーレイアイコン
オーバーレイアイコンを表示するには...例えばアイコンにシステムストックアイコンを使うには、次のような関数を準備し、
uses
..., Winapi.ShellAPI;
function GetStockIcon(SIID: SHSTOCKICONID): HICON;
begin
Result := 0;
var sii := Default(TSHStockIconInfo);
sii.cbSize := SizeOf(sii);
if Succeeded(SHGetStockIconInfo(SIID, SHGSI_ICON or SHGSI_SMALLICON, sii)) then
Result := sii.hIcon;
end;
こんな感じのコードになります。
procedure TForm1.Button1Click(Sender: TObject);
begin
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := Self.Handle;
var StockIcon := GetStockIcon(SIID_ERROR);
try
WinTaskbar.SetOverlayIcon(StockIcon, 'エラー');
finally
DestroyIcon(StockIcon);
end;
finally
WinTaskbar.Free;
end;
end;
オーバーレイアイコンを消すには 0 を代入すれば OK です。
procedure TForm1.Button2Click(Sender: TObject);
begin
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := Self.Handle;
WinTaskbar.SetOverlayIcon(0, ''); // 消去
finally
WinTaskbar.Free;
end;
end;
See also:
■ タスクバーサムネイル (タブ)
VCL でしか使わないのなら Vcl.Taskbar を使った方が便利です。
・'タブ'(フォーム)を登録してタスクバー プレビューに複数の画像を表示する
RegisterTab() でフォームのハンドルを追加します。登録されたフォームはサムネイル表示されます。
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Math, Winapi.DwmApi, System.Win.Taskbar, Unit2, Unit3{$IFDEF FRAMEWORK_FMX}, FMX.Platform.Win{$ENDIF};;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
WinTaskbar: TWinTaskbar;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TThread.ForceQueue(nil,
procedure
begin
WinTaskbar := TWinTaskbar.Create;
WinTaskbar.MainWindow := Self.Handle;
// Form2 をタブに追加
Form2.Show;
WinTaskbar.RegisterTab(Form2.Handle);
DwmInvalidateIconicBitmaps(Form2.Handle);
// Form3 をタブに追加
Form3.Show;
WinTaskbar.RegisterTab(Form3.Handle);
DwmInvalidateIconicBitmaps(Form3.Handle);
end);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
//WinTaskbar.UnRegisterTab(Form3.Handle);
//WinTaskbar.UnRegisterTab(Form2.Handle);
WinTaskbar.Free;
end;
end.
FireMoneky では WinTaskbar.MainWindow に ApplicationHWND を渡し、Form2.Handle を FormToHWND(Form2) のように書き換えてください (Form3.Handle も同様)。
・フォームの一部の領域を切り取って表示する
SetThumbnailClip() でクリッピング領域を指定します。クリッピングを解除するには ClearThumbnailClip() を呼びます。
この機能は Windows 11 では利用できないようです。クリッピング領域を指定しても常に全体が描画されます。
Windows 11 では代わりにカスタマイズ描画 (後述) を試してみてください。
・新しい画像を生成して表示
サムネイルのカスタマイズ描画です。ミニプレイヤー等を作るのに使えます。
サンプルではサムネイル / ライブプレビューともにクライアント領域を描画しています。PaintTo の部分をお好きな描画処理に差し替えてください。
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Math, Winapi.DwmApi, System.Win.Taskbar;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
WinTaskbar: TWinTaskbar;
procedure WMDwmSendIconicThumbnail(var Msg: TMessage); message WM_DWMSENDICONICTHUMBNAIL;
procedure WMDwmSendIconicLivePreviewBitmap(var Msg: TMessage); message WM_DWMSENDICONICLIVEPREVIEWBITMAP;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
TThread.ForceQueue(nil,
procedure
begin
WinTaskbar := TWinTaskbar.Create;
WinTaskbar.MainWindow := Self.Handle;
{$WARN SYMBOL_PLATFORM OFF}
var lEnabled: LongBool := True;
DwmSetWindowAttribute(Self.Handle, DWMWA_HAS_ICONIC_BITMAP, @lEnabled, SizeOf(lEnabled));
DwmSetWindowAttribute(Self.Handle, DWMWA_FORCE_ICONIC_REPRESENTATION, @lEnabled, SizeOf(lEnabled));
{$WARN SYMBOL_PLATFORM ON}
DwmInvalidateIconicBitmaps(Self.Handle);
end);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
WinTaskbar.Free;
end;
// ライブプレビューの独自描画
procedure TForm1.WMDwmSendIconicLivePreviewBitmap(var Msg: TMessage);
begin
var lBmp := TBitmap.Create;
try
lBmp.SetSize(ClientWidth, ClientHeight);
lBmp.PixelFormat := pf24bit;
// 描画処理
Self.PaintTo(lBmp.Canvas, 0, 0); // 自分自身のクライアント領域
lBmp.PixelFormat := pf32bit;
// ライブプレビュー画像の登録
DwmSetIconicLivePreviewBitmap(Self.Handle, lBmp.Handle, nil, 0);
finally
lBmp.Free;
end;
Msg.Result := 0;
end;
// サムネイルの独自描画
procedure TForm1.WMDwmSendIconicThumbnail(var Msg: TMessage);
begin
var lMaxH := HIWORD(Msg.LParam);
var lMaxW := LOWORD(Msg.LParam);
if (Width <= 0) or (Height <= 0) then
Exit;
var lBmpSrc := TBitmap.Create;
try
lBmpSrc.SetSize(Width, Height);
lBmpSrc.PixelFormat := pf24bit;
// 描画処理
Self.PaintTo(lBmpSrc.Canvas, 0, 0); // 自分自身のクライアント領域
lBmpSrc.PixelFormat := pf32bit;
// リサイズ処理
var lScale := Min(lMaxW / lBmpSrc.Width, lMaxH / lBmpSrc.Height);
var lDstW := Max(1, Round(lBmpSrc.Width * lScale));
var lDstH := Max(1, Round(lBmpSrc.Height * lScale));
var lBmpDst := TBitmap.Create;
try
lBmpDst.SetSize(lDstW, lDstH);
lBmpDst.PixelFormat := pf32bit;
SetStretchBltMode(lBmpDst.Canvas.Handle, HALFTONE);
SetBrushOrgEx(lBmpDst.Canvas.Handle, 0, 0, nil);
StretchBlt(
lBmpDst.Canvas.Handle, 0, 0, lDstW, lDstH,
lBmpSrc.Canvas.Handle, 0, 0, lBmpSrc.Width, lBmpSrc.Height,
SRCCOPY
);
// サムネイル画像の登録
DwmSetIconicThumbnail(Self.Handle, lBmpDst.Handle, 0);
finally
lBmpDst.Free;
end;
finally
lBmpSrc.Free;
end;
Msg.Result := 0;
end;
end.
See also:
・TabProperties
タブのプロパティは SetTabProperties() で指定します。AStpFlags に指定可能な値は次の通りです。
| TWinTaskbar | TTaskbar | 説明 |
|---|---|---|
| STPF_USEAPPTHUMBNAILALWAYS | AppThumbAlways | 常にサムネイルがプレビューされる |
| STPF_USEAPPTHUMBNAILWHENACTIVE | AppThumbWhenActive | フォームがアクティブな時だけサムネイルがプレビューされる |
| STPF_USEAPPPEEKALWAYS | AppPeekAlways | 常にライブプレビューが表示される |
| STPF_USEAPPPEEKWHENACTIVE | AppPeekWhenActive | フォームがアクティブな時だけライブプレビューが表示される |
AStpFlags にはサムネイル系 (THUMBNAIL) のいずれかとライブプレビュー系 (PEEK) のいずれか、最大で 2 つを組み合わせられます。
See also:
■ サムネイルツールバー
自 EXE に埋め込まれたリソースを読み出してサムネイルツールバーにボタンを追加するサンプルソースコードです。
・アイコン形式 (*.ICO) の画像をボタンとして使う
以下はボタンとしてアイコン (*.ICO) 形式のファイルを使う場合です。アイコンはリソースとして EXE に埋め込んであるものとします。
uses
..., System.Win.Taskbar, Winapi.ShlObj, Winapi.ShellAPI;
...
private
{ Private 宣言 }
procedure WMCommand(var Msg: TWMCommand); message WM_COMMAND;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
TThread.ForceQueue(nil,
procedure
begin
var hIconPrev := LoadIcon(HInstance, 'ICON_REWIND');
var hIconNext := LoadIcon(HInstance, 'ICON_FORWARD');
var Buttons: array [0..1] of TTHUMBBUTTON;
FillChar(Buttons, SizeOf(Buttons), 0);
// 前へ (ID = 0)
Buttons[0].dwMask := THB_ICON or THB_TOOLTIP or THB_FLAGS;
Buttons[0].iId := 0;
Buttons[0].hIcon := hIconPrev;
StrLCopy(Buttons[0].szTip, '前へ', Length(Buttons[0].szTip) - 1);
Buttons[0].dwFlags := THBF_ENABLED;
// 次へ (ID = 1)
Buttons[1].dwMask := THB_ICON or THB_TOOLTIP or THB_FLAGS;
Buttons[1].iId := 1;
Buttons[1].hIcon := hIconNext;
StrLCopy(Buttons[1].szTip, '次へ', Length(Buttons[1].szTip) - 1);
Buttons[1].dwFlags := THBF_ENABLED;
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := Self.Handle;
WinTaskbar.ThumbBarAddButtons(Buttons);
finally
WinTaskbar.Free;
end;
end);
end;
procedure TForm1.WMCommand(var Msg: TWMCommand);
begin
if Msg.NotifyCode = THBN_CLICKED then
begin
case Msg.ItemID of
0:
ShowMessage('前へ');
1:
ShowMessage('次へ');
end;
end;
inherited;
end;
コード例のように ForceQueue() を使って登録処理をメッセージキューの最後に突っ込まないとサムネイルツールバーが表示されないかもしれません。
FireMonkey の場合
FireMonkey の場合、メッセージがアプリケーションの HWND に飛んでくるのでこれを捕まえなくてはなりません。ちょっと面倒なので、以下の記事にある FMX.Win.MessageService を利用する事にします。
uses
..., System.Win.Taskbar, Winapi.Windows, Winapi.ShlObj, Winapi.Messages,
FMX.Platform, FMX.Platform.Win, FMX.Win.MessageService;
...
private
{ private 宣言 }
WinTaskbar: TWinTaskbar;
procedure AppMessage(var Msg: TMsg; var Handled: Boolean);
...
procedure TForm1.FormCreate(Sender: TObject);
begin
TMessageService.AppService.OnMessage := Self.AppMessage;
TThread.ForceQueue(nil,
procedure
begin
var hIconPrev := LoadIcon(HInstance, 'ICON_REWIND');
var hIconNext := LoadIcon(HInstance, 'ICON_FORWARD');
var Buttons: array [0..1] of TTHUMBBUTTON;
FillChar(Buttons, SizeOf(Buttons), 0);
// 前へ (ID = 0)
Buttons[0].dwMask := THB_ICON or THB_TOOLTIP or THB_FLAGS;
Buttons[0].iId := 0;
Buttons[0].hIcon := hIconPrev;
StrLCopy(Buttons[0].szTip, '前へ', Length(Buttons[0].szTip) - 1);
Buttons[0].dwFlags := THBF_ENABLED;
// 次へ (ID = 1)
Buttons[1].dwMask := THB_ICON or THB_TOOLTIP or THB_FLAGS;
Buttons[1].iId := 1;
Buttons[1].hIcon := hIconNext;
StrLCopy(Buttons[1].szTip, '次へ', Length(Buttons[1].szTip) - 1);
Buttons[1].dwFlags := THBF_ENABLED;
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := ApplicationHWND;
WinTaskbar.ThumbBarAddButtons(Buttons);
finally
WinTaskbar.Free;
end;
end);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
WinTaskbar.Free;
TMessageService.AppService.OnMessage := nil;
end;
procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
case Msg.Message of
WM_COMMAND:
if HiWord(Msg.wParam) = THBN_CLICKED then
case LoWord(Msg.wParam) of
0:
ShowMessage('前へ');
1:
ShowMessage('次へ');
end;
end;
end;
・BITMAP 形式 (*.BMP) の画像をボタンとして使う
以下はボタンとしてビットマップ (*.BMP) 形式のファイルを使う場合です。ビットマップはリソースとして EXE に埋め込んであるものとします。
ビットマップの場合はイメージリストを作成し、リストのインデックスを使ってボタンを登録します。
uses
..., System.Win.Taskbar, Winapi.ShlObj, Winapi.ShellAPI, Winapi.CommCtrl;
...
private
{ Private 宣言 }
procedure WMCommand(var Msg: TWMCommand); message WM_COMMAND;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
TThread.ForceQueue(nil,
procedure
begin
var hImageList := ImageList_Create(16, 16, ILC_COLOR24 or ILC_MASK, 2, 1);
var MaskColor := RGB(255, 0, 255); // 透過色: マゼンタ
var hBmpPrev := LoadBitmap(HInstance, 'BITMAP_LEFT');
var hBmpNext := LoadBitmap(HInstance, 'BITMAP_RIGHT');
ImageList_AddMasked(hImageList, hBmpPrev, MaskColor); // Index: 0
ImageList_AddMasked(hImageList, hBmpNext, MaskColor); // Index: 1
if hBmpPrev <> 0 then
DeleteObject(hBmpPrev);
if hBmpNext <> 0 then
DeleteObject(hBmpNext);
var Buttons: array [0..1] of TTHUMBBUTTON;
FillChar(Buttons, SizeOf(Buttons), 0);
// 前へ (ID = 0)
Buttons[0].dwMask := THB_BITMAP or THB_TOOLTIP or THB_FLAGS;
Buttons[0].iId := 0;
Buttons[0].iBitmap := 0;
StrLCopy(Buttons[0].szTip, '前へ', Length(Buttons[0].szTip) - 1);
Buttons[0].dwFlags := THBF_ENABLED;
// 次へ (ID = 1)
Buttons[1].dwMask := THB_BITMAP or THB_TOOLTIP or THB_FLAGS;
Buttons[1].iId := 1;
Buttons[1].iBitmap := 1;
StrLCopy(Buttons[1].szTip, '次へ', Length(Buttons[1].szTip) - 1);
Buttons[1].dwFlags := THBF_ENABLED;
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := Self.Handle;
WinTaskbar.ThumbBarSetImageList(Self.Handle, hImageList);
WinTaskbar.ThumbBarAddButtons(Buttons);
finally
WinTaskbar.Free;
end;
end);
end;
procedure TForm1.WMCommand(var Msg: TWMCommand);
begin
if Msg.NotifyCode = THBN_CLICKED then
begin
case Msg.ItemID of
0:
ShowMessage('前へ');
1:
ShowMessage('次へ');
end;
end;
inherited;
end;
イメージリストは透過色としてマゼンタを指定していますが、32bit BITMAP (ILC_COLOR32) を使えばアルファチャンネル透過を使うことが出来ます。その際には ImageList_AddMasked() ではなく ImageList_Add() を使って画像をイメージリストに追加してください。
VCL の場合には TImageList を使う事もできます。簡単ですね。
procedure TForm1.FormCreate(Sender: TObject);
begin
TThread.ForceQueue(nil,
procedure
begin
var Buttons: array [0..1] of TTHUMBBUTTON;
FillChar(Buttons, SizeOf(Buttons), 0);
// 前へ (ID = 0)
Buttons[0].dwMask := THB_BITMAP or THB_TOOLTIP or THB_FLAGS;
Buttons[0].iId := 0;
Buttons[0].iBitmap := 0;
StrLCopy(Buttons[0].szTip, '前へ', Length(Buttons[0].szTip) - 1);
Buttons[0].dwFlags := THBF_ENABLED;
// 次へ (ID = 1)
Buttons[1].dwMask := THB_BITMAP or THB_TOOLTIP or THB_FLAGS;
Buttons[1].iId := 1;
Buttons[1].iBitmap := 1;
StrLCopy(Buttons[1].szTip, '次へ', Length(Buttons[1].szTip) - 1);
Buttons[1].dwFlags := THBF_ENABLED;
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := Self.Handle;
WinTaskbar.ThumbBarSetImageList(Self.Handle, ImageList1.Handle);
WinTaskbar.ThumbBarAddButtons(Buttons);
finally
WinTaskbar.Free;
end;
end);
end;
ICON / BMP ボタンは混在させる事も可能です。
See also:
■ ジャンプリスト
ジャンプリスト関連は System.Win.Taskbar には含まれていません。
詳しくは以下の記事を参照してください。
See also:
■ フルスクリーン時のタスクバー
TWinTaskbar には MarkFullscreenWindow というメソッドがあります。これはアプリケーションがフルスクリーン状態/解除になったことを通知するものです。
第二パラメータを True にして呼ぶとフォームがフルスクリーンになるというものではなく、フルスクリーン時にタスクバーが前面に出てこようとするのを抑制するためのものです。
procedure TForm1.Button1Click(Sender: TObject);
begin
var WinTaskbar := TWinTaskbar.Create;
try
WinTaskbar.MainWindow := Self.Handle;
// 現在のフォームをフルスクリーンとしてマーク
WinTaskbar.MarkFullscreenWindow(Self.Handle, True); // フルスクリーン
finally
WinTaskbar.Free;
end;
end;
FireMoney フォームにある FullScreen プロパティとの関連性はありません。
おわりに
FireMonkey でタスクバーを使う場合には System.Win.Taskbar を使う事になるかと思いますが、TWinTaskbar はタスクバー関連を集約してしまっているので、少々使い勝手がよくないですね。
索引
[ ↑ 目次へ ]
-
Delphi XE6 以降。 ↩




