Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "WinAPI";
Текущий архив: 2005.09.04;
Скачать: [xml.tar.bz2];

Вниз

Повторный запуск   Найти похожие ветки 

 
Volumer   (2005-07-12 19:42) [0]

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


 
Starcom   (2005-07-12 19:50) [1]

program Project1;
uses
 Windows, // Обязательно
 Forms,
 Unit1 in "Unit1.pas" {Form1};

{$R *.RES}
Const
MemFileSize = 1024;
MemFileName = "one_inst_demo_memfile";
Var
MemHnd : HWND;
begin
 // Попытаемся создать файл в памяти
 MemHnd := CreateFileMapping(HWND($FFFFFFFF),
                             nil,
                             PAGE_READWRITE,
                             0,
                             MemFileSize,
                             MemFileName);
 // Если файл не существовал запускаем приложение
 if GetLastError<>ERROR_ALREADY_EXISTS then
 begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
 end;
 CloseHandle(MemHnd);
end.


 
Fay ©   (2005-07-12 19:52) [2]

2 Starcom   (12.07.05 19:50) [1]
// Попытаемся создать файл в памяти
Интересное заявление 8)


 
Квэнди ©   (2005-07-12 19:55) [3]

program Project1;

uses
 Forms,
 Windows,
 Unit1 in "Unit1.pas" {Form1};

{$R *.RES}

var
 hwnd: THandle;

begin
 hwnd := FindWindow("TForm1", "Form1");
 if hwnd = 0 then
 begin
   Application.Initialize;
   Application.CreateForm(TForm1, Form1);
   Application.Run;
 end
 else
   SetForegroundWindow(hwnd)
end.


 
Poirot ©   (2005-07-12 20:02) [4]

А искать видимо не научили или плохи читали то, что написано перед тем как новый топик создавать?:)) Целая статья тут есть на эту тему:) гы:) и LOL :)


 
Starcom   (2005-07-12 20:05) [5]

То Цвай!

program Project1;

uses
 Forms,
 Windows, // Обязательно !!
 Unit1 in "Unit1.pas" {Form1};

{$R *.res}
var
MutexHandle : THandle;
const
MutexName = "one_inst_demo_mutex";
begin
 // Пробуем открыть Mutex по имени
 MutexHandle := OpenMutex(MUTEX_ALL_ACCESS, false, MutexName);
 if MutexHandle <> 0 then begin
  // Копия нашего приложения уже запущена - Mutex уже есть
  CloseHandle(MutexHandle);
  halt;
 end;
 // Создание Mutex
 MutexHandle := CreateMutex(nil, false, MutexName);
 Application.Initialize;
 Application.CreateForm(TForm1, Form1);
 Application.Run;
 // Уничтожаем наш Mutex при завершении приложения
 CloseHandle(MutexHandle);
end.


 
Alexander Panov ©   (2005-07-12 20:46) [6]

Starcom   (12.07.05 19:50) [1]
Starcom   (12.07.05 20:05) [5]

Нет перевода окна в активное состояние.

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

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


 
BiN ©   (2005-07-12 20:59) [7]

Имхо, самый верный ответ - в посте [1].
Только я бы еще в эту секцию записал значение хэндла окна, которому нужно передать фокус в сабжевой ситуации.


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

Квэнди ©   (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;


 
Alexander Panov ©   (2005-07-12 21:22) [9]

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

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

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

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


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

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


 
Eraser ©   (2005-07-13 02:05) [11]

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


 
BiN ©   (2005-07-13 10:50) [12]

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.



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

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

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

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

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


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

BiN ©   (13.07.05 10:50) [12]

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

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


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

>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], то очень даже неплохо получается...


 
VKSam ©   (2005-07-13 19:43) [16]

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


 
Eraser ©   (2005-07-14 01:15) [17]

BiN ©   (13.07.05 10:50) [12]

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


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

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

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

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

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

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

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


 
Alexander Panov ©   (2005-07-14 13:02) [18]

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


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.


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

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

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

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


 
Alexander Panov ©   (2005-07-14 13:24) [20]

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


Как?;)


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

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

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


 
Alexander Panov ©   (2005-07-14 14:08) [22]

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

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


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

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

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


 
Eraser ©   (2005-07-14 14:46) [24]

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

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


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

Eraser ©   (14.07.05 14:46) [24]


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


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


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

Eraser ©   (14.07.05 14:46) [24]


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


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


 
Eraser ©   (2005-07-14 15:16) [27]

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


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



Страницы: 1 вся ветка

Форум: "WinAPI";
Текущий архив: 2005.09.04;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.02 c
1-1123661183
Andry
2005-08-10 12:06
2005.09.04
Размер шрифта


2-1070652959
Арс
2003-12-05 22:35
2005.09.04
Помощь


14-1123448767
LoGeen
2005-08-08 01:06
2005.09.04
Пользование клиентом форума


3-1122012785
Hursand
2005-07-22 10:13
2005.09.04
displaytext


14-1123411063
Gamer
2005-08-07 14:37
2005.09.04
Трудности перевода





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский