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!

Single Instance Applications

Status
Not open for further replies.

OmniCog

Programmer
Jan 23, 2004
27
0
0
CA
Hi,

I'm using mutex to insure that only one instance of one of my application is running with this code (in Project.dpr):
Code:
var
 hMutex: THandle;
begin
  hMutex := CreateMutex(nil, False, 'termBlaster');
  if WaitForSingleObject (HMutex, 0) = WAIT_TIMEOUT then
  begin
    MessageDlg ('Only one allowed!', mtInformation, [mbOK], 0);
    Application.Terminate;
    exit;
  end;

I've also associated the application to some file extensions. But if they have the application running, and the user double clicks on an associated file in Windows Explorer all they get is 'Only one allowed!'. How can I get the current instance to load the file instead of displaying 'Only one allowed!'.
 
I got 80% of the way through typing an answer before I realised I was on the Delphi forum and not the VB.Net forum.

I can't remember what's available in the various Delphi units, but in .NET I would use System.Diagonostics.Process to locate:

The Process ID of this process and then look for a Process with the same name but a different Process ID. If found bring the other process to the front and terminate this one.

Hopefully Delphi provides similar functionality, but my code sample would be obviously be useless here.

Hope this helps.

[vampire][bat]
 
Maybe you could do something like this:

When you detect that there is already an instance open
and the new instances was supposed to load a file you
could have it create a file in for example the
application.exename path.

Now in the old instance you could use a timer to check
wether or not this file exists and when it does read the
file name and path out from that file and load it. Then of course you would have to delete the file that was created
afterwards.

[bobafett] BobbaFet [bobafett]

Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
faq102-5352
 
He is a better way to use a mutex.

Code:
var
  mAppMutexHnd: cardinal;
const
  APPLICATION_MUTEX_ID = 'AppMutex';
begin
  Application.Initialize;
  // Create mutex to show application is running
  mAppMutexHnd := CreateMutex(nil, BOOL(1), PChar APPLICATION_MUTEX_ID));
  // check if application was already running
  if (mAppMutexHnd = 0) or (GetLastError <> 0) then
  begin
    MessageBox(0, 'An instance of this application is already running.', 'Application already running', MB_ICONHAND);
    EXIT;
  end;
  try
    Application.Run;
  finally
    // release application's mutex that indicates it is running
    CloseHandle(mAppMutexHnd);
  end;
end.

I will look in to your original issue though. :)

-Markus Rissmann
 
I use the following trick :

Code:
begin
 {$IFDEF MSWINDOWS}
 if ParamCount > 0 then                
  begin
  // there are some command line parameters, put them in the registry
   WriteCmdLineToRegistry(CmdLine);
  end;
 {$ENDIF}
 hwnd_first := FindWindow('TFrm_main', STR_APPNAME);
 if hwnd_first <> 0 then                   // app resides already in memory ??
  begin
   PostMessage(hwnd_first, WM_SYSTEM_UPDATED, 0, 0); // inform other program that registry has been updated
  end
 else
  begin
   Application.Initialize;
   Application.CreateForm(TFrm_Main, Frm_Main);
   Application.Run;
  end;
end.

my main form has a public method declared like this

Code:
procedure SystemUpdated(var msg : TMessage); message WM_SYSTEM_UPDATED;

// definition of WM_SYSTEM_UPDATED
{ custom windows messages }
 WM_USER                         = 22000; 
 WM_SYSTEM_UPDATED               = WM_USER+1;

so this is what happens:

run the first instance of the application.
run a second instance of the application, this one will detect that there's already on in memory, so it will store it's parameters (like a filename) in a specific registry location and then posts a user defined message to the existing app. the existing app receives the message from the queue and will load the the stored registry data...

Cheers,
Daddy

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Thanks for your comments.

TometaSoftware: It looks good, but how is it better? It seems to accomplish the same thing.. I'm fairly new to mutex. I'll be looking into mutexes.

whosrdaddy: I'm trying to make your code with mutex, because I will need to use them anyways.

Thanks!
 
Omnicog,

mutex is indeed a solution here, but don't forget you need to find the Handle of the running app's window to do the post message trick...

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
My post was to help you do the mutex correctly. My way is one of the safest ways that I have found to make sure that the mutex is never left behind.

I am still researching your original question:

"How can I get the current instance to load the file instead of displaying 'Only one allowed!'."

It would be nice to know.
 
I did post one solution for that, I'll be the first to admit that it's not a very nifty way of doing it, but it'll
work.

[bobafett] BobbaFet [bobafett]
Code:
if not Programming = 'Severe Migraine' then
                       ShowMessage('Eureka!');
 
I'm sorry, BobbaFet didn't mean to leave you out :D

I like your solution, but it could probably be done with a registry entry. I do appreciate your help though, thanks.
 
Alright, I got something working that using mutex and postmessage (no extra registry entries or files). It does not handle multiple command line arguments.

In case someone else is, in the App unit (Credit go to TometaSoftware, whosrdaddy for the hard parts, and random google results for the whole atom tables thing):
Code:
var
 hMutex: THandle;
 hwndFirst: HWND;
 lParam: Word;
 commandArg: AnsiString;
const
  APPLICATION_MUTEX_ID = 'AppMutex';
begin
  Application.Initialize;

  {See TometaSoftware's post for a better example}
  hMutex := CreateMutex(nil, True, PChar (APPLICATION_MUTEX_ID));

  if (hMutex = 0) or (GetLastError <> 0) then
  begin
    {See whosrdaddy's post for a better example}
    hwndFirst := FindWindow ('TfrmMain', nil);
    if ParamStr(1) <> '' then
    begin
      // Get the first command line argument
      commandArg := ParamStr(1);
      // Add to global atom table and get it's id
      lParam := GlobalAddAtom(PAnsiChar(commandArg));
      // Pass length of the string and the id
      PostMessage (hwndFirst, WM_SYSTEM_UPDATED, Length(commandArg), lParam)
    end
    exit;
  end;

Reciever:
Code:
const
  WM_SYSTEM_UPDATED = WM_USER + 1;
...
procedure SystemUpdateMessage(var Msg: TMessage); message WM_SYSTEM_UPDATED;
...

procedure TfrmMain.SystemUpdateMessage(var Msg: TMessage);
var
  FileName: String;
  Buffer: PChar;
begin
  // Get FileName from the global atom table
  Buffer := StrAlloc(Msg.wParam + 1);
  GlobalGetAtomName(Msg.lParam, Buffer, Msg.wParam + 1);
  GlobalDeleteAtom(Msg.lParam);
  FileName := StrPas(Buffer);
  // Do something with the FileName
  DoSomethingWithIt(FileName);
end;

If anyone is interested, I found this AFTER already putting most of the pieces together [sadeyes] that does exactly the same thing, but is a bit complex and probably does it better. It allows for more than one argument.

 
Omnicog, be aware that the line
Code:
 hwndFirst := FindWindow ('TfrmMain', nil);
is not 100% foulproof, suppose you have a second app (something totally different) with the same formclass name (TfrmMain). So you'll need to find a way to make SURE you have the right Hwnd...

-----------------------------------------------------
What You See Is What You Get
Never underestimate tha powah of tha google!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top