Форум: "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.016 c