15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Activating a previous instance of an app revisited

MARGHAL@gw2k.com
Friday, May 03, 1996

Environment:  Visual C++ 4.1   with  Win95 and WinNT	

Ever wonder why the question of how to limit an application to a single
instance and activate the original instance comes up so often? Having had to
implement this functionality myself this week, I now know why. The sample
programs don't work, the knowledgebase articles and faq are out of date, and
the answers I've seen posted to this list were incomplete or wouldn't work 
in
all cases. Here's a slightly different way to solve the problem.

Actually there are two questions to answer:

1) How to detect a previous instance of a program.
2) How to activate the first instance, or request it to do something.

The first question has been discussed on the list in detail recently with 
several good approaches suggested.  I used the approach that creates and 
checks a named mutex in the InitInstance call.

The second question is, I think, the harder one: how to get the handle of
the main window of the first instance. The common answer is to use
FindWindow or EnumWindow, however, I prefer not to use those functions.
EnumWindow is somewhat inefficient and using FindWindow pretty much requires
me to register my own class (another task for which a *complete* example 
is lacking).

The technique I used is to create a shared data segment in the application's
EXE, declare a global handle variable in the shared segment, and store the
window handle of the first instance's frame window in the global variable.
Subsequent instances of the application can get the handle from the global
variable and use it to manipulate the first instance.


If you haven't used shared data segments in an EXE before here's how:

In the cpp file for the CWinApp object create a shared data segment like 
this:

    #pragma data_seg (".MYAPP_SHARED" )    
    // We will set this to the main window handle in CWinApp's 
ActivateFrame.
    HWND gAppHwnd = 0;  // Must be initialized, or it won't be in shared 
memory
    #pragma data_seg () // End the shared segment.


In the cpp file for CMainFrame, declare the shared global variable as 
extern:

    extern HWND gAppHwnd;   


Override the ActivateFrame member function in CMainFrame and save the 
app's handle.

    // Save the app's handle in shared global memory.
    if ( gAppHwnd == 0 )
        gAppHwnd = AfxGetMainWnd()->GetSafeHwnd();


At the beginning of CWinApp's InitInstance, check for a previous instance 
with
a couple of helper functions:

    // Allow one and only one instance of this application to run.
    if ( FoundPrevInstance() )
    {
        UsePrevInstance();
        return FALSE;
    }


FoundPrevInstance looks like this:

// 
----------------------------------------------------------------------------
//  FoundPrevInstance
//
//  We want only one instance of this app to run at a time.  This function 
//  determines whether the app is already running.
//
//  Returning FALSE from this routine indicates that another instance of the
//  app is *not* running.
//  Returning TRUE indicates that another instance of the app is running.
// 
----------------------------------------------------------------------------

BOOL COneinstApp::FoundPrevInstance()

{
    BOOL bFound = FALSE;             

    // Handle to mutex that indicates another instance is running.
    HANDLE hMutexOneInstance = NULL; 

    // The first instance of the app will successfully create this mutex.  
When 
    // later instances try to create the mutex, they will fail because it
    // already exists, indicating to us that a previous instance is running.
    
    const char szMutex[] = "PreventSecondInstanceOfThisApp";
    hMutexOneInstance = CreateMutex( NULL, TRUE, szMutex );
    if( GetLastError() == ERROR_ALREADY_EXISTS )
    {
        bFound = TRUE;
    }
    
    if ( hMutexOneInstance ) 
    {
        ReleaseMutex( hMutexOneInstance );
    }

    return bFound;
}


UsePrevInstance looks like this:

// 
----------------------------------------------------------------------------
//  UsePrevInstance
//
//  This function tells an already running instance of GWLIS to prompt for 
//  login info for all registered applications.
// 
----------------------------------------------------------------------------

void COneinstApp::UsePrevInstance()

{
    //
    // The original instance of this program saved its main frame window 
    // in a global variable in shared memory. Now we are going to use that
    // handle to activate the first instance. 
    // 

    // Associate the CWnd object with the handle of the original instance
    // so we can use MFC functions on the window.

    CWnd FirstInst;   
    FirstInst.Attach( gAppHwnd );
    
    if(FirstInst.IsIconic())
    {
        FirstInst.ShowWindow(SW_SHOWNORMAL);
    }
    FirstInst.BringWindowToTop();
    FirstInst.GetLastActivePopup()->BringWindowToTop();
    
    // Detach the handle from the CWnd or die.
    FirstInst.Detach();

    // If we wanted to ask the original instance to do something specific
    // we could define a user-defined message, implement a handler for the
    // user defined message in CMainFrame, and use code like this:

    #ifdef TELL_THE_FIRST_INSTANCE_SOMETHING
    
    ::PostMessage( gAppHwnd, WM_SOME_USER_DEFINED_MSG, 0, 0 );

    #endif 
}



Lastly, You need to add a .DEF file to the project that looks like this:

;////////////////////////////////////////////////////////////////////////////
//
NAME      APPNAME
CODE      PRELOAD MOVEABLE DISCARDABLE
DATA      PRELOAD MOVEABLE MULTIPLE
HEAPSIZE  1024

SECTIONS
    .MYAPP_SHARED READ WRITE SHARED

;////////////////////////////////////////////////////////////////////////////
//


I hope someone finds this technique useful.
Al Margheim



Frederic Steppe -- FredericS@msn.com
Thursday, May 09, 1996

[Mini-digest: 3 responses]

>Environment:  Visual C++ 4.1   with  Win95 and WinNT	
>1) How to detect a previous instance of a program.
>2) How to activate the first instance, or request it to do something.

Wouldn't it be easiest to broadcast a registered user message and just get the 
answer ?
The existing instance may get the message, reply and activates itself with 
just a few lines of code.

Or isn't it that simple ?

Frederic Steppe (frederic@msn.com)
-----From: mikeblas@interserv.com

On Fri, 03 May 96, MARGHAL@gw2k.com wrote:
>Environment:  Visual C++ 4.1   with  Win95 and WinNT	

Thanks.

>Actually there are two questions to answer:

>1) How to detect a previous instance of a program.
>2) How to activate the first instance, or request it to do something.

>The first question has been discussed on the list in detail recently with 
>several good approaches suggested.  I used the approach that creates and 
>checks a named mutex in the InitInstance call.

Great.  That's the most robust method.  But maybe you should use an event 
instead.

>The second question is, I think, the harder one: how to get the handle of
>the main window of the first instance.

You don't need it.  If you used an event instead of a mutex, it would be easy 
to have the other application activate itself.  I think you should do this:

1) Create an event named with your application's name.
2) If that mutex exists, go to step A, below
3) Create a new thread.  Give it a pointer to your CWinApp.
4) Have that thread wait for two objects: the named event and an unnamed 
event in your application object.
5) continue initialing your application.
6) When your user exits, destroy the named event and signal the unnamed 
event.  Wait for your extra thread to die.  When it dies, exit.

in the extra thread:

4a) you're waiting (as above, remember?) on either of two objects: the 
unnamed event or the named event.
4b) if the named event fires, use SetForegroundWindow() on your 
pApp->m_pMainWnd. go back to step 4a.
4c) if the unnamed event fires, terminate this thread.

If your app starts and find that the named event already exists:

A) Since the event means someone else is running, singal it.
B) Exit your applicaiton.  The "come to the foreground" is implemented by the 
other app's secondary thread in step 4b.

> FindWindow() ... or EnumWindow() ...

I think that a thread that sleeps all day is actually cheaper than using 
EnumWindow and FindWindow and a shared data area.

>The technique I used is to create a shared data segment in the application's
>EXE, declare a global handle variable in the shared segment, and store the
>window handle of the first instance's frame window in the global variable.

This is a very awkward approach.  Some applications (and, more importantly, 
some users) don't like it when a window suddenly comes to the foreground.  
That's mainly because it screws up the keyboard input focus.  You can tailor 
step 4b above so that any state of your application can be checked.  Maybe 
you can just ring the bell, or maybe you could just come to the foreground if 
its safe, or come to the foreground later if it isn't.  Me, I like to format 
the user's hard drive.

.B ekiM
--
TCHAR szDisc[] = _T("These words are my own; I do not speak for Microsoft.");

-----From: Mario Contestabile 

Your e-mail is very similar to a posting I wrote on the subject a while back,
which is in the faq 4.0 (section 11.8), yet questions on preventing multiple 
instances
are very popular.

mcontest@universal.com



Mike Blaszczak -- mikeblas@msn.com
Saturday, May 11, 1996

----------
From: 	owner-mfc-l@netcom.com on behalf of Frederic Steppe
Sent: 	Thursday, May 09, 1996 12:26

>>Environment:  Visual C++ 4.1   with  Win95 and WinNT	
>>1) How to detect a previous instance of a program.
>>2) How to activate the first instance, or request it to do something.

> Wouldn't it be easiest to broadcast a registered user message
> and just get the answer ?

It's not that simple because broadcast messages must be asynchronously posted 
and not synchronously sent. That means that you must have some other mechanism 
for the second instance to wait for an explicitly sent reply from the first 
instance.  Since posting messages is asynchronous, you don't know what the 
first instance is doing. It might be too busy to pump messages at the moment 
and not respond to your second instance. So the second instance will have to 
build in a time out of at least, say, half a second.  And that means your 
application will necessarily take half a second to start because the second 
instance has to time out before it knows its the first instance and is 
actually allowed to run.

.B ekiM
TCHAR sz[] = _T("What's that smell?");



Eric Raymond -- RAYMOND@btw.com
Monday, May 13, 1996

[Mini-digest: 3 responses]


> It's not that simple because broadcast messages must be asynchronously 
posted
> and not synchronously sent. That means that you must have some other
> mechanism
> for the second instance to wait for an explicitly sent reply from the 
first
> instance.  Since posting messages is asynchronous, you don't know what the 

> first instance is doing. It might be too busy to pump messages at the 
moment
> and not respond to your second instance. So the second instance will have 
to
> build in a time out of at least, say, half a second.  And that means your
> application will necessarily take half a second to start because the 
second
> instance has to time out before it knows its the first instance and is
> actually allowed to run.
>
> .B ekiM
> TCHAR sz[] = _T("What's that smell?");

Clearly this is desirable behavior for many programs.
Clearly this is not a simple/obvious thing to do correctly.

Seems like this is good criteria for deciding what to put into MFC (or any 
library).

-----From: Andrew Dalgleish 


1.  Use a mutex to detect whether you are the 1st or 2nd instance. Create   
the mutex in CYourApp::InitInstance(), release it in   
CYourApp::ExitInstance().
2.  Use a broadcast registered message to re-activate the 1st instance.   
This avoids finding and manipulating windows which belong to another   
instance. Drawbacks - doesn't reactivate if a message box is open.

The shared global variable approach posted earlier by Al Margheim works   
well, but needs a semaphore to serialize access to the variable.

Regards,
Andrew Dalgleish

-----From: "Frederic Steppe" 

>>>Environment:  Visual C++ 4.1   with  Win95 and WinNT	
>
>> Wouldn't it be easiest to broadcast a registered user message
>> and just get the answer ?
>
>It's not that simple because broadcast messages must be asynchronously posted 

>and not synchronously sent. That means that you must have some other 
mechanism 
>for the second instance to wait for an explicitly sent reply from the first 
>instance.
> ...

You're right.  Thank you for the clear explaination.  I'll refer to the 
response you gave some days ago about the same subject,  (using one thread 
waiting for two objects).

Please thake this as a real 'thank you' message.  I mean it.  You deserve it.

Frederic Steppe (frederics@msn.com)




Wolfgang Loch -- Wolfgang.Loch@RZ.TU-Ilmenau.DE
Thursday, May 23, 1996

Environment: Visual C++ 2.0, WinNT

..a never ending story..

Here is my approach to detect a previous instance:

* Create an application specific value in the registry under
HKEY_LOCAL_MACHINE
* Check for the existence of that value in InitInstance()
* if it exists reactivate the previous instance by sending a broadcast
message
* if not, create the value in the registry
* delete the registry value at ExitInstance()
Hint: * Make sure to create the registry value als VOLATILE to be able
to start your app after a system crash.

I found the registry to be a convinient place for inter process data
exchange.

Wolfgang Loch (Wolfgang.Loch@rz.tu-ilmenau.de)



Niels Ull Jacobsen -- nuj@kruger.dk
Wednesday, May 29, 1996

[Mini-digest: 6 responses]

At 11:16 23-05-96 +0200, you wrote:
>Environment: Visual C++ 2.0, WinNT
>
>..a never ending story..
>
>Here is my approach to detect a previous instance:
>
>* Create an application specific value in the registry under
>HKEY_LOCAL_MACHINE
>* Check for the existence of that value in InitInstance()
>* if it exists reactivate the previous instance by sending a broadcast
>message
>* if not, create the value in the registry
>* delete the registry value at ExitInstance()
>Hint: * Make sure to create the registry value als VOLATILE to be able
>to start your app after a system crash.

The major problem with this is: What if your program is terminated
prematurely (killed, the machine crashes, power failure, whatever)?
The user will never be able to start it again!

Also, this won't work if two instances are started (nearly)
simultaneously. If the first process is deactivated after the chekck, but=
 before
having created the key, it will fail. Or if one instance is exiting,
it may have stopped pumping messages but not yet deleted the registry key.
It will never get the broadcasted message.

>
>I found the registry to be a convinient place for inter process data
>exchange.

Why? A machine crash could fill up the registry with unwanted data?
Why not use the registry for what it was intended for and use the
inter-process communication facilities provided (memory-mapped files)?

>Wolfgang Loch (Wolfgang.Loch@rz.tu-ilmenau.de)
Niels Ull Jacobsen, Kr=FCger A/S (nuj@kruger.dk)
Everything stated herein is THE OFFICIAL POLICY of the entire Kruger grou=
p
and should be taken as legally binding in every respect. Pigs will grow
wings and fly.

-----From: "David Pham" 


Environment: Visual C++ 4.1, WinNT 4.0 Beta

Here is how to detect a previous instance:

Search MSDN for PSS ID Number: Q141752
SAMPLE: Limiting 32-bit Applications to a Single Instance

How it works:

1. In your InitInstance(), use the FindWindow() to look for your own class name (Say 
classABC).

2. If you can not find that window, then it must tbe the 1st 
instance. In this case, register your class name classABC using 
AfxRegisterClass(). In your CMainFrame::PrecreateWindow() add the 
line:

cs.lpszClass = _T("classABC");

Then do your bussiness as usual. Remember to 
::UnregisterClass() your classABC in your ExitInstance() if you 
register your class name.

PS> You must FindWindow() using CLASS NAME so it will always find the 
2nd instance whether your document view is maximized or not. 

Good luck
_______________________________________________

David Pham                dpham@b-r.com
System Analyst            (713) 676-3824
Brown & Root, Inc.        1-800-888-ROOT X3824
Houston, TX U.S.A         FAX: 713-676-4808
_______________________________________________
-----From: Mario Contestabile

>Environment: Visual C++ 2.0, WinNT
>..a never ending story..
No kidding.

>Here is my approach to detect a previous instance:
>* Create an application specific value in the registry under
>HKEY_LOCAL_MACHINE
>* Check for the existence of that value in InitInstance()
>* if it exists reactivate the previous instance by sending a broadcast
>message
>* if not, create the value in the registry
>* delete the registry value at ExitInstance()
Ouch, sounds like the perfect way to create a race condition between the two 
applications.
Since you never know when there will be a task switch, theoretically you
could have a condition where the app is started twice in a close time span.

>Hint: * Make sure to create the registry value als VOLATILE to be able
>to start your app after a system crash.
This won't work on Win95.

>I found the registry to be a convinient place for inter process data
>exchange.
Perhaps for very small, very seldomly needed information. For most
real world apps a DLL or MMFile would be more appropriate.

mcontest@universal.com

-----From: Ron Forrester 

What if your application crashes before you hit ExitInstance()?

rjf

-----From: Andrew Dalgleish 


The registry can be a handy place to save data, but it lacks the ability   
to do a "test & set" as a single atomic operation.

This approach can fail if two instances are started at almost exactly the   
same time.

Instance 1 tests flag
Instance 2 tests flag
Instance 1 sets flag


Regards,
Andrew Dalgleish
andrewd@axonet.com.au

-----From: "David Ohlssen" 

The code below depends on how "volatile" the registry entry is after
an app crash versus a system crash.  How about just creating a mutex,
which has to be done without asking for ownership, then requesting
ownership in a second step.  If that fails, the app is already 
running.   If you ask for ownership with the create, it does not seem 
to distinguish properly.   Many other ways can be used to tell the old 
app to jump up, eg create/change the value of a registry val to "Y".  
The old app can look for the value change in its idle, and if "Y", 
set it back to "N" and jump up by ...

    m_pMainWnd->SetActive();  // Or whatever view you want to activate
    m_pMainWnd->ShowWindow(SW_RESTORE);  // Don't forget this.

David O. Ohlssen
I  _____________________________________  I
I  | Davido@Commerce.CTech.ac.ZA       |  I
I  |    __  ____________               |  I
I  |   /  \/ Cape Town  \  _           |  I
I  |__/     South Africa \/ \__________|  I
I_________________________________________I




| Вернуться в корень Архива |