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

Delphi 重新激活托盘中运行的应用程序的最佳方式是什么?,delphi,mutex,minimize,trayicon,invocation,Delphi,Mutex,Minimize,Trayicon,Invocation,我有一个delphi应用程序,运行最小化到一个托盘图标。双击托盘图标时,应用程序将打开一个非模式用户界面窗体 我已经为应用程序添加了逻辑,以检测它是否已经在运行。如果它没有运行,它会启动并模仿托盘 如果它已经在运行,我希望它将控制权传递给自己的第一个实例,打开非模态表单,然后退出(第二个实例)。最好的方法是什么 短暂性脑缺血发作 R检测给定应用程序的另一个实例的方法是,该应用程序创建一个命名的互斥体或将文件锁定在一个已知位置,这样,当您尝试创建相同的互斥体或锁定相同的文件时,第二个实例将触发错误

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

我已经为应用程序添加了逻辑,以检测它是否已经在运行。如果它没有运行,它会启动并模仿托盘

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

短暂性脑缺血发作
R

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

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

Microsoft way并非完美无瑕,因此我更喜欢老派:

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;
const WM\u KNOCK\u KNOCK=WM\u USER+42;
{或WM_用户+265或任何您喜欢的数字,请参阅PSDK文档为什么WM_用户范围}
{或不注册Windows消息}
{...}
程序TMAInformCreate(发送方:ToObject);
变量
窗口:HWND;
开始
Window:=FindWindow(PChar({MainForm.}ClassName),nil;
{ 
我现在既不记得它到底是怎么工作的也没有时间去调查,
因此,快速而肮脏的有效性测试如下:
}
断言(非(HandleAllocated和(Window=Handle)),“失败,使用回退”);
{
如果窗口为0,则
开始
PostMessage(窗口,WM_-KNOCK_-KNOCK,0,0);
停止
结束;
{常规初始化}
结束;
现在,第一个实例的WM_KNOCK_KNOCK消息处理程序执行唤醒例程


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



回退:如果断言失败,请注意什么代码只依赖于类函数
ClassName
,这样就可以很容易地从
FormCreate
中移出,并在应用程序创建它之前调用它。

Microsoft way并非完美无瑕,所以我更喜欢老派:

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;
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.
const WM\u KNOCK\u KNOCK=WM\u USER+42;
{或WM_用户+265或任何您喜欢的数字,请参阅PSDK文档为什么WM_用户范围}
{或不注册Windows消息}
{...}
程序TMAInformCreate(发送方:ToObject);
变量
窗口:HWND;
开始
Window:=FindWindow(PChar({MainForm.}ClassName),nil;
{ 
我现在既不记得它到底是怎么工作的也没有时间去调查,
因此,快速而肮脏的有效性测试如下:
}
断言(非(HandleAllocated和(Window=Handle)),“失败,使用回退”);
{
如果窗口为0,则
开始
PostMessage(窗口,WM_-KNOCK_-KNOCK,0,0);
停止
结束;
{常规初始化}
结束;
现在,第一个实例的WM_KNOCK_KNOCK消息处理程序执行唤醒例程


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


回退:如果断言失败,请注意什么代码只依赖于类函数
ClassName
,这样就可以很容易地从
FormCreate
中移出,并在应用程序创建它之前调用它。

只编程一个互斥体;
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.
//取消定义此{.$define useMutex}以使其成为多实例应用程序。 {$define useMutex} 使用 形式, 窗户, 信息, 'MainForm.pas'{frmMain}中的MainForm; {$R*.res} {$ifdef useMutex} 变量 互斥体:THandle; {$endif} 函数pBuffStr(变量S1:String;S:String):PChar; 开始 FillChar(S1,SizeOf(S1),#0){清除目标字符串} S1:=S+#0;{将其设置为等于源} 结果:=@S1[1];{结果是一个PChar指针} 结束; 过程WindowToTop(WN:String); 变量 iTitle:整数; S1,S:字符串; 完成:布尔; 开始 完成:=错误; 未完即开始 如果Pos(“;”,WN)>0,则开始 S:=副本(WN,1,位置(“;”,WN)-1); WN:=副本(WN,位置(“;”,WN)+1,长度(WN)); 结束,否则开始 S:=WN; 完成:=真; 结束;{if Pos} iTitle:=FindWindow(零,pBuffStr(S1,S)); 如果是0,那么 如果未设置ForeGroundIndow(iTitle),则 GetLastError(); Application.ProcessMessages; 结束;{未完成时} 结束; 过程RestoreWindow(WN:String); 变量 iTitle:整数; Dest,S:字符串; 完成:布尔; 开始 完成:=错误; 未完即开始 如果Pos(“;”,WN)>0,则开始{是否有多个名称} S:=复制(WN,1,Pos(“;”,WN)-1);{复制原始文件的名字} WN:=复制(WN,Pos(“;”,WN)+1,长度(WN));{减少原始字符串} 结束,否则开始 S:=WN;{只有一个名字,所以复制它} 完成:=True;{此循环已完成} 结束;{if Pos} iTitle:=FindWindow(nil,pBuffStr(Dest,S));{搜索窗口名} 如果iTitle 0,则{如果找到,则还原它} DefWindowProc(iTitle、WM_SYSCOMMAND、SC_RESTORE、SC_RESTORE); 结束;{未完成时} 结束; //================================================================= 程序批准; 开始 应用程序初始化; 应用程序名称:=“仅一个程序”; 阿普利克