Link to home
Start Free TrialLog in
Avatar of zitt
zitt

asked on

D3: Determining if App is already running (no HPrevInst)

I have a D1 app that I'm migrating to Win32 via Delphi 3. In this app, I used the HPrevInst property of TApplication to detect that I already have the application running. When I moved the code to Delphi 3, I've found that HPrevInst nolonger exists?!??!

So, the question is this:

How to I determine that my D3 app is already running?

Note: I want to know if my program is running, not just any application.

Note#2: Simple is better. I'm looking to write a function to replace the HPrevInst property so I don't have to patch a lot of code.

(sometimes I just wanna slap Borland for this kinda crap)
John
Avatar of JimBob091197
JimBob091197

Hi

You can try checking if your main form's window already exists.
In your project code, before "Application.Initialize;" add the following:

if (ProgramIsRunning) then
  { Exit the program or show some message... };

Implement ProgramIsRunning as follows (maybe you want to call it HPrevInst??):

function ProgramIsRunning: Boolean;
begin
  // TMyMainForm is your apps main form.
  // I.e. it is the form class called in the project code by:
  //      Application.CreateForm(TMyMainForm, MyMainForm);
  Result := (FindWindow('TMyMainForm', nil) > 0);
end;

Regards,
JB
I agree with JimBob there....

you can also try this code :

MessageID := RegisterWindowMessage('Check if already running');
if hPrevInst <> 0 then
begin
  PostMessage(hwnd_Broadcast,MessageId,0,0);
end
else
begin
  normal startup code.
....
end;

Then you can restore the original instance by adding this code.

create this procedure ...

procedure TYourForm.OnAppMessage(var Msg : TMsg;var Handled : Boolean);
begin
  if Msg.Message = MessageId then
  begin
    Application.Restore;
    TYourForm.SetFocus;
  end;{if Msg.Message = MessageId then}
end;

Then add this to the onFormCreate...

procedure TYourForm.FormCreate(Sender: TObject);
begin
  Application.OnMessage := OnAppMessage;
end;


Avatar of zitt

ASKER

I don't understand how

if hPrevInst <> 0 then

Can work in a Delphi3.0 app, when hPrevInst is not defined in Delphi3.

Are you attempting to combine your answer with JimBob's comment?

Also, BoRiS's answer is more complicated than I'd like. The impact to my D1 code should be minimal.

Does anyone know how D1 implemented the hPrevInst property?

John
Avatar of zitt

ASKER

I tired Jim's comments and I can't get
FindWindow('TMyMainForm', nil)

to return zero when there my app isn't running.

The end result here folks is that, I want to determine my if my program is already running... do some "checks" to determine if I should halt this new instance of my program.
Hi zitt

The following code is from a test program I've just written.  This works fine in Delphi 3.  The 1st program runs.  When you try to run a 2nd instance, it pops up a msg and then closes.

// --- Start of Project code ---
program Project1;

uses
  Forms, Windows, Dialogs,
  Unit1 in 'Unit1.pas' {frmTest};

{$R *.RES}

begin
  if (FindWindow('TfrmTest', nil) > 0) then
    ShowMessage('Already running...')
  else begin
    Application.Initialize;
    Application.CreateForm(TfrmTest, frmTest);
    Application.Run;
  end;
end.

// --- End of Project code ---


Unit1.pas has a form called frmTest (class is thus TfrmTest).
Your FindWindow may have returned non-zero if there was already an existing window in the system with the same class name.  (E.g. TForm1)  Try giving your main form a name (TfrmThisIsUnique) to test this method...

Regards,
JB
"I don't understand how"

      if hPrevInst <> 0 then

Answer:

If it is 0 then there is no previous instance otherwise your app is already running somewhere.

Yes I'm was trying to elaborate on JimBob's comment
Hi zitt

HPrevInst IS defined in D3 (in System.pas) but it may (??) not be used in Win 95...

JB
From: "The Graphical Gnome" <rdb@ktibv.nl>

Taken from Delphi 2 Developers Guide by Pacheco and Teixeira with heavy modifications.

Usage: In the Project source change to the following


------------------------------------------------------------------------


if InitInstance then
  begin
     Application.Initialize;
     Application.CreateForm(TFrmSelProject, FrmSelProject);
     Application.Run;
  end;

unit multinst;
{
  Taken from Delphi 2 Developers Guide by Pacheco and Teixeira
  With heavy Modifications.

  Usage:
  In the Project source change to the following

  if InitInstance then
  begin
     Application.Initialize;
     Application.CreateForm(TFrmSelProject, FrmSelProject);
     Application.Run;
  end;

   That's all folks ( I hope ;()
}


interface

uses Forms, Windows, Dialogs, SysUtils;

const
  MI_NO_ERROR          = 0;
  MI_FAIL_SUBCLASS     = 1;
  MI_FAIL_CREATE_MUTEX = 2;

{ Query this function to determine if error occurred in startup. }
{ Value will be one or more of the MI_* error flags. }

function GetMIError: Integer;
Function InitInstance : Boolean;

implementation

const
  UniqueAppStr : PChar;   {Change for every Application}

var
  MessageId: Integer;
  WProc: TFNWndProc = Nil;
  MutHandle: THandle = 0;
  MIError: Integer = 0;


function GetMIError: Integer;
begin
  Result := MIError;
end;

function NewWndProc(Handle: HWND; Msg: Integer; wParam,
                    lParam: Longint): Longint; StdCall;
begin

  { If this is the registered message... }
  if Msg = MessageID then begin
    { if main form is minimized, normalize it }
    { set focus to application }
    if IsIconic(Application.Handle) then begin
      Application.MainForm.WindowState := wsNormal;
      ShowWindow(Application.Mainform.Handle, sw_restore);
    end;
    SetForegroundWindow(Application.MainForm.Handle);
  end
  { Otherwise, pass message on to old window proc }
  else
    Result := CallWindowProc(WProc, Handle, Msg, wParam, lParam);
end;

procedure SubClassApplication;
begin
  { We subclass Application window procedure so that }
  { Application.OnMessage remains available for user. }
  WProc := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC,
                                    Longint(@NewWndProc)));
  { Set appropriate error flag if error condition occurred }
  if WProc = Nil then
    MIError := MIError or MI_FAIL_SUBCLASS;
end;

procedure DoFirstInstance;
begin
  SubClassApplication;
  MutHandle := CreateMutex(Nil, False, UniqueAppStr);
  if MutHandle = 0 then
    MIError := MIError or MI_FAIL_CREATE_MUTEX;
end;

procedure BroadcastFocusMessage;
{ This is called when there is already an instance running. }
var
  BSMRecipients: DWORD;
begin
  { Don't flash main form }
  Application.ShowMainForm := False;
  { Post message and inform other instance to focus itself }
  BSMRecipients := BSM_APPLICATIONS;
  BroadCastSystemMessage(BSF_IGNORECURRENTTASK or BSF_POSTMESSAGE,
                         @BSMRecipients, MessageID, 0, 0);
end;

Function InitInstance : Boolean;
begin
  MutHandle := OpenMutex(MUTEX_ALL_ACCESS, False, UniqueAppStr);
  if MutHandle = 0 then
  begin
    { Mutex object has not yet been created, meaning that no previous }
    { instance has been created. }
    ShowWindow(Application.Handle, SW_ShowNormal);
    Application.ShowMainForm:=True;
    DoFirstInstance;
    result := True;
  end
  else
  begin
    BroadcastFocusMessage;
    result := False;
  end;
end;

initialization

begin
   UniqueAppStr := Application.Exexname;
   MessageID := RegisterWindowMessage(UniqueAppStr);
   ShowWindow(Application.Handle, SW_Hide);
   Application.ShowMainForm:=FALSE;
end;

finalization
begin
  if WProc <> Nil then
    { Restore old window procedure }
    SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(WProc));
end;
end.
Why so much code? It seems a bit like shooting canaries with dumdum bullets. I simply did like this:

program SingleInst;

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

var
  Handle1 : LongInt;
  Handle2 : LongInt;

{$R *.RES}

  Procedure Run;
  Begin
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  End;

begin
  Application.Initialize;
  If LowerCase(ParamStr(1))='/debug' then Run // See not to interfear with Dephi projects at designtime
  else
  Begin
    Handle1 := FindWindow('TForm1',nil);
    if handle1 = 0 then Run 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;
end.

..Just remember to rename your Form instance to anything but Form1 when using using this.

Regards
Williams
williams2:  Your comment is almost identical to my own!!  (See my 1st & 2nd comments...)  All you've done is to add code to restore the prev app and bring it to foreground, but zitt is having trouble even getting FindWindow to work!

JB
Hi JimBob

Sorry, I missed your comment, hehe. I just saw Waldeks answer and felt like doing some Cut'n'paste job. But you're absolutely right. :-)

Regards
Williams
Avatar of zitt

ASKER

Waldek and others,

I appreciate your comments and proposed answer(s). I'm "rejecting" Waldek's answer not for his effort or solution, but because his solution is too complicated to implement without a complex "patch".

JimBob,

Yours was the most elegant, I want to give you the points. Please respond to the question with an answer so I can award points.

Technical:
The problem with my FindWindow was that under the Delphi3 IDE, the code:

  if (FindWindow('TfrmTest', nil) > 0) then
    ShowMessage('Already running...')

was detecting the "open" project in Delphi 3 and returning that my app was already running. I figured this out by closing Delphi and running the standalone EXE.

Now the question is:

Without a lot of code (or a command-line parameter), how does one detect he's trying to run under the IDE?

Thanks for all the responses!
John
Yup! That's nice! The most simple thing is to add a "/debug" parameter in the Delphi project paramters menu.
I've tried to prevent this otherwise, but it's not trivial. I tried locating the application objects ComponentState property, and see to it to be different than csDesigning. That didn't seem to work, so I tried using messagehandling instead. That did work fine, but it's a bit complex. (Though that might be because of my incomplete knowledge to messagehandling).
Another approach could be to use DDE instead, and that's a typical MS solution, but it suck's a bit in my opninion.

All in all I ended up using a /debug parameter, since it doesn't need much attention compared to other approaches, but if you come up with other ideas, I'm very interested.

Regards
Williams
ASKER CERTIFIED SOLUTION
Avatar of JimBob091197
JimBob091197

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi JB

I owe you one. :-)

Regards
Williams
Avatar of zitt

ASKER

JimBob,

I'm very pleased with all your answers. Everything has worked as expected.

Again, The key to getting FindWindow to work was realizing that it will return non-zero if your project is open in Delphi3.

John