另见:
How can I tell if another instance of my program is already running?
我在启动应用程序之前使用以下代码,以检查是否有其他实例 已经开始了:
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;
...
但是,如果它已经启动,我需要显示该应用程序。问题是以这种方式显示最小化按钮不再起作用,当我单击任务栏中的图标时,它“取消最小化”,并且显示的动画就像它被最小化一样。我错过了什么吗?有什么方法可以在最小化时激活和显示外部应用程序吗?
答案 0 :(得分:6)
这是一个完整的项目,它只运行一个应用程序实例,并且应该已经运行了实例窗口。
您可以下载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.
在操作系统版本上测试:
已知问题和限制:
MainFormOnTaskbar
完全没有考虑在内;它必须在此时设置为True 答案 1 :(得分:4)
您要求显示主窗体,但如果MainFormOnTaskBar
为假,则最小化应用程序隐藏窗口本身可能会最小化。
不要从oustide调用ShowWindow方法。恕我直言,如果你将消息传递给应用程序并从内部响应,调用Application.Restore
`方法,它执行正确的ShowWindow调用等等,这样会更好。
答案 2 :(得分:3)
这是VCL应用程序的一个非常常见的问题,多年来在Borland / CodeGear / Embarcadero论坛中被多次询问和回答。以这种方式使用ShowWindow()
对VCL窗口不起作用,因为MainForm
在运行时与TApplication
对象交互的方式,特别是在不同版本的Delphi中。您应该做的是让第二个实例向第一个实例发送自定义消息,然后让第一个实例在收到消息时根据需要自行恢复,例如通过设置其MainForm.WindowState
属性或调用{ {1}}等等,让VCL为你解决细节问题,比如@jachguate建议。
答案 3 :(得分: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);
请注意,_PreviousHandle
的正确类型为HWND
,而非THandle
。