Как определить, что приложение уже запущено?

Модераторы: Duncon, Naeel Maqsudov, Игорь Акопян, Хыиуду

BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Здравствуйте!
Не подскажете, как из Delphi-йной программы можно определить, что другая копия этой программы на данном компьютере уже запущена?

Попытался использовать функция FindWindow (как это делается в "клссических" примерах по Си). Делал вызов этой функции сразу после begin (т.е. даже ДО Application.Initialize), указав в качестве имени класса окна имя формы приложения (здесь трудностей не было, имя класса "подсмотрел" через микрософтовский Spy). Но функция "ответила", что такое окно уже существует! Откуда сделал вывод, что "где-то там, внутри" окно уже существует, и приложение нашло "само себя"...

Заранее спасибо.
Аватара пользователя
Duncon
Сообщения: 2085
Зарегистрирован: 10 окт 2004, 14:11
Откуда: Питер
Контактная информация:

Код: Выделить всё

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.
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Duncon, спасибо. Но ты уверен, что ЭТО работает?
Ранее я практически именно так и делаел, лишь вторым параметром в FindWindow передавал NIL (в Си я поступал так же).

Попробовал указывать и второй параметр (как в твоем примере).
Я так понял, что туда ты передавал атрибут Caption, указанный в свойствах формы (у меня заголовок окна изменен со стандартного - в твоем примере "Form1" на другой - "Кандидаты").

Результат - тот же самый. FindWindow возвращает НЕНУЛЕВОЕ значение.

Пробовал даже вынести вызов FindWindow в отдельный unit, в инициализационную часть и подключить этот Unit в списке uses ДО Forms, предположив, что так проверка вызовется до инициализации unit-а Forms. Но не помогло. Все равно FindWindow что-то находит. :(
Blood_Magic
Сообщения: 273
Зарегистрирован: 30 июн 2005, 14:53

Duncon писал(а):

Код: Выделить всё

program Project1][/quote]

не сработает если резко запустить 2 раза
- Чем юзер похож на обезьяну?
- Он жмет на все, что жмется, дергает все, что дергается и крутит все, что крутится.
- Чем юзер отличается от обезьяны?
- У обезьяны хватает ума не воспроизводить ту последовательность, которая приводит к краху системы.
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

Обычно для этой цели используют мьютекс или др.объекты синхронизации.
См. пример: http://www.akzhan.midi.ru/win32api/26.htm
Аватара пользователя
Duncon
Сообщения: 2085
Зарегистрирован: 10 окт 2004, 14:11
Откуда: Питер
Контактная информация:

Вот еще вариант

Код: Выделить всё

begin
  if HPrevInst <>0 then
  begin
    ActivatePreviousInstance;
    Halt;
  end;
end;
Или у кого притензии может вам так проще будет....

Код: Выделить всё

unit FirstHinstanceRunning;

interface

uses
  Windows,
  Forms,
  StrUtils,
  SysUtils;

function FirstHinstanceRunning(RunMode: Integer = 0): boolean;

implementation

function FirstHinstanceRunning(RunMode: Integer = 0): boolean;
const
  MemFileSize = 127;

var
  MemHnd: HWND;
  MemFileName: string;
  lpBaseAddress: ^HWND;
  FirstAppHandle: HWND;

begin
  Result := False;
  MemFileName := Application.ExeName;
  case RunMode of
    0:
      MemFileName := AnsiReplaceText(MemFileName, '\', '/');
    1:
      MemFileName := ExtractFileName(MemFileName);
  else
    Exit;
  end;
  //если FileMapping есть - то происходит OpenFileMapping
  MemHnd := CreateFileMapping(HWND($FFFFFFFF), nil,
    PAGE_READWRITE, 0, MemFileSize, PChar(MemFileName));
  if GetLastError <> ERROR_ALREADY_EXISTS then
  begin
    if MemHnd <> 0 then
    begin
      lpBaseAddress := MapViewOfFile(MemHnd, FILE_MAP_WRITE, 0, 0, 0);
      if lpBaseAddress <> nil then
        lpBaseAddress^ := Application.Handle;
    end;
  end
  else
  begin
    //    MemFileHnd := OpenFileMapping(FILE_MAP_READ, False, PChar(MemFileName));
    Result := True;
    if MemHnd <> 0 then
    begin
      lpBaseAddress := MapViewOfFile(MemHnd, FILE_MAP_READ, 0, 0, 0);
      if lpBaseAddress <> nil then
      begin
        FirstAppHandle := lpBaseAddress^;
        ShowWindow(FirstAppHandle, SW_restore);
        SetForegroundWindow(FirstAppHandle);
      end;
    end;
  end;
  if lpBaseAddress <> nil then
    UnMapViewOfFile(lpBaseAddress);
end;

В тексте проекта * .dpr вызов функции выглядит приблизительно следующим образом

program OneHinstance;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1},
  FirstHinstanceRunning in '..\..\FirstHinstanceRunning.pas';

{$R *.res}

begin
  Application.Initialize;

  if FirstHinstanceRunning(0) then
    Exit;

  Application.CreateForm(TForm1, Form1);
  Application.Run;

  //  CloseHandle(MemHnd);  
end.
psix
Сообщения: 52
Зарегистрирован: 29 июл 2004, 13:53
Откуда: Россия, Москва
Контактная информация:

Самое простое - CreateMutex() - проверка буквально в одну строчку.
большинство приложений делают именно так.
Аватара пользователя
LAngel
Сообщения: 277
Зарегистрирован: 30 мар 2005, 08:19
Откуда: Ульяновск
Контактная информация:

FindWindow "всегда" находит окно, только когда приложение запущено в дизайн-тайм (под оболочкой дельфы), потому что в оболочке существует окно с тем же классом и заголовком.
Попробуй закрыть проект и запустить exe, тогда все будет работать как положено :)
С уважением, Lost Angel...
corb
Сообщения: 4
Зарегистрирован: 13 авг 2006, 01:51

У меня такой вопрос - мне надо считать, сколько приложений запущено.
Я вижу вариант c map файлом: создавать его на 4 байта больше и в них записывать 1. В другом экземпляре при открытии мэп-файла, увеличивать счетчик на 1, а при завершении программы его уменьшать.
Вопрос такой: может есть какой-нибудь объект синхронизации, автоматически поддерживающий счет?
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

Автоматически счет поддерживают семафоры, но не в том смысле, как надо для ваших целей.
Mapping - неплохой вариант.
Ответить