2010-06-27 8 views
10

In Windows Vista/7/2008/2008R2 starten, ist es überhaupt möglich, einen Prozess in einer Benutzersitzung von einem Dienst zu starten? Insbesondere wäre die lokale Sitzung am nützlichsten.einen Prozess in Benutzersitzung von einem Dienst

Alles, was ich habe gelesen, scheint zu sagen, dass dies nicht möglich ist, aber ich dachte, ich hier fragen würde, bevor vollständig aufzugeben.

Ich bin Codierung in VB.NET, sondern Vorschläge in etwas nehmen.

+0

Welche Benutzer-ses sion? –

+0

Der lokale interaktive Benutzer ist das, worauf ich mich konzentriere. Ich bin hauptsächlich daran interessiert, die Workstation vom Dienst zu sperren, muss aber abhängig von bestimmten Bedingungen auch andere Programme ausführen. – Brad

Antwort

18

Es ist wirklich möglich. Das Hauptproblem, das Sie haben, ist, dass Windows als Terminalserver und eine Benutzersitzung als Remotesitzung angesehen werden sollte. Ihr Dienst sollte in der Lage sein, einen Prozess zu starten, der in der Remotesitzung ausgeführt wird.

Übrigens, wenn Sie einen Dienst schreiben, der unter Windows XP läuft und nicht zu einer Domäne hinzugefügt wird und der schnelle Benutzerwechsel aktiviert ist, können Sie die gleichen Probleme haben, einen Prozess auf dem zweiten (dritten) zu starten und so weiter) angemeldet Benutzer Desktop.

Ich hoffe, Sie haben ein Benutzer-Token, das Sie zum Beispiel in Bezug auf Identitätswechsel erhalten oder Sie haben eine dwSessionId Sitzung. Wenn Sie es nicht haben, können Sie versuchen, verwenden einige WTS-Funktion (Remote Desktop Services API http://msdn.microsoft.com/en-us/library/aa383464.aspx, zum Beispiel WTSEnumerateProcesses oder WTSGetActiveConsoleSessionId) oder LSA-API, um die entsprechenden Benutzer-Session, um herauszufinden, (LsaEnumerateLogonSessions siehe http://msdn.microsoft.com/en-us/library/aa378275.aspx und LsaGetLogonSessionData siehe http://msdn.microsoft.com/en-us/library/aa378290.aspx) oder ProcessIdToSessionId (siehe http://msdn.microsoft.com/en-us/library/aa382990.aspx).

Sie können GetTokenInformation Funktion mit dem Parameter TokenSessionId (siehe http://msdn.microsoft.com/en-us/library/aa446671.aspx) verwenden, um die Session-ID dwSessionId der Benutzer-Sitzung zu erhalten, wenn Sie die Benutzer hClient Token kennen.

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, ...); 

Dieser Code ist nur ein Schema. EnablePrivilege ist eine einfache Funktion verwendet AdjustTokenPrivileges zu aktivieren SE_TCB_NAME Privileg (siehe http://msdn.microsoft.com/en-us/library/aa446619.aspx als Vorlage). Es ist wichtig, dass der Prozess, von dem aus Sie einen Prozess starten, über TCB-Berechtigungen verfügt. Wenn Ihr Service jedoch unter dem lokalen System ausgeführt wird, verfügen Sie über ausreichende Berechtigungen. Das folgende Codefragment funktioniert übrigens nicht nur mit dem lokalen Systemkonto, sondern auch mit dem Konto SE_TCB_NAME, um die aktuelle Terminalserversitzung wechseln zu können.

Eine weitere Bemerkung. Im obigen Code starten wir einen neuen Prozess mit demselben Account wie der aktuelle Prozess (zB Lokales System). Sie ändern einen Code ändern, um ein anderes Konto zu verwenden, z. B. das Benutzer-Token hClient. Es ist nur wichtig, eine primary token zu haben. Wenn Sie ein Identitätswechsel-Token haben, können Sie es wie im obigen Code in das primäre Token konvertieren.

In der Struktur STARTUPINFO in CreateProcessAsUser verwendet werden, sollten Sie lpDesktop = WinSta0 \ Default“verwenden.

Verlassen Sie sich auf Ihre Anforderungen könnte es auch CreateEnvironmentBlock benötigt werden, um zu verwenden, um eine neue Umgebung Block zu erstellen, die Sie auf den neuen vorbei wird Prozess.

ich empfehle Sie auch How to ensure process window launched by Process.Start(ProcessStartInfo) has focus of all Forms? zu lesen, wo ich beschreiben, wie zu erzwingen, dass der Prozess im Vordergrund auf dem Anwender-Desktop gestartet.

+0

Ich habe gerade ein Problem gelöst, das ich mit einem Dienst hatte, der das tut, und Ihre Antwort hier gefunden ... Sehr hilfreich. In meinem Fall bekam ich einen nervigen "Zugriff verweigert" -Fehler, als ich einen Datei-Öffnen-Dialog von meinem erstellten Prozess ausführte (ansonsten funktionierte der Dialog gut). Der Aufruf von 'CreateEnvironmentBlock' und das Übergeben des Ergebnisses an' CreateProcessAsUser' scheint das Problem behoben zu haben. Muss ich wirklich diesen 'lpDesktop'-Wert meiner 'STARTUPINFO'-Struktur festlegen? Ich habe es einfach als NULL verlassen. Außerdem funktionierte es ohne das Setzen der SE_TCB_NAME-Berechtigung. – paddy

+0

@paddy: Sorry, aber alle Schritte sind wirklich erforderlich, wenn Sie einen gängigen Fall haben. Wenn der Dienst unter einem Benutzerkonto und nicht als System läuft, müssen Sie 'TokenSessionId' wirklich einstellen, damit der interaktive Benutzer die Fenster des gestarteten Prozesses sehen kann. Um 'SetTokenInformation' mit' TokenSessionId' verwenden zu können, muss das Dienstkonto die Berechtigung 'SE_TCB_NAME' besitzen und aktiviert werden. Wenn der Bildschirmschoner zum Zeitpunkt des Startens des Prozesses funktioniert, sollte "lpDesktop" eingestellt werden, um entweder den Desktop des Bildschirmschoners oder den Standardbenutzerdesktop anzuzeigen. – Oleg

3

Ja, mit CreateProcessAsUser Sie dies tun können. MSDN enthält Beispielartikel und es gibt einige Vorbehalte.

+0

Bitte geben Sie keine MSDN-Links mit Versionsnummern in ihnen an, es sei denn, es gibt einen Grund, diese Version immer zu verwenden. –

+0

Will, John. Danke für die Korrektur. – holtavolt

+0

Danke, ich arbeite jetzt an den API-Deklarationen für .NET. – Brad

3

Es kann getan werden, aber es ist nicht als gute Praxis für Dienstleistungen, die direkt mit den Nutzern Sitzungen zu interagieren, wie es ernsthafte Sicherheitslücken schaffen.

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

Ein besserer Ansatz ist 2-Programme zu erstellen, einen Back-End-Service und ein Front-End-Client UI-Programm. Das Service-Backend wird ständig ausgeführt und stellt seine Operationen mithilfe von WCF bereit (z. B.). Das Client-Programm könnte beim Start einer Benutzersitzung ausgeführt werden.

+0

Ich glaube nicht, dass der Inhalt des Artikels, den Sie verlinkt haben, noch für 2008 gültig ist (r2). Session null isolation et al ... –

+1

Das ist nicht ganz dasselbe. Brad fragt nach dem Erstellen eines/new/-Prozesses mit den Anmeldeinformationen des Benutzers und führt keine interaktiven Vorgänge in der Benutzersitzung als Dienstprozess durch. –

+1

Das Starten von Prozessen in einer Benutzersitzung erfolgt weiterhin mit dem Desktop eines Benutzers. Lässt Haare nicht spalten seine noch nicht gute Übung. Link war nicht das Beste, dem ich zustimme :) –

4

Wenn es hilft, wurde ich mit einem ähnlichen Problem konfrontiert b ut wollte eine reine Powershell-Lösung.

ich zusammengeschustert Bits von anderen Websites und kam mit dieser:

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 
} 

Ja, es ist ein wenig seltsam, aber es funktioniert - und was am wichtigsten ist man es von innen ps Remoting

Verwandte Themen