Board index » cppbuilder » Finding the active instance of a program

Finding the active instance of a program


2004-09-30 04:28:53 PM
cppbuilder5
Hello,
I have a program which has to have only one instance running. For this, I
use a Mutex, which allows me to detect the existence of a previous instance
of the program.
If the program is already running, I want to restore it and, possibly, send
it a custom message (if the program is launched by clicking on a file). What
I am doing is :
::CreateMutex(NULL,NULL,"MyProgram");
if(GetLastError()) {
Application->Title = "";
HWND hPrevApp = ::FindWindow(NULL,"MyProgram");
if(hPrevApp) {
PostMessage(hPrevApp, WM_SYSCOMMAND,
SC_RESTORE, 0);
ShowWindow(hPrevApp, SW_RESTORE);
SetForegroundWindow(hPrevApp);
// other code possibly here
}
}
}
else {
// run the program
This does work, but as it uses FindWindow to find the instance of Myprogram,
it runs into problems if, for instance MyProgram is installed in a directory
called MyProgram... To cure it, I could change my application title to
something that has a better chance of being unique (as MyProgramLeProgramme,
or MyProgram_x235Rt), but then, this gets displayed in the tray icon, as the
name of the application.
So, is there a way either
1- (better) to retrieve the HWND of the running instance without tampering
with the application title, from the Mutex directly, or without FindWindow()
2- to make the above approach transparent, by allowing the tray icon to
display a different name than the Application->Title
Thanks in advance,
Francois
2-
 
 

Re:Finding the active instance of a program

I ran into exactly this problem. I got round it by making my app's title
intially "Newmyapp", and then calling FindWindow for "myapp", and, if it
wasn't there, change this instance's title to "myapp". Seems to work fine.
--
Mark Jacobs
DK Computing
www.dkcomputing.co.uk
"François Charton" < XXXX@XXXXX.COM >wrote in message
| I have a program which has to have only one instance running. For this, I
| use a Mutex, which allows me to detect the existence of a previous instance
| of the program.
| If the program is already running, I want to restore it and, possibly, send
| it a custom message (if the program is launched by clicking on a file). What
| I am doing is :
| ::CreateMutex(NULL,NULL,"MyProgram");
| if(GetLastError()) {
| Application->Title = "";
| HWND hPrevApp = ::FindWindow(NULL,"MyProgram");
| if(hPrevApp) {
| PostMessage(hPrevApp, WM_SYSCOMMAND,
| SC_RESTORE, 0);
| ShowWindow(hPrevApp, SW_RESTORE);
| SetForegroundWindow(hPrevApp);
| // other code possibly here
| }
| }
| }
| else {
| // run the program
| This does work, but as it uses FindWindow to find the instance of Myprogram,
| it runs into problems if, for instance MyProgram is installed in a directory
| called MyProgram... To cure it, I could change my application title to
| something that has a better chance of being unique (as MyProgramLeProgramme,
| or MyProgram_x235Rt), but then, this gets displayed in the tray icon, as the
| name of the application.
|
| So, is there a way either
| 1- (better) to retrieve the HWND of the running instance without tampering
| with the application title, from the Mutex directly, or without FindWindow()
| 2- to make the above approach transparent, by allowing the tray icon to
| display a different name than the Application->Title
 

Re:Finding the active instance of a program

"François Charton" < XXXX@XXXXX.COM >wrote:
Quote

I have a program which has to have only one instance running.
You're going to run into problems restoring the first instance
because of the way that windows works. It incorrectly record
the window state unless the window's exStyle includes the
WS_EX_APPWINDOW flag. However, applying the WS_EX_APPWINDOW
flag is problematic because it causes 2 icons to appear on the
taskbar - one for the application and one for the form.
Now you *can* get it restored from within the second instance
*but* the window state will be messed up. The form will look
as if it's been restored but when the user tries to minimize
it, they won't be able to because windows thinks that the
state is already minimized.
The solution is to override the form's WndProc method and keep
track of the window state yourself when ever the window state
changes:
protected: // User declarations
virtual void __fastcall WndProc(TMessage &Message);
void __fastcall TForm1::WndProc(TMessage &Message)
{
static bool IsMinimized = false;
if( Message.Msg == WM_SYSCOMMAND )
{
if( Message.WParam == SC_MINIMIZE )
{
IsMinimized = true;
}
else
{
IsMinimized = false;
}
}
else if( Message.Msg == UWM_MYCUSTOMMESSAGE )
{
if( IsMinimized )
{
Application->Restore();
}
}
TForm::WndProc(Message);
}
Quote
[...] To cure it, I could change my application title to
something that has a better chance of being unique
Your best choice is to change the main form's class name from
TForm1 or TMainForm to something like TMyCompanyNameAppName
which should make that unique.
Then if the mutex already exists, use the win32 api EnumWindows
to find the handle to the first instance. I wrote a component
that works off of the unique main form class name but for it
to be a component, it had to hook backwards (upwards) into
it's parent's WndProc which is different:
groups.google.com/groups
The only thing left is for you to modify the component to
account for passing data to the first instance - which BTW you
couldn't do from within the WinMain which is where all of your
code is now.
I think that the easiest solution is to send the first instance
a WM_COPYDATA message just before you send it the
SINGLE_INSTANCE_RESTORE_MESSAGE.
That way the component will still be generic enough to work
with other apps and all you have to do in your app is
something like:
protected: // User declarations
virtual void __fastcall WndProc( TMessage &Message );
private: // User declarations
char *Buffer;
void __fastcall AppActivate( TObject* );
public: // User declarations
__fastcall TForm1(TComponent* Owner);
__fastcall ~TForm1();
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
Buffer = NULL;
Application->OnActivate = AppActivate;
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
if( Buffer )
{
delete [] Buffer;
}
}
//-------------------------------------------------------------
void __fastcall TForm1::AppActivate(TObject *Sender)
{
if( Buffer )
{
// do something with Buffer
delete [] Buffer;
Buffer = NULL;
}
}
//-------------------------------------------------------------
void __fastcall TForm1::WndProc(TMessage &Message)
{
if( Message.Msg == WM_COPYDATA )
{
COPYDATASTRUCT *CDS = reinterpret_cast<COPYDATASTRUCT*>( Message.LParam );
if( CDS->lpData )
{
Buffer = new char[ CDS->cbData + 1 ];
strncpy( Buffer, CDS->lpData, CDS->cbData );
}
}
}
//-------------------------------------------------------------
~ JD
 

{smallsort}

Re:Finding the active instance of a program

"Mark Jacobs" <www.jacobsm.com/mjmsg?Borland%20Newsgroup>a écrit
dans le message de news: XXXX@XXXXX.COM ...
Quote
I ran into exactly this problem. I got round it by making my app's title
intially "Newmyapp", and then calling FindWindow for "myapp", and, if it
wasn't there, change this instance's title to "myapp". Seems to work fine.
That's what I did :
Quote
| Application->Title = "";
| HWND hPrevApp = ::FindWindow(NULL,"MyProgram");
| if(hPrevApp) {
| PostMessage(hPrevApp, WM_SYSCOMMAND,
| SC_RESTORE, 0);
| ShowWindow(hPrevApp, SW_RESTORE);
| SetForegroundWindow(hPrevApp);

The problem is that myapp tends to be installed (by users) in a directory
called myapp... or users might want to save their myapp documents in a
directory called myapp. There is no way to prevent this. If this directory
happens to be opened which I call FindWindow(), Windows will retrieve the
handle of the directory, not that of the program.
Francois
 

Re:Finding the active instance of a program

"JD" wrote:
Quote

You're going to run into problems restoring the first instance
because of the way that windows works. It incorrectly record
the window state unless the window's exStyle includes the
WS_EX_APPWINDOW flag. However, applying the WS_EX_APPWINDOW
flag is problematic because it causes 2 icons to appear on the
taskbar - one for the application and one for the form.

Now you *can* get it restored from within the second instance
*but* the window state will be messed up. The form will look
as if it's been restored but when the user tries to minimize
it, they won't be able to because windows thinks that the
state is already minimized.

The solution is to override the form's WndProc method and keep
track of the window state yourself when ever the window state
changes:

ok, I will try that. I didn't see the problem so far, but I probably did not
test it enough.
Quote
The only thing left is for you to modify the component to
account for passing data to the first instance - which BTW you
couldn't do from within the WinMain which is where all of your
code is now.

Why ? I was under the impression that having a ::RegisterWindowsMessage()
(or even a WM_USER message) to the corresponding in the WinMain (just before
the mutex creation code) would allow me to create a specific message to this
effect, which both instances would understand. I would then have a
SendMessage() which the other instance would receive and handle (in its
WndProc).
Quote
I think that the easiest solution is to send the first instance
a WM_COPYDATA message just before you send it the
SINGLE_INSTANCE_RESTORE_MESSAGE.

Can't I just pass a point to the data to in the SendMessage parameters
(using SendMessage instead of PostMessage, so that the memory zone
referenced by the pointer is not freed before I need it)?
Francois
 

Re:Finding the active instance of a program

JD,
Finally got it to work, here is my solution, together with a few comments...
Quote
Now you *can* get it restored from within the second instance
*but* the window state will be messed up. The form will look
as if it's been restored but when the user tries to minimize
it, they won't be able to because windows thinks that the
state is already minimized.

The solution is to override the form's WndProc method and keep
track of the window state yourself when ever the window state
changes:

Done that, using a WM_USER message, but same logic as yours... Pretty much
works as far as I have seen.
Quote
>[...] To cure it, I could change my application title to
>something that has a better chance of being unique

Your best choice is to change the main form's class name from
TForm1 or TMainForm to something like TMyCompanyNameAppName
which should make that unique.

Then if the mutex already exists, use the win32 api EnumWindows
to find the handle to the first instance. I wrote a component
that works off of the unique main form class name but for it
to be a component, it had to hook backwards (upwards) into
it's parent's WndProc which is different:

Actually I think I found a better way : FindWindowEx(NULL, NULL,
"TMyForm","MyApp");
this looks on both the title of the application and the name of the class...
This seems like the simplest way.
Quote
The only thing left is for you to modify the component to
account for passing data to the first instance - which BTW you
couldn't do from within the WinMain which is where all of your
code is now.

here is the way I did in (all in WinMain(), my mainform loading does quite a
bit of things, and I did not want to go through it).
in winmain :
::CreateMutex(NULL,NULL,"MedialandMut");
if(GetLastError()) {
Application->Title = "";
// getting the handle
HWND hPrevApp =
::FindWindowEx(NULL,NULL,"TMainForm","Medialand");
if(hPrevApp) {
// restore an iconic app
::SendMessage(hPrevApp, WM_USER+941,0,0);
// send it to the top of the z-buffer
::SetForegroundWindow(hPrevApp);
// have a parameter (trying to open a document)
if(ParamCount()==1) {
String current = ParamStr(1);
COPYDATASTRUCT cp;
cp.dwData=0;
cp.cbData=current.Length()+1;
cp.lpData=current.c_str();
// send it and process it
::SendMessage(hPrevApp,WM_COPYDATA,NULL,(unsigned int)&cp);
}
}
}
else {
in my main form
void __fastcall TMainForm::WndProc( TMessage &Message )
{
static bool IsMinimized = false;
if( Message.Msg == WM_SYSCOMMAND ){
if( Message.WParam == SC_MINIMIZE )IsMinimized = true;
else IsMinimized = false;
}
if( Message.Msg == WM_COPYDATA ) {
COPYDATASTRUCT *CDS = reinterpret_cast<COPYDATASTRUCT*>(
Message.LParam );
if( CDS->lpData )
{
char *Buffer = new char[ CDS->cbData + 1 ];
strncpy( Buffer, (char*)CDS->lpData, CDS->cbData );
// whatever processing there is to do (opening a document in this case)
delete[] Buffer;
}
}
if(Message.Msg==WM_USER+941) {
if(IsMinimized) Application->Restore();
}
TForm::WndProc( Message );
}
Thanks for the help
Francois
 

Re:Finding the active instance of a program

"François Charton" < XXXX@XXXXX.COM >wrote:
Quote

[...] I was under the impression that having a
::RegisterWindowsMessage() [...] would allow me to create a
specific message to this effect, which both instances would
understand.
That is correct.
Quote
I would then have a SendMessage() which the other instance
would receive and handle (in its WndProc).
That's also correct.
Quote
>I think that the easiest solution is to send the first instance
>a WM_COPYDATA message just before you send it the
>SINGLE_INSTANCE_RESTORE_MESSAGE.
Can't I just pass a point to the data to in the SendMessage
parameters [...]
No. You can't pass pointers around like that across application
boundries. For the second instance to tell the first instance
about any parameters that were included in the second instance,
you have to use shared memory or WM_COPYDATA or write it to a
temporary file.
~ JD
 

Re:Finding the active instance of a program

"François Charton" < XXXX@XXXXX.COM >wrote:
Quote

[...] I think I found a better way : FindWindowEx(NULL, NULL,
"TMyForm","MyApp");

this looks on both the title of the application and the name
of the class... This seems like the simplest way.
It won't *always* work with MDI applications because the title
changes when there is an open MDI child form which is why I
enumerated the windows instead.
~ JD
 

Re:Finding the active instance of a program

"JD" < XXXX@XXXXX.COM >a écrit dans le message de news:
415c5773$ XXXX@XXXXX.COM ...
Quote

"François Charton" < XXXX@XXXXX.COM >wrote:
>
>[...] I think I found a better way : FindWindowEx(NULL, NULL,
>"TMyForm","MyApp");
>
>this looks on both the title of the application and the name
>of the class... This seems like the simplest way.

It won't *always* work with MDI applications because the title
changes when there is an open MDI child form which is why I
enumerated the windows instead.

Should it? My impression was that in a run-once application, displaying the
name of the active child form in the icon tray was sort of useless (only one
instance of the app is open at any time, so no confusion is possible, and
when the app is minimised, knowing which child is active is not very
important).
Besides, you can still modify the parent form caption...
Francois
 

Re:Finding the active instance of a program

"François Charton" < XXXX@XXXXX.COM >wrote:
Quote

[...] displaying the name of the active child form in the
icon tray was sort of useless
I didn't mean the application title. I meant the window's
title which is the form's caption.
Quote
so no confusion is possible,
Only as long as it's not an MDI application because as soon as
you have a child form open, the main form's caption changes.
Try it ... you'll see.
Quote
Besides, you can still modify the parent form caption...
When would you do this to prepare for the user launching a
second instance?
~ JD
 

Re:Finding the active instance of a program

"JD" < XXXX@XXXXX.COM >a écrit dans le message de news:
415c6c28$ XXXX@XXXXX.COM ...
Quote

Only as long as it's not an MDI application because as soon as
you have a child form open, the main form's caption changes.
Try it ... you'll see.

Got it, when a MDI child is maximised (didn't notice it because my app,
which is actually an MDI app, has little need for this...), the main form
caption changes, and this confuses FindWindow().
There seem to be three ways around this :
1- FindWindows(MyClass, NULL), provided the string MyClass is special enough
to have a reasonable chance of being unique... In the approach you suggest,
I think it would be sufficient.
2- EnumWindows() with a callback function to this effect, the callback
looking for instance, at the name of the window (calling GetWindowText(),
searching to the application name as its prefix), and looking up the class
name... this is a bit more involved, but possibly more suited to components,
as you now have two ways to check for unicity
3- the bullet-proof (I believe) version, would imply registering a specific
message in the app, which would be sent instead of FindWindow() to all
topmost windows, one if its parameters would be the sender HWND, in return
the previous instance, if it exists, would send back another specific
message, with its HWND as the parameter (there are probably many variations
upon this theme), allowing the second instance to retrieve the handle of the
first... SendMessageTimeout() would have to be called for this, to allow for
the case where no instance exists. Kind of a "any one out there?" question.
Francois
 

Re:Finding the active instance of a program

"François Charton" < XXXX@XXXXX.COM >wrote:
Quote

[...] 3- the bullet-proof (I believe) version, would imply
registering a specific message in the app, which would be
sent instead of FindWindow() to all topmost windows, [...]
Sounds good in theory but that relies on all of the other open
applications to behave as expected - that they were programmed
well. In practice, I found that this approach can cause other
programs to hang and even crash.
The unique class name is the only reliable solution which will
work with FindWindow or EnumWindows.
~ JD
 

Re:Finding the active instance of a program

"JD" < XXXX@XXXXX.COM >wrote:
Quote

[...] The unique class name is the only reliable solution
which will work with FindWindow or EnumWindows.
Correction: FindWindow *could* find the second instance
instead of the first instance if you're only using the class
name - which is the case with an MDI application.
You *must* enumerate the windows for the code to work 100% for
MDI applications.
~ JD
 

Re:Finding the active instance of a program

Avoiding Multiple Instances of an Application
www.codeproject.com/cpp/avoidmultinstance.asp
By Joseph M. Newcomer
Avoiding Multiple Instances of an Application - Why FindWindow doesn't work
www.developerfusion.com/show/1716/5/
By Joseph M. Newcomer
Avoiding Multiple Instances of an Application
www.flounder.com/nomultiples.htm
By Joseph M. Newcomer
"François Charton" < XXXX@XXXXX.COM >wrote in message
Hello,
I have a program which has to have only one instance running...
So, is there a way...
 

Re:Finding the active instance of a program

Also "How to limit 32-bit applications to one instance by using Visual C++"
support.microsoft.com/kb/q243953/