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
zittAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

JimBob091197Commented:
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
0
BoRiSCommented:
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;


0
zittAuthor Commented:
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
0
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

zittAuthor Commented:
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.
0
JimBob091197Commented:
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
0
BoRiSCommented:
"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
0
JimBob091197Commented:
Hi zitt

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

JB
0
WaldekCommented:
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.
0
williams2Commented:
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
0
JimBob091197Commented:
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
0
williams2Commented:
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
0
zittAuthor Commented:
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
0
williams2Commented:
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
0
JimBob091197Commented:
Hi again

I take it then that you are satisfied with "Result := (FindWindow('TMyMainForm', nil) > 0);"?

BTW, to determine if the Delphi IDE is running, I have the following function.  (Note: it checks for the Delphi main window and the Object Inspector (even if the Obj Inspector is not visible it still works).  You can check for other windows too (e.g. Watch window), but these 2 should be enough.  Also, this function checks if Delphi is running, but not necessarily that your app is the current Delphi project!!)

function DelphiIsRunning: Boolean;
begin
  Result := (FindWindow('TAppBuilder', nil) > 0) and
            (FindWindow('TPropertyInspector', nil) > 0);
end;

Cheers,
JB
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
williams2Commented:
Hi JB

I owe you one. :-)

Regards
Williams
0
zittAuthor Commented:
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
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.