Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2019年01月23日] [更新 2019年01月23日] Delphi サンプルプログラム集
648_他のアプリのメインフォームの最小化と元に戻す
動作確認等 Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32, Delphi 6(UP2) Pro
648_OtherApp_MinimizeRestore.zip [4,787 KB] 2019年01月23日版 (EXE 同梱)







他のアプリのウィンドウの最小化と元に戻す


他のアプリのウィンドウを最小化して元に戻すサンプルです。
ウィンドウは一般的にはウィンドウハンドルを持つコントロールを意味しますが、本ページで扱っていのはフォームです。ウィンドウ操作用の関数はいくつかありますが、本ページでは WM_SYSCOMMAND メッセージを使用しています。

本ページには Delphi 6 で作成したアプリを操作する例がありますが、基本的に Windows Vista 以降に対応したアプリケーションが対象です。






01_メモ帳のウィンドウを最小化して元に戻す


他のアプリのウィンドウの例として、メモ帳のウィンドウを最小化して元に戻すテストです。


図1
操作用のプログラムの実行画面

リスト1
メモ帳のウィンドウを最小化、または最小化したメモ帳のウィンドウを元に戻すサンプル
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  ShellAPI;

//=============================================================================
//  メモ帳を起動
//  uses に ShellAPI が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShellExecute(0, '', 'notepad.exe', nil, nil, SW_SHOW);
end;

//=============================================================================
//  メモ帳が起動していたら最小化する
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LhWindow : HWND;
begin
  // メモ帳のメインウィンドウのハンドルを取得
  LhWindow := FindWindow('Notepad', nil);

  if LhWindow <> 0 then begin
    PostMessage(LhWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
  end;
end;

//=============================================================================
//  メモ帳が起動して最小化していたら元に戻す
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LhWindow : HWND;
begin
  // メモ帳のメインウィンドウのハンドルを取得
  LhWindow := FindWindow('Notepad', nil);

  if LhWindow <> 0 then begin
    PostMessage(LhWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
  end;
end;

end.




02_Delphi 2007 以降で作成したアプリを最小化して元に戻す


これ以降は Delphi で作成したアプリが操作対象です。
Delphi で作成したアプリに限らず、アプリのメインフォーム (メインウィンドウ) を最小化すると子フォーム (子フォームのウィンドウ) も最小化されます。子フォームを最小化してもメインフォームは最小化されません したかって、対象のアプリのウィンドウを最小化するには、メインフォームのウィンドウハンドルを取得して操作します。
前項のメモ帳のサンプルも、Notepad というクラス名から取得したウィンドウハンドルがメインフォームのウィンドウのハンドルです。

操作用のコードは、操作対象のウィンドウハンドルが異なっている以外はメモ帳のウィンドウを操作する場合と全く同じです。

操作対象のアプリは Windows Vista 以降に対応している、つまり、Delphi 2007 以降で作成したアプリとします。ただし、Application.MainFormOnTaskbar の値を False にして作成したアプリは、このサンプルでは正常に操作できません。初回は最小化できて元に戻せても、繰り返し実行すると最小化ができまなくなります。



[備考 1]
メインフォームは特別です。メインフォームを閉じると子フォームも閉じてアプリは終了します。子フォームを閉じてもアプリは終了しません。
子フォームはメインフォームより前面に表示されます。メインフォームは子フォームより前面には表示できません。

[備考 2]
Application.MainFormOnTaskbar はプロジェクトのソースコードで設定されています。プロジェクトのソース以外で設定するとアプリの挙動が怪しくなることがあります。


図2
メインフォームを最小化すると子フォームも最小化する
  • 最小化の操作を別のプログラムから実行するのが本ページのサンプル
図3
操作用のプログラムの実行画面

リスト2
他のアプリのメインウィンドウを最小化、または最小化したウィンドウを元に戻すサンプル
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  ShellAPI;

{$R *.DFM}

//=============================================================================
//  操作対象のアプリを起動
//  uses に ShellAPIが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
begin
  LFilePath := ExpandFileName('..\Target_Normal\Target.exe');
  ShellExecute(0, '', PChar(LFilePath), nil, nil, SW_SHOW);
end;

//=============================================================================
//  対象のアプリのメインフォームを最小化
//  対象のウィンドウがメインフォームの場合は子フォームも最小化する
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LhWindow : HWND;
begin
  // 対象のウィンドウハンドル
  LhWindow := FindWindow('TTargetForm', nil);

  if LhWindow <> 0 then begin
    PostMessage(LhWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
  end;
end;

//=============================================================================
//  最小化したウィンドウを元に戻す
//  上のコードの実行前に最小化されている子フォームは元に戻らない
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LhWindow : HWND;
begin
  LhWindow := FindWindow('TTargetForm', nil);

  if LhWindow <> 0 then begin
    PostMessage(LhWindow, WM_SYSCOMMAND, SC_RESTORE, 0);

    // 前面に表示するなら、例えば
    SetForegroundWindow(LhWindow);
  end;
end;

end.


以下が操作対象の他のアプリのメインフォームのコードです。子フォームを表示するだけです。
子フォームはサイズを変更していますが、それ以外の変更はしていません。



リスト3
操作対象の他のアプリのメインフォームのコード
unit TargetUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TTargetForm = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  TargetForm: TTargetForm;

implementation

{$R *.dfm}

uses Unit1;

procedure TTargetForm.Button1Click(Sender: TObject);
begin
  TargetSubForm.Left := Self.Left + 100;
  TargetSubForm.Top := Self.Top + 100;
  TargetSubForm.WindowState := wsNormal;
  TargetSubForm.Show;
end;

end.


メインフォームから表示する子フォームの親をアプリケーションのハンドルにすると、メインフォームを子フォームよりも前面に表示できるようになります。



リスト4
操作対象であるアプリのメインフォームから表示する子フォームのコード
メインフォームを前面に表示できるようにする場合
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TTargetSubForm = class(TForm)
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  TargetSubForm: TTargetSubForm;

implementation

{$R *.dfm}

{ TTargetSubForm }

//=============================================================================
//  CreateParams メソッド処理
//  メインフォームを前面に表示できるようにする
//=============================================================================
procedure TTargetSubForm.CreateParams(var Params: TCreateParams);
begin
  inherited;

  // 子フォームの WndParent のデフォルト値は Application.MainFormHandle
  Params.WndParent := Application.Handle;
end;

end.




03_Delphi 6 で作成して Vista 対応にしたアプリの最小化と元に戻す


Delphi 2007 よりも前のバージョンで作成したアプリにはサイズが ( 0, 0 ) の「秘密のウィンドウ」があります。この秘密のウィンドウに対して Windows Vista に対応する処理をすれば、メモ帳に対する操作と同じコードで最小化の操作ができます。
添付の操作対象のプログラムは、Delphi 6 で作成していますが、DelFusa さんのサイトからダウンロードしたユニット AppWinFix を使用してその対応をしています。

操作用のコードは、操作対象のウィンドウハンドルが異なっている以外はメモ帳のウィンドウを操作する場合と全く同じです。

なお、AppWinFix.pas の最後の行にある以下のコードで、ModalPopupMode が未定義のエラーとなったので、この行をコメントアウトしてコンパイルしています。このプロパティは Delphi 2006 で実装されています。


    Application.ModalPopupMode := pmExplicit;


[備考 1]
アプリケーションのアイコンは、[プロジェクト] [オプション] の [アプリケーション] で設定するアイコンのことです。フォームのアイコンは、TForm のオブジェクト・インスペクタの Icon プロパティで設定するアイコンのことです。フォームのアイコンが設定されていない場合、一般的にはアプリケーション (プロジェクト) のアイコンが使われます。

[備考 2]
Windows Vista は 2006年にリリースされ、2017年4月11日 (日本時間で4月12日) にマイクロソフト社によるサポートが終了しました。


図4
秘密のウィンドウを Windows Vista 対応にした Delphi 6 のアプリ
  • タスクバーのアインがメインフォームのアイコンになる
  • メインフォームは子フォームよりも前面には表示できない
    (メインフォームと子フォームの区別が容易)
  • [Button2] で最小化した後、タスクバーのアイコンをクリックすると元に戻る

リスト5
他のアプリのメインウィンドウを最小化、または最小化したウィンドウを元に戻すサンプル
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  ShellAPI;

{$R *.DFM}

//=============================================================================
//  操作対象のアプリを起動
//  uses に ShellAPIが必要
//
//  対象の Target.exe は Delphi 6(UP2) Pro で作成している
//  対象の EXE は以下からダウンロードした AppWinFix を使用して Vista 対応している
//
//  [ DelFusa Blog 総本山 Vista対応VCL、AppWinFix ]
//  http://delfusa.blog65.fc2.com/?mode=m&no=36&photo=true
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
begin
  LFilePath := ExpandFileName('..\Target_D6_AppWinFix\Target.exe');
  ShellExecute(0, '', PChar(LFilePath), nil, nil, SW_SHOW);
end;

//=============================================================================
//  対象のアプリのメインフォームを最小化
//  対象のウィンドウがメインフォームの場合は子フォームも最小化する
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LhWindow : HWND;
begin
  // 対象のウィンドウハンドル
  LhWindow := FindWindow('TTargetForm', nil);

  if LhWindow <> 0 then begin
    PostMessage(LhWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
  end;
end;

//=============================================================================
//  最小化したウィンドウを元に戻す
//  上のコードの実行前に最小化されている子フォームは元に戻らない
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LhWindow : HWND;
begin
  LhWindow := FindWindow('TTargetForm', nil);

  if LhWindow <> 0 then begin
    PostMessage(LhWindow, WM_SYSCOMMAND, SC_RESTORE, 0);

    // 前面に表示するなら、例えば
    SetForegroundWindow(LhWindow);
  end;
end;

end.


以下が操作対象のアプリのメインフォームのコードです。
AppWinFix で実装されている TCustomMainForm を継承してフォームを生成します。子フォームは実行時に生成するようにしています。



リスト6
操作対象であるアプリのメインフォームのコード。uses に AppWinFix を追加
unit TargetUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, AppWinFix;

type
  TTargetForm = class(TCustomMainForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  TargetForm: TTargetForm;

implementation

{$R *.dfm}

uses Unit1;

procedure TTargetForm.Button1Click(Sender: TObject);
begin
  if Assigned(TargetSubForm) then begin
    TargetSubForm.WindowState := wsNormal;
  end else begin
    TargetSubForm := TTargetSubForm.Create(Self, Self.Handle);
    TargetSubForm.Left := Self.Left + 100;
    TargetSubForm.Top := Self.Top + 100;
  end;
  TargetSubForm.Show;
end;

procedure TTargetForm.Button2Click(Sender: TObject);
begin
  Self.WindowState := wsMinimized;
end;

end.


操作対象のアプリのメインフォームから表示する子フォームのコードです。
AppWinFix で実装されている TCustomSubForm を継承して生成します。



リスト7
操作対象であるアプリのメインフォームから表示する子フォームのコード
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, AppWinFix;

type
  TTargetSubForm = class(TCustomSubForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  TargetSubForm: TTargetSubForm;

implementation

{$R *.dfm}

{ TTargetSubForm }

procedure TTargetSubForm.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Action := caFree;
  TargetSubForm := nil;
end;

end.




04_Delphi 6 で作成して Vista 未対応アプリの最小化と元に戻す


Delphi 2007 よりも前のバージョンで作成したアプリは、秘密のウィンドウに対する処理をしないと、ここまでのサンプルでは対象アプリの最小化等の操作を正常に実行できません。
そのようなアプリでは、メインフォームではなく、アプリケーションウィンドウに対して操作コードを実行します。Delphi 2007 よりも前のバージョンの Delphi では、メインフォームはアプリケーションウィンドウが生成しますから、メインフォームのオーナーウィンドウを取得すると、それがアプリケーションのウィンドウになります。
以下はその実際のサンプルです。


[備考 1]
Aplication.MainFormOnTaskbar を False にするとアプリケーションウィンドウが有効になります。したがって、Aplication.MainFormOnTaskbar が False のアプリは、本項のサンプルを使用すれば、メインフォームを最小化したり、最小化から元に戻す操作ができます。

[備考 2]
Delphi 2007 より前 (Delphi 2006 以前) のバージョンで作成したアプリを Windows Vista 以降で使用するには、秘密のウィンドウに対応する処理をして、アプリケーションマニフェストを作成して使用するのが基本と思われます。


図5
秘密のウィンドウを Windows Vista に対応してない Delphi 6 のアプリ
  • タスクバーのアイコンはアプリのアイコン
  • メインフォームが子フォームよりも前面に表示できてしまう
    (メインフォームと子フォームの区別がしにくい)
  • [Button2] で最小化した後、タスクバーのアイコンをクリックしても元に戻らない


リスト8
他のアプリのメインウィンドウを最小化、または最小化したウィンドウを元に戻すサンプル
Windows Vista に未対応の Delphi 製アプリ用
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  ShellAPI;

{$R *.DFM}

//=============================================================================
//  操作対象のアプリを起動
//  uses に ShellAPIが必要
//
//  対象の Target.exe は Delphi 6(UP2) Pro で作成している
//  対象の EXE はWindows Vista 対応にはしていない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
begin
  LFilePath := ExpandFileName('..\Target_D6\Target.exe');
  ShellExecute(0, '', PChar(LFilePath), nil, nil, SW_SHOW);
end;

//=============================================================================
//  対象のウィンドウを最小化
//  対象のウィンドウがメインフォームの場合は子フォームも最小化する
//
//  Delphi 2007 より前で作成したアプリで Vista 対応にしていない場合は
//  アプリケーションウィンドウに対して操作する
//  メインフォームを作成したのはアプリケーションウィンドウ
//  したがって GetWindow(LhWindow, GW_OWNER) で取得できる
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LhWindow : HWND;
  LhAppWnd : HWND;
begin
  // 対象のウィンドウハンドル
  LhWindow := FindWindow('TTargetForm', nil);
  LhAppWnd := 0;
  if LhWindow <> 0 then begin
    LhAppWnd  := GetWindow(LhWindow, GW_OWNER);
  end;

  if LhAppWnd <> 0 then begin
    LhWindow := LhAppWnd;
  end;
  if LhWindow <> 0 then begin
    PostMessage(LhWindow, WM_SYSCOMMAND, SC_MINIMIZE, 0);
  end;
end;

//=============================================================================
//  最小化したウィンドウを元に戻す
//  上のコードの実行前に最小化されている子フォームは元に戻らない
//
//  Delphi 2007 より前で作成したアプリで Vista 対応にしていない場合は
//  アプリケーションウィンドウに対して操作する
//  メインフォームを作成したのはアプリケーションウィンドウ
//  したがって GetWindow(LhWindow, GW_OWNER) で取得できる
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LhWindow : HWND;
  LhAppWnd : HWND;
begin
  // 対象のウィンドウハンドル
  LhWindow := FindWindow('TTargetForm', nil);
  LhAppWnd := 0;
  if LhWindow <> 0 then begin
    LhAppWnd  := GetWindow(LhWindow, GW_OWNER);
  end;

  if LhAppWnd <> 0 then begin
    LhWindow := LhAppWnd;
  end;
  if LhWindow <> 0 then begin
    PostMessage(LhWindow, WM_SYSCOMMAND, SC_RESTORE, 0);

    // 前面に表示するなら、例えば
    SetForegroundWindow(LhWindow);
  end;
end;

end.


以下が操作対象の他のアプリのメインフォームのコードです。
子フォームはサイズを変更していますが、それ以外の変更はしていません。



リスト9
操作対象である他のアプリのメインフォームのコード。AppWinFix を使用していない
unit TargetUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TTargetForm = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  TargetForm: TTargetForm;

implementation

{$R *.dfm}

uses Unit1;

procedure TTargetForm.Button1Click(Sender: TObject);
begin
  TargetSubForm.Left := Self.Left + 100;
  TargetSubForm.Top := Self.Top + 100;
  TargetSubForm.WindowState := wsNormal;
  TargetSubForm.Show;
end;

procedure TTargetForm.Button2Click(Sender: TObject);
begin
   Self.WindowState := wsMinimized;
end;

end.