Программный перезапуск приложения 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;

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

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

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


person jpfollenius    schedule 21.12.2010    source источник
comment
зачем перезапускать - просто активируй уже запущенный инстанс   -  person pastacool    schedule 21.12.2010
comment
как это то же самое? Работающий экземпляр уже активен, и я хочу, чтобы он был перезапущен.   -  person jpfollenius    schedule 21.12.2010
comment
Вы когда-нибудь тестировали возврат ReleaseMutex?   -  person Sertac Akyuz    schedule 21.12.2010
comment
Освобождение - это не то, что вам нужно делать с мьютексом. Вместо этого закройте его.   -  person Rob Kennedy    schedule 21.12.2010


Ответы (9)


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

person Mihai Limbășan    schedule 21.12.2010
comment
+1; Это механизм, который делают многие приложения для выполнения обновлений. - person Jeroen Wiert Pluimers; 21.12.2010

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

person David Heffernan    schedule 21.12.2010
comment
Потому что в течение некоторого ограниченного времени два экземпляра будут работать параллельно (отключение требует некоторого времени). Этого я хочу избежать. - person jpfollenius; 21.12.2010
comment
поэтому вам нужно выключить и перезапустить, или будет достаточно повторной активации уже запущенного приложения (взгляните на ссылку, которую я опубликовал)? - person pastacool; 21.12.2010
comment
@Smasher в этом случае выполняет все необходимые операции по завершению работы в режиме единственного экземпляра, а затем после завершения всей работы по завершению работы отпустите мьютекс и вызовите ShellExecute. - person David Heffernan; 21.12.2010
comment
@Smasher Все, что вам нужно сделать, это поместить ShellExecute после существующей версии мьютекса. Предположительно, вы уже выполняете всю работу по завершению работы перед тем, как освободить существующий мьютекс? - person David Heffernan; 21.12.2010
comment
@ Дэвид: Нет, и это не так просто. Но я думаю, вы правы, что это самое простое решение. - person jpfollenius; 21.12.2010
comment
@Smasher Я не думаю, что будет сложно получить выпуск мьютекса как последнее, что вы делаете. В любом случае, если это не так, у вас уже проблема, потому что другой экземпляр может запуститься после того, как вы освободили мьютекс, но пока вы все еще дорабатываете свое приложение. - person David Heffernan; 21.12.2010
comment
@Smasher, например, вы можете рассмотреть возможность использования System.ExitProc, но есть много способов добиться этого. - person David Heffernan; 21.12.2010
comment
@ Дэвид: хорошо, теперь я установил глобальную переменную RestartApp в True и завершил приложение (отправив WM_CLOSE в главное окно). В конце исходного кода проекта (после Application.Run) я освобождаю мьютекс, а затем вызываю ShellExecute, если установлен флаг перезапуска. К сожалению, ShellExecute, похоже, не работает, и все, что я получаю, - это сообщение о сбое Windows. Есть ли причина, по которой я не могу позвонить ShellExecute в это место? - person jpfollenius; 21.12.2010
comment
@Smasher Какой дескриптор вы передаете ShellExecute? Я бы просто передал NULL (= 0). Иначе я не могу придумать, почему он потерпит неудачу. - person David Heffernan; 21.12.2010
comment
Уже прохожу 0. Странно. К сожалению, отладчик здесь не поможет. - person jpfollenius; 21.12.2010

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

if (Pos ('/ WaitForShutDown', CmdLine) ‹> 0) then WaitForSingleObject (ShutDownMutexHandle, INFINITE);

Кроме того, в свою программу после всех доработок и выпуска общих ресурсов включите что-то вроде

ReleaseMutex (ShutDownMutexHandle);

person Eldar Isayev    schedule 21.12.2010
comment
Спасибо! Но ShutdownMutexHandle будет неопределенным при запуске программы. Как бы вы это определили? CreateMutex завершится с ошибкой ERROR_ALREADY_EXISTS ... - person jpfollenius; 21.12.2010
comment
хорошо, думаю, я нашел это: OpenMutex должно быть правильным вызовом для получения дескриптора. - person jpfollenius; 21.12.2010

ИЗМЕНИТЬ ...

В ПОРЯДКЕ. Теперь я верю, что знаю, в чем ваша проблема ... У вас проблемы с доработкой программных модулей!

Попробуйте добавить в программный раздел как первый модуль мой нижний модуль 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, что означает, что приложение может снова запуститься безопасно!

person GJ.    schedule 21.12.2010
comment
Даже если я отпущу мьютекс перед запуском второго экземпляра, он не сработает, потому что отключение занимает некоторое время, и два экземпляра не могут работать параллельно ... из вопроса. - person jpfollenius; 21.12.2010
comment
@Smasher: У вас проблемы с доработкой программы! Чеч мой улучшенный ответ! - person GJ.; 22.12.2010

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

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

person Jens Mühlenhoff    schedule 21.12.2010
comment
Проблема с Sleep в том, что нет возможности найти подходящее значение. Полностью зависит от компьютера, загруженных данных и прочего. - person jpfollenius; 21.12.2010

привет, прочтите следующую статью Зарко Гаджича - там вы получить несколько идей, образец кода и даже целый компонент для использования.

hth, райнхард

person pastacool    schedule 21.12.2010
comment
Я считаю, что это более актуально - delphi.about.com/cs/adptips2001/a /bltip0601_2.htm - person RBA; 21.12.2010

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

person Sertac Akyuz    schedule 21.12.2010
comment
Нет, вообще не беспокойтесь о освобождении мьютекса. Право собственности на мьютекс совершенно не имеет значения. Что вам нужно сделать, так это уничтожить мьютекс с помощью CloseHandle. Это все. (Это, конечно, требует сохранения возвращаемого значения из CreateMutex.) - person Rob Kennedy; 21.12.2010

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

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

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

person marceldanilo    schedule 11.02.2015
comment
Чтобы избежать битых ссылок, разместите здесь соответствующую информацию, содержащуюся в ссылке. - person Benoît Latinier; 11.02.2015

(побеждая идею сна)

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

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

person ciuly    schedule 23.01.2016