Перезапустите приложение Delphi программно

Не должно быть возможности запустить несколько экземпляров моего приложения. Поэтому источник проекта содержит:

CreateMutex (nil, False, PChar (ID));
if (GetLastError = ERROR_ALREADY_EXISTS) then
  Halt;

Теперь я хочу перезапустить свое приложение программно. Обычный способ будет:

AppName := PChar(Application.ExeName) ;
ShellExecute(Handle,'open', AppName, nil, nil, SW_SHOWNORMAL) ;
Application.Terminate;

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

Есть ли способ перезапустить приложение с такими характеристиками? (Если возможно без дополнительного исполняемого файла)

Заранее спасибо.

9 ответов

Решение

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

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

РЕДАКТИРОВАТЬ...

ХОРОШО. Теперь я верю, что знаю, где твоя проблема... У тебя проблемы с доработкой программных модулей!

Попробуйте в разделе программы добавить в качестве первого модуля мой нижний модуль RestartMutex.

program MyProgramName;  
uses
  Mutex,
  Forms,
...

;

unit RestartMutex;
interface

var
  Restart: boolean = false;

implementation

uses
  windows,
  ShellApi;

var
  MutexHandle: cardinal;
  AppName: PChar;
const
  ID = 'MyProgram';

initialization
  MutexHandle := CreateMutex (nil, False, PChar (ID));
  if (GetLastError = ERROR_ALREADY_EXISTS) then
    Halt;

finalization
  ReleaseMutex(MutexHandle);
  if Restart then
  begin
    AppName := PChar('MyProgramName.exe') ;
    ShellExecute(0,'open', AppName, nil, nil, SW_SHOWNORMAL) ;
  end: 
end.

Если вы хотите перезапустить приложение, просто установите для переменной Restart значение true и затем завершите приложение.

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

Включите в свой ShellExecute некоторый параметр, например, /WaitForShutDown и создайте еще один мьютекс. В вашей программе перед инициализацией, например, в свой файл.dpr, вставьте что-то вроде:

if (Pos ('/ WaitForShutDown', CmdLine)<> 0) тогда WaitForSingleObject(ShutDownMutexHandle, INFINITE);

Кроме того, в вашей программе, после всех доработок и освобождения ваших общих ресурсов, включите что-то вроде

ReleaseMutex(ShutDownMutexHandle);

Вы можете передать аргумент командной строки, такой как "restart" и запустить Sleep(), прежде чем пытаться получить Mutex или попытаться получить мьютекс в цикле, который некоторое время спит.

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

Привет, взгляните на следующую статью Zarko Gajic - там вы найдете некоторые идеи, пример кода и даже целый компонент для использования.

hth, reinhard

(бить идею сна)

если вы хотите убедиться, что исходный процесс действительно завершен / закрыт перед созданием мьютекса, то одна идея состоит в том, чтобы передать PID новому процессу (командная строка самая простая, любой другой метод IPC также работает), затем используйте OpenProcess(SYNCHRONIZE, false, pid) и WaitForSingleObject (я бы использовал цикл с тайм-аутом (100 мс - это хорошее значение) и действовал бы соответственно, если исходный процесс слишком долго закрывался)

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

Оформить заказ таким образом:

Просто запускает новое приложение и убивает текущее;

http://www.delphitricks.com/source-code/windows/restart_the_own_program.html

Ваш ReleaseMutex возможно, сбой, так как вы передаете "False" для "bInitialOwner" во время вызова CreateMutex, Либо иметь первоначальное право собственности на мьютекс, либо вызвать CloseHandle вместо 'ReleaseMutex' передавая свой дескриптор мьютекса.

Другие вопросы по тегам