Newsgroup: it.comp.programmare.win32
Titolo: Verificare se una applicazione e' gia' in esecuzione

Salve a tutti.
Qualcuno sa indicarmi come e'possibile verificare se una applicazione e'
gia' in esecuzione in modo eseguendola una seconda volta si possa dar focus
alla prima istanza?
Grazie.
Luigi
luigi_...@yahoo.it

 alla prima istanza?
Ci sono vari approcci, ma la maggior parte di quelli che si vedono
proporre in giro non sono "solidi".
Ad esempio, la prima idea puo` essere quella di svolgere, all'inizio
del WinMain, una enumerazione delle finestre esistenti, come una
FindWindow oppure, equivalentemente:
char NomeMiaClasse[] = "NomeDellaMiaClasse";
BOOL CALLBACK
TrovaClasse(HWND hwnd, LPARAM lParam)
{
    char buffer[sizeof(NomeMiaClasse)+1];
    if(GetClassName(hwnd, buffer, sizeof(buffer))) {
        if(0==strcmp(buffer,NomeMiaClasse)) {    // e' della nostra classe
            *((HWND*)lParam) = hwnd;      // finestra trovata
            return FALSE;                            // interrompiamo il
ciclo
        }
    }
    return TRUE;        // continuiamo il ciclo
}
int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    HWND hw=0;
    if(!EnumWindows(TrovaClasse,(LPARAM)&hw)) {
        // uscire segnalando l'errore con una message box
    }
    if(hw) {
        // hw e` l'HWND di una window della nostra classe,
        // quindi si puo` attivarla e terminare
    } else {
        // createwindow con la nostra classe, e partire
        // normalmente
    }
}
Un grave difetto di questo approccio e` che il nome della
classe di finestra non e` "riservato"... in questo modo si
potrebbe avere una "falsa positiva", trovando, per sbaglio,
una finestra che non c'entra con le nostre ma ha per caso
lo stesso nome di classe.  Questo problema si puo`
aggirare, ad esempio, usando un nome di classe basato
su di una GUID.
Un approccio che garantisce di non avere false positive
e` di usare, nella procedura passata ad EnumWindows
(...per questo ho usato EnumWindows invece di
FindWindow...:-) la GetWindowThreadProcessId su ogni
HWND passata; con il process id cosi` ottenuto, si
ottiene la handle del processo con OpenProcess; poi,
con la EnumProcessModules (nella libreria ImageHelp),
si scorrono i moduli di quel processo, e con
GetModuleFileNameEx si ottiene la "fully qualified
path" del file EXE di quel processo.  Si tratta della
nostra applicazione se, e solo se, parte dallo stesso
file EXE (non occorre "cablare" nel nostro codice il
nome e la path del nostro EXE, possiamo infatti
recuperarla agevolmente con GetModuleFileName(0)).
(Equivalente, come e` chiaro, scoprire prima quale
sia un eventuale processo che parte dal nostro stesso
EXE, con EnumProcesses poi come sopra, e _dopo_
fare la EnumWindows/GetWindowThreadProcessId,
sapendo gia` quale processo stiamo cercando, per
trovare le finestre top-level di quel dato processo).
Ci sono anche altri approcci piu` o meno equivalenti
(ad esempio, se il nostro processo esegue una certa
specifica SetProp sulla sua finestra principale, la
funzione da passare ad EnumWindows puo` tentare
le GetProp per verificare se ha trovato la finestra
giusta, ecc ecc).
Altri approcci non esattamente equivalenti sono
basati sull'idea che l'istanza attiva depositi i propri
dati (HWND da usare, ad esempio) in un qualche
posto globale (la cosa piu` semplice e` secondo
me una SetProp della GetDesktopWindow).  Questi
possono essere molto piu` semplici di quelli basati
sulla "ricerca".
Tutto cio` puo` evitare le false positive, ma, senza
un ulteriore accorgimento, non una falsa negativa...
Supponiamo che due istanze del nostro processo
partano una subito dopo l'altra -- non e` un caso
raro e astratto: puo` succedere spesso se l'utente
ad esempio fa un doppio clic, per abitudine, quando
ha l'Active Desktop dove ciascuno dei due clic fa
partire un'istanza!
Se la logica del nostro start-up e`, come in tutti
gli approcci sinora accennati, del tipo...:
    1. cerca se c'e` gia` un'altra istanza
    2. se si`, attivala
    3. se no, l'unica istanza siamo noi, lavoriamo
        (genera finestra, ecc ecc)
puo` benissimo darsi che le nostre due istanze
A e B eseguano entrambe il punto 1 piu` o
meno allo stesso tempo, prima che alcuna
delle due abbia completato il "genera finestra"
del passo 3.  Allora, entrambe usciranno dal
passo 1 con l'informazione "non c'e` un'altra
istanza" -- ciascuna pensera` di essere l'unica
e quindi ciascuna eseguira` il passo 3 (ci sono
complicazioni se al passo 1 cerchiamo i processi
invece delle finestre, ma non piacevoli...:-).
Questo e` un classico problema di sincronizzazione
e si puo` risolvere solo con gli oggetti che il sistema
operativo mette a disposizione appunto per questo
scopo; il piu` semplice e` il Mutex, l'oggetto di mutua
esclusione.
Alla partenza, dunque, il nostro WinMain deve
cercare di acquisire un Mutex, con un nome che,
per garantirne l'unicita`, puo` essere costruito
basandosi su una GUID.
Quando lo ottiene, sa di essere l'unica istanza
della nostra applicazione che in questo momento
e` in fase di startup.  Puo` dunque, ad esempio,
usare uno qualsiasi dei sistemi visti sopra, senza
piu` rischio di false positive.  Finito il passo 1,
verra` rilasciato il mutex.
(Inoltre, il mutex puo` essere usato anche per
proteggere lo _shutdown_ dell'applicazione,
per il caso scalognato che una istanza sia in
partenza proprio mentre l'altra e` in chiusura,
ma non ha ancora distrutto la sua finestra
principale...).
Gia` che si usa un mutex per sincronizzare,
puo` essere meglio, invece degli schemi di cui
sopra, usare un altro oggetto di sistema operativo
(la shared memory -- ne basta una fettina!-)
per fare si` che la istanza esistente "comunichi"
a quella in partenza i propri dati, come l'HWND
da attivare.  (Notare che il mutex serve in ogni
caso, perche` anche gli accessi alla shared
memory vanno "serializzati", cioe` protetti
contro accidentali accessi simultanei non
coordinati).  Pero`, il SetProp di GetDesktopWindow
resta probabilmente piu` semplice.
Alex


Occorre rendere univoca l'istanza dell'applicazione,
ad esempio tramite un named mutex da creare e/o testare prima di
svolgere qualunque altra elaborazione.
Il mutext dovra' essere creato nell'opportuno spazio dei nomi,
dato che se il tuo applicativo gira sotto terminal server,
piu' utenti possono legalmente usarlo in sessioni diverse.
Qunado la seconda istanza dell'applicazione tenta di apire il mutex,
otterra' un errore "oggetto gia' esistente".
A questo punto, occorre trovare hWnd della finestra principale
della prima istanza dell'applicativo e "mostrarla" con uno
ShowWindow(). La API SetFocus() funziona per le finestre della
coda di input del tuo thread.
Per trovare la finestra temo occorra enumerare tutte le finestre con
EnumWindows().
Sent via Deja.com 
http://www.deja.com/
Before you buy.