2010-06-27 12 views
10

In Windows Vista/7/2008/2008R2, è possibile avviare una procedura in una sessione utente da un servizio? Nello specifico, la sessione locale sarebbe molto utile.Avvio di un processo nella sessione utente da un servizio

Tutto quello che ho letto sembra dire che questo non è possibile, ma ho pensato di chiedere qui prima di rinunciare completamente.

Sto codificando in VB.NET, ma prenderò suggerimenti in qualsiasi cosa.

+0

ses Quale dell'utente Sion? –

+0

L'utente interattivo locale è ciò su cui mi sto concentrando. Sono principalmente interessato a bloccare la workstation dal servizio, ma ho la necessità di eseguire anche altri programmi, a seconda di determinate condizioni. – Brad

risposta

18

È davvero possibile. Il problema principale che si ha è che Windows dovrebbe essere visto come un server terminal e una sessione utente come una sessione remota. Il servizio dovrebbe essere in grado di avviare un processo eseguito nella sessione remota che appartiene all'utente.

A proposito, se si scrive un servizio che gira sotto Windows XP che non viene aggiunto a un dominio e si attiva la commutazione rapida utente, è possibile avere gli stessi problemi per avviare un processo in esecuzione sul secondo (terzo e così via) desktop degli utenti registrati.

Spero che tu abbia un token utente, che ricevi ad esempio per quanto riguarda la rappresentazione o hai un dwSessionId di sessione. Se non lo avete potete provare utilizzare alcuni WTS-funzione (Servizi Desktop remoto API http://msdn.microsoft.com/en-us/library/aa383464.aspx, ad esempio WTSEnumerateProcesses o WTSGetActiveConsoleSessionId) o LSA-API per scoprire la sessione di utenti corrispondente (LsaEnumerateLogonSessions vedere http://msdn.microsoft.com/en-us/library/aa378275.aspx e LsaGetLogonSessionData vedere http://msdn.microsoft.com/en-us/library/aa378290.aspx) o ProcessIdToSessionId (vedi http://msdn.microsoft.com/en-us/library/aa382990.aspx).

È possibile utilizzare GetTokenInformation funzione con il parametro TokenSessionId (vedi http://msdn.microsoft.com/en-us/library/aa446671.aspx) per ricevere l'ID di sessione dwSessionId della sessione utenti se si conosce il gettone utenti hClient.

BOOL bSuccess; 
HANDLE hProcessToken = NULL, hNewProcessToken = NULL; 
DWORD dwSessionId, cbReturnLength; 

bSuccess = GetTokenInformation (hClient, TokenSessionId, &dwSessionId, 
           sizeof(DWORD), &cbReturnLength); 
bSuccess = OpenProcessToken (GetCurrentProcess(), MAXIMUM_ALLOWED, &hProcessToken); 
bSuccess = DuplicateTokenEx (hProcessToken, MAXIMUM_ALLOWED, NULL, 
          SecurityImpersonation, 
          TokenPrimary, &hNewProcessToken); 
EnablePrivilege (SE_TCB_NAME); 
bSuccess = SetTokenInformation (hNewProcessToken, TokenSessionId, &dwSessionId, 
           sizeof(DWORD)); 
bSuccess = CreateProcessAsUser (hNewProcessToken, NULL, szCommandToExecute, ...); 

Questo codice solo uno schema. EnablePrivilege è una semplice funzione utilizzata AdjustTokenPrivileges per abilitare il privilegio SE_TCB_NAME (vedere http://msdn.microsoft.com/en-us/library/aa446619.aspx come modello). È importante che il processo da cui si avvia un processo disponga del privilegio TCB, ma se il servizio viene eseguito con il Sistema locale si dispone di autorizzazioni sufficienti. A proposito, il frammento di codice seguente funziona non solo con l'account di sistema locale, ma l'account deve avere il privilegio SE_TCB_NAME per poter cambiare la sessione corrente del server terminal.

Un'altra osservazione. Nel codice sopra iniziamo un nuovo processo con lo stesso account del processo corrente (ad esempio Sistema locale). Si modifica la modifica di un codice per utilizzare un altro account, ad esempio il token utente hClient. È solo importante avere un primary token. Se si dispone di un token di rappresentazione, è possibile convertirlo nel token principale esattamente come nel codice sopra.

Nella struttura STARTUPINFO utilizzato in CreateProcessAsUser si dovrebbe usare lpDesktop = WinSta0 \ Default".

dipendono dai requisiti potrebbe essere anche necessario utilizzare CreateEnvironmentBlock per creare un nuovo blocco di ambiente che si passerà alla nuova processo.

vi consiglio anche di leggere How to ensure process window launched by Process.Start(ProcessStartInfo) has focus of all Forms? dove descrivo come forzare che il processo sarà avviato in primo piano sul desktop degli utenti.

+0

Ho appena lavorato a un problema che stavo facendo con un servizio che sta facendo questo, e ho trovato la tua risposta qui ... Molto utile. Nel mio caso, ho ricevuto un fastidioso errore di "accesso negato" quando ho eseguito una finestra di dialogo di apertura file dal mio processo di generazione (altrimenti la finestra di dialogo funzionava bene). Chiamare 'CreateEnvironmentBlock' e passare il risultato a' CreateProcessAsUser' sembra averlo risolto. Devo davvero impostare il valore 'lpDesktop' della mia struttura' STARTUPINFO'? L'ho appena lasciato come NULL. Inoltre, ha funzionato senza impostare il privilegio SE_TCB_NAME. – paddy

+0

@paddy: Siamo spiacenti, ma tutti i passaggi sono davvero necessari se si dispone di alcuni casi comuni. Se il servizio viene eseguito con un account utente e non come Sistema, è necessario impostare 'TokenSessionId' per consentire all'utente interattivo di vedere le finestre del processo avviato. Per poter usare 'SetTokenInformation' con' TokenSessionId', l'account del servizio deve avere il privilegio 'SE_TCB_NAME' e deve abilitarlo. Se lo screen saver funziona al momento dell'avvio del processo, si dovrebbe impostare 'lpDesktop' in modo da visualizzare sia il desktop dello screen saver che il desktop dell'utente predefinito. – Oleg

3

Sì, utilizzando CreateProcessAsUser è possibile farlo. MSDN ha articoli di esempio e ci sono alcuni avvertimenti.

+0

per favore non postare collegamenti MSDN con numeri di versione al loro interno a meno che non ci sia un motivo per usare sempre quella versione. –

+0

Lo farò, John. Grazie per la correzione. – holtavolt

+0

Grazie, sto lavorando alle dichiarazioni API per .NET ora. – Brad

3

Può essere fatto ma non è considerato una buona pratica per i servizi di interagire direttamente con le sessioni degli utenti in quanto può creare seri problemi di sicurezza.

http://support.microsoft.com/kb/327618

Un approccio migliore è quello di creare 2 programmi, un servizio di back-end e un programma client di interfaccia utente front-end. Il backend del servizio viene eseguito sempre ed espone le sue operazioni utilizzando WCF (ad esempio). Il programma client può essere eseguito all'avvio della sessione di un utente.

+0

Non credo che i contenuti dell'articolo che hai collegato siano ancora validi per il 2008 (r2). Session null isolation et al ... –

+1

Non è proprio la stessa cosa. Brad sta chiedendo di creare un/nuovo/processo con le credenziali dell'utente, non eseguendo operazioni interattive nella sessione dell'utente come processo del servizio. –

+1

L'avvio dei processi nella sessione di un utente continua a interagire con il desktop di un utente. Non fa rompere i capelli non è ancora una buona pratica. Link wasnt il migliore sono d'accordo :) –

4

Se aiuta, mi trovai di fronte a un problema simile, b ut voleva una pura soluzione PowerShell.

ho messo insieme i bit da altri siti web e arrivato fino a questo:

function Invoke-CommandInSession 
{ 
    [CmdletBinding()] 
    param(
     [Parameter(Mandatory = $true)] 
     [ValidateNotNullOrEmpty()] 
     [ScriptBlock] $expression 
    ) 

    $commandLine = “powershell“ 

    ## Convert the command into an encoded command for PowerShell 
    $commandBytes = [System.Text.Encoding]::Unicode.GetBytes($expression) 
    $encodedCommand = [Convert]::ToBase64String($commandBytes) 
    $args = “-Output XML -EncodedCommand $encodedCommand” 


    $action = New-ScheduledTaskAction -Execute $commandLine -Argument $args 
    $setting = New-ScheduledTaskSettingsSet -DeleteExpiredTaskAfter ([Timespan]::Zero) 
    $trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date) + ([Timespan]::FromSeconds(5))) 
    $task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $setting 
    Register-ScheduledTask "Invoke-CommandInSession - $([Guid]::NewGuid())" -InputObject $task 
} 

Sì, è un po 'strano, ma funziona - e per di più si può chiamare dall'interno ps servizi remoti

Problemi correlati