
{ TPrevInst: component to limit multiple instances of your program
  Target   : Delphi 2.x
  Version  : 1.0
  Date     : 25 August 1997
  Freeware
  
  NOTE     : I took almost all code here from The Unofficial Newsletter 
             of Delphi Users - Issue #16 - September 1996 and made this
             component for easy use.
             
             Article's title:
             Limiting Multiple Instances Of Your Program
             by Robert Vivrette - CIS: 76416,1373
                
	     See the end of this document.All mr Vivrette's article included.

 USING TPrevInst
             1. Install PrevInst.pas as usual (menu Component|Install)
                There is no .dcr file
             2. Drop an instance of TPrevInst on your main form	
             3. Set property AllowedInst to how many allowed instances 
                you want (default:=1)
             4. On your main's form OnCreate add:
                PrevInst1.Execute
             5. Run your program.


Don't forget:Thanks to mr Vivrette




Theodoros Bebekis "Lathe Viosas", Salonica, Greece.
Hmmm....!



}


{************************* unit PrevInst ********************************}


unit PrevInst;

interface

uses
  Forms,Windows,SysUtils,Classes,Dialogs;


type

  TPrevInst = class(TComponent)
  private
    FAllowedInst: integer;

  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function Execute: boolean; virtual;

  published
    property AllowedInst: integer read FAllowedInst write FAllowedInst;

  end;

procedure Register;



implementation


var
  MyAppName   : array[0..255] of Char;
  MyClassName : array[0..255] of Char;
  NumFound    : Integer;
  LastFound   : HWnd;
  MyPopup     : HWnd;



{------------------------------Create-----------------------------------------}

constructor TPrevInst.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  AllowedInst:=1;
end;


{------------------------------Destroy-----------------------------------------}

destructor TPrevInst.Destroy;
begin
  inherited Destroy;
end;


{------------------------LookAtAllWindows-----------------------------------}

function LookAtAllWindows(Handle: HWND; Temp:LongInt): BOOL; stdcall;
var
  WindowName : array[0..255] of Char;
  ClassName  : array[0..255] of Char;
begin

  // Go get the windows class name
  if GetClassName(Handle,ClassName,SizeOf(ClassName)) > 0 then
    // Is the window class the same?
    if StrComp(ClassName,MyClassName) = 0 then
      // Get its window caption
      if GetWindowText(Handle,WindowName,SizeOf(WindowName)) > 0 then
        // Does this have the same window title?
        if StrComp(WindowName,MyAppName)=0 then
        begin
          Inc(NumFound);
          if Handle <> Application.Handle  // Are the handles different?
          then LastFound := Handle;  // Save it so we can bring it to the top later.
        end;

end;   { LookAtAllWindows }



{-------------------------------Execute----------------------------------------}

function TPrevInst.Execute: boolean;
begin

  Result:=False;
  NumFound := 0; LastFound := 0;
  // First, determine what this application's name is
  GetWindowText(Application.Handle,MyAppName,SizeOf(MyAppName));
  // Now determine the class name for this application
  GetClassName(Application.Handle,MyClassName,SizeOf(MyClassName));
  // Now count how many others out there are Delphi apps with this title
  EnumWindows(@LookAtAllWindows,0);
  if NumFound > AllowedInst then
  // There is another instance running, bring it to the front!
  begin
    ShowMessage('There are ' + IntToStr(AllowedInst) +' instance(s)' + #13 +
                'of '   + MyAppName                                  + #13 +
                'already running');
    MyPopup := GetLastActivePopup(LastFound);
    BringWindowToTop(LastFound);        // Bring it to the top in the ZOrder
    if IsIconic(MyPopup)                // Is the window iconized?
    then ShowWindow(MyPopup,SW_RESTORE) // Restore it to its original position
    else SetForegroundWindow(MyPopup);  // Bring it to the front

    Result:=True;   // ?
    Application.Terminate;
  end
  else Result:=False;

end;  { Execute }



{------------------------------------Register----------------------------------}

procedure Register;
begin
  RegisterComponents('Samples', [TPrevInst]);
end;

end.


{************************* End of unit PrevInst ****************************}




Limiting Multiple Instances Of Your Program
by Robert Vivrette - CIS: 76416,1373
In Delphi 1.0 and other 16-bit programming environments it was quite simple to determine if another instance of your application was already running. The variable hprevinst held a value that defined if another copy was running, and by examining this value you could prevent a second copy from launching.
With the 32-bit version of Delphi, the problem runs a little deeper and cannot be solved this way. 32-bit applications all run in their own address space and so you could have multiple copies of an app running and each would think it was the only one. There are occasions however when you may want to restrict multiple instances of your program. The project below demonstrates how this is done.

Essentially, this technique scans the Windows environment looking for other applications that might be duplicate copies. When you launch the application, it uses the EnumWindows function along with a user-defined callback function to look at each window currently present. For each window, we look to see if it has the same class name and same window title as the application that is attempting to launch. We should expect to see at least one window that matches this criteria namely the application that is performing the test (i.e. it is seeing itself). We will always see this one because the application has already been created by the time we conduct the test.

If there are any additional windows present that match this search criteria, then we can assume that a copy of the application is already running and we will terminate the one that is making this test. As a side effect of how I programmed this, you can modify the AllowedInstances constant to permit, say, two or three copies of the program to run before preventing others. Not that many people will ever do this, but it is there nonetheless.
Delphi applications will have a class of 'TApplication' unless it has been changed somehow. Their name will be the name of the program, not the name of the main form. For example, if your project source said 'Program Project1' at the top, the applications 'name' or window title would be '
Project1'. This behavior exists because Delphi applications create a phantom window that is the 'Application'. The main form is a child window of this application window.

If we find that a copy of the application is running, it would be appropriate to bring it to the top of the Window Z-order so that the user can see it. Remember, the user might not be aware that another copy is running so bringing it to the top will make this clear. If this step was not done, an application could be minimized and the user would be clicking away on the application icon trying to launch a copy and nothing would be happening.

program Project1;

uses
  Forms, Windows, SysUtils,
  unit1 in 'unit1.pas' {Form1};

{$R *.RES}

const
  AllowedInstances = 1;

var
  MyAppName   : Array[0..255] of Char;
  MyClassName : Array[0..255] of Char;
  NumFound    : Integer;
  LastFound   : HWnd;
  MyPopup     : HWnd;

function LookAtAllWindows(Handle: HWND; Temp: LongInt): BOOL; stdcall;
var
  WindowName : Array[0..255] of Char;

  ClassName  : Array[0..255] of Char;
begin
  // Go get the windows class name
  if GetClassName(Handle,ClassName,SizeOf(ClassName)) > 0 then
    // Is the window class the same?
    if StrComp(ClassName,MyClassName) = 0 then
      // Get its window caption
      if GetWindowText(Handle,WindowName,SizeOf(WindowName)) > 0 then
        // Does this have the same window title?

        if StrComp(WindowName,MyAppName)=0 then
          begin
            inc(NumFound);
            // Are the handles different?
            if Handle <> Application.Handle then
              // Save it so we can bring it to the top later.
              LastFound := Handle;
          end;
end;

begin
  NumFound := 0; LastFound := 0;
  // First, determine what this application's name is

  GetWindowText(Application.Handle,MyAppName,SizeOf(MyAppName));
  // Now determine the class name for this application
  GetClassName(Application.Handle,MyClassName,SizeOf(MyClassName));
  // Now count how many others out there are Delphi apps with this title
  EnumWindows(@LookAtAllWindows,0);
  if NumFound > AllowedInstances then
    // There is another instance running, bring it to the front!
    begin
      MyPopup := GetLastActivePopup(LastFound);

      // Bring it to the top in the ZOrder
      BringWindowToTop(LastFound);
      // Is the window iconized?
      if IsIconic(MyPopup) then
        // Restore it to its original position
        ShowWindow(MyPopup,SW_RESTORE)
      else
        // Bring it to the front
        SetForegroundWindow(MyPopup);
    end
  else
    // None running - allow this instance to continue
    begin
      // This is the code that normally would be in the project source

      Application.Initialize;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end
end.



{***********************************************************************}


In a new article mr Vivrette writes:

The Unofficial Newsletter of Delphi Users - Issue #20 - March 1997

A Bit More About Previous Instances
by Robert Vivrette - RobertV@compuserve.com
In Issue #16 of UNDU, I presented an article about limiting multiple instances of a program in Delphi

It turns out there is one additional issue that needs addressing Using the source in issue #16, if you run an application, then minimize it, then try to launch it again, it restores the original copy (as it should). However, you can not then re-minimize the application. It just ignores you.
The problem stems from the fact that there is a hidden application window floating around. The applications MainForm is a child of this window. When you restore just the main form, the application still thinks it's minimized. Then when you click on the minimize button of the main form, the application says "forget it... I am already minimized". The solution is that the application needs to be restored instead of the main form being restored. When you are searching for a second instance of the application, you really should be looking for the application window and not the main form window. However, it can be done the latter way also. Here is a modified example of the DPR source that shows how this can be done to make the technique work correctly.

program Project0;

uses
  Windows,
  Forms,
  Unit0 in 'Unit0.pas' {Form1};

var
  Handle1 : LongInt;
  Handle2 : LongInt;

{$R *.RES}

begin
  Application.Initialize;
  Handle1 := FindWindow('TForm1',nil);
  if handle1 = 0 then
    begin
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end
  else
    begin
      {Obtain handle to owner of Main Form. This is the application window}

      Handle2 := GetWindow(Handle1,GW_OWNER);
      {Hide application window to avoid zoom effect}
      ShowWindow(Handle2,SW_HIDE);
      {Restore application window}
      ShowWindow(Handle2,SW_RESTORE);
      {Set Main Form as foreground window}
      SetForegroundWindow(Handle1);
    end;
end.


Note that we first find the Main form window, then use GetWindow to find its owner. Then we send the restore to that window, and then set the main form as the foreground window. When the restore goes to the application window, it restores the main form. I am sending a hide to the application first to avoid a zoom effect from the Win95 task bar. If you comment out the line with SW_Hide, you will see what I mean.
Also keep in mind that this technique works correctly only outside of the Delphi IDE. When you try to run an application from the IDE, the design-time copy of the main form is still around and Windows see's that as another instance of the program according to our test.

1995, 1996, 1997 - Robert Vivrette - Prime Time Programming



I did not notice such a problem with this component
Thanks again
Theodoros Bebekis


