![]() |
Dienst: Aktuellen Inputdesktop auslesen
Bis Windows XP konnte man ja auch den aktuellen Inputdesktop auslesen, wenn das Programm als Service (Dienst) ausgeführt wurde. Zumindest dann, wenn es ein interaktiver Dienst war, dem man also die Kommunikation mit dem Desktop erlaubt.
Seit Vista können Meldungen des interaktiven Dienstes nicht mehr auf dem normalen Input Desktop ausgegeben werden, Windows gibt dann eine Info aus, dass eine Meldung vorliegt, die kann man sich dann in einem extra Fenster ansehen. Leider ist aber anscheinend auch das auslesen des Desktops nicht mehr erlaubt, wenn das Programm als Dienst läuft. Oder gibt es eine Variante, wie das doch noch möglich ist? Der einzige WorkAround, der mir hierzu einfällt wäre, dass der Dienst, wenn ein User-Desktop aktiv ist ein Programm startet, welches dann - anstatt des Dienstes - den Bildschirminhalt zum Client überträgt. Auf diese Extra-Schleife würde ich aber gerne verzichten. Ein Auszug aus dem Code, der bis Windows XP noch funktionierte:
Delphi-Quellcode:
function hs_GetInputDesktop: String; var hd: HDESK; szb: Array [0..80] of char; needed: DWord; begin hd := OpenInputDesktop (0, false, DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or DESKTOP_ENUMERATE or DESKTOP_HOOKCONTROL or DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS or DESKTOP_JOURNALPLAYBACK or DESKTOP_JOURNALRECORD or DESKTOP_SWITCHDESKTOP or GENERIC_WRITE); if hd = 0 then begin Result := ''; end else begin if GetUserObjectInformation (hd, UOI_NAME, @szb, SizeOf(szb), needed) then begin Result := (String (szb)); end else begin Result := ''; end; end; if hd <> 0 then begin CloseDesktop (hd); end; end; function hs_GetThreadDesktop (TID: DWord): String; var hd: HDESK; szb: Array [0..80] of char; needed: DWord; begin hd := GetThreadDesktop (TID); if hd = 0 then begin Result := ''; end else begin if GetUserObjectInformation (hd, UOI_NAME, @szb, SizeOf(szb), needed) then begin Result := String (szb); end else begin Result := ''; end; end; end; procedure TWatchThread.Execute; // Diese Funktion kopiert den aktuellen Bildschirminhalt in // ein bitmap, aber ressorcenschonend var aHDC: HDC; i, c, L, al: Integer; hd, hd_old: HDESK; dtw: THandle; begin cs.enter; hd_Old := 0; if hs_RunAsService then begin hd_old := GetThreadDeskTop (GetCurrentThreadID); // Das funktioniert nur noch bis Windows XP if hs_GetInputDesktop <> hs_GetThreadDesktop (GetCurrentThreadID) then begin if hs_GetInputDesktop = 'Winlogon' then begin hd := OpenDesktop ('Winlogon', 0, false, DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or DESKTOP_ENUMERATE or DESKTOP_HOOKCONTROL or DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS or DESKTOP_JOURNALPLAYBACK or DESKTOP_JOURNALRECORD or DESKTOP_SWITCHDESKTOP or GENERIC_WRITE); if hd <> 0 then begin if SetThreadDesktop (hd) then begin end; end; end else begin hd := OpenInputDesktop (0, false, DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or DESKTOP_ENUMERATE or DESKTOP_HOOKCONTROL or DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS or DESKTOP_JOURNALPLAYBACK or DESKTOP_JOURNALRECORD or DESKTOP_SWITCHDESKTOP or GENERIC_WRITE); if hd <> 0 then begin if SetThreadDesktop (hd) then begin end; end else begin // Desktop konnte nicht geholt werden end; end; end; end else begin hd := OpenInputDesktop (0, false, DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or DESKTOP_ENUMERATE or DESKTOP_HOOKCONTROL or DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS or DESKTOP_JOURNALPLAYBACK or DESKTOP_JOURNALRECORD or DESKTOP_SWITCHDESKTOP or GENERIC_WRITE); if hd = 0 then begin if (GetOsName = 'Windows Vista') or (GetOsName = 'Windows Seven') or (GetOsName = 'Windows Eight') or (GetOsName = 'Windows Ten') then begin // UAC-Fenster ist Aktiv //Client informieren. ... end; //... Hier Bildschirminhalt kopieren und zum Client transferieren end; |
AW: Dienst: Aktuellen Inputdesktop auslesen
Das geht per Definition nicht mehr. Aus Sicherheitsgründen.
Ein Programm im Benutzerkontext, das die Daten anzeigt, ist genau das richtige. Wir benutzen dafür eine Webseite. Im Dienst ist dafür ein Datasnap Server integriert. Das hat auch den Vorteil, dass sich der Dienst voll auf die Daten konzentrieren kann und keine GUI-Logik mehr benötigt. |
AW: Dienst: Aktuellen Inputdesktop auslesen
Ja, das hatte ich befürchtet.
Gerade mal getestet, wenn das Serverprogramm als Dienst läuft, dann kann ich zwar mit dem Dienst Dateien auf dem Rechner kopieren, löschen, umbenennen etc. aber nicht ausführen? ShellExecute scheint dann nicht zu funktionieren? Wie starte ich dann mit dem Dienst ein GUI-Programm im gerade aktuellen User-Kontext? Auf einen Input-Desktop kann ich ja mit dem Dienst nicht wechseln. |
AW: Dienst: Aktuellen Inputdesktop auslesen
Ha, genau damit habe ich mich letztens auch rumgeschlagen. Es geht. Man muss auch nicht bei jedem Wechsel eine neue Instanz starten, obwohl es viele Programme gibt, die es genau so machen.
Es ist aber auch tatsächlich immer noch möglich (zumindest unter Windows 7), GUI-Prozesse mit System-Rechten laufen zu lassen. Auch wenn Microsoft das extrem verschleiert (aus gutem Grund). Man braucht nur (*) einen Dienst, der das Programm einmal am Anfang mit System-Rechten startet. Wenn das Programm mit System-Rechten erst mal läuft, dann klappt auch das Wechseln des Desktops wieder. Hier mal meine Funktion zum Starten eines Prozesses mit System-Rechten (das Ergebnis von mehreren Tagen Recherche):
Delphi-Quellcode:
Aber bitte mit Vorsicht genießen!
function CreateElevatedUserProcess(ProcessPath: String; ProcessArgs: String; out Log: String): Boolean;
var OwnToken: HANDLE; NewToken: HANDLE; SecurityAttributes: SECURITY_ATTRIBUTES; SessId: DWORD; UiAccess: DWORD; CommandLine: String; StartupInfo: TSTARTUPINFO; ProcessInformation: PROCESS_INFORMATION; begin Log := 'OpenProcessToken'; Result := OpenProcessToken(GetCurrentProcess, TOKEN_ALL_ACCESS or TOKEN_ADJUST_SESSIONID, @OwnToken); if not Result then exit; SecurityAttributes.nLength := sizeof(SecurityAttributes); SecurityAttributes.bInheritHandle := TRUE; SecurityAttributes.lpSecurityDescriptor := nil; Log := 'DuplicateTokenEx'; Result := DuplicateTokenEx( OwnToken, MAXIMUM_ALLOWED, @SecurityAttributes, SecurityImpersonation, TokenPrimary, @NewToken); if not Result then exit; Log := 'WTSGetActiveConsoleSessionId'; SessID := WTSGetActiveConsoleSessionId; Log := 'SetTokenInformation (TokenSessionId)'; Result := SetTokenInformation(NewToken, TokenSessionId, @SessId, sizeof(SessId)); if not Result then exit; UiAccess := 1; Log := 'SetTokenInformation (UiAccess)'; Result := SetTokenInformation(NewToken, TokenUIAccess, @UiAccess, sizeof(UiAccess)); if not Result then exit; FillChar(StartupInfo, sizeof(StartupInfo), 0); StartupInfo.wShowWindow := SW_SHOWDEFAULT; StartupInfo.lpDesktop := 'winsta0\Default'; CommandLine := '"' + ProcessPath + '"' + ' ' + ProcessArgs; Log := 'CreateProcessAsUser'; Result := CreateProcessAsUser( NewToken, LPCTSTR(ProcessPath), LPTSTR(CommandLine), @SecurityAttributes, @SecurityAttributes, FALSE, 0, nil, nil, @StartupInfo, @ProcessInformation); end; Du könntest dich bei mir revanchieren, indem du mal testest, ob das ganze unter Windows 8 und Windows 10 auch noch funktioniert ;). Habe es bisher nur unter Windows 7 getestet. Edit: (*) Achja, es gibt noch ein paar Dinge zu beachten. Programm und Dienst (?) müssen digital signiert sein und in einem „sicheren“ Ordner liegen. Andernfalls wird der Start verweigert. Ein „sicherer“ Ordner ist z.B. „C:\Programme“. Sind noch mal zusätzliche Hürden von Microsoft. |
AW: Dienst: Aktuellen Inputdesktop auslesen
Hey, da hab ich ja echt Glück gehabt!
Klappt auch, insofern schon mal vielen Dank!:thumb::thumb: Und hier die erste Rückmeldung: Funktioniert auch unter Windows 8.1. Dabei war der Dienst noch nicht mal signiert und lag auch nicht in einem geschützten Bereich (in der installierten Version wird beides aber der Fall sein, insofern ist es egal, ob diese Anforderungen nun letztlich gegeben sein müssen oder nicht). Zu Windows 10 gebe ich später noch eine Rückmeldung. |
AW: Dienst: Aktuellen Inputdesktop auslesen
Vielleicht noch eine Nachfrage:
Wie bekommt mein Service jetzt eigentlich mit, dass gerade kein Anmelde- oder Gespert-Desktop angezeigt wird, bzw. dass jetzt ein User-Desktop aktiv ist. Alle Anfragen zur Windows-Station liefern zu jeder Zeit leider nur "Service-0x0-3e7$" zurück. Ein "InputDesktop" steht ja nicht zur Verfügung, anhand dessen ich einen Wechsel des Desktops testen könnte. |
AW: Dienst: Aktuellen Inputdesktop auslesen
Das müsstest du aus dem vom Dienst gestarteten Prozess heraus machen. Der Dienst selbst hat keinen Zugriff darauf.
Kann sein, dass das mit der Signatur und dem sicheren Ort doch nur für den gestarteten Prozess gilt und nicht für den Dienst. Deswegen hatte ich auch hinter Dienst ein Fragezeichen gesetzt. Aber wenn man schon den Prozess signiert, kann man den Dienst ja gleich auch signieren. |
AW: Dienst: Aktuellen Inputdesktop auslesen
Ah ja, habe gerade gesehen, mein Dienst kann das Programm schon starten, auch wenn noch gar keiner am PC angemeldet ist. Nehme an, das geht dann automatisch in den ersten User-Desktop rein.
Und von da aus kann das GUI-Programm dann arbeiten wie sonst auch. Wobei dann insgesamt nicht mehr so ein riesengroßer Unterschied zum Autostart ist, zumindest wenn sonst eh nur ein User am PC arbeitet. |
AW: Dienst: Aktuellen Inputdesktop auslesen
Wobei die saubere Lösung dennoch ein komplett eigenständiges Programm wäre, das im Autostart liegen oder vom Benutzer gestartet werden kann.
Ein weiterer Vorteil der Lösung ist, dass man nicht befürchten muss, dass diese in zukünftigen Windowsversionen plötzlich nicht mehr funktioniert... Und ich persönlich würde eine Lösung, die das anders macht, auch nicht einsetzen. In einer Lösung, die in Firmen zum Einsatz kommt, wäre das z.B. in der Regel ein NoGo. |
AW: Dienst: Aktuellen Inputdesktop auslesen
Das nur mit dem Autostart könnte man machen, wenn man z.B. per Remote einem User (z.B. im Unternehmen) Hilfestellung geben möchte.
Autostart reicht natürlich nicht, wenn ich mich per Remote auf den Login-Desktop schalten muss oder Remote UAC-Abfragen bestätigen muss. Da brauche ich schon den Dienst, der mir das ermöglicht. Man kann mein Programm für beide Fälle einrichten. Und hier will ich noch die versprochene Info nachzureichen: Das Abfragen des InputDesktops über einen gestarteten Prozess im Rahmen von UAC-Abfragen oder LogIn-Bildschirme funktioniert unter Vista, Seven, 8.x und Windows 10. Es funktioniert auch unter Windows Server 2008, leider aber nicht unter Windows Server 2012, da bleibt das gestartete Programm im Kontext des Dienstes (Im WinLogOn-Desktop). Da hat sich dann wohl leider was geändert....:cry: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:12 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz