2015-06-24 24 views
5

Sto creando un kill switch nella mia applicazione in modo che solo un'istanza possa essere eseguita contemporaneamente su un singolo computer. Sto realizzare questo inviando messaggi tra il processo di un'applicazione in esecuzione e il processo della nuova istanza dell'applicazione:PInvoke PostMessage non funziona su tutti gli account utente

[DllImport("user32.dll", EntryPoint = "PostMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] 
private static extern int PostMessage(int hwnd, int wMsg, int wParam, int lParam); 

non posso usare Process.Kill perché se un altro utente è in esecuzione l'applicazione e l'utente corrente non ha privilegi sufficienti ottengo problemi.

Quando si eseguono 2 istanze sotto lo stesso account utente non ho problemi. I messaggi vengono inviati correttamente e ricevuti correttamente. Tuttavia, quando si esegue un'istanza da un account utente, quindi si cambia utente e si esegue una seconda istanza, la prima istanza non riceve i messaggi.

Ecco la mia logica per la sottoscrizione ai messaggi di finestra:

var wih = new WindowInteropHelper(this); 
var hwndSource = HwndSource.FromHwnd(wih.Handle); 
var hwndSourceHook = new HwndSourceHook(HookHandler); 

if (hwndSource != null) 
    hwndSource.AddHook(hwndSourceHook); 

Ed ecco il mio gestore gancio:

private IntPtr HookHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
    { 
     handled = false; 

     switch (msg) 
     { 
      case 0x400: // interprocess message received 
       App.InterprocessManager.BuildString(lParam); 
       break; 
     } 
     return IntPtr.Zero; 
    } 

Ecco la logica per inviare il messaggio:

private void SendString() 
    { 
     //create byte array 
     byte[] ba = null; 
     //encode string to byte array 
     if (object.ReferenceEquals(enc, Encoding.UTF8)) 
     { 
      ba = Encoding.UTF8.GetBytes(lParam); 
     } 
     else if (object.ReferenceEquals(enc, Encoding.Unicode)) 
     { 
      ba = Encoding.Unicode.GetBytes(lParam); 
     } 
     else if (object.ReferenceEquals(enc, Encoding.ASCII)) 
     { 
      ba = Encoding.ASCII.GetBytes(lParam); 
     } 
     else 
     { 
      ba = Encoding.Default.GetBytes(lParam); 
     } 
     int i = 0; 
     for (i = 0; i <= ba.Length - 1; i++) 
     { 
      //start post message 
      PostMessage(hwnd, wMsg, wParam, ba[i]); 
     } 
     //post a terminator message to destination window 
     PostMessage(hwnd, wMsg, wParam, 0); 
    } 

No Win32 gli errori vengono impostati dalla funzione PostMessage. Non riesco a trovare alcuna documentazione riguardante la pubblicazione di messaggi tra processi attraverso account utente. È qualcosa che non può essere effettivamente fatto?

+4

L'altro utente esegue il programma nella propria sessione con il proprio heap del desktop. Le probabilità che tu stia usando l'handle della finestra corretto sono piuttosto basse, dato che non puoi trovarlo dalla tua sessione. Andare avanti con il modo comune di farlo, utilizzando un mutex denominato.Prefix il suo nome con 'Global \' in modo che sia visibile attraverso le sessioni. E tieni presente che l'utente sarà completamente ignaro del motivo per cui non può avviare il programma e non avrà idea di come risolverlo. Visto quanto è ostile, è certamente meglio non farlo affatto. –

+0

Recupero l'handle con Process.MainWindowHandle, quindi non sono sicuro del motivo per cui non otterremmo l'handle corretto a meno che il sistema non imposti un handle arbitrario a questo punto. Utilizzo già il mutex per determinare se è in esecuzione un'altra istanza, tuttavia lo scenario su cui stiamo lavorando è il programma di esecuzione A dell'utente, il sistema di blocchi utente A, l'utente B si firma, tenta di eseguire il programma. In questo scenario, vogliamo che l'utente B sia in grado di eseguire, quindi è necessario chiudere il programma nella sessione dell'utente A. Uccidere il programma non è l'ideale, tuttavia utilizziamo protocolli di comunicazione che possono essere utilizzati solo uno alla volta. – flamebaud

+2

Tale handle è utilizzabile nella sessione che lo ha creato, il messaggio PostMessage() non va da nessuna parte. –

risposta

1

Non è possibile inviare messaggi finestra a un processo in una sessione diversa. È necessario utilizzare un meccanismo IPC basato sul kernel piuttosto che sugli oggetti utente.

Nel vostro scenario, un mutex con nome e un semaforo con nome è probabilmente tutto ciò di cui avete bisogno. Si noti che i nomi devono avere un prefisso Global\ per consentire a più sessioni di condividere un singolo oggetto.

All'avvio del processo, prima creare o aprire il semaforo. Imposta il conteggio iniziale su zero.

Quindi creare il mutex denominato con bInitialOwner impostato su TRUE e controllare l'ultimo codice di errore per verificare se il mutex esisteva già.

Se il mutex non esisteva già, tu sei la prima istanza, quindi crea una discussione per attendere sul semaforo. Se viene segnalato il semaforo, uscire dal processo. Assicurati di non rilasciare il mutex finché non è sicuro che l'altro processo venga eseguito; in caso di dubbio, non rilasciarlo affatto. Windows lo segnalerà come abbandonato una volta che il processo è terminato.

Se il mutex era già esistente, un'altra istanza è in esecuzione. Segnala il semaforo, quindi attendi il mutex. Una volta ottenuta la proprietà del mutex, è sicuro continuare. Devi quindi creare una discussione per attendere sul semaforo, nel caso in cui un'altra istanza arrivi e ti voglia picchiare.

Se è necessario un canale di comunicazione più complicato tra i processi, ad esempio se è necessario passare lo stato corrente del canale di comunicazione, la scelta migliore è probabilmente named pipes.