6

参照:
プログラムの別のインスタンスが既に実行されているかどうかを確認するにはどうすればよいですか?

アプリケーションを開始する前に次のコードを使用して、別のインスタンスが既に開始されているかどうかを確認します。

var _PreviousHandle : THandle;
begin
  _PreviousHandle := FindWindow('TfrmMainForm',nil);
  if _PreviousHandle <> 0 then
  begin
    ShowMessage('Application "" is already running!');
    SetForegroundWindow(_PreviousHandle);
    ShowWindow(_PreviousHandle, SW_SHOW);
    Application.Terminate;
    Exit;
  end;
...

ただし、開始されている場合は、そのアプリケーションを表示する必要があります。問題は、このように表示された後、最小化ボタンが機能しなくなり、タスクバーのアイコンをクリックすると「最小化が解除」され、表示されるアニメーションが最小化されたようになります。何か不足していますか?最小化されているときに外部アプリケーションをアクティブにして表示する適切な方法はありますか?

4

4 に答える 4

6

これは、アプリケーションの 1 つのインスタンスのみを実行し続け、既に実行中のインスタンス ウィンドウを前面に表示する完全なプロジェクトです。

testing project次のコードをダウンロードするか、試すことができます。

Project1.dpr

program Project1;

uses
  Forms,
  Windows,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

var
  Mutex: THandle;
const
  AppID = '{0AEEDBAF-2643-4576-83B1-8C9422726E98}';
begin
  MessageID := RegisterWindowMessage(AppID);

  Mutex := CreateMutex(nil, False, AppID);
  if (Mutex <> 0) and (GetLastError = ERROR_ALREADY_EXISTS) then
  begin
    PostMessage(HWND_BROADCAST, MessageID, 0, 0);
    Exit;
  end;

  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Unit1.pas

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
  private
    function ForceForegroundWindow(WndHandle: HWND): Boolean;
    function ForceRestoreWindow(WndHandle: HWND; Immediate: Boolean): Boolean;
  protected
    procedure WndProc(var AMessage: TMessage); override;
  end;

var
  Form1: TForm1;
  MessageID: UINT;

implementation

{$R *.dfm}

{ TForm1 }

function TForm1.ForceForegroundWindow(WndHandle: HWND): Boolean;
var
  CurrThreadID: DWORD;
  ForeThreadID: DWORD;
begin
  Result := True;
  if (GetForegroundWindow <> WndHandle) then
  begin
    CurrThreadID := GetWindowThreadProcessId(WndHandle, nil);
    ForeThreadID := GetWindowThreadProcessId(GetForegroundWindow, nil);
    if (ForeThreadID <> CurrThreadID) then
    begin
      AttachThreadInput(ForeThreadID, CurrThreadID, True);
      Result := SetForegroundWindow(WndHandle);
      AttachThreadInput(ForeThreadID, CurrThreadID, False);
      if Result then
        Result := SetForegroundWindow(WndHandle);
    end
    else
      Result := SetForegroundWindow(WndHandle);
  end;
end;

function TForm1.ForceRestoreWindow(WndHandle: HWND;
  Immediate: Boolean): Boolean;
var
  WindowPlacement: TWindowPlacement;
begin
  Result := False;
  if Immediate then
  begin
    WindowPlacement.length := SizeOf(WindowPlacement);
    if GetWindowPlacement(WndHandle, @WindowPlacement) then
    begin
      if (WindowPlacement.flags and WPF_RESTORETOMAXIMIZED) <> 0 then
        WindowPlacement.showCmd := SW_MAXIMIZE
      else
        WindowPlacement.showCmd := SW_RESTORE;
      Result := SetWindowPlacement(WndHandle, @WindowPlacement);
    end;
  end
  else
    Result := SendMessage(WndHandle, WM_SYSCOMMAND, SC_RESTORE, 0) = 0;
end;

procedure TForm1.WndProc(var AMessage: TMessage);
begin
  inherited;
  if AMessage.Msg = MessageID then
  begin
    if IsIconic(Handle) then
      ForceRestoreWindow(Handle, True);
    ForceForegroundWindow(Application.Handle);
  end;
end;

end.

OS バージョンでテスト済み:

  • Windows 8.1 64 ビット
  • Windows 7 SP1 64 ビット ホーム プレミアム
  • Windows XP SP 3 32 ビット プロフェッショナル

既知の問題と制限:

  • MainFormOnTaskbarまったく考慮されていません。この時点で True に設定する必要があります
于 2013-01-21T15:00:35.513 に答える
4

MainFormOnTaskBarメイン フォームを表示するように要求していますが、 falseの場合、アプリケーションをタスク バーに最小化すると、アプリケーションの非表示ウィンドウ自体が最小化される場合があります。

外部から ShowWindow メソッドを呼び出さないでください。私見では、アプリケーションにメッセージを渡して内部から応答し、Application.Restore` メソッドを呼び出して、とりわけ適切な ShowWindow 呼び出しを実行する方が良いでしょう。

于 2013-01-21T18:38:31.517 に答える
3

これは VCL アプリでよくある問題であり、長年にわたって Borland/CodeGear/Embarcadero フォーラムで何度も質問され、回答されてきました。この方法での使用ShowWindow()は、VCL ウィンドウではうまく機能しません。これは、特に異なるバージョンの Delphi では、実行時に がオブジェクトとMainForm対話する方法が原因です。TApplication代わりにすべきことは、2 番目のインスタンスが最初のインスタンスにカスタム メッセージを送信するようにし、その後、最初のインスタンスがメッセージを受信したときに必要に応じてそれ自体を復元できるようにすることMainForm.WindowStateですApplication.Restore()。 @jachguateが提案したように、VCLが詳細を解決します。

于 2013-01-21T20:09:26.647 に答える
2

以下は私にとってはうまくいきます。ただし、質問を完全に理解しているとは 100% 確信が持てないので、間違っている場合はお知らせください。

var
  _PreviousHandle: HWND;
  WindowPlacement: TWindowPlacement;
....
WindowPlacement.length := SizeOf(WindowPlacement);
GetWindowPlacement(_PreviousHandle, WindowPlacement);
if WindowPlacement.flags and WPF_RESTORETOMAXIMIZED<>0 then
  WindowPlacement.showCmd := SW_MAXIMIZE
else
  WindowPlacement.showCmd := SW_RESTORE;
SetWindowPlacement(_PreviousHandle, WindowPlacement);
SetForegroundWindow(_PreviousHandle);

_PreviousHandleisHWNDと notの正しい型であることに注意してくださいTHandle

于 2013-01-21T14:32:27.917 に答える