7

It should not be possible to run multiple instances of my application. Therefore the project source contains:

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

Now I want to restart my application programmatically. The usual way would be:

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

But this won't work in my case because of the mutex. Even if I release the mutex before starting the second instace it won't work because shutdown takes some time and two instance cannot run in parallel (because of common resources and other effects).

Is there a way to restart an application with such characteristics? (If possible without an additional executable)

Thanks in advance.

jpfollenius
  • 16,456
  • 10
  • 90
  • 156

9 Answers9

15

Perhaps you should think outside the box. Instead of futzing with the mutex / instance logic, you could simply create another executable that waits for your app to close then starts it again. As an added bonus, you can later use this mechanism to, for example, update some of your main app's binaries. It's also much easier to run it elevated instead of maintaining different integrity levels inside the same app, etc.

Mihai Limbășan
  • 64,368
  • 4
  • 48
  • 59
2

Why can't you just release the mutex before attempting to restart? If by some chance another instance gets going before the one you explicitly invoke with the restart that doesn't matter, you'll still have your app up and running again with whatever changes effected that required the restart. I don't think you need any of the complexity of the other solutions.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Because for some limited time two instances will run in parallel (shutdown takes some tiem). That's what I want to avoid. – jpfollenius Dec 21 '10 at 10:53
  • so you need to shutdown and re-start or would re-activating the already running app be enough (take a look at the linke I've posted)? – macf00bar Dec 21 '10 at 10:56
  • @Smasher in that case do all the shutdown stuff that you need to do in single instance mode, and then after all the shutdown work is done, release the mutex and call ShellExecute. – David Heffernan Dec 21 '10 at 10:58
  • @Smasher All you need to do is put the ShellExecute after the existing mutex release. Presumably you are already doing all the shutdown work before releasing your existing mutex? – David Heffernan Dec 21 '10 at 11:23
  • @David: No I don't and it's not that easy. But I guess you are right that this is the easiest solution. – jpfollenius Dec 21 '10 at 11:26
  • @Smasher I don't think it's hard to get the mutex release as the final thing you do. In any case if it isn't then you **already** have a problem because another instance can start after your have released the mutex but while you are still finalising you app. – David Heffernan Dec 21 '10 at 12:00
  • @Smasher for example you could consider using System.ExitProc, but there are many many ways to achieve this. – David Heffernan Dec 21 '10 at 12:02
  • @David: okay, I do now set a global variable `RestartApp` to `True` and terminate the application (by sending `WM_CLOSE` to the main window). At the end of the project source (after `Application.Run`) I release the mutex and then call `ShellExecute` if the restart flag is set. Unfortunately `ShellExecute` seems to fail and all I get is a Windows crash message. Is there any reason why I can't call `ShellExecute` at this place? – jpfollenius Dec 21 '10 at 13:07
  • @Smasher What Handle are you passing to ShellExecute? I'd just pass NULL (=0). Otherwise I can't think off the top of my head why it would fail. – David Heffernan Dec 21 '10 at 13:15
  • I'm already passing 0. Strange. Unfortunately the debugger is not of any help here. – jpfollenius Dec 21 '10 at 13:45
1

Include in your ShellExecute some parameter, for example, /WaitForShutDown and create one more mutex. In your program, before the initialization, for example, in its .dpr file, insert something like:

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

Also, in your program, after all the finalizations and releasing your common resources, include something like

ReleaseMutex(ShutDownMutexHandle);

  • Thanks! But `ShutdownMutexHandle` will be undefined at program start. How would you define it? `CreateMutex` will fail with `ERROR_ALREADY_EXISTS` error... – jpfollenius Dec 21 '10 at 11:01
  • okay I guess I found it: `OpenMutex` should be the right thing to call to obtain the handle. – jpfollenius Dec 21 '10 at 11:25
1

EDIT...

OK. Now I belive that I know where is your problem... You have problems with program units finalization!

Try to add at program section as first unit my bottom RestartMutex unit.

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.

When you want to restart application just set variable Restart to true and than terminate an application.

So, because is RestartMutex added as first in program section, this will couse that finalisation of unit RestartMutex will hepped nearly at the end of closing an application and all other units will do finalization before unit RestartMutex, that mean the Application can start safe again!

GJ.
  • 10,810
  • 2
  • 45
  • 62
  • "Even if I release the mutex before starting the second instace it won't work because shutdown takes some time and two instance cannot run in parallel" ... from the question. – jpfollenius Dec 21 '10 at 11:27
  • @Smasher: You have problems with program finalization! Chech my improved answer! – GJ. Dec 21 '10 at 21:31
0

checkout this way:

Simply runs a new application and kills the currernt one;

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

0

(beating the sleep idea)

if you want to make sure the original process is really terminated/closed before you create the mutex, then one idea is to pass the PID to the new process (command line is the easiest, any other IPC method works as well), then use OpenProcess(SYNCHRONIZE, false, pid) and WaitForSingleObject (I'd use a loop with a timeout (100 ms is a good value) and act accordingly if the original process takes too long to close)

What I ended up doing, beside the above, was to also create a RestartSelf procedure in the same unit with the mutex, and do the logic there, in order to keep the single instance and restart logic in the same place (the parameter being hardcoded, you don't want hardcoded stuff to be scattered around your application(s).

ciuly
  • 532
  • 5
  • 13
0

You could pass a command line argument like "restart" and run a Sleep() before you try to acquire the Mutex or try to acquire the mutex in a loop that sleeps a while.

Also you could set up communication between both processes, but that might be overkill.

Jens Mühlenhoff
  • 14,565
  • 6
  • 56
  • 113
  • 2
    The problem with the `Sleep` is that there is no way to find an appropriate value. Totally depends on the computer, the loaded data and other stuff. – jpfollenius Dec 21 '10 at 10:57
0

hi take a look a the following article by Zarko Gajic - there you will get some ideas, sample code and even a whole component to use.

hth, reinhard

macf00bar
  • 673
  • 1
  • 14
  • 32
  • I believe this is more relevant - http://delphi.about.com/cs/adptips2001/a/bltip0601_2.htm – RBA Dec 21 '10 at 12:33
0

Your ReleaseMutex is probably failing since you're passing 'False' for 'bInitialOwner' while calling CreateMutex. Either have the initial ownership of the mutex, or call CloseHandle instead of 'ReleaseMutex' passing your mutex handle.

Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • 2
    No, don't worry about *releasing* the mutex at all. Ownership of the mutex is completely irrelevant. What you need to do is to *destroy* the mutex with `CloseHandle`. That's all. (This of course requires storing the return value from `CreateMutex`.) – Rob Kennedy Dec 21 '10 at 17:01