Перезапустите приложение 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' передавая свой дескриптор мьютекса.