Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Making sure app is only opened once

Status
Not open for further replies.

djjd47130

Programmer
Nov 1, 2010
480
US
I've seen tons of examples for identifying if app is already running, and close if it is. Well I found one function that does the job really easily, very little code, and works. All the rest either is a lot of funky code, or just doesn't work. Well Now I have this function in my app's unit, and when trying to use it, the function always returns True, because literally the app IS running (but this is the only instance that is running).

So the question is how do I make this function return an Integer representing how many times the process is running?

Source found at
And after my little modifications:
Code:
program RMProTray;

uses
  Forms,
  Dialogs,
  Windows,
  TlHelp32,
  SysUtils,
  uRMPTray in 'uRMPTray.pas' {frmMain},
  AlertThread in '..\Small\AlertThread.pas';

function ProcessExists(exeFileName: string): Boolean;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
  Result := False;
  while Integer(ContinueLoop) <> 0 do begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
      UpperCase(ExeFileName))) then Result := True;
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;

{$R *.res}

begin
  if ProcessExists('RMProTray.exe') then begin
    MessageDlg('App already running!', mtError, [mbOK], 0);
    Application.Terminate;
  end else begin                       
    Application.Initialize;
    Application.CreateForm(TfrmMain, frmMain);
    Application.Run;
  end;
end.


JD Solutions
 
Never mind, I figured it out...

Code:
program RMProTray;

uses
  Forms,
  Dialogs,
  Windows,
  TlHelp32,
  SysUtils,
  uRMPTray in 'uRMPTray.pas' {frmMain},
  AlertThread in '..\Small\AlertThread.pas';

function ProcessExists(exeFileName: string): Boolean;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
  Result := False;
  while Integer(ContinueLoop) <> 0 do begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
      UpperCase(ExeFileName))) then Result := True;
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;

function ProcessCount(const ExeName: String): Integer;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  FSnapshotHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize:= SizeOf(FProcessEntry32);
  ContinueLoop:= Process32First(FSnapshotHandle, FProcessEntry32);
  Result:= 0;
  while Integer(ContinueLoop) <> 0 do begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(ExeName)) or (UpperCase(FProcessEntry32.szExeFile) =
      UpperCase(ExeName))) then Inc(Result);
    ContinueLoop:= Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;

{$R *.res}

begin
  if ProcessCount('RMProTray.exe') > 1 then begin
    Application.Terminate;
  end else begin                       
    Application.Initialize;
    Application.CreateForm(TfrmMain, frmMain);
    Application.Run;
  end;
end.

JD Solutions
 
You can limit the number of instances of your application to one, with the use of a mutex. (Shift-Ctrl-G can be used to create the GUID - used as the mutex name, which is guaranteed to be statistically unique).

Code:
uses Windows, Dialogs, ..;

var
   MyMutex: THandle;
begin
   MyMutex := CreateMutex(nil, False, '{9C7EB9EC-EDE2-4B9E-BB60-599258DD3DE6}');
   try
      if GetLastError = ERROR_ALREADY_EXISTS then
      begin
         MessageDlg('App already running!', mtError, [mbOK], 0);
         Application.Terminate;
      end
      else
      begin
         Application.Initialize;
         Application.CreateForm(TfrmMain, frmMain);
         Application.Run;
      end;
   finally
      ReleaseMutex(MyMutex);
      CloseHandle(MyMutex);
   end;
end.
 
Thank you, looks like a nifty trick. I still like my function though because it can be used for any process, not just my own. For example, if you put 'svchost.exe' through it, you might get a result of 10 or so instances. Or sqlservr.exe to see how many instances of SQL are running.

JD Solutions
 
What OS are you running this on? You may have issues with Vista and Win7 on "seeing" other processes without elevating the privileges.
 
I do agree with you that your routine does return back the number of instances of a particular process, so it serves a different purpose than limiting an application to a single instance.

If your executable is copied to another file, with a new file name, then it will no longer limit your application to one instance. The main reason I limit my application to one instance is the use of resources. If there are two or more instances running, there will be conflicts on the use of those resources, so I need to ensure a single instance no matter what. Using a mutex ensures it no matter what the executable name is, or where on the file system it is located.
 
Using a mutex ensures it no matter what the executable name is, or where on the file system it is located.

Or semaphores, which is how I handle this task.

Code:
MySem := CreateSemaphore(nil,0,1,'SSaverSemaphore');
if ((MySem <> 0) and (GetLastError = ERROR_ALREADY_EXISTS)) then
  begin
    CloseHandle(MySem);
    Halt;
  end;

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
Thanks for the tip, never thought of it because I don't expect my app to be renamed.

JD Solutions
 
Keeping the FAQ I wrote, but changed something in it to get ExtractFileName(Application.ExeName) rather than hard-coding the application name. The Mutex or Semaphore methods deserve their own FAQ for "How do I make sure my application is only open once?" - I'll leave it to you if you want since you know what you're doing with it.

JD Solutions
 
I have another similar question. Suppose the app is launched and it discovers there's already an instance open. How would I force that existing instance to come in front of the user? Like if it's minimized, I'd like the existing app to be restored instead of opening a new one.

JD Solutions
 
faq102-6959 should answer a number of these questions. The main job to do that is find out the proper window handle of the previous instance.

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.
 
Aha! I looked over those FAQ and must have skipped over that one. Thank you.

JD Solutions
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top