重新激活托盘中运行的应用程序的最佳方法是什么?

发布于 2024-09-10 09:32:00 字数 204 浏览 0 评论 0原文

我有一个 delphi 应用程序,它运行时最小化为托盘图标。双击托盘图标时,应用程序将打开一个非模式用户界面表单。

我已向应用程序添加了逻辑来检测它是否已经在运行。如果它没有运行,它会启动并将自身最小化到托盘中。

如果它已经在运行,我希望它将控制权传递给其自身的第一个实例并打开非模式窗体,然后退出(第二个实例)。最好的方法是什么?

TIA 右

I have a delphi app that runs minimized to a tray icon. When the tray icon is double clicked the app opens a non-modal user interface form.

I have added logic to the app to detect whether it is already running. If it isn't running, it starts up and miminizes itself to the tray.

If it is already running, I want it to pass control to the first instance of itself and open the non-modal form, and then exit (the second instance). What's the best way to do this?

TIA
R

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

白色秋天 2024-09-17 09:32:00

推荐检测另一个实例的方法给定的应用程序是让该应用程序创建一个命名互斥体或在众所周知的位置锁定文件,以便当您尝试创建相同的互斥体或锁定同一文件时,第二个实例将触发错误。一旦您知道有另一个实例正在运行,您就可以找到该实例的进程句柄,并向其发送一条消息以在其最小化时进行恢复。

The recommended method of detecting another instance of a given application is for that application to create a named mutex or lock a file in a well known location, so that the second instance will trigger an error when you try to create the same mutex or lock the same file. Once you know there's another instance running, you can find the process handle for that instance and send it a message to restore if its minimized.

宫墨修音 2024-09-17 09:32:00

微软的方式并不是完美无缺的,所以我更喜欢老派:

const WM_KNOCK_KNOCK = WM_USER + 42;
{ or WM_USER + 265 or any number you like, consult PSDK documentation why WM_USER range }
{ or do RegisterWindowMessage }

{...}

procedure TMainForm.FormCreate(Sender: TObject);
var
  Window: HWND;
begin 
  Window := FindWindow(PChar({MainForm.}ClassName), nil);
  { 
  i neither remember how it works exactly nor have time to investigate right now, 
  so quick and dirty validity test follows:
  }
  Assert(not (HandleAllocated and (Window = Handle)), 'failed, use fallback');
  {
  if Window <> 0 then
  begin
    PostMessage(Window, WM_KNOCK_KNOCK, 0, 0);
    Halt;
  end;

  { regular initialization }

end;

现在,第一个实例的 WM_KNOCK_KNOCK 消息处理程序执行唤醒例程。


当您在 Shell_NotifyIcon 包装器(Application.Restore,也许?)中收到 WM_LBUTTONUP (或者可能是 WM_LBUTTONDBLCLK)时,我几乎不知道您到底做了什么。正如 Chris Thornton 所说,不存在“最小化到托盘”这样的状态,它是人为的。


后备:如果断言失败,请注意哪些代码仅依赖于类函数 ClassName,因此可以轻松地从 FormCreate 中移出并在应用程序创建它之前调用。

Microsoft way is not flawless, so i do prefer old school:

const WM_KNOCK_KNOCK = WM_USER + 42;
{ or WM_USER + 265 or any number you like, consult PSDK documentation why WM_USER range }
{ or do RegisterWindowMessage }

{...}

procedure TMainForm.FormCreate(Sender: TObject);
var
  Window: HWND;
begin 
  Window := FindWindow(PChar({MainForm.}ClassName), nil);
  { 
  i neither remember how it works exactly nor have time to investigate right now, 
  so quick and dirty validity test follows:
  }
  Assert(not (HandleAllocated and (Window = Handle)), 'failed, use fallback');
  {
  if Window <> 0 then
  begin
    PostMessage(Window, WM_KNOCK_KNOCK, 0, 0);
    Halt;
  end;

  { regular initialization }

end;

Now, WM_KNOCK_KNOCK message handler of first instance performs wakeup routine.


i have little clue what exactly you do when you receive WM_LBUTTONUP (or perhaps WM_LBUTTONDBLCLK) in your Shell_NotifyIcon wrapper (Application.Restore, maybe?). As, Chris Thornton said, there is no such state as 'minimized to tray', it is artifical.


Fallback: if assertion fails, note what code depends only on class function ClassName so could be easily moved out of FormCreate and invoked before Application creates it.

醉城メ夜风 2024-09-17 09:32:00
program Only_One_Mutex;

//undefine this   {.$define useMutex} to make it a multi instance app.
{$define useMutex}

uses
  Forms,
  Windows,
  Messages,
  MainForm in 'MainForm.pas' {frmMain};

{$R *.res}

{$ifdef useMutex}
var
  Mutex : THandle;
{$endif}


function pBuffStr( Var S1: String; S:String ): PChar;
begin
  FillChar(S1,SizeOf(S1),#0); {clear out the destination string}
  S1:= S+#0;                  {set it equal the source}
  Result:= @S1[1];            {result is a PChar pointer }
end;

procedure WindowToTop( WN: String );
  var
    iTitle: integer;
    S1,S  : String;
    Done: Boolean;
begin
  Done:= False;
  While NOT Done do begin
    if Pos(';',WN) > 0 then begin
      S:= Copy(WN,1,Pos(';',WN)-1);
      WN:= Copy(WN,Pos(';',WN)+1,Length(WN));
    end else begin
      S:= WN;
      Done:= True;
    end; {if Pos}
    iTitle:= FindWindow( nil, pBuffStr(S1,S) );
    if iTitle <> 0 then
      if NOT SetForegroundWindow( iTitle ) then
        GetLastError();
    Application.ProcessMessages;
  end; {while NOT Done}
end;

procedure RestoreWindow( WN: String );
  var
    iTitle: integer;
    Dest, S  : String;
    Done: Boolean;
begin
  Done:= False;
  While NOT Done do begin
    if Pos(';',WN) > 0 then begin             {is there more than ONE name}
      S:= Copy(WN,1,Pos(';',WN)-1);           {copy the first name of the original}
      WN:= Copy(WN,Pos(';',WN)+1,Length(WN)); {reduce the original string}
    end else begin
      S:= WN;                                 {only one name, so copy it}
      Done:= True;                            {this loop is done}
    end; {if Pos}
    iTitle:= FindWindow( nil, pBuffStr(Dest,S) ); {search for the window name}
    if iTitle <> 0 then                           {if found, then restore it}
      DefWindowProc(iTitle, WM_SYSCOMMAND, SC_RESTORE, SC_RESTORE);
  end; {while NOT Done}
end;


//=================================================================

procedure AppRun;
begin
  Application.Initialize;
  Application.Title := 'Only One Prog';
  Application.CreateForm(TfrmMain, frmMain);
  Application.Run;
end;

begin

{$ifdef useMutex}
  //global var declarations in the mainform.
  {=====================================================================}
  //ATitle MUST match the assigned Application.Title in AppRun
  //and Application.Title can "NOT" be a constant or var.
  ATitle   := 'Only One Prog';

  { THIS IS HOW IT KEEPS THE SECOND INSTANCE FROM STARTING,
    by using a MUTEX, and a MAINFORM window title  }
  //any text appender will work.
  AMutex   := ATitle + ' Mutex Thu, Jul/12/2012';

  //mainform's caption
  ACaption := ATitle + ', Mainform Caption';

  //a label on the mainform
  ALabel   := ATitle + ', MainForm Label-using mutex';
  {=====================================================================}

  Mutex := CreateMutex(nil, True, PAnsiChar( AMutex ));
  if (GetLastError = ERROR_ALREADY_EXISTS) then begin
    try
      RestoreWindow( ACaption );
      WindowToTop( ACaption );       //main form's name
    finally
      CloseHandle(Mutex);
    end;
  end else
    if  (Mutex <> 0)
    AND (GetLastError <> ERROR_ALREADY_EXISTS)
    then begin
     try
       AppRun;
     finally
       CloseHandle(Mutex);
     end;
    end;
{$else}
  //global var declarations in the mainform.
  {=====================================================================}
  ATitle   := 'More than One';                  //global declaration in the mainform.
  //mainform's caption - THIS IS HOW IT KEEPS THE SECOND INSTANCE FROM STARTING
  ACaption := ATitle + ', Mainform Caption';//global declaration in the mainform.
  //a label on the mainform
  ALabel   := ATitle + ', MainForm Label-multi exe';  //global declaration in the mainform.
  {=====================================================================}
  AppRun;
{$endif}

end.


unit MainForm;

interface

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


type
  TfrmMain = class(TForm)
    le1: TLabelEffect;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

  //these GLOBAL vars, are assigned values in the program source (.dpr) file.
  ATitle,
  ACaption,
  ALabel,
  AMutex  :String;

implementation

{$R *.dfm}

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  Caption     := ACaption;     //used to ID this form...
  le1.Caption := ALabel;
end;

end.
program Only_One_Mutex;

//undefine this   {.$define useMutex} to make it a multi instance app.
{$define useMutex}

uses
  Forms,
  Windows,
  Messages,
  MainForm in 'MainForm.pas' {frmMain};

{$R *.res}

{$ifdef useMutex}
var
  Mutex : THandle;
{$endif}


function pBuffStr( Var S1: String; S:String ): PChar;
begin
  FillChar(S1,SizeOf(S1),#0); {clear out the destination string}
  S1:= S+#0;                  {set it equal the source}
  Result:= @S1[1];            {result is a PChar pointer }
end;

procedure WindowToTop( WN: String );
  var
    iTitle: integer;
    S1,S  : String;
    Done: Boolean;
begin
  Done:= False;
  While NOT Done do begin
    if Pos(';',WN) > 0 then begin
      S:= Copy(WN,1,Pos(';',WN)-1);
      WN:= Copy(WN,Pos(';',WN)+1,Length(WN));
    end else begin
      S:= WN;
      Done:= True;
    end; {if Pos}
    iTitle:= FindWindow( nil, pBuffStr(S1,S) );
    if iTitle <> 0 then
      if NOT SetForegroundWindow( iTitle ) then
        GetLastError();
    Application.ProcessMessages;
  end; {while NOT Done}
end;

procedure RestoreWindow( WN: String );
  var
    iTitle: integer;
    Dest, S  : String;
    Done: Boolean;
begin
  Done:= False;
  While NOT Done do begin
    if Pos(';',WN) > 0 then begin             {is there more than ONE name}
      S:= Copy(WN,1,Pos(';',WN)-1);           {copy the first name of the original}
      WN:= Copy(WN,Pos(';',WN)+1,Length(WN)); {reduce the original string}
    end else begin
      S:= WN;                                 {only one name, so copy it}
      Done:= True;                            {this loop is done}
    end; {if Pos}
    iTitle:= FindWindow( nil, pBuffStr(Dest,S) ); {search for the window name}
    if iTitle <> 0 then                           {if found, then restore it}
      DefWindowProc(iTitle, WM_SYSCOMMAND, SC_RESTORE, SC_RESTORE);
  end; {while NOT Done}
end;


//=================================================================

procedure AppRun;
begin
  Application.Initialize;
  Application.Title := 'Only One Prog';
  Application.CreateForm(TfrmMain, frmMain);
  Application.Run;
end;

begin

{$ifdef useMutex}
  //global var declarations in the mainform.
  {=====================================================================}
  //ATitle MUST match the assigned Application.Title in AppRun
  //and Application.Title can "NOT" be a constant or var.
  ATitle   := 'Only One Prog';

  { THIS IS HOW IT KEEPS THE SECOND INSTANCE FROM STARTING,
    by using a MUTEX, and a MAINFORM window title  }
  //any text appender will work.
  AMutex   := ATitle + ' Mutex Thu, Jul/12/2012';

  //mainform's caption
  ACaption := ATitle + ', Mainform Caption';

  //a label on the mainform
  ALabel   := ATitle + ', MainForm Label-using mutex';
  {=====================================================================}

  Mutex := CreateMutex(nil, True, PAnsiChar( AMutex ));
  if (GetLastError = ERROR_ALREADY_EXISTS) then begin
    try
      RestoreWindow( ACaption );
      WindowToTop( ACaption );       //main form's name
    finally
      CloseHandle(Mutex);
    end;
  end else
    if  (Mutex <> 0)
    AND (GetLastError <> ERROR_ALREADY_EXISTS)
    then begin
     try
       AppRun;
     finally
       CloseHandle(Mutex);
     end;
    end;
{$else}
  //global var declarations in the mainform.
  {=====================================================================}
  ATitle   := 'More than One';                  //global declaration in the mainform.
  //mainform's caption - THIS IS HOW IT KEEPS THE SECOND INSTANCE FROM STARTING
  ACaption := ATitle + ', Mainform Caption';//global declaration in the mainform.
  //a label on the mainform
  ALabel   := ATitle + ', MainForm Label-multi exe';  //global declaration in the mainform.
  {=====================================================================}
  AppRun;
{$endif}

end.


unit MainForm;

interface

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


type
  TfrmMain = class(TForm)
    le1: TLabelEffect;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

  //these GLOBAL vars, are assigned values in the program source (.dpr) file.
  ATitle,
  ACaption,
  ALabel,
  AMutex  :String;

implementation

{$R *.dfm}

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  Caption     := ACaption;     //used to ID this form...
  le1.Caption := ALabel;
end;

end.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文