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 TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

only one instance of application? 2

Status
Not open for further replies.

CADTenchy

Technical User
Dec 12, 2007
237
GB
I had a good read around, and this seemed to be perfect:


It stopped me running a second instance, and popped the first to front, if not already there. Great!

But then I discovered if I copied the same exe to another folder, and ran that one, I could get two instances.

Is there a (not too complicated!) way to capture more than one instance of the same program, including when run from different places?


Steve (Delphi 2007 & XP)
 
This is a good example of the writer not quite understanding what the code is doing and not seeing a specific situation where a bug would present itself. In this case from glancing at the code on the page, Mr. Gajic seems to not notice that the method he is using involves file mapping, which depends on the path the program is in.

Anyhow...(one of those "I hate API Windows manipulation" things coming up) I'll post in pieces to explain what is going on:

Code:
program semtest;

uses
  Forms,
  Dialogs,
  Sysutils,
  Windows,
  Messages,
  semunit in 'semunit.pas' {Form1};

This is obviously the main source module of the project. We would have to put code here, since we need to stop running the standard VCL inits (Application.*) if we find another copy of the program is running. "semunit" is a VCL form, which has a button that quits the program, but it really doesn't matter for our example.

Code:
{$R *.RES}
var
  MySem: THandle; // semaphore address
  WinHandle: HWND; // window handle address
begin

Code:
 { Create semaphore, check for previous program existence, end if there }
  MySem := CreateSemaphore(nil,0,1,'MySemTest');
  if ((MySem <> 0) and (GetLastError = ERROR_ALREADY_EXISTS)) then
    begin
      MessageDlg('Program is already running', mtError, [mbOK], 0);

Here's what's doing the job. When you start your program, you set up a semaphore with whatever name you want. In the process of creating it, you can error-check for whether one already exists. The if statement is what is doing that.

Code:
      WinHandle := FindWindow(PChar('TApplication'), PChar('semtest'));
      if IsWindowVisible(WinHandle) then
        SendMessage(WinHandle, WM_SYSCOMMAND, SC_RESTORE, 0);

Now this is what is bringing the window to foreground. The first statement is to locate the window handle of the other program. (This is where the "I hate API window manipulation" comes in) Each window has an associated class name and window name. Hopefully, they are relatively unique, or this code falls apart.

Of course, if you browse windows (using GetWindow or EnumWindows), you'll find that there are windows with pretty wierd class and window names. This necessitates browsing for them if you are uncertain or using a program like Windowse. The standard for Delphi apps seems to be "TApplication" for class name and the caption name for the minimized window for the window name.

(of course if there's a much better way to do this, I would love to know.)

2nd line: Does it have a window associated with it?
3rd line: sends a message to the window to restore it.

Code:
      CloseHandle(MySem);
    end
  else
    begin
      Application.Initialize;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end;
  CloseHandle(MySem);
end.

else block of the code contains the Application.* calls.

HTH some.

----------
Measurement is not management.
 

Thanks a lot Glenn, that works way better.

Only issue is it's not bringing the existing instance to the front after the dialogue is dismissed.

Just to double check I pasted it in correctly:
Code:
begin
 { Create semaphore, check for previous program existence, end if there }
  MySem := CreateSemaphore(nil,0,1,'MySemTest');
  if ((MySem <> 0) and (GetLastError = ERROR_ALREADY_EXISTS)) then
    begin
      MessageDlg('Program is already running', mtError, [mbOK], 0);
      WinHandle := FindWindow(PChar('TApplication'), PChar('semtest'));
      if IsWindowVisible(WinHandle) then
        SendMessage(WinHandle, WM_SYSCOMMAND, SC_RESTORE, 0);
       CloseHandle(MySem);
    end
  else

Steve (Delphi 2007 & XP)
 

OK, I fixed the un-edited paste error of below:

Code:
WinHandle := FindWindow(PChar('TApplication'), PChar('[red]semtest[/red]'));

But still no joy on bringing to front.
I looked with Windowse, and it does seem to be Tapplication.
(Parents tab in Windowse, hover over main form's title bar)


Steve (Delphi 2007 & XP)
 
Remember to change this part, too.

Code:
MySem := CreateSemaphore(nil,0,1,[COLOR=red]'MySemTest'[/color]);

Anyway, to look for the window name (which is probably what you're not finding), you might have to run a phrase through Pos() using GetWindow (which works the same as FindFirst/FindNext) and GetWindowText, especially if the window caption changes. But the key thing that helps is to look at your project name (which is likely what it will be). The program project name for the app I posted here was "semtest". Likely what should go there is your project name, unless you change the window caption (not form caption - the form caption on my VCL form was "Semaphore Test").

But again, we're going into the "I hate API windows manipulation" thing again - it's not that easy to figure out what you should be looking for. This is especially true, since it seems all Delphi apps have a class name of TApplication, which makes checking both a necessity. Compare that with Internet Explorer - if I check for class name of "IEFrame", I know I have an IE window.

----------
Measurement is not management.
 
A little better code to try, if it helps:

Code:
WinHandle := FindWindow(PChar('TApplication'), PChar(Application.Title));

Application.Title is the text that shows up when you minimize your app on the toolbar.

----------
Measurement is not management.
 

Hmm still no good.
I have a line already in my project file defining application title:
Code:
      Application.Initialize;
      Application.Title := 'MyAppName';
[/code}

It is what appears when I  hover over it's button on the taskbar.
It did have spaces in it, so I just tried simplified one word version.
Now, my exe, project, main form caption, and project title are all named the same.

Still not playing the game :-(

Is window caption the same as Application Title (from Project/Project Options/Application) ?

I googled and looked in Delphi help for Findwindow (and enumwindow), it looked a bit beyond me. Also, assuming I knew what to do with it, where would I put it?


Steve (Delphi 2007 & XP)
 

I forgot to ask also, out of interest, why is it
Code:
if IsWindowVisible(WinHandle) then

not

Code:
if not IsWindowVisible(WinHandle) then

as I would have thought?

I tried this just in case :)


Steve (Delphi 2007 & XP)
 
Is window caption the same as Application Title (from Project/Project Options/Application) ?

I believe so, yes.

if IsWindowVisible(WinHandle) then

Why this? IsWindowVisible is as the name says, it returns true if there is a visible window associated with the application. With many apps on the system there isn't (like systray apps), and there's no point in trying to show a window on an app that doesn't have one.

----------
Measurement is not management.
 
CADTenchy - I've created a faq to show you what I've always used and has always worked well for me.

faq102-6959


Roo
Delphi Rules!
 

Thanks Roo, I appreciate that!

Got that working no problem.

One Q, is it possible to make it restore the first instance if it is minimised?
I tried using SW_RESTORE, which was stated to do so on delphi.about.com, but it did not work.




Steve (Delphi 2007 & XP)
 
I tried using SW_RESTORE,...
Not necessary. It will do that for you as written. See the DPR example at the end of the FAQ.

Roo
Delphi Rules!
 
BTW: I enjoyed viewing your slide shows &TY4*! :-D

Roo
Delphi Rules!
 

Glad you liked the slide shows. Hope you liked the site, it was a bit of a trial getting the scripts (not written by me I might add) to co-exist with my pages the way I wanted, and pass W3C. I've not had time for any activities of late!

You're very welcome, as is Glenn, I do appreciate the efforts you all make here to help us beginners.

Back on track, I did look at the ActivatePrevInstance procedure more closely, and could see the if minimised part.

Why could it not be working for me?

It brings to front the first instance of my app if it's not minimised ok tho.

Steve (Delphi 2007 & XP)
 
You're right: just tested - if the app is minimized, it will open a second instance. I don't think it did under 98SE when it was written. This is strange though, I have apps out there under WXP that won't run a second instance even if minimized. When I get to the office tomorrow, I'll compare that code with what I posted.

Windows function IsIconic(PrevWnd) is what appears to be failing.
Roo
Delphi Rules!
 
Good greef! After further testing, it is the line above "IsIconic" that is causing it to fail:
Code:
  if Boolean(IsDelphi) then Exit;
What that means is that Delphi must NOT be running! Please exit the IDE and run your app from a shortcut, the Run box, or the command line.

It should now work. Please let me know. And thanks for pointing that out, it should be noted in the FAQ and I'll add that next. Like I said in the FAQ, "overzealous clean-up of comments". :p

Roo
Delphi Rules!
 

Actually for me (on laptop and CAD station), the original code (from the FAQ) DOES prevent a second instance 100%.

What is does not do is restore the window to visible if it is minimised to taskbar.

Delphi running doesn't seem to have a bearing on that for me, just fired the app up on a fresh boot, before starting Delphi, and works same as when IDE running.

FYI, when I was testing the code, I would compile the app, then close the Delphi launched instance, and launch it from it's shortcut(s).
Although, I did notice the code still successfully prevented the second instance running if the first instance was launched from a compile in Delphi.


Steve (Delphi 2007 & XP)
 
Interesting... For me, compiled with D7E under WXP-SP3, it does restore the window to visible if it is minimised to taskbar.

But if Delphi is running, it does allow second instance.

However the app I'm testing only has a single form. I've seen where if Hide is called before calling ChildForm.ShowModal, then when minimized, it minimizes to the Desktop, and not to the Taskbar.

My XP is rather tweaked: Classic Start Menu (and Classic Folders): ON, Lock taskbar: OFF, Auto-hide taskbar: ON, Keep taskbar on top: ON, Group similar buttons: OFF, and Show Quick Launch: ON.

I don't know what other differences we may have to cause this variation. I'm on an HP laptop too, but doubt that matters. [ponder]


Roo
Delphi Rules!
 

Just tried your settings, which I normally do run on the desktop, still the same.

I'm not unduly worried, it does do the main part, stopping the second instance, and it brings to front non-minimised, so I'm still happy bunny.

TVM


Steve (Delphi 2007 & XP)
 

OK, I had a eureka moment.
I applied the same code to another older project, a much simpler one than my current one, and all is well.

It does exactly as it says on the tin.

So I guess somewhere along the line I'm 'breaking' it in my current project.
I think when I'm nearly done I'll see if I can find out why..

Thanks again Roo!

Steve (Delphi 2007 & XP)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top