Delphi: как правильно использовать ShowWindow во внешнем приложении [дубликат]
этот вопрос уже есть ответ здесь:
- как я могу сделать второй экземпляр моей программы передать управление обратно в первый экземпляр? 1 ответ
Читайте также:
как я могу сказать, если другой экземпляр программы уже запущен?
перед запуском приложения я использую следующий код, чтобы проверить, есть ли другой экземпляр из него уже начато:
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;
...
однако, если он запущен, мне нужно показать это приложение. Проблема заключается в том, что после того, как она показана таким образом, кнопка "свернуть" больше не работает, и когда я нажимаю значок на панели задач, он "unminimizes" и анимация, которая отображается, как будто она была свернута. Я что-то упускаю? есть ли правильный способ активировать и показать внешнее применение пока оно уменьшено?
4 ответов
вот полный проект, который продолжает работать только один экземпляр приложения, и которые должны принести уже запущено окно на передний план.
можно скачать testing project
или попробуйте код, который следует:
Проект1.ДНР
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 в это время
вы просите свою основную форму показать, но может произойти, что само скрытое окно приложения сворачивается, когда вы сворачиваете приложение на панель задач, в случае MainFormOnTaskBar
ложные.
не вызывайте метод ShowWindow из oustide. IMHO лучше, если вы передадите сообщение в приложение и ответите изнутри, вызвав Application.Restore
` метод, который выполняет правильные вызовы ShowWindow среди прочего.
Это очень распространенная проблема с приложениями VCL, и на протяжении многих лет на форумах Borland/CodeGear/Embarcadero задавали и отвечали много раз. Используя ShowWindow()
таким образом, не работает для окон VCL очень хорошо из-за того, как MainForm
взаимодействует с TApplication
объект во время выполнения, особенно в разных версиях Delphi. Вместо этого вы должны отправить второму экземпляру пользовательское сообщение в первый экземпляр, а затем позволить первому экземпляру восстановить сама по мере необходимости, когда он получает сообщение, например, установив его MainForm.WindowState
свойства или вызов Application.Restore()
и т. д., И пусть VCL разработает детали для вас, как предложил @jachguate.
следующее хорошо работает для меня. Я не на 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
is HWND
, а не THandle
.