Disabling second run

VSmolensky
VSmolensky used Ask the Experts™
on
How can I disable the second run of my application when it's already open?

Vadim Smolensky,
Saint-Petersburg, Russia
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Go to Project -> View Source and change your project source to be as follows:

var
  Mutex : THandle;
{$R *.RES}

begin
  Mutex := CreateMutex(nil, True, 'MyAppName');
  if (Mutex <> 0) and (GetLastError = 0) then
  begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1); // of course, change it to whatever name your main form is
    Application.Run;
    if Mutex <> 0 then
      CloseHandle(Mutex);
  end;
end.
Wim ten BrinkSelf-employed developer

Commented:
Dear DragonSlayer,

Your code is okay, but I have a few minor suggestions...

var
  Mutex : THandle;
{$R *.RES}

begin
  Mutex := CreateMutex(nil, True, 'MyAppName');
  if (Mutex <> 0) then begin
    if (GetLastError <> ERROR_ALREADY_EXISTS) then begin
      Application.Initialize;
      Application.CreateForm(TForm1, Form1); // of course, change it to whatever name your main form is
      Application.Run;
    end;
    CloseHandle(Mutex);
  end;
end.

First of all, check if the last error does not equal ERROR_ALREADY_EXISTS because that will tell you the Mutex already existed when you created it. Also, be aware that if you create the mutex succesfully, you will have to release it too and you only released it when GetLastError equals 0. Finally, you don't have to check if (Mutex <> 0) at the bottom because you're already in an IF statement that tells you that it's not 0... :-)

Comments made free of charge. Points should go to DragonSlayer.
hehehe ;-)

Been very active lately huh Alex... only another 48k points to catch up... I must work harder! :-)
Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

Wim ten BrinkSelf-employed developer

Commented:
Btw, in case you're interested, at http://www.workshop-alex.org/Sources/TrayIcon/untExtraInterfaces.html I have created several wrappers around Mutexes, Semaphores and a few other things. If you would use this unit, the *.dpr would look like this:

var
  Mutex : IMutex;
{$R *.RES}

begin
  Mutex := NewMutex('MyAppName', True);
  if Assigned(Mutex) and not Mutex.Existing then begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1); // of course, change it to whatever name your main form is
    Application.Run;
    Mutex := nil;
  end;
end.
Wim ten BrinkSelf-employed developer

Commented:
Yep... I'm gaining on you... :-) I'm already ahead of you in the yearly top-15. :-P
> Yep... I'm gaining on you
You ought to... you are so much more of an expert than me, hehe :-)

oh, and thanks for the corrections ;-)
Another way using unique class name of the main form (without mutex):

in your main form:

protected
 { Protected declarations }
   procedure CreateParams(var Params: TCreateParams); override;
...
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.WinClassName := 'My App Unique String ID';
end;

and in your dpr:

begin
  if FindWindow('My App Unique String ID', nil) <> 0 then exit;

  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Regards, Geo
Wim ten BrinkSelf-employed developer

Commented:
Interesting approach, Geo. :-)
Actually, I like it because if you do find the window then you can also send it to the foreground, thus the user can see the application again. Very useful when the application is minimized or hidden. That would be better even than just not executing anything because the user will just wonder why the application doesn't run. Thus:

var
  AWnd: HWND;

begin
  AWnd := FindWindow( sUniqueName, nil );
  if ( AWnd = 0 ) then begin
    Application.Initialize;
    Application.CreateForm( TFormMain, FormMain );
    Application.Run;
  end
  else begin
    ShowWindow( AWnd, SW_SHOWNORMAL );
    SetForegroundWindow( AWnd );
  end;
end.

sUniqueName is a const that is defined in the same unit as the form. This makes sure that I cannot make any spelling errors in the name. The ShowWindow will show the window and even restore it if it's minimized. Then it's forced to the foreground so the user sees it.
Thanks, Geo. I never considered this option before. :-)
or do some FlashWindow trick instead of restoring the form ;-)

yea, nice touch, geo :-)
Wim ten BrinkSelf-employed developer

Commented:
Uhoh... If Geo gains points too now it would be a lot harder to get past him... :-) The horror, since Geo is right behind me in the yearly top-15. ;-P
Workshop_Alex,

That was the main goal for doing so and you got it at once :-)
The only difference is that I used another way of restoring (without changing the current normal/maximized state):

  h := FindWindow(...);
  if IsWindow(h) then begin
    if IsIconic(h) then ShowWindow(h, SW_RESTORE);
    SetForegroundWindow(h);
  end else begin
    Application.Initialize;
    Application.CreateForm( TFormMain, FormMain );
    Application.Run;
  end;

Regards, Geo
>since Geo is right behind me in the yearly top-15. ;-P

LOL. As a matter of fact I had a lot of free time last month and made more than 26000 p. My absolute peak, I think ! It won't happen again in the near future because three IBM AIX machines are comming next week (I hope) which I'll have to deal with too.
Wim ten BrinkSelf-employed developer

Commented:
@Geo,
For some reason the "ShowWindow(h, SW_RESTORE);" doesn't work on my W2K system. The form stays minimized. As if the form doesn't respond well to this message... SW_Restore did the trick, though...
Strange, there is one W2K Pro SP3 where that's been working well for one and a half year.
*horrors*

*gasp*

now only realise that i'm so far behind in the top 15 yearly ... :'(
Wim ten BrinkSelf-employed developer

Commented:
@DragonSlayer,
Don't worry... In about 31 days you could get back to the top again. :-)
I do wonder if we'll ever manage to get past kretzschmar. It must be very lonely, that high up the top... ;-)
Yea... meikl, if you are reading this... perhaps you would like to request for the CS to reduce your points, so that you may have some sense of competition with us, hehehe...

var
  EEStatistics: TEEStatistics;
begin
  EEStatistics := EEDatabase.SearchByName('kretzschmar');
  if Assigned(EEStatistics) then
    EEStatistics.TotalPoints['Delphi Programming'] := EEStatistics.TotalPoints['Delphi Programming'] div 2;
  EEStatistics.Post;
end;
Hmm... but that would make madshi as #1...
Wim ten BrinkSelf-employed developer

Commented:
It's not fair, though... Besides, as page editor meikl could change some of your scores if you ever tried that... :-P
then I'll add one more line:

EEStatistics.Privileges := EEStatistics.Privileges - [ppModifyScore, ppKickUser];

Author

Commented:
Gentlemen, you remind me of the animation movie "Monsters Inc." where professional scarers collected points by scaring children. They also competed for higher position in their rating, and I also feel now like a scared child. I'll be happy to split these 300 points between the three of you if you explain to me in more details what 'My App Unique String ID' or sUniqueName are like.
It is a string constant or variable (of type string or PChar). Its value must be unique for one particular type of application (and be different for your next one-instance app). If you have two different apps, let's say App1 and App2, and your users are allowed to execute app1 and app2 only once at a time each but they could have one app1 and one app2 working simultaneously then:

the string for App1 could be: 'VSmolensky App1'
and for App2 should be different:  'VSmolensky App2'

If these strings for both apps are the same if you run App1 you won't be able to start App2.

Regards, Geo

Author

Commented:
Thank you, it works OK except one thing: after trying to run a second instance and showing the first one which has been minimized, I can't minimize it again. It stubbornly stays normal.

Tried both SW_SHOWNORMAL and SW_RESTORE. Something is still wrong...
Wim ten BrinkSelf-employed developer
Commented:
Weird. I now notice the same weird behaviour. I start the first instance, minimize it, start the second one (with SW_SHOWNA) and the first instance pops up again. However, when I try to minimize it, it just thinks it's still minimized! Right-clicking on the icon in the toolbar on the bottom shows that I can only "restore" the window. But it's already restored!!!

This must be a bug in Delphi somewhere...

And guess what? I know what causes the bug... When a Delphi application is running, there are actually TWO windows active. You can "see" this second window when you only have your application running and do a "Tile horizontally" on your desktop. Most applications will then cover the whole screen but Delphi applications only cover half the screen. This is caused by the TApplication class, which also has a (secret) window visible on-screen. When you minimize your application, you actually minimize TApplication, which minimizes all other windows. But when we do the ShowWindow, we only show the mainform but the application window still stays closed. And thus the system continues to think the application is iconized...

But knowing the problem doesn't always provide a solution...

Commented:
ok, forget using Mutex, its a cheap doosile way of limiting your app, I really don't like using it my self, but I like to use style with my apps, the code below is how it should be done and controlled, u want to control how many times your appliation can be opened, with the code below you can select how many times your app can be opened, if u only want 1 instence then we will just put a 1, if we want more then we change the integer to any number we wish to have.

if you would like only 1 instance of your app, then each time you open a new instance, it will 'restore' your 'original app'. this is a good default way of controling the number of instance, and allowing your original app to be shown, even when your original app is minimized it will be restored, just in the same way other apps like DC++ P2P program.

first we will open the source dpr. open the dpr file and exit it with the below, control the number of insentces we change to 1 to any number we would like.
====
  if not precheck.RestoreIfRunning(Application.Handle, 1) then
  begin
    Application.Initialize;
    Application.Title := 'Tester';
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end;
====

//then we make a new unit called 'precheck' and we put the following code into the unit, each time you want to control your app put a precheck.pas  unit into your project and the above source and it will control your app numbers.
====
unit precheck;

interface
uses Windows, SysUtils;

function RestoreIfRunning(const AppHandle : THandle; MaxInstances : integer = 1) : boolean;

implementation

type
  PInstanceInfo = ^TInstanceInfo;
  TInstanceInfo = packed record
    PreviousHandle : THandle;
    RunCounter : integer;
  end;

var
  MappingHandle: THandle;
  InstanceInfo: PInstanceInfo;
  MappingName : string;

  RemoveMe : boolean = True;

function RestoreIfRunning(const AppHandle : THandle; MaxInstances : integer = 1) : boolean;
begin
  Result := True;

  MappingName :=StringReplace(ParamStr(0),'\','',[rfReplaceAll, rfIgnoreCase]);

  MappingHandle := CreateFileMapping($FFFFFFFF,
                                     nil,
                                     PAGE_READWRITE,
                                     0,
                                     SizeOf(TInstanceInfo),
                                     PChar(MappingName));

  if MappingHandle = 0 then
    RaiseLastOSError
  else
  begin
    if GetLastError <> ERROR_ALREADY_EXISTS then
    begin
      InstanceInfo := MapViewOfFile(MappingHandle,
                                    FILE_MAP_ALL_ACCESS,
                                    0,
                                    0,
                                    SizeOf(TInstanceInfo));

      InstanceInfo^.PreviousHandle := AppHandle;
      InstanceInfo^.RunCounter := 1;

      Result := False;
    end
    else //already runing
    begin
      MappingHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, PChar(MappingName));
      if MappingHandle <> 0 then
      begin
        InstanceInfo := MapViewOfFile(MappingHandle,
                                      FILE_MAP_ALL_ACCESS,
                                      0,
                                      0,
                                      SizeOf(TInstanceInfo));

        if InstanceInfo^.RunCounter >= MaxInstances then
        begin
          RemoveMe := False;

          if IsIconic(InstanceInfo^.PreviousHandle) then
            ShowWindow(InstanceInfo^.PreviousHandle, SW_RESTORE);
          SetForegroundWindow(InstanceInfo^.PreviousHandle);
        end
        else
        begin
          InstanceInfo^.PreviousHandle := AppHandle;
          InstanceInfo^.RunCounter := 1 + InstanceInfo^.RunCounter;

          Result := False;
        end
      end;
    end;

  end;
end;

initialization

finalization
  //remove one instance
  if RemoveMe then
  begin
    MappingHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, PChar(MappingName));
    if MappingHandle <> 0 then
    begin
      InstanceInfo := MapViewOfFile(MappingHandle,FILE_MAP_ALL_ACCESS,0,0,SizeOf(TInstanceInfo));

      InstanceInfo^.RunCounter := -1 + InstanceInfo^.RunCounter;
    end
    else
      RaiseLastOSError;
  end;

  if Assigned(InstanceInfo) then UnmapViewOfFile(InstanceInfo);
  if MappingHandle <> 0 then CloseHandle(MappingHandle);

end.
====

Sal.

Commented:
copy and edit into your dpr source project.

{$R *.res}

begin
  if not precheck.RestoreIfRunning(Application.Handle, 1) then//change the number to control the number of apps you wish to show.
  begin
    Application.Initialize;
    Application.Title := 'Tester';
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end;
end.

//put the precheck.pas into your app code above.

its really that simple
Wim ten BrinkSelf-employed developer

Commented:
SaLz, if you've read all the posts, you'd noticed that there is another, simpler solution too. So now we just have three different solutions... But if you ask me, your solution is just extremely complicated. Even worse, if you copy the executable to another folder, then you can execute both executables and thus run the application twice again. But okay, you do get a handle to the right window handle that needs to be restored. I just think it's an extremely complicated technique...

I like Geo's solution better. :-)

Commented:
yep, your right alex, the author shouldn't use my code, VSmolensky, don't use my code, use the others instead.

Author

Commented:
Yes, I'm using Geo's solution as a more simple and elegant one. The only shortcoming of it (and of any of the three) is the operator ShowWindow which definitely doesn't work as well as I need.
Hi,

No problem since Workshop_Alex found the reason. The solution is:

var
  h, h1: HWND;
begin
  h := FindWindow('My App Unique String ID', nil);
  if IsWindow(h) then begin
    h1 := GetWindow(h, GW_OWNER);
    if IsWindow(h1) then begin
      h := h1;
    end;
    ShowWindow(h, SW_RESTORE);
    SetForegroundWindow(h);
  end else begin
    Application.Initialize;
    Application.CreateForm( TFormMain, FormMain );
    Application.Run;
  end;
end.

Regards, Geo

Author

Commented:
Yes, now it works perfectly well!
Thank you!
Wim ten BrinkSelf-employed developer

Commented:
I like this better, Geo:

var
  AWnd: HWND;
  AParentWnd: HWND;

begin
  AWnd := FindWindow( sUniqueName, nil );
  if IsWindow( AWnd ) then begin
    AParentWnd := GetWindow( AWnd, GW_OWNER );
    if IsWindow( AParentWnd ) then AWnd := AParentWnd;
    if IsIconic( AWnd ) then OpenIcon( AWnd );
    SetForegroundWindow( AWnd );
  end
  else begin
    Application.Initialize;
    Application.CreateForm( TFormMain, FormMain );
    Application.Run;
  end;
end.

Same stuff, actually, although I use OpenIcon where you use ShowWindow.

And when you look at this easy code, you also realize why the code provided by Salz is a bit too complicated just for this purpose. Of course Salz's code does show a nice way to share data between two applications. But this code is a lot cleaner. Sorry, Salz. But simpler solutions are just simpler... ;-)

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial