Как я могу сказать, что другой экземпляр моей программы уже запущен?

Как узнать, запущен ли один экземпляр моей программы? Я думал, что смогу сделать это с файлом данных, но это будет просто грязно : (

Я хочу сделать это, поскольку я хочу, чтобы только 1 экземпляр когда-либо был открыт в одной точке.

11 ответов


вы можете создать семафор и остановить выполнение (поместите этот код в ваш *.dpr-файл) и вывести запущенное приложение на экран.

var
  Semafor: THandle;

begin
  { Don't start twice ... if already running bring this instance to front }
  Semafor := CreateSemaphore(nil, 0, 1, 'MY_APPLICATION_IS_RUNNING');
  if ((Semafor <> 0) and { application is already running }
     (GetLastError = ERROR_ALREADY_EXISTS)) then 
  begin
    RestoreWindow('TMyApplication');
    CloseHandle(Semafor);
    Halt;
  end;

  Application.CreateForm(....);    
  Application.Initialize;
  Application.Run;
  CloseHandle(Semafor);
end;

редактировать (добавил RestoreWindow способ):

на aFormName - это имя вашего основного класса формы в вашем приложении.

procedure RestoreWindow(aFormName: string);
var
  Wnd,
  App: HWND;    
begin
  Wnd := FindWindow(PChar(aFormName), nil);
  if (Wnd <> 0) then 
  begin { Set Window to foreground }
    App := GetWindowLong(Wnd, GWL_HWNDPARENT);
    if IsIconic(App) then 
      ShowWindow(App, SW_RESTORE);

    SetForegroundwindow(App);
  end;
end;

как предложил Джон, вы можете попробовать создать мьютекс. Звоните CreateMutex. Если вы получаете ненулевой дескриптор назад, то вызовите GetLastError. Он скажет вам, были ли вы тем, кто создал мьютекс, или мьютекс был уже открыт раньше (Error_Already_Exists). Обратите внимание, что это не необходимо приобрести право собственности на мьютекс. Мьютекс не используется для взаимного исключения. Он используется потому, что это именованный объект ядра. Событие или семафор тоже может работать.

метод мьютекса дает вам логический ответ: Да, есть другой экземпляр, или нет, нет.

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

будьте осторожны при выборе имени мьютекса. Внимательно прочитайте документацию и имейте в виду, что некоторые символы (например, обратная косая черта) не разрешены в некоторых версиях ОС, но необходимы для определенных функций в других версиях ОС.

также помните о проблеме других пользователей. Если ваша программа может быть запущена через удаленный рабочий стол или быстрое переключение пользователей, то могут быть другие пользователи, уже работающие с вашей программой, и вы не захотите чтобы ограничить запуск программы текущим пользователем. В этом случае не используйте глобальное имя. Если ты ... --15-->do хотите ограничить доступ для всех пользователей, затем убедитесь, что атрибуты безопасности объекта мьютекса таковы, что каждый сможет открыть дескриптор для него. Использование нулевого указателя для lpSecurityAttributes параметр недостаточно для этого; "дескриптор безопасности по умолчанию", который упоминает MSDN, дает полный доступ к текущему пользователю и не имеет доступа к другим.

ты разрешено редактировать файл DPR вашей программы. Обычно это хорошее место для подобных вещей. Если вы подождете, пока OnCreate событие одной из ваших форм, тогда ваша программа уже имеет немного импульса к нормальной работе, поэтому неловко пытаться завершить программу в этот момент. Лучше прекратить, прежде чем слишком много работы UI было сделано. Например:

var
  mutex: THandle;
  mutexName: string;
begin
  mutexName := ConstructMutexName();

  mutex := CreateMutex(nil, False, PChar(mutexName));

  if mutex = 0 then
    RaiseLastOSError; // Couldn't open handle at all.

  if GetLastError = Error_Already_Exists then begin
    // We are not the first instance.
    SendDataToPreviousInstance(...);
    exit;
  end;
  // We are the first instance.

  // Do NOT close the mutex handle here. It must
  // remain open for the duration of your program,
  // or else later instances won't be able to
  // detect this instance.

  Application.Initialize;
  Application.CreateForm(...);
  Application.Run;
end.

возникает вопрос о том, когда закрыть дескриптор мьютекса. Тебе не нужно закрывать его. Когда ваш процесс наконец, завершается (даже если он аварийно завершает работу), ОС автоматически закрывает все оставшиеся дескрипторы, и когда больше нет открытых дескрипторов, объект мьютекса будет уничтожен (таким образом, позволяя другому экземпляру вашей программы запускаться и считать себя первым экземпляром).

но вы, возможно, захотите закрыть ручку в любом случае. Предположим, вы решили реализовать SendDataToPreviousInstance функция, которую я упомянул в коде. Если вы хотите получить фантазии, вы могли бы учитывать, что предыдущий экземпляр уже завершает работу и не может принимать новые данные. Тогда вы не захотите закрывать второй экземпляр. Первый экземпляр может закрыть дескриптор мьютекса, как только он узнает, что он закрывается, фактически становясь экземпляром "хромой утки". Второй экземпляр попытается создать дескриптор мьютекса, преуспеет и будет считать себя реальным первым экземпляром. Предыдущий экземпляр закроется бесперебойно. Использовать CloseHandle чтобы закрыть мьютекс; вызовите его из основной формы OnClose обработчик событий или где еще вы вызываете Application.Terminate, например.


всемогущий JVCL имеет компонент для этой цели. См. "TJvAppInstances".


создать система мьютекс.

У меня нет кода Delphi, но вот код c++:

HANDLE Mutex;

const char MutexName[] = "MyUniqueProgramName";

Mutex = OpenMutex(MUTEX_ALL_ACCESS, false, MutexName);

if (Mutex)
     throw Exception("Program is already running.");
else
     Mutex = CreateMutex(NULL, true, MutexName);

нормальным решением является создание имени, общесистемного мьютекс.

  • Если вам удастся создать его, вы один работает приложение.
  • если вы этого не сделаете, вы знаете, что есть другой.

EDIT:

Я не предоставил код, поскольку я не знаю Delphi. Я могу предоставить код C#, если это будет полезно.


Я хотел бы добавить один пункт к отличный ответ от Роба Кеннеди (помимо того, что было бы лучше сделать функцию из его кода, а не копировать все в файл DPR. Вам нужно только два параметра, имя мьютекса и логическое значение, должен ли мьютекс быть для каждого пользователя или для всей системы).

ответ не придает большого значения именованию мьютекса. Если вы ожидаете, что ваша программа будет установлена через Inno Setup (и, возможно другие инструменты настройки тоже) вы должны выбрать имя тщательно, так как мьютекс может быть использован, чтобы программа установки проверить, работает ли приложение в настоящее время, и предупредить пользователя, что они должны закрыть все экземпляры приложения. Если вы решите разрешить один экземпляр программы для каждого пользователя, вам может потребоваться создать второй общесистемный мьютекс, так как для установки может потребоваться отсутствие запущенных экземпляров приложения на всех для того, чтобы иметь возможность заменить файлы. Имя это должно использоваться для синхронизации с установщиком InnoSetup должен быть жестко закодирован.


Я бы сказал, что есть несколько различных стратегий, которые вы можете использовать. Но самый простой (и не специфичный для платформы) - это тот, который вы сами предложили, а именно, в начале программы проверить, есть ли файл блокировки, созданный в определенном месте. Если этот файл блокировки существует, то другой экземпляр уже запущен, если он не существует, то другого экземпляра нет. Когда программа завершает работу, вы удаляете файл блокировки.

, используя эту стратегию у вас есть еще одна проблема, что произойдет, если ваша программа аварийно завершит работу? Файл блокировки все еще остается, и этот конкретный случай должен быть обработан.

другой стратегией является общесистемное решение мьютекса, где вы регистрируете свое присутствие в операционной системе (или также правдоподобно, что это делается автоматически). Когда второй экземпляр пытается запустить, он проверяет, есть ли уже активный процесс с определенным идентификатором. Если он уже существует, второй процесс выбирает не запускаться и при необходимости фокусирует окно первого процесса (если рассматриваемому процессу принадлежит окно).

однако эта стратегия специфична для платформы, и реализация будет отличаться от платформы к платформе.


вы можете просто использовать функцию findwindow Windows api. В delphi имя класса окна совпадает с именем класса, можно переопределить имя класса, переопределив функцию CreateParams. Чтобы проверить, существует ли окно, добавьте код до создания главного окна перед приложением.Инициализировать;

Program test
var 
  handle :HWND;
begin
  handle := FindWindow('TMySuperApp', nil);

  if IsWindow(handle) then
  begin 
       //app is running
       exit;
  end.

  Application.Initialize;
  Application.CreateForm(TMySuperApp, SuperApp);
  Application.Run;
end;

управление количеством экземпляров приложения:

http://delphi.about.com/od/windowsshellapi/l/aa100703a.htm


см. этот блок (используя CreateMutex):UiApp

кроме того, на этой странице вы можете прочитать Преимущества и недостатки для этой работы с различными методами (мьютекс, FindWindows,...).

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

с уважением и извините за мой плохой английский.


Нефтали-Герман Эстевес -


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