?
Solved

Disabling second run

Posted on 2004-12-01
32
Medium Priority
?
330 Views
Last Modified: 2010-04-04
How can I disable the second run of my application when it's already open?

Vadim Smolensky,
Saint-Petersburg, Russia
0
Comment
Question by:VSmolensky
  • 11
  • 8
  • 6
  • +2
32 Comments
 
LVL 14

Assisted Solution

by:DragonSlayer
DragonSlayer earned 600 total points
ID: 12714477
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.
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12714556
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.
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 12714588
hehehe ;-)

Been very active lately huh Alex... only another 48k points to catch up... I must work harder! :-)
0
2018 Annual Membership Survey

Here at Experts Exchange, we strive to give members the best experience. Help us improve the site by taking this survey today! (Bonus: Be entered to win a great tech prize for participating!)

 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12714593
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.
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12714600
Yep... I'm gaining on you... :-) I'm already ahead of you in the yearly top-15. :-P
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 12714612
> 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 ;-)
0
 
LVL 17

Expert Comment

by:geobul
ID: 12714757
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
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12714942
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. :-)
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 12715060
or do some FlashWindow trick instead of restoring the form ;-)

yea, nice touch, geo :-)
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12715157
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
0
 
LVL 17

Expert Comment

by:geobul
ID: 12715159
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
0
 
LVL 17

Expert Comment

by:geobul
ID: 12715210
>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.
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12715661
@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...
0
 
LVL 17

Expert Comment

by:geobul
ID: 12715954
Strange, there is one W2K Pro SP3 where that's been working well for one and a half year.
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 12716478
*horrors*

*gasp*

now only realise that i'm so far behind in the top 15 yearly ... :'(
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12717559
@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... ;-)
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 12717673
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;
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 12717686
Hmm... but that would make madshi as #1...
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12717967
It's not fair, though... Besides, as page editor meikl could change some of your scores if you ever tried that... :-P
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 12718127
then I'll add one more line:

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

Author Comment

by:VSmolensky
ID: 12725473
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.
0
 
LVL 17

Expert Comment

by:geobul
ID: 12726004
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
0
 

Author Comment

by:VSmolensky
ID: 12726451
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...
0
 
LVL 17

Assisted Solution

by:Wim ten Brink
Wim ten Brink earned 600 total points
ID: 12727480
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...
0
 
LVL 2

Expert Comment

by:SaLz
ID: 12728730
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.
0
 
LVL 2

Expert Comment

by:SaLz
ID: 12728803
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
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12730174
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. :-)
0
 
LVL 2

Expert Comment

by:SaLz
ID: 12733826
yep, your right alex, the author shouldn't use my code, VSmolensky, don't use my code, use the others instead.
0
 

Author Comment

by:VSmolensky
ID: 12736482
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.
0
 
LVL 17

Accepted Solution

by:
geobul earned 800 total points
ID: 12737181
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
0
 

Author Comment

by:VSmolensky
ID: 12737609
Yes, now it works perfectly well!
Thank you!
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12738478
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... ;-)
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
This is an update to some code that someone else posted on Experts Exchange. It is an alternate approach, I think a little easier to use, & makes sure that things like the Task Bar will update.
Stellar Phoenix SQL Database Repair software easily fixes the suspect mode issue of SQL Server database. It is a simple process to bring the database from suspect mode to normal mode. Check out the video and fix the SQL database suspect mode problem.
Hi, this video explains a free download that you can incorporate into your Access databases, or use stand-alone for contact management. Contacts -- Names, Addresses, Phone Numbers, eMail Addresses, Websites, Lists, Projects, Notes, Attachments…
Suggested Courses

601 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question