2012-02-26 8 views
11

Sto tentando di inviare un messaggio a un'applicazione in esecuzione con un account utente diverso (un utente che ha anche effettuato l'accesso con un account diverso sul computer, usando l'interruttore rapido utente su XP e versioni successive ed esegui l'applicazione).Invio di un messaggio a un'applicazione in esecuzione su un account utente secondario connesso

Lo sfondo è che la mia applicazione può aggiornarsi, ma per fare ciò tutte le istanze in esecuzione devono essere chiuse per prime.
Le istanze devono essere arrestate (invece di uccidere semplicemente il processo), quindi l'aggiornamento lo fa inviando loro un messaggio personalizzato (con SendMessage). Per inviare un messaggio ho bisogno di un handle per la finestra principale del processo.

Questo funziona correttamente utilizzando EnumWindows - finché le istanze sono in esecuzione con lo stesso account utente, poiché EnumWindows non elenca le finestre appartenenti a un altro utente.

Così ho provato un approccio diverso. Ho usato CreateToolhelp32Snapshot per elencare per primo tutti i processi in esecuzione sul sistema e quindi per ripetere i thread chiamando lo CreateToolhelp32Snapshot di nuovo. Con quegli id ​​di thread potrei quindi elencare le loro finestre usando EnumThreadWindows.

Ancora una volta funziona correttamente, ma .. ancora una volta solo per l'utente attualmente connesso. Il problema qui è che anche se CreateToolhelp32Snapshot elenca gli ID di processo appartenenti a un utente diverso, non elenca gli ID dei thread che appartengono ad essi. Il codice per questo è un po 'lungo, ma se è necessario posso modificarlo - per favore lascia un commento per quello.

Quindi, come è possibile ottenere l'handle della finestra principale della mia applicazione in esecuzione su un altro account utente connesso?

+0

Potrebbe essere necessario usare qualcosa come named pipe per aggirare queste restrizioni di sicurezza.Quello che stai cercando di fare è simile ai servizi di sessione 0 che comunicano con il desktop interattivo e quindi le soluzioni che funzionano lì funzioneranno per te. –

+2

Mi sembra anche un po 'banale arrestare i processi dell'utente senza dare loro la possibilità di obiettare. La maggior parte degli updaters aspetta solo fino al prossimo avvio dell'app per l'aggiornamento. –

+0

+1 a David's. Persino Windows ti avvisa degli altri utenti connessi se provi a chiudere mentre c'è un'altra sessione attiva. Tutto ciò che "parla" ai processi attraverso i confini delle sessioni è un po 'imbarazzante e spaventoso. Immagina semplicemente un'applicazione che fa scattare qualcosa come "L'utente" Amministratore "ha questo documento aperto, vuoi che io interrompa quel processo in modo da poter lavorare sul documento?' –

risposta

10

Utilizzare qualcosa che è noto per funzionare tra le sessioni; Questo genere di cose viene spesso utilizzato per le comunicazioni del servizio desktop, quindi cercalo se vuoi google. Ecco il mio suggerimento:

  1. Creare un evento che verrà utilizzato solo per attivare lo stato di "necessità di arresto". Utilizzare la funzione CreateEvent assicurandosi di iniziare il proprio nome con Global\ in modo che sia valido per tutte le sessioni.
  2. All'avvio dell'applicazione creare una discussione che apre l'evento denominato (utilizza la stessa funzione CreateEvent, prestare molta attenzione al non errore ERROR_ALREADY_EXISTS). Quel thread dovrebbe semplicemente attendere l'evento. Quando l'evento viene attivato, invia il messaggio richiesto alla tua finestra principale. Quella discussione può farlo facilmente e in modo sicuro perché è in esecuzione all'interno di il processo. Il thread sarà per lo più inattivo, in attesa dell'attivazione dell'evento, quindi non preoccuparti della penalità della CPU.
  3. Il programma di aggiornamento dell'applicazione deve semplicemente attivare l'evento denominato.

Questa è solo un'idea, sono sicuro che ce ne sono altri.

+1

+1 cancellando la mia risposta. completamente dimenticato di eventi che è la soluzione migliore in questo caso. – kobik

+0

Grazie, questo ha risolto il mio problema perfettamente. All'inizio era un po 'complicato, perché dovevo impostare un descrittore di sicurezza che consentisse l'accesso a tutti alla creazione dell'evento. In caso contrario, ho ricevuto un errore di accesso negato quando ho tentato di aprire l'evento con un altro account. Ma ora funziona, quindi grazie :-) – Chris

+1

Si noti che la creazione di oggetti nello spazio dei nomi Globale richiede il privilegio SeCreateGlobalPrivilege, a meno che ciò non avvenga nella sessione della console. Solo gli amministratori hanno questo privilegio. Questo comportamento è documentato qui (ultimo paragrafo): http://msdn.microsoft.com/en-us/library/windows/desktop/aa382954(v=vs.85).aspx – Remko

7

I tubi sono eccessivi. Un evento di reimpostazione manuale globale (ad esempio "Global \ MyApplicationShutdownEvent") che causa la morte delle istanze dell'applicazione dovrebbe essere sufficiente.

+0

Anche se sono d'accordo con David Heffernan. Questo è piuttosto maleducato. – arx

+2

+1 dubito che tu possa farlo molto più semplice di questo. –

0

A rischio di essere deriso, hai guardato allo zeroMQ, questo è un uso perfetto per esso e molto affidabile e stabile.

C'è un Delphi wrapper

Problemi correlati