Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Повторный запуск [D7, WinXP]


Страницы:   1 2

Volumer ©   (2005-07-12 19:42:00) 

Как можно отследить повторный запуск программы и передать фокус уже запущеной, а копию закрыть?


Eraser   (2005-07-14 15:16:00) [1]

Игорь Шевченко ©
использовать специализированное решение для каждого конкретного случая


Вот и нашли истину.


Игорь Шевченко   (2005-07-14 15:14:00) [2]

Eraser ©   (14.07.05 14:46) [24]


> Однако опять же бывает потребность передать командную строку
> в работающее приложение


Или не бывает. В данном случае, на мой взгляд, оптимальнее использовать специализированное решение для каждого конкретного случая.


Игорь Шевченко   (2005-07-14 15:14:00) [3]

Eraser ©   (14.07.05 14:46) [24]


> Однако опять же бывает потребность передать командную строку
> в работающее приложение


Или не бывает. В данном случае, на мой взгляд, оптимальнее использовать специализированное решение для каждого конкретного случая.


Eraser   (2005-07-14 14:46:00) [4]

Игорь Шевченко ©

Однако опять же бывает потребность передать командную строку в работающее приложение, и опять же прйдётся вставлять пользовательское сообщение.
___
Самый простой вариант - использовать компонент из JEDI ))


Игорь Шевченко   (2005-07-14 14:15:00) [5]

Alexander Panov ©   (14.07.05 14:08) [22]

Не вижу проблемы, если честно.


Alexander Panov   (2005-07-14 14:08:00) [6]

Игорь Шевченко ©   (14.07.05 14:02) [21]

Однако это не гарантирует от наличия окон одинакового класса с одним и тем же заголовком.


Игорь Шевченко   (2005-07-14 14:02:00) [7]

Alexander Panov ©   (14.07.05 13:24) [20]

Например так, как делал Borland с Delphi - искал окно класса TAppBuilder, находил, выводил его на передний план, новую копию закрывал.


Alexander Panov   (2005-07-14 13:24:00) [8]

Игорь Шевченко ©   (14.07.05 13:22) [19]
"Зачем делать сложно то, что проще простого" ?


Как?;)


Игорь Шевченко   (2005-07-14 13:22:00) [9]

Alexander Panov ©   (14.07.05 13:02) [18]

"Зачем делать сложно то, что проще простого" ?

(с) Наутилус помпилиус


Alexander Panov   (2005-07-14 13:02:00) [10]

Вот вариант без маппинга:


program UniqueApp;

uses
 Forms,
 windows,
 Messages,
 Unit1 in 'Unit1.pas' {Form1};

function ThrFunc(p: Pointer): Integer;
var
 FMsg: TMsg;
 FMsgNum: DWORD;
begin
 Result := 0;
 FMsgNum := RegisterWindowMessage(UniqueStr);
 PeekMessage(FMsg,0,0,0,PM_NOREMOVE);
 PostMessage(HWND_BROADCAST,FMsgNUm,GetCurrentThreadId,0);
 while GetMessage(FMsg,0,0,0) do
 begin
   if (FMsg.message=FMsgNum) or (FMsg.message=WM_QUIT) then Exit;
 end;
end;

function AlreadyExists: Boolean;
var
 Id: Cardinal;
 H: THandle;
 RC: Integer;
begin
 H := BeginThread(nil,0,ThrFunc,nil,0,Id);
 Result := False;
 try
   RC := WaitForSingleObject(H,500);
   if RC=WAIT_OBJECT_0 then Result := True;
 finally
   PostThreadMessage(Id,WM_QUIT,0,0);
 end;

end;

{$R *.res}

begin
 Application.Initialize;
 if AlreadyExists then Exit;
 Application.CreateForm(TForm1, Form1);
 Application.Run;
end.


unit Unit1;

interface

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

const
 UniqueStr='awdjhsdkfjhepqowuieofijfgkljhf';

type
 TForm1 = class(TForm)
   procedure FormCreate(Sender: TObject);
 private
   FMsgNum: DWORD;
   procedure AppMessage(var Msg: TMsg; var Handled: Boolean);
 public
   { Public declarations }
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
 if Msg.message = FMsgNum then
 begin
   Handled := True;
     PostThreadMessage(Msg.wParam,FMsgNum,0,0);
     SetForegroundWindow(Handle);
     Application.MessageBox(PChar(Format('Приложение %s уже запущено',[Application.Title])),
       'Внимание!',MB_OK or MB_SYSTEMMODAL or MB_ICONWARNING);
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 Msg: TMsg;
begin
 FMsgNum := RegisterWindowMessage(UniqueStr);
 PeekMessage(Msg,0,FMsgNum,FMsgNum,PM_REMOVE);
 Application.OnMessage := AppMessage;
end;

end.


Eraser   (2005-07-14 01:15:00) [11]

BiN ©   (13.07.05 10:50) [12]

Почему не событие? почему не любой другой именнованный объект ядра? или атом, например?


Моя вина, конечно же можно использовать любой именованый объект ядра, просто я исп. мьютекс.

WM_USER нельзя использовать для посылки оконных сообщений за пределами одного процесса.

Опять соглашусь. Но например такая программа, как винамп не придерживатся этого правила, хотя там исп. пользовательские сообщения для других целей.

Нужно ли объяснять, что текст и имя класса не могут служить идентификатором окна, не говоря уже о том, что перебор окон (FindWindow, EnumWindows и т.п.) - не лучший пример достижения производительности.

Как выход - использовать broadcast рассылку.

Почему-то во всех виденных мной FAQ-ах отсутствует вполне логичный и простой способ:

А где вывод окна на передний план? )


VKSam   (2005-07-13 19:43:00) [12]

Я немного не понял. Тебе надо просто перезапустить программу из самой себя?


y-soft   (2005-07-13 17:40:00) [13]

>Alexander Panov ©   (12.07.05 21:22) [9]

К сожалению, это будет работать только в системе  не выше W98.

А для показа top-окна приложения можно использовать

ShowWindow(Wnd, SW_SHOWNORMAL);
SetForegroundWindow(Wnd);


Привет, Саша!

Да уж, намудрила M$ с SetForegroundWindow...
Ни один из способов, описанных в

http://www.rsdn.ru/article/qna/ui/wndsetfg.xml?print

и даже

http://www.rsdn.ru/?Forum/Info.aspx?name=FAQ.winapi.wndsetfg

не дает 100% гарантии успеха во всех версиях Windows... :(

Но если дополнить эти вызовы следующим:

  Application.MessageBox(PChar(Format('Приложение %s уже запущено',[Application.Title])),
     'Внимание!',MB_OK or MB_SYSTEMMODAL or MB_ICONWARNING);



то после нажатия пользователем кнопки "ОК" все будет выводиться на передний план даже в WinXP SP2...

Если скомбинировать с подходом BiN ©   (13.07.05 10:50) [12], то очень даже неплохо получается...


BiN   (2005-07-13 14:06:00) [14]

BiN ©   (13.07.05 10:50) [12]

Этот пример, правда, напрашивается на переполнение буфера:
CopyMemory(@(lpCustomData^.Buffer), @Paramstr(1)[1], Length(Paramstr(1)))

но кому нужно будет, тот пусть исправит сам.


y-soft   (2005-07-13 13:57:00) [15]

>BiN ©   (13.07.05 10:50) [12]

Способ действительно красивый и продуманный...

Почему-то во всех виденных мной FAQ-ах отсутствует вполне логичный и простой способ

В FAQ'ах действительно практически не встречается (там почему-то любят FindWidow), но вообще подобный подход используется довольно часто, кое-где в инете есть статьи (но не на русском и не для для Delphi). Например, нечто подобное используется для управления плейером Foobar2000 через параметры командной строки...


BiN   (2005-07-13 10:50:00) [16]

Eraser ©   (13.07.05 02:05) [11]
Ну зачем в очередной раз изобретать двухколёсное трансп. средство? Не подскажите?
Почти во всех FAQ есть примеры, через гугл точно найти можно.



Приведенный тобой метод имеет ряд существенных слабых мест, и вот почему:

1. Создаётся именованый мьютекс (не событие!).


Почему не событие? почему не любой другой именнованный объект ядра? или атом, например?


2. Параметры запуска передаются через пользовательское сообщение (напр. WM_USER + 1234).


WM_USER нельзя использовать для посылки оконных сообщений за пределами одного процесса. Для этого нужно использовать RegisterWindowMessage. Ну да ладно, с помощью пользовательского сообщения нельзя передать между процессами данные длиной больше 8-ми байт, имя файла, например, как будем передавать? Тогда уж лучше использовать WM_COPYDATA, но какому окну будем посылать сообщение? см. следующий пункт.


3. Окно с уже запущеной копией передвигается на передний план (полно примеров в сети).


Нужно ли объяснять, что текст и имя класса не могут служить идентификатором окна, не говоря уже о том, что перебор окон (FindWindow, EnumWindows и т.п.) - не лучший пример достижения производительности.

Почему-то во всех виденных мной FAQ-ах отсутствует вполне логичный и простой способ:


////Unit1.pas

unit Unit1;

interface

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

type
 TForm1 = class(TForm)
 protected
   procedure WndProc(var Message: TMessage); override;
 private
   { Private declarations }
 public
   { Public declarations }
 end;

const
 DATA_SIZE = 1024;

type
 TCustomData = packed record
   ClientHandle: THandle;
   Buffer: array [0..DATA_SIZE-SizeOf(THandle)-1] of char;
 end;
 PCustomData = ^TCustomData;

const
 UniqueName = 'AppNotificationNameSpace';

var
 Form1: TForm1;
 hSection: DWORD;
 NewInstMessageValue: DWORD;
 lpCustomData: PCustomData;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.WndProc(var Message: TMessage);
begin
 inherited;
 if Message.Msg=NewInstMessageValue then
 begin
   ShowMessage(Format('New instance running: PID: %d, TID: %d, Param: %s',
               [Message.WParam, Message.LParam, Pchar(@(lpCustomData.Buffer))]));
   ZeroMemory(@(lpCustomData^.Buffer), SizeOf(lpCustomData^.Buffer));
 end;
end;

end.

///Project1.dpr

program Project1;

uses
 windows,
 SysUtils,
 Forms,
 Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

var
 InstExists: Boolean;
begin
 NewInstMessageValue:=RegisterWindowMessage(Pchar(UniqueName+'msg'));
 if NewInstMessageValue=0 then
   RaiseLastOSError;
 hSection:=CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, DATA_SIZE, Pchar(UniqueName+'map'));
 if hSection=0 then
   RaiseLastOSError();
 InstExists:=GetLastError=ERROR_ALREADY_EXISTS;
 try
   lpCustomData:=MapViewOfFile(hSection, FILE_MAP_ALL_ACCESS, 0, 0, DATA_SIZE);
   if lpCustomData=nil then
     RaiseLastOSError;
   if InstExists then
   begin
     if (ParamCount>0) and (Length(ParamStr(1))>0) then
       CopyMemory(@(lpCustomData^.Buffer), @Paramstr(1)[1], Length(Paramstr(1)));
     SendMessage(lpCustomData^.ClientHandle, NewInstMessageValue, GetCurrentProcessId, GetCurrentThreadId);
   end
   else
   begin
     Application.Initialize;
     Application.CreateForm(TForm1, Form1);

     ZeroMemory(lpCustomData, DATA_SIZE);
     lpCustomData.ClientHandle:= Form1.Handle;
     Application.Run;
   end;
 finally
   if lpCustomData<>nil then
     UnmapViewOfFile(lpCustomData);
   CloseHandle(hSection);
 end;
end.


Eraser   (2005-07-13 02:05:00) [17]

Ну зачем в очередной раз изобретать двухколёсное трансп. средство? Не подскажите?
Почти во всех FAQ есть примеры, через гугл точно найти можно.
___
1. Создаётся именованый мьютекс(не событие!).
2. Параметры запуска передаются через пользовательское сообщение (напр. WM_USER + 1234).
3. Окно с уже запущеной копией передвигается на передний план (полно примеров в сети).


Квэнди   (2005-07-12 21:25:00) [18]

Согласен, об этом не подумал... окончание рабочего дня сказывается...(


Alexander Panov   (2005-07-12 21:22:00) [19]

Квэнди ©   (12.07.05 21:04) [8]

К сожалению, это будет работать только в системе  не выше W98.

А для показа top-окна приложения можно использовать

 ShowWindow(Wnd, SW_SHOWNORMAL);
 SetForegroundWindow(Wnd);


Квэнди   (2005-07-12 21:04:00) [20]

Квэнди ©   (12.07.05 19:55) [3]

Попробуй так свернутое окно?


Согласен, виноват...

procedure TForm1.FormCreate(Sender: TObject);
begin
 if hPrevInst <> 0 then begin
   MessageDlg('Программа уже запущена!', mtError, [mbOk], 0);
   Application.Terminate;
 end;
end;


или

procedure TForm1.FormCreate(Sender: TObject);
var
 Wnd : hWnd;
 buff : array[0.. 127] of Char;
begin
 Wnd := GetWindow(Handle, gw_HWndFirst);
 while Wnd <> 0 do begin
   if (Wnd <> Application.Handle) and (GetWindow(Wnd, gw_Owner) = 0) then
   begin
     GetWindowText (Wnd, buff, sizeof (buff ));
     if StrPas (buff) = Application.Title then
     begin
       MessageDlg('Приложение уже загружено', mtWarning, [mbOk], 0);
       Halt;
     end;
   end;
   Wnd := GetWindow (Wnd, gw_hWndNext);
 end;
end;


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо