2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Delphi】System.Win.Taskbar の使い方

2
Last updated at Posted at 2026-10-31

はじめに

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 に設定できる状態は次の通りです。

アイコン 定数 説明
image.png TBPF_NOPROGRESS 進捗表示なし (アクティブ)
image.png TBPF_INDETERMINATE 処理中&マーキー表示 (進捗不明)
image.png TBPF_NORMAL 処理中&進捗表示
image.png TBPF_PAUSED 中断中&再開待ち
image.png 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() でフォームのハンドルを追加します。登録されたフォームはサムネイル表示されます。

unit1.pas
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 の部分をお好きな描画処理に差し替えてください。

unit1.pas
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 はタスクバー関連を集約してしまっているので、少々使い勝手がよくないですね。

索引

[ ↑ 目次へ ]

  1. Delphi XE6 以降。 ↩

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?