HOME - - - - - - - TUTORIALS INDEX - - - - - - - - - - - - Other material for programmers

Delphi: Restricting a program to running only once, using mutexes

This tutorial is pretty rough around the edges, but was far enough along that I thought you'd want what's here. I'll try to revisit it, polish it, later.

This is not typical of other programs in my tutorials. Amoung other things, it is highly derivative of other people's efforts I found on the web. Also it involves direct edits of the application's .dpr unit, which is generally unwise.

Start a new app. Put a label on main form, text "Welcome". Rename form DD44f1 Save app:

Unit 1 as DD44u1.pas App as DD44

Don't try to run the program again until you are told to. Next...

Use View|Units to open DD44.dpr (the .dpr may be hidden) in your code editor. It should start...
program DD44;

uses
  Forms,
  DD44u1 in 'DD44u1.pas' {Form1};

{$R *.RES}
Add "Windows," (not in quotes!) just after "uses", and replace the default....
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
... with ....
 {Attempt to create a named mutex}
  CreateMutex(nil, false, 'MyApp');
 {if it failed then there is another instance}
  if GetLastError = ERROR_ALREADY_EXISTS then begin
   {Send all windows our custom message - only our other}
   {instance will recognise it, and restore itself}
    SendMessage(HWND_BROADCAST,
                RegisterWindowMessage('MyApp'),
                0,
                0);
   {Lets quit}
    Halt(0);
  end;
  Application.Initialize;
 {Tell Delphi to un-hide it's hidden application window}
 {This allows our instance to have a icon on the task bar}
  Application.ShowMainForm := true;
  ShowWindow(Application.Handle, SW_RESTORE);
  Application.CreateForm(TForm1, Form1);
  Application.Run;
(The rems were provided for you Joe C. Hecht (joehecht@xx.xx) in his newsgroup post...

Subject: Re: How to prevent another instance of same application?
Newsgroups: borland.public.delphi.vcl.components.writing
Date: 1999/01/12

Now change over to editing DD44.pas.

For those who do NOT want to be taken through the process by the hand... what we are moving towards is....

===========Start of "the answer"=====
unit DD44u1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  OldWindowProc : Pointer; {Variable for the old windows proc}
  MyMsg : DWord; {custom systemwide message}

function NewWindowProc(WindowHandle : hWnd;
                       TheMessage   : LongInt;
                       ParamW       : LongInt;
                       ParamL       : LongInt) : LongInt stdcall;
begin
  if TheMessage = MyMsg  then begin
   {Tell the application to restore, let it restore the form}
    SendMessage(Application.handle, WM_SYSCOMMAND, SC_RESTORE, 0);
    SetForegroundWindow(Application.Handle);
   {We handled the message - we are done}
    Result := 0;
    exit;
  end;
 {Call the original winproc}
  Result := CallWindowProc(OldWindowProc,
                           WindowHandle,
                           TheMessage,
                           ParamW,
                           ParamL);
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
 {Register a custom windows message}
  MyMsg := RegisterWindowMessage('MyApp');
 {Set form1's windows proc to ours and remember the old window proc}
  OldWindowProc := Pointer(SetWindowLong(Form1.Handle,
                                         GWL_WNDPROC,
                                         LongInt(@NewWindowProc)));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 {Set form1's window proc back to it's original procedure}
  SetWindowLong(Form1.Handle,
                GWL_WNDPROC,
                LongInt(OldWindowProc));
end;

begin
 {Tell Delphi to hide it's hidden application window for now to avoid}
 {a "flash" on the taskbar if we halt due to another instance}
  ShowWindow(Application.Handle, SW_HIDE);
end.
======End of "the answer"=====================


Taking that step by step....

Just after DD44u1.pas's "{$R *.DFM}", add...
var
  OldWindowProc : Pointer; {Variable for the old windows proc}
  MyMsg : DWord; {custom systemwide message}

function NewWindowProc(WindowHandle : hWnd;
                       TheMessage   : LongInt;
                       ParamW       : LongInt;
                       ParamL       : LongInt) : LongInt stdcall;
begin
  if TheMessage = MyMsg  then begin
   {Tell the application to restore, let it restore the form}
    SendMessage(Application.handle, WM_SYSCOMMAND, SC_RESTORE, 0);
    SetForegroundWindow(Application.Handle);
   {We handled the message - we are done}
    Result := 0;
    exit;
  end;
 {Call the original winproc}
  Result := CallWindowProc(OldWindowProc,
                           WindowHandle,
                           TheMessage,
                           ParamW,
                           ParamL);
end;
---
Then double click on DD44's form to create an empty OnFormCreate handler, and make it....
begin
 {Register a custom windows message}
  MyMsg := RegisterWindowMessage('MyApp');
 {Set form1's windows proc to ours and remember the old window proc}
  OldWindowProc := Pointer(SetWindowLong(Form1.Handle,
                                         GWL_WNDPROC,
                                         LongInt(@NewWindowProc)));
---
Go into the object inspector, go to Form1's event handlers, double click in the edit box to the right of "OnDestroy" to set up a blank OnDestroy handler, and make it....
begin
 {Set form1's window proc back to it's original procedure}
  SetWindowLong(Form1.Handle,
                GWL_WNDPROC,
                LongInt(OldWindowProc));
---
Just before the final "end." of DD44u1.pas, add....
begin
 {Tell Delphi to hide it's hidden application window for now to avoid}
 {a "flash" on the taskbar if we halt due to another instance}
  ShowWindow(Application.Handle, SW_HIDE);
(No... I didn't know that the units of an application could have a main block, either!)

That's it! Your program should now work. Of course, you cannot even try to start two instances of the program from the Delphi IDE. But you don't have to shut down the Delphi while you try to start the program using, say, Windows Explorer.

There is a little frill in the code: If the program is already running, and that instance is minimized, then it will be restored by a second attempt to run the program.

I would replace "MyApp" with something unique to the program you wish to protect from multiple instances. A date/time stamp is useful. I used DD44030504 in my work, as it is program DD44, and I did this write up on the 3rd of May 2004. You have to replace "MyApp" in three places: twice in DD44.dpr, and once in DD44u1.pas.

I was a little surprised that there was no "You can't start a second instance" message... but in fact it did me a favor. I needed the routines to allow a program to be restarted on a school computer (using Windows scheduler) each afternoon if pupils had forgotten, but I could not have more than one instance running. As the restart attempt will be done by an unattended machine, a message which should be responded to could be a nuisance. If you want such a message, I wish you luck! I tried to cause one a number of ways... without success!


Another interesting newsgroup discussion (see groups.google.com) covered a way to allow multiple instances of a single application, but only if each was working with a different directory of the hard disc. The basic idea was to have a file in the directory which could be locked for one instance's exclusive use. The thread was as follows:

From: Andy (x.x@x.x) Subject: preventing runing another instance of an app from the same dir Newsgroups: alt.comp.lang.borland-delphi Date: 2003-11-30 14:17:03 PST


   Search this site or the web        powered by FreeFind
 
  Site search Web search
Site Map    What's New    Search


Click here if you're feeling kind! (Promotes my site via "Top100Borland")
Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows pc) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!

Click here to visit editor's freeware, shareware page.


Link to Tutorials main page
Here is how you can contact this page's author, Tom Boyd.


Valid HTML 4.01 Transitional Page WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....