Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Nachricht auf allen Desktops anzeigen - und wieder schließen (https://www.delphipraxis.net/117589-nachricht-auf-allen-desktops-anzeigen-und-wieder-schliessen.html)

xpmd 21. Jul 2008 15:54


Nachricht auf allen Desktops anzeigen - und wieder schließen
 
Hi Leute, ich habe ein Problem vor dem ich bereits seit einer Woche sitze. Mittlerweile sind mir die Lösungsansätze ausgegangen, vielleicht weiß ja jemand den "Trick", den ich benötige.

Das Problem bezieht sich rein auf Windows Vista. Unter Windows XP hat jeder der Lösungsansätze funktioniert. Drum poste ich jetz auch mal keien Code, da das Problem bei der Vorgehensweise sitzt und nicht im Code selbst.

Aufgabe:
Ich habe einen Dienst unter dem lokalen SYSTEM Account laufen. Dieser Dienst soll eine Nachricht z.B. MessageBox in JEDER aktiven Terminal-Session, der interaktiven Console sowie dem Logon-Screen anzeigen. Anschließend muss der Dienst dieses Fenster aber auch automatisch Schließen können. Und genau beim Schließen liegt das Problem!

Lösungsansatz 1:
Über WTSEnumSessions alle aktiven Terminal-Sessions auslesen. An jede Session dann per WTSSendMessage und MB_SERVICE_NOTIFICAION eine Nachricht schicken. Funktioniert soweit Prima - sowohl XP und Vista zeigen die Nachricht überall wie gewünscht an. Problem ist nun das Fenster wieder zu schließen: Ich habe probiert über EnumDesktops / OpenDesktop / EnumDesktopWindows alle Fenster durchzulaufen und das Entsprechende dann per PostMessage->WM_CLOSE zu schließen. Unter XP kein Problem. Unter Vista gibts aber, dank der Isolation der Session 0, keinen Zugriff auf die MessageBox. Auch SetThreadDesktop hilft nicht bzw schlägt fehl.

Lösungsansatz 2:
Habe nun einen Trick versucht: Die Funktion welche alle Desktops/Fenster durchläuft und das Nachrichtenfenster schließen soll, wurde in eine separate Exe ausgelagert. Anschließend wird für jede aktive Terminal-Session über WTSQueryUserToken der User ermittelt und mit CreateProcessAsUser diese Schließen-Exe unter jedem angemeldeten User gestartet. Geht unter Vista leider auch nicht, Die EnumDesktopWindows listet die MessageBox nicht auf.

Lösungsansatz 3:
Die Nachricht nicht per WTSSendMessage sondern als eigene Form anzeigen. EnumDesktops ermittelt alle aktiven Desktops. Anschließend wird für jede anzuzeigende Nachricht ein Thread gestartet, welcher mit SetThreadDesktop den jeweiligen Desktop setzt und die Form anzeigt. Anschließend wartet der Thread auf eine interne Nachricht um sein eigenes Fenster wieder zu schließen. Funktioniert unter XP wieder wunderbar. Unter Vista wird der Default-Desktop zwar übernommen, das Fenster erscheint allerdings nicht.

Ich bin am verzweifeln. Weiß evtl jemand noch einen Lösungsansatz? Für Ideen wäre ich sehr dankbar!

md

Apollonius 21. Jul 2008 15:59

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Die Ansätze 1 und 3 können gar nicht funktionieren: Die TS eines Prozesses kann nicht geändert werden.
Der Ansatz 2 ist korrekt. Hast du in der StartupInfo-Struktur Desktop und Window-Station angegeben? Hast du im Token die SessionID gesetzt?

xpmd 21. Jul 2008 16:11

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Merci für die flotte antwort. :thumb:

Ne hab ich nicht gemacht :oops: Werds morgen mal probieren und gib dann bescheid. Für heute hab ich erstmal genug :lol:

gore 21. Jul 2008 16:20

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
> Die TS eines Prozesses kann nicht geändert werden.

Ich bin der Meinung, dass ich darüber schon was gelesen habe (ging evt. via einer WTS Funktion). Ich würde in jede aktive Session eine ShowAndCloseMsgbox.exe via CreateProcessAsUser injizieren. Datenaustausch zum Service via named pipes.

Apollonius 21. Jul 2008 16:24

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Ja und? Die TS eines neuen Prozesses wird mit dem Token festgelegt - sobald der Prozess läuft, ist sie fest. Nichts anderes habe ich behauptet.

NickelM 21. Jul 2008 17:41

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Sagt mal kann mann nicht vieleicht das Massage-Fenster mit dem WinSpy Programm von dem einem hier im Forum (sorry für das anreden, aber ich weiß grad net wer das nochmal war) verwenden und mal starten und versuchen das Handle rauszufinden und dan mit ShowWindow(Handle, sw_close); (soviel ich jetzt aus Kopf weiß) wieder schließen?????

Oder bin ich da voll auf dem Holzweg :oops:

Dezipaitor 21. Jul 2008 22:37

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Schreibe einen eigenen Prozess, der die Nachricht anzeigt und dann sich später selbst beendet.

Und dazu nehme:
Jwscl implementiert JwCreateProcessInSession

Die aktualisierte Version im SVN trunk Ordner funktioniert auch unter Windows 2000 Workstation.

Delphi-Quellcode:
{[B]JwCreateProcessInSession[/B] creates a new process in a user's session using various ways to
achieve success.
This procedure needs JwInitWellKnownSIDs to be called.
To run a process in another session you need to have SYSTEM rights.


@param ApplicationName defines the application to be run in the session
@param CommandLine defines the parameters for the application
@param CurrentDirectory defines the start folder of the app.
@param SessionID defines the target session where the new application is to be started.
@param CreationFlags defines creation flags that are delivered to CreateProcess parameter with
 same name
@param Desktop defines the target windowstation and desktop name. If empty
the default target is "winsta0\default"
@param StartupInfo defines startup info delivered to to CreateProcess parameter with
 same name. Don't forget to initialize the structure first before calling this procedure.
<code lang="delphi>
ZeroMemory(@StartupInfo, sizeof(StartupInfo));
</code>
@param WaitForProcess defines whether the procedure should wait for the process to end
and clean up all allocated resources or just return to the caller. In last case
the caller is responsible to free the returned token, the environment block and
the users profile
@param Output contains returned data in case parameter WaitForProcess is false.
The caller is responsible to free the contained member allocation
@param LogServer receives a log server instance. It is used to log events for
mostly debugging purposes. If this parameter is nil, no events are logged  

raises
 EJwsclProcessIdNotAvailable: will be raised if no token could be found
for the given SessionID
 EJwsclNilPointer: will be raised if JwInitWellKnownSIDs was not called before
}

new32 22. Jul 2008 20:51

OT: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Zitat:

Zitat von Apollonius
Die TS eines neuen Prozesses wird mit dem Token festgelegt

Das Access-Token eines Prozesses lässt sich allesdings auch nach Prozessbeginn ändern! :wink:

Dezipaitor 22. Jul 2008 22:38

Re: OT: Nachricht auf allen Desktops anzeigen - und wieder s
 
Zitat:

Zitat von new32
Das Access-Token eines Prozesses lässt sich allesdings auch nach Prozessbeginn ändern! :wink:

Das funktioniert jedoch nicht mehr in Vista. Es geht nur solange der Prozess noch nicht gestartet wurde (CREATE_SUSPENDED)
und dann nicht mehr.

Apollonius 22. Jul 2008 23:23

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Klar lässt sich das Token ändern. Ein eventuelles Ändern der Session-ID wirkt sich aber nicht auf den Prozess aus. Es würde zu arg abstrusen Effekten führen, wenn man die Session eines Prozesses ändern könnte, während er läuft. Denk nur mal daran, dass er ein Handle zu einem Session-lokalen Objekt (z.B. Desktop) haben könnte. Dieses Handle verlöre seinen Sinn.

Dezipaitor 23. Jul 2008 08:17

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Wie gesagt, ein Prozess kann nicht das Token eines anderern Prozesses nachträglich ersetzen unter Vista. Es gibt eine 5.

Wenn die SessionID gesetzt wurde und der Prozess läuft, liefert SetTokenInformation einen Fehler zurück.

new32 23. Jul 2008 09:29

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Danke für den Hinweis!

Eines meiner Programme benutzt nämlich ZwSetInformationProcess um sein Token mit bestimmten Rechten auszustatten.
Und tatsächlich Vista sagt nur c0000005 dazu. :oops:

xpmd 23. Jul 2008 10:50

geschafft
 
Zitat:

Zitat von Apollonius
Die Ansätze 1 und 3 können gar nicht funktionieren: Die TS eines Prozesses kann nicht geändert werden.
Der Ansatz 2 ist korrekt. Hast du in der StartupInfo-Struktur Desktop und Window-Station angegeben? Hast du im Token die SessionID gesetzt?

Danke! Habs jetzt mit Ansatz 2 umgesetzt und so klappts auch. Das Setzen der WindowStation/Desktop für den Prozessstart ist gar nicht nötig.
Das Programm startet sich 1x pro Terminal Session mit dem Parameter STOPX selbst. Durch den Start mit diesem Parameter werden im entsprechenden Kontext alle Fenster der entsprechenden Session durchlaufen.

Da die WTSSendMessage MB_SERVICE_NOTIFICATION nun auch die MessageBox im Login-Screen und beim "gesperrtem" Bildschirm anzeigt, muss ich es noch irgendwie schaffen an diese Fenster zu kommen. Ich lasse vom Hauptprozess unter Session0 / System nochmal zusätzlich alle verfügbaren WindowStations und Desktops nach der MessageBox durchsuchen. Allerdings wird sie nicht gefunden. Ich werde da noch probieren, einen zusätzlichen Prozess unter expliziter Angabe der WinSta0\Winlogon und WinSta0\Disconnect zu starten.

Delphi-Quellcode:
for i := 0 to Length(SessionInfos) - 1 do begin
  UserToken := QueryUserToken(SessionInfos[i].SessionId);
  ZeroMemory(@SI, SizeOf(SI));
  ZeroMemory(@PI, SizeOf(PI));
 
  SI.lpDesktop := nil;
  SI.dwFlags := STARTF_USESHOWWINDOW;
  SI.wShowWindow := SW_HIDE;
  SI.cb := SizeOf(si);
 
  DuplicateTokenEx(UserToken, MAXIMUM_ALLOWED, nil, SecurityImpersonation, TokenPrimary, DuplicatedToken);
  if(DuplicatedToken <> 0) then begin
    SetTokenSessionId(DuplicatedToken, SessionInfos[i].SessionId);
    CreateProcessAsUser(DuplicatedToken,PAnsiChar(ParamStr(0)) , PAnsiChar(ParamStr(0) + ' STOPX'), nil, nil, false,          CREATE_NEW_CONSOLE, nil, nil, SI, PI);
  end;
  CloseHandle(UserToken);
  CloseHandle(DuplicatedToken);
end;
hier der Code für SetTokenSessionId:

Delphi-Quellcode:
{$EXTERNALSYM _SetTokenInformation}
function _SetTokenInformation(TokenHandle: Cardinal; TokenInformationClass: Integer; TokenInformation: PDWORD; TokenInformationLength: DWORD): bool; stdcall;
function SetTokenSessionId(UserToken: Cardinal; SessionId: integer): bool;

implementation

function _SetTokenInformation; external 'Advapi32.dll' name 'SetTokenInformation';
function SetTokenSessionId(UserToken: Cardinal; SessionId: integer): bool;
var Information: DWORD;
begin
  Information := DWORD(SessionId);
  result := _SetTokenInformation(UserToken, 12, @Information, Cardinal(SizeOf(Information)));
end;

xpmd 23. Jul 2008 11:46

Re: geschafft
 
Zitat:

Zitat von xpmd
Da die WTSSendMessage MB_SERVICE_NOTIFICATION nun auch die MessageBox im Login-Screen und beim "gesperrtem" Bildschirm anzeigt, muss ich es noch irgendwie schaffen an diese Fenster zu kommen. Ich lasse vom Hauptprozess unter Session0 / System nochmal zusätzlich alle verfügbaren WindowStations und Desktops nach der MessageBox durchsuchen. Allerdings wird sie nicht gefunden. Ich werde da noch probieren, einen zusätzlichen Prozess unter expliziter Angabe der WinSta0\Winlogon und WinSta0\Disconnect zu starten.

Klappt leider nicht. :-(

Unter diesen Desktops werden keine Fenster gefunden. Hat jemand ne Idee wie ich diese auch noch zum schließen bringe?

Dezipaitor 23. Jul 2008 12:57

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Wenn du pro Session eh schon ein Programm hast, dann kannst du die DialogBox auch gleich selbst anzeigen :D

Schau dir mal an, von welchem Prozess der Dialog angezeigt wird. Wenn der Prozess Systemrechte besitzt, dann wirst du es nicht finden,
wenn deine Suche nicht auch von einem Prozess mit Sysrechten ausgeführt wird.

xpmd 23. Jul 2008 13:20

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Laut den Infos von hier: http://www.uvnc.com/vista/ müsste ich den Schließen-Prozess unter jeder Session am Desktop Winlogon mit SYSTEM-Rechten starten, um auf dessen Fenster zugreifen zu können. Die WTSSendMessage Nachricht wird offenbar von einem Windows-Systemprozess angezeigt. Frage ist jetz wie ich eben einen Prozess z.B. unter Session 2\Winsta0\Winlogon und User SYSTEM starte.

STARTUPINFO lpDesktop auf Winsta0\Winlogon setzen und über SetTokenInformation die Session des UserTokens auf die entsprechende Session setzen würde ich jetzt sagen. Aber wie geht das mit User SYSTEM?

QueryUserToken auf Session 0 liefert einen Token 0 zurück. Diesen dupliziert ergibt auch 0 ;-) Entsprechen kann ich keine andere Session über SetTokenInformation setzen und den Schließen-Prozess damit starten. Hat SYSTEM überhaupt ein UserToken? Oder bin ich da auf dem Holzweg?

:|

Apollonius 23. Jul 2008 13:59

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Wenn dein Service als System läuft, hat er auch ein entsprechendes Token. Also kannst du einfach dein Prozess-Token duplizieren und das Duplikat verwenden, nachdem du dort die Session-ID gesetzt hast.

Dezipaitor 23. Jul 2008 18:17

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
vorsicht: WTSQueryUserToken liefert unter xp das user token zurück.

xpmd 24. Jul 2008 14:19

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
es ist zum verzweifeln :(

Jetz paßt alles soweit, die MEssageBox wird überall wieder geschlossen bis auf einen Fall: Wenn mehrere Sessions unter Vista aktiv sind, wird das Fenster zwar in jeder session geschlossen. Scheinbar überwacht aber WTSSendMessage jedes Nachrichtenfenster, ob es automatisch geschlossen wird ohne dass es der User sehen konnte. Also quasi Wenn es in einer Session die grad nicht verbunden ist, geschlossen wird. Anders kann ichs mir nicht erklären. Das Fenster wird laut Debug-Outputs geschlossen erscheint aber sofort drauf wieder mit neuem Handle. Irgendwie eine undokumentierte Funktion von WTSSendMEssage?

eine Lösung wüsst ich für den Fall noch. Ich lass den Schließen-Prozess so lange im Kreis laufen bis keine MessageBox mehr existiert. Der prozess läuft quasi dann so lange bis sich der angemeldete Benutzer zu der Session verbindet. Anschließend kann das Fenster dann geschlossen werden.

Echt frustrieren das. Mal sehen obs so dann auch wirklich zu 100% alle existierenden Nachrichtenfenster schließen kann, oder ob der 281 Stolperstein auftaucht :wall:

Dezipaitor 24. Jul 2008 15:02

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Warum machst du nicht einfach deinen eigenen MessageBox Prozess?

xpmd 24. Jul 2008 15:19

Re: Nachricht auf allen Desktops anzeigen - und wieder schli
 
Momentan klappts jetzt aber mit dem Prozess der so lange läuft bis die Session aktiv wird und das Fenster sich nicht wieder von alleine öffnet. Wenn das auch nicht funktioniert hätte bzw nochmal n neues Hindernis gekommen wär hätt ichs mit der eingenen MessageBox gemacht.

Man muss es halt erst rausfinden dass WTSSendMessage so reagiert. Dachte immer es liegt am User/Desktop Kontext :/

Wärs denn möglich mit einer eigenen Message-Box das Fenster auch im Login-Screen und dem Gesperrt-Desktop anzeigen zu lassen, so wie WTSSendMessage mit MB_SERVICE_NOTIFICATION das tut? Evtl wird das ganze ja mal erweitert.


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:46 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