Программирование на Delphi - обмен опытом / Блокирование запуска второго экземпляра программы

© Зайцев Олег 1998-2004
Лучшая портативная техника. Плееры Камеры Телефоны Компьютеры
Покупателям, пришедшим на www.porta.ru по этой ссылке - дополнительная скидка 1%
Железо | Система | WEB | Компоненты | Графика | Ссылки | Мультимедиа | Сети | Прочее | Реестр | Литература

Статистика

Блокирование запуска второго экземпляра программы

Рекомендую:
Главная страница \ Системное программирование \ Блокирование запуска второго экземпляра программы

  • Блокирование запуска второго экземпляра программы

    Блокирование запуска второго экземпляра программы

    Введение * * Задать вопрос Наверх
    Блокирование запуска нескольких экземпляров программы в ряде случаев бывает очень полезен. Реализовать его можно несколькими способами:

    Метод с использованием 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