heathprovost
asked on
Neat method for blocking multiple instances
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(Applicati on.Handle, 'SOmeWEIRDnameFoRTheSemaph Ore') then
begin
Application.Initialize;
Application.CreateForm(TFo rm1, 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_AL L_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(MyHand le);
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
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(Applicati
begin
Application.Initialize;
Application.CreateForm(TFo
Application.Run;
end;
end.}
interface
uses Windows;
function HavePrevInstance(AppHandle
implementation
function HavePrevInstance(AppHandle
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_AL
//This replaces the value of MyHandle with the Application handle of the prev instance
ReleaseSemaphore(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(MyHand
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
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks
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.
B) I added an initialization section with logic to call application.terminate if a previous instance is found. I use the ExtractFilename(paramstr(0
ASKER
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
ReleaseSemaphore(MyHandle,
to this
ReleaseSemaphore(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
ASKER
Also, do you mind emailing me your modifications? That initialization thing sounds pretty neat.
Heath
heath_provost@hotmail.com
Heath
heath_provost@hotmail.com
ASKER
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
MyHandle := CreateSemaphore(nil, AppHandle, AppHandle, PChar(UniqueName));
to this
MyHandle := CreateSemaphore(nil, AppHandle, AppHandle + 1, PChar(UniqueName));
Heath
ASKER
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.
///////////////////////COD E 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(Applicati on.Handle, 'SOmeWEIRDnameFoRTheSemaph Ore') then
begin
Application.Initialize;
Application.CreateForm(TFo rm1, 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_AL L_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(MyHand le, 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(MyHand le);
end;
end;
end.
//////////////////CODE ENDS
Sorry about that. You learn something new everyday.
Heath
///////////////////////COD
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(Applicati
begin
Application.Initialize;
Application.CreateForm(TFo
Application.Run;
end;
end.}
interface
uses Windows;
function HavePrevInstance(AppHandle
implementation
function HavePrevInstance(AppHandle
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_AL
//at this point MyHandle is our app handle plus one. now we reduce count by one to get back our original handle
WaitForSingleObject(MyHand
//This replaces the value of MyHandle with the Application handle of the prev instance and pushes our count back up again
ReleaseSemaphore(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(MyHand
end;
end;
end.
//////////////////CODE ENDS
Sorry about that. You learn something new everyday.
Heath
ASKER
Thanks
Heath