以编程方式重新启动 Delphi 应用程序

7

我的应用程序不应该能够运行多个实例。因此,项目源代码包含:

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;

但是在我的情况下这不起作用,因为有互斥锁。即使我在启动第二个实例之前释放互斥锁,也不会起作用,因为关闭需要一些时间,两个实例不能并行运行(因为共享资源和其他影响)。
有没有一种方法可以重新启动具有这种特性的应用程序?(如果可能,不需要额外的可执行文件)
提前感谢。

为什么要重新启动 - 只需激活已经运行的实例即可。 - macf00bar
1
这两者怎么可能是一样的呢?当前实例已经在运行,但我希望它能够重新启动。 - jpfollenius
1
你曾经测试过'ReleaseMutex'的返回值吗? - Sertac Akyuz
“释放”并不是你需要对互斥锁执行的操作,而应该是关闭它。 - Rob Kennedy
9个回答

15

也许你应该跳出固定思维。不要纠结于互斥/实例逻辑,而是可以创建另一个可执行文件来等待你的应用程序关闭,然后重新启动它。作为额外的奖励,您还可以稍后使用此机制更新一些主应用程序的二进制文件。这样做更容易提升权限,而不是在同一应用程序内维护不同的完整性级别,等等。


+1;这是许多应用程序执行更新的机制。 - Jeroen Wiert Pluimers

2
为什么在尝试重新启动之前不能释放互斥锁?如果有另一个实例在您显式调用重启之前启动,那没关系,您仍然可以使用需要重新启动的任何更改使应用程序再次运行。我认为您不需要其他解决方案的复杂性。

你需要关闭并重新启动,还是重新激活已经运行的应用程序就足够了(请查看我发布的链接)? - macf00bar
在这种情况下,@Smasher,请在单实例模式下执行所有需要执行的关闭操作,然后在完成所有关闭工作后释放互斥锁并调用ShellExecute。 - David Heffernan
@Smasher,你所需要做的就是在现有互斥体释放之后放置ShellExecute。你应该已经在释放现有互斥体之前完成了所有关闭工作,对吧? - David Heffernan
@Smasher,我认为释放互斥锁并不难作为你所做的最后一件事情。无论如何,如果不是这样,那么你已经有了一个问题,因为在你释放互斥锁但仍在完成应用程序时,另一个实例可以启动。 - David Heffernan
@Smasher 例如,您可以考虑使用System.ExitProc,但有许多许多方法可以实现此目的。 - David Heffernan
显示剩余5条评论

1

编辑...

好的。现在我相信我知道你的问题出在哪里了......你有程序单元完成的问题!

尝试在程序部分作为第一个单元添加我的底部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单元之前进行最终化,这意味着应用程序可以再次安全启动!


即使在启动第二个实例之前释放互斥锁,也无法工作,因为关闭需要一些时间,两个实例不能并行运行。 - jpfollenius
@Smasher:你在程序完成方面遇到了问题!看看我的改进答案吧! - GJ.

1
在您的ShellExecute中包含一些参数,例如/WaitForShutDown,并创建一个额外的互斥体。在程序初始化之前,在其.dpr文件中插入以下内容:
如果(Pos('/WaitForShutDown',CmdLine) <> 0)then WaitForSingleObject(ShutDownMutexHandle,INFINITE);
此外,在所有最终化和释放共享资源之后,还要包括以下内容:
ReleaseMutex(ShutDownMutexHandle);

谢谢!但是ShutdownMutexHandle在程序开始时将是未定义的。你会如何定义它?CreateMutex会因为ERROR_ALREADY_EXISTS错误而失败... - jpfollenius
好的,我想我找到了:OpenMutex 应该是正确的调用方式来获取句柄。 - jpfollenius

0

(打破睡眠想法)

如果您想确保在创建互斥对象之前原始进程已经真正终止/关闭,那么一个想法是将PID传递给新进程(命令行是最简单的方法,任何其他IPC方法也可以),然后使用OpenProcess(SYNCHRONIZE,false,pid)和WaitForSingleObject(我会使用带有超时的循环(100毫秒是一个不错的值),并根据原始进程关闭所需的时间采取相应措施)

除了上述方法外,我还在同一单元中创建了一个RestartSelf过程,并在那里执行逻辑,以便将单个实例和重启逻辑放在同一个地方(参数被硬编码,您不希望硬编码的内容散布在您的应用程序中)。


0
你可以传递一个命令行参数,比如“restart”,然后在尝试获取互斥锁之前运行Sleep(),或者在一个循环中尝试获取互斥锁并休眠一段时间。
此外,你也可以设置两个进程之间的通信,但这可能有些过度。

2
Sleep函数的问题在于找不到适当的值。这完全取决于计算机、已加载的数据和其他因素。 - jpfollenius

0

嗨,请看一下以下文章由Zarko Gajic撰写 - 在那里您将获得一些想法、示例代码甚至是整个组件可供使用。

祝好, Reinhard


我认为这更相关 - http://delphi.about.com/cs/adptips2001/a/bltip0601_2.htm - RBA

0

你的 ReleaseMutex 可能会失败,因为在调用 CreateMutex 时将 'False' 传递给了 'bInitialOwner'。要么拥有互斥锁的初始所有权,要么调用 CloseHandle 并传递互斥锁句柄而不是使用 'ReleaseMutex'。


2
不用担心释放互斥锁。互斥锁的所有权完全无关紧要。你需要做的是使用CloseHandle销毁互斥锁。就这样。(当然,这需要存储CreateMutex的返回值。) - Rob Kennedy

0

1
为避免链接失效,请在此处发布链接中包含的相关信息。 - Benoît Latinier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接