Блокирование запуска второго экземпляра программы Блокирование запуска второго экземпляра программы
Введение |
*
| *
|
| |
Блокирование запуска нескольких экземпляров программы в ряде случаев
бывает очень полезен. Реализовать его можно несколькими способами:
- Поиск экземпляра приложения (путем анализа запущенных процессов, видимых окон ..)
- Создания некоторого именованного глобального системного объекта (файла в памяти,
Mutex, Event) при запуске программы.
- Рассылка широковещательных сообщений, на которые должны отвечать все экземпляры
приложения кроме того, которое рассылает сообщение - получение ответа является сигналом
о том, что в памяти есть хотя бы один запущенный экземпляр
Метод с использованием hPrevInst |
D1
| *
|
| |
Каждый экземпляр программы имеет ссылку на свою предыдущую копию - hPrevInst: hWnd.
Ее можно проверить перед созданием приложения и при необходимости отреагировать
соответствующим образом. Если запущена только одна копия, то эта ссылка равна
нулю.
Только для Delphi 1.
Пример использования hPrevInst:
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;
Данный пример не всегда применим - часто заголовок приложения меняется при каждом старте,
поэтому рассмотрим более надежный способ - через FileMapping
Дело в том, что можно в памяти создавать временные файлы. При перезагрузке они
теряются, а так существуют. Кстати, этот метод можно использовать и для обмена
информацией между вашими приложениями.
Кроме приведенного выше способа реализации есть еще более простая (и столь же
некорректная) реализация:
hwnd := FindWindow('TForm1', 'Form1');
Идея та-же - если окно класса TForm1 с именем Form1 обнаружено, то hwnd будет
содержать его Handle. Только где гарантия, что у другого приложения не будет такого
окна ...
Метод с использованием FileMapping |
*
| *
|
| |
Это один из наиболее корректных способов, известных и опробованных мной на
настроящий момент. Способ основан на том, что в системе может существовать
только один объект типа FileMapping (образмфайла в памяти) с заданным именем.
В момент запуска приложение пытается создать FileMapping с именем , униканьным
для приложения разработчика - в случае возникновени ошибки ERROR_ALREADY_EXISTS
можно сделать вывод, что экземпляр приложения уже запущен.
Пример реализации:
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.
Метод с использованием Mutex |
*
| *
|
| |
Данный метод аналогичен методу, основанному на применении FileMapping -
метод основан на том, что в системе одновременно может существовать только
один Mutex с заданным именем.
Пример реализации:
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.
В Интернет можно встретить реализацию данного способа без OpenMutex - в этой
реализации после вызова CreateMutex производится проверка, не вернул ли он
ERROR_ALREADY_EXISTS. Этот метод не срабатывает на XP (проверено).
Метод с использованием Event |
*
| *
|
| |
Данный метод аналогичен методу, основанному на применении Mutex -
метод основан на том, что в системе одновременно может существовать только
один event с заданным именем.
Пример реализации:
program Project1;
uses
Forms,
Windows, // Обязательно !!
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
var
EventHandle : THandle;
const
EventName = 'one_inst_demo_event';
begin
// Пробуем открыть Event по имени
EventHandle := OpenEvent(EVENT_ALL_ACCESS, false, EventName);
if EventHandle <> 0 then begin
// Копия нашего приложения уже запущена - Event уже есть
CloseHandle(EventHandle);
halt;
end;
// Создаем Event
EventHandle := CreateEvent(nil, false, false, EventName);
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
// Уничтожаем наш Event при завершении приложения
CloseHandle(EventHandle);
end.
© Зайцев Олег, "Программирование на Delphi - обмен опытом" 1999-2004. При использовании любых материалов данного сайта
необходимо указывать источник информации. Дата обновления: 22.11.2004. Сайт размещен на хостинге AGAVA - Хостинг от AGAVA.ru