Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

Вы можете создать Семафор и прекратить выполнение (поместите код в ваш файл *.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;

EDIT (добавлен метод 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;

Ответ 2

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

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

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

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

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

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

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.

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

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

Ответ 4

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

У меня нет кода Delphi, но здесь код С++:

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);

Ответ 5

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

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

EDIT:

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

Ответ 6

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

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

Ответ 7

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

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

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

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

Ответ 8

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

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;

Ответ 10

Смотрите это устройство (с помощью CreateMutex): UiApp

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

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

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


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

Ответ 11

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