2010-07-20 7 views
1

У меня есть приложение delphi, которое работает с минимальным значком в трее. Когда значок в трее дважды щелкнут, приложение открывает форму немодального пользовательского интерфейса.Каков наилучший способ реактивировать приложение, работающее в лотке?

Я добавил логику в приложение, чтобы определить, запущен ли он. Если он не запущен, он запускается и мимизирует себя в лоток.

Если он уже запущен, я хочу, чтобы он передал управление самому первому экземпляру и открыл немодальную форму, а затем вышел (второй экземпляр). Каков наилучший способ сделать это?

ТИА R

+2

Действительно не имеет ничего общего с системным лотком. Решение такое же, если это просто регулярное окно, сведено к минимуму. Или открыть, но похоронить за другими окнами. –

ответ

2

Microsoft способ не безупречен, поэтому я предпочитаю старую школу:

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 обработчик сообщений первой инстанции выполняет процедуру пробуждения.


я мало понятия, что именно вы делаете, когда вы получаете WM_LBUTTONUP (или, возможно, WM_LBUTTONDBLCLK) в вашем Shell_NotifyIcon обертку (Application.Restore, может быть?). Как сказал Крис Торнтон, нет такого состояния, как «сведено к минимуму», это искусственно.


Запасной: если утверждение не будет успешным, какой код зависит только от класса функции ClassName так может быть легко перемещен из FormCreate и ссылаться Application создает его.

+0

Практически там. Я обрабатываю сообщение в первом экземпляре, показываю форму, вызываю BringToFront, SetFocus, но я не могу получить форму, чтобы показать ее как самую верхнюю, и не фокусировать ее. – rossmcm

+0

И почему 42? Дуглас Адамс? – rossmcm

+0

Сообщения жесткого кодирования как wm_user + - это хороший способ вставить себя в – Jherico

5

Метод обнаружения другого экземпляра данного приложения recommended для этого приложения, чтобы создать именованный мьютекс или заблокировать файл в хорошо известном месте, так что второй экземпляр вызовет ошибку когда вы пытаетесь создать один и тот же мьютекс или заблокируете тот же файл. Как только вы узнаете, что есть еще один экземпляр, вы можете найти дескриптор процесса для этого экземпляра и отправить ему сообщение для восстановления, если его свести к минимуму.

+1

Убедитесь, что вы вызываете AllowSetForegroundWindow перед тем, как перенести другой экземпляр на передний план. – glob

+0

Я не могу найти, где объявлено AllowSetForegroundWindow ...? – rossmcm

+0

Не будет ли названное событие (объект события Windows, а не метод событий Delphi) лучшим выбором, чем мьютекс? Его можно использовать для сигнализации запущенного приложения, а также для его обнаружения. – dummzeuch

1
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.