Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Neat method for blocking multiple instances

Posted on 1999-01-25
8
Medium Priority
?
750 Views
Last Modified: 2008-02-01
Here is a neat method for blocking multiple instances of an application from running.  I came up with this while answering another question on EE, and thought it might be nice to post for all to see.  I have never seen this method anywhere else and it seems like it might possibly be something other members of EE could use or improve upon. Anyway here it is


unit PREVINST;

{Unit PREVINST.pas -
      Include this unit in your project file (.dpr) to keep multiple instances
  of your application from running.  Works the same way as the standard method
  of using mutexes, except uses a semaphore and stores the application handle in
  the semaphore's count so we can get it to show the original instance.
  You would call it like this:

  program Project1;

      uses
        Forms,
        Unit1 in 'Unit1.pas',
        PREVINST in 'PREVINST.pas';

      begin
    if not HavePrevInstance(Application.Handle, 'SOmeWEIRDnameFoRTheSemaphOre') then
    begin
          Application.Initialize;
              Application.CreateForm(TForm1, Form1);
              Application.Run;
    end;
  end.}

interface

uses Windows;

function HavePrevInstance(AppHandle: THandle; UniqueName: String): boolean;

implementation

function HavePrevInstance(AppHandle: THandle; UniqueName: String): boolean;
const
  ERROR_ALREADY_EXISTS = 183; //Not in Windows.pas - dont know why
  SEMAPHORE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or 3; //Ditto
  WM_SYSCOMMAND = 274; //Define here so dont have to include Messages.pas
var
  MyHandle: THandle;
begin
  Result := False; //Set Initial Result to false
  //Now we create the semaphore and store the applications handle as its count
  MyHandle := CreateSemaphore(nil, AppHandle, AppHandle, PChar(UniqueName));
  //The following will be true if an instance of out app is already running
  if (MyHandle <> 0) and (GetLastError = ERROR_ALREADY_EXISTS) then
  begin
    Result := True; //Set to result to true
    //Now we open the semaphore to get its handle
    MyHandle := OpenSemaphore(SEMAPHORE_ALL_ACCESS, False, PChar(UniqueName));
    //This replaces the value of MyHandle with the Application handle of the prev instance
    ReleaseSemaphore(MyHandle, 0, @MyHandle);
    //now we have the handle to the previous instance - do whatever you want
    //This is what I usually do but you could pop up a messagebox or whatever
    if IsIconic(MyHandle) then
          PostMessage(MyHandle, WM_SYSCOMMAND, SC_RESTORE, 0)
    else
          SetForeGroundWindow(MyHandle);
  end;
end;

end.




Benefits of this method.

1. Same as using a mutex - can create using a name that is very unlikely to exist anywhere else on the system.

2. Stores the apps handle so the findwind calls to get it are not needed.

3. Very simple to add to existing code and keeps the app from starting at all if a previous instance is found.



Any suggestions on how this might be improved upon are welcome.

Heath
0
Comment
Question by:heathprovost
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
8 Comments
 
LVL 5

Author Comment

by:heathprovost
ID: 1363244
Could someone answer this so that it gets pushed into the PAQs?

Thanks

Heath
0
 

Accepted Solution

by:
lorenkoss earned 0 total points
ID: 1363245
Consider it answered.. And thanks.. I like the method!
0
 
LVL 5

Author Comment

by:heathprovost
ID: 1363246
Thanks
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 2

Expert Comment

by:cqhall
ID: 1363247
A) This technique does not appear to work in NT. The counter in the ReleaseSemaphore returns a random value instead of the handle (presumably) stored there.  I have tried in three systems - NT4/SP3, NT4/SP4 and Windows 95b.  Only Windows 95b returned the proper handle to the previous instance. NT does tell me a previous instance exists, so I can quit - I just can't get its handle to communicate with it.

B) I added an initialization section with logic to call application.terminate if a previous instance is found.  I use the ExtractFilename(paramstr(0)) as the unique name.  The result is that I can simply include the PrevInst.pas unit in the project if I only allow one copy.  I had built a similar unit in Delphi 1.

0
 
LVL 5

Author Comment

by:heathprovost
ID: 1363248
cqhall, if you ever look at this again.  Try changing this line

ReleaseSemaphore(MyHandle, 0, @MyHandle);

to this

ReleaseSemaphore(MyHandle, 1, @MyHandle);


I dont have NT to test this on, but I think that this would solve the problem.  I didnt read the API help thouroughly when I wrote this code.  ReleaseSemaphore fails when called with zero for the increment parameter.  It even fails under 95! (I wasnt bothering to check for success so I never noticed before)  But for some reason under 95 it still places the previous count into pointer passed to it.  My guess is NT does not exhibit the same behavior.  Passing a 1 should allow the function to succeed.

Heath  
0
 
LVL 5

Author Comment

by:heathprovost
ID: 1363249
Also, do you mind emailing me your modifications?  That initialization thing sounds pretty neat.

Heath

heath_provost@hotmail.com
0
 
LVL 5

Author Comment

by:heathprovost
ID: 1363250
btw - you also have to change this line

MyHandle := CreateSemaphore(nil, AppHandle, AppHandle, PChar(UniqueName));

to this

MyHandle := CreateSemaphore(nil, AppHandle, AppHandle + 1, PChar(UniqueName));

Heath
0
 
LVL 5

Author Comment

by:heathprovost
ID: 1363251
Yep, you were right and so was I.  I sent a friend with NT and app using the original code and then an app using the following modified code.  The original did not work, but the following code did.  I had to change more than I though but this works for both Windows 95/98 and NT.



///////////////////////CODE STARTS

unit PREVINST;

{Unit PREVINST.pas -
Include this unit in your project file (.dpr) to keep multiple instances
  of your application from running.  Works the same way as the standard method
  of using mutexes, except uses a semaphore and stores the application handle in
  the semaphore's count so we can get it to show the original instance.
  You would call it like this:

  program Project1;

uses
   Forms,
   Unit1 in 'Unit1.pas',
   PREVINST in 'PREVINST.pas';

begin
    if not HavePrevInstance(Application.Handle, 'SOmeWEIRDnameFoRTheSemaphOre') then
    begin
     Application.Initialize;
   Application.CreateForm(TForm1, Form1);
   Application.Run;
    end;
  end.}

interface

uses Windows;

function HavePrevInstance(AppHandle: THandle; UniqueName: String): boolean;

implementation

function HavePrevInstance(AppHandle: THandle; UniqueName: String): boolean;
const
  ERROR_ALREADY_EXISTS = 183; //Not in Windows.pas - dont know why
  SEMAPHORE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or 3; //Ditto
  WM_SYSCOMMAND = 274; //Define here so dont have to include Messages.pas
var
  MyHandle: THandle;
begin
  Result := False; //Set Initial Result to false
  //Now we create the semaphore and store the applications handle as its count adding one to it so we can keep toggling it back and forth
  MyHandle := CreateSemaphore(nil, AppHandle + 1, AppHandle + 1, PChar(UniqueName));
  //The following will be true if an instance of out app is already running
  if (MyHandle <> 0) and (GetLastError = ERROR_ALREADY_EXISTS) then
  begin
    Result := True; //Set to result to true
    //Now we open the semaphore to get its handle
    MyHandle := OpenSemaphore(SEMAPHORE_ALL_ACCESS, False, PChar(UniqueName));
    //at this point MyHandle is our app handle plus one. now we reduce count by one to get back our original handle
    WaitForSingleObject(MyHandle, 0);
    //This replaces the value of MyHandle with the Application handle of the prev instance and pushes our count back up again
    ReleaseSemaphore(MyHandle, 1, @MyHandle);
    //now we have the handle to the previous instance - do whatever you want
    //This is what I usually do but you could pop up a messagebox or whatever
    if IsIconic(MyHandle) then
     PostMessage(MyHandle, WM_SYSCOMMAND, SC_RESTORE, 0)
    else
     SetForeGroundWindow(MyHandle);
  end;
end;

end.

//////////////////CODE ENDS

Sorry about that.  You learn something new everyday.

Heath
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

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

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
How to fix incompatible JVM issue while installing Eclipse While installing Eclipse in windows, got one error like above and unable to proceed with the installation. This video describes how to successfully install Eclipse. How to solve incompa…
Suggested Courses

604 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