【问题标题】:How can I make the second instance of my program pass control back to the first instance?如何让我的程序的第二个实例将控制权传递回第一个实例?
【发布时间】:2014-03-25 14:42:39
【问题描述】:

我已经用 Delphi XE3 创建了一个应用程序。 我的应用程序有一个托盘图标(我为此使用 TCoolTrayIcon),所以当用户最小化它时,任务栏上没有图标,而只有托盘图标。

为了避免我的应用程序不止一个,我使用以下代码:

procedure CreateMutexes(const MutexName: String);
const
  SECURITY_DESCRIPTOR_REVISION = 1;
var
  SecurityDesc: TSecurityDescriptor;
  SecurityAttr: TSecurityAttributes;
  MutexHandle: THandle;
begin
  InitializeSecurityDescriptor(@SecurityDesc, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(@SecurityDesc, True, nil, False);
  SecurityAttr.nLength := SizeOf(SecurityAttr);
  SecurityAttr.lpSecurityDescriptor := @SecurityDesc;
  SecurityAttr.bInheritHandle := False;
  MutexHandle := CreateMutex(@SecurityAttr, False, PChar(MutexName));

  if MutexHandle <> 0 then
    begin
      if GetLastError = ERROR_ALREADY_EXISTS then
        begin
          MessageBox(0, 'You cannot start more than one instance of ContLab.'
                      + #13#10 + 'Use the instance has already started.',
                       'ContLab', mb_IconHand);

          CloseHandle(MutexHandle);
          Halt;
        end
    end;

  CreateMutex(@SecurityAttr, False, PChar('Global\' + MutexName));
end;

这样,当用户启动应用程序 2 次时,他会收到一条错误消息,并且第二个实例被终止。

现在我不想显示错误消息,而是打开应用程序第一个实例的主窗体并终止第二个实例。

有可能吗?

【问题讨论】:

  • 是的,当然可以。你真正的问题是什么?
  • 发现了一个骗子:stackoverflow.com/a/460480/1970843
  • @KenWhite,您的评论是对 GabrielF 评论的欺骗 :-) 无论如何,这个问题是关于如何打开第一个实例的主要形式,而链接的问题询问如何检测另一个应用程序是否正在运行.
  • 我同意@Lurd。尽管假定的副本包含对此问题的答案,但它们是单独的问题。另一个问题的答案是范围蔓延。它回答了合乎逻辑的下一个问题(这个),但不是特别很好。最好将这两个任务放在单独的问题中。这就是我投票重新提出这个问题的原因。

标签: delphi


【解决方案1】:

您需要向其他应用程序发送一条消息以请求它显示自己。

首先你需要找到另一个应用程序的主窗口。有很多方法可以做到这一点。例如,您可以使用FindWindow。或者您可以使用EnumWindows 枚举顶级窗口。通常,您会检查匹配的窗口文本和/或类名。

一旦你找到了另一个实例的主窗口,你需要让它能够将自己设置为前景窗口。您需要致电AllowSetForegroundWindow

var
  pid: DWORD;
....
GetWindowThreadProcessId(hwndOtherInstance, pid);
AllowSetForegroundWindow(pid);

然后向窗口发送用户定义的消息。例如:

const
  WM_RESTOREWINDOW = WM_APP;
....
SendMessage(hwndOtherInstance, WM_RESTOREWINDOW, 0, 0);

最后,您的其他实例的主窗体需要监听此消息。

type
  TMainForm = class(TForm)
  ....
  protected
    procedure WMRestoreWindow(var Message: TMessage); message WM_RESTOREWINDOW;
  ....
  end;

当它遇到消息时它必须这样做:

procedure TMainForm.WMRestoreWindow(var Message: TMessage);
begin
  inherited;
  Visible := True;
  Application.Restore;
  Application.BringToFront;
end;

我对您的互斥锁处理代码有点怀疑。我不明白对安全属性的需求,因为您是在本地命名空间中创建它。但随后我看到对CreateMutex 的第二次调用忽略了返回值,但在全局命名空间中创建了一个对象。

【讨论】:

  • 您不需要用户定义的消息机制。您只需要调用 ShowWindow 和 SetForegroundWindow 到第一个实例的句柄(我已将所有代码链接到一个骗子)。这个解决方案太复杂了。
  • @GabrielF 感谢 cmets。我想你不认为他们是迂腐的。我知道我没有。
  • 删除了我的答案,您向第一个应用程序发送消息的方式是要走的路。
  • @LURD 你能解释一下你的答案有什么问题吗?
  • @TLama、TrayIcon 和 oneinstance 一直很麻烦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-22
  • 2020-04-29
  • 1970-01-01
相关资源
最近更新 更多