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

这是一个完整的项目,它只继续运行应用程序的一个实例,并且应该将已经运行的实例窗口放在前面。

您可以下载testing project或尝试以下代码:

项目1.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.

单元1.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.

在操作系统版本上测试:

  • Windows 8.1 64 位
  • Windows 7 SP1 64 位家庭高级版
  • Windows XP SP 3 32 位专业版

已知问题和限制:

  • MainFormOnTaskbar根本不考虑;此时必须设置为True
于 2013-01-21T15:00:35.513 回答
4

您要求显示主窗体,但是当您将应用程序最小化到任务栏时,可能会出现应用程序隐藏窗口本身被最小化的情况,以防万一MainFormOnTaskBar

不要从外部调用 ShowWindow 方法。恕我直言,最好将消息传递给应用程序并从内部响应,调用Application.Restore` 方法,该方法执行正确的 ShowWindow 调用等。

于 2013-01-21T18:38:31.517 回答
3

这是 VCL 应用程序中非常常见的问题,多年来在 Borland/CodeGear/Embarcadero 论坛中被多次询问和回答。由于在运行时与对象交互的方式,特别是在不同版本的 Delphi 中,ShowWindow()以这种方式使用VCL 窗口时效果不佳。您应该做的是让第二个实例向第一个实例发送自定义消息,然后让第一个实例在收到消息时根据需要恢复自身,例如通过设置其属性或调用等,并让VCL 为您制定详细信息,就像@jachguate 建议的那样。MainFormTApplicationMainForm.WindowStateApplication.Restore()

于 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 回答