Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Eine Session (oder Sitzung) ist ein abgetrennter Bereich, der eigene WindowStations und Desktops besitzt (und einiges andere).
Jeder Benutzer, der sich per Winlogon einloggt, wird eine neue SessionID zugewiesen. In Windows 2000 gibt es nur die 0. In XP bekommt der erste Benutzer die 0, alle anderen, 1 oder größer. Dienste tummeln sich immer in Session 0 und sind daher mit dem ersten eingeloggten Benutzer in einem Boot. Daher können Dienst auf den Benutzerdesktop zugreifen und CreateCompatibilityQueryUserToken findet Explorer.exe In Vista jedoch, hat der erste Benutzer die Session ID #1 und die Dienste #0. Da jede Session ihren eigenen Desktop hat (vereinfacht ausgedrückt) und Explorer.exe nur für normale Benutzer gestartet wird, gibt es Probleme. Die Session ID #2, dich ich angegeben habe, habe ich mit der Absicht angegeben, dass man einen Fehler bekommst. Nur in diesem Fall macht man sich Gedanken drüber. (Hatte vorhin wenig Zeit was zu schreiben). |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Vielen Dank für die Erläuterung! Damit wird so einiges klarer.
Hole mir die TokenSessionID jetzt dynamisch vom aktuellen Benutzer und erstelle für diesen auch gleich den passenden Umgebungsblock. Scheint alles zu funktionieren. :) Intern werde ich also drei verschiedene Blöcke für 2000, XP und Vista basteln, damit das jeweils korrekt funktioniert. Gehe ich richtig in der Annahme, dass sich 2003 wie XP verhält? Und 2008 wie Vista? Stehen mir momentan leider nicht zum Testen zur Verfügung... Christian, ich möchte noch loswerden, dass ich ganz großen Respekt vor Deinem Wissen und Deiner Leistung habe. Allein wenn ich sehe, was alles in der JWSCL steckt, kann ich nur staunen. Wenn man aber noch bedenkt, dass da jemand dahinter steckt, der das alles versteht und systematisch aufgebaut hat, kann man nur noch gratulieren. Dann nimmst Du Dir aber noch die Zeit, mir kleinem Programmierer, bei meinen Problemen zu helfen. Einfach klasse. Vielen Dank für alles! :hello: |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Great that you got it all working! Maybe you can post your final code for future reference?
Just adding some notes, also future reference: Vista SP1 and Server 2008 share the same kernel and terminal server. I don't see why WTSQueryUserTokenEx should fail on vista (it uses undocumented api from JwaWinsta but actually it does exactly the same as WTSQueryUserToken internally). Don't forget this note about WTSGetActiveConsoleSession: Return Value The session identifier of the session that is attached to the physical console. If there is no session attached to the physical console, (for example, if the physical console session is in the process of being attached or detached), this function returns 0xFFFFFFFF. On Windows 2000 with Terminal Server it's safe to assume session 0 is the console session. If you launch a process as user system you always get the environment of Default User because system doesn't have one, same goes for CURRENT_USER in the registry. |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Sure, this is my final code (so far ;)):
Delphi-Quellcode:
Getestet mit 2000, XP und Vista und für gut befunden. :)
ZeroMemory(@StartupInfo, sizeof(StartupInfo));
StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.lpDesktop := 'winsta0\default'; pCmdLine := TJwPChar('"'+App+'" ' + Parameters); pCurDir := Nil; if Length(CurDir) > 0 then pCurDir := TJwPChar(CurDir); //get the token from the service system session ServiceToken := TJwSecurityToken.CreateTokenEffective(MAXIMUM_ALLOWED); //copy the token to be able to change the TokenSessionId //Info: Win2000: Only 0 // WinXP: Service=0, 1.User=0, 2.User=1, 3.User=2, ... // WinVista: Service=0, 1.User=1, 2.User=2, 3.User=3, ... CopiedToken := TJwSecurityToken.CreateDuplicateExistingToken(ServiceToken.TokenHandle, MAXIMUM_ALLOWED); //get the token of the logged in user if is2000 then UserToken := TJwSecurityToken.CreateCompatibilityQueryUserToken(MAXIMUM_ALLOWED, 'explorer.exe') else //XP, 2003, Vista, 2008 UserToken := TJwSecurityToken.CreateWTSQueryUserTokenEx(nil, WtsGetActiveConsoleSessionID); //give the copied token the same sessionid as the logged in user CopiedToken.TokenSessionId := UserToken.TokenSessionId; //create the environment block using the logged in user JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, true); try if not CreateProcessAsUser( { TODO : UNICODE VERSION? } CopiedToken.TokenHandle, TJwPChar(App), //__in_opt LPCTSTR lpApplicationName, pCmdLine, //__inout_opt LPTSTR lpCommandLine, nil, //__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, nil, //__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, true, //__in BOOL bInheritHandles, CREATE_NEW_CONSOLE or CREATE_DEFAULT_ERROR_MODE or CREATE_UNICODE_ENVIRONMENT, //__in DWORD dwCreationFlags, pEnv, //__in_opt LPVOID lpEnvironment, pCurDir, //__in_opt LPCTSTR lpCurrentDirectory, StartupInfo, //__in LPSTARTUPINFO lpStartupInfo, ProcInfo //__out LPPROCESS_INFORMATION lpProcessInformation ) then raiseLastOsError; finally DestroyEnvironmentBlock(pEnv); end; CloseHandle(ProcInfo.hProcess); CloseHandle(ProcInfo.hThread); "is2000" ist eine Prozedur, die prüft, ob das aktuelle Betriebssystem Win2000 ist. Müsste man sich also noch dazubasteln. Einige der Schritte (z.B. SessionID kopieren, CreateProcessAsUser statt CreateProcess, ...) sind nicht für alle Betriebssysteme notwendig, schaden aber auch nicht. Ich fand es sinnvoller, den Code übersichtlich zu lassen, als wirklich unterschiedliche Blöcke für jedes Betriebssystem zu erstellen. Was ich noch nicht so recht weiß, ist, ob es wirklich notwendig ist, die Unicode Version von CreateProcessAsUser (mit entsprechenden Parametern) zu verwenden. Sehe aber auch keine Nachteile, weshalb ich das wohl noch machen werde. Anmerkungen und Verbesserungsvorschläge werden natürlich gern angenommen. ;) |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Zitat:
Delphi-Quellcode:
sowieso. Da komme ich ja nicht drumherum, wenn ich die Benutzerumgebung mitgeben will.
JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, false);
Wenn ich TJwAutoPointer.Wrap(...) verwende, brauche ich dann die try-finally Blöcke trotzdem noch oder reicht das tatsächlich einfach nach jedem Erzeugen das Token einmal zu wrappen? Deine anderen Hinweise habe ich soweit befolgt und den Code angepasst. Danke! |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Entweder wrappen oder try/finally.
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Ja was ist denn? Funktioniert es oder nicht?
|
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Ja, klar. Hatte das ja schon bei meinem letzen Posting geschrieben. Habe eben noch die kleineren Anpassungen, die Du geschrieben hast, eingebaut.
So sieht das ganze in der letzten Version aus:
Delphi-Quellcode:
procedure StartApp(const App, Parameters, CurDir : TJwString);
var StartupInfo : TStartupInfoA; ProcInfo : TProcessInformation; pEnv : Pointer; pCurDir, pCmdLine : TJwPChar; ServiceToken, CopiedToken, UserToken : TJwSecurityToken; begin ZeroMemory(@StartupInfo, sizeof(StartupInfo)); StartupInfo.cb := SizeOf(StartupInfo); StartupInfo.lpDesktop := 'winsta0\default'; pCmdLine := TJwPChar('"'+App+'" ' + Parameters); pCurDir := Nil; if Length(CurDir) > 0 then pCurDir := TJwPChar(CurDir); //get the token from the service system session ServiceToken := TJwSecurityToken.CreateTokenEffective(MAXIMUM_ALLOWED); TJwAutoPointer.Wrap(ServiceToken); //copy the token to be able to change the TokenSessionId //Info: Win2000: Only 0 // WinXP: Service=0, 1.User=0, 2.User=1, 3.User=2, ... // WinVista: Service=0, 1.User=1, 2.User=2, 3.User=3, ... CopiedToken := TJwSecurityToken.CreateDuplicateExistingToken(ServiceToken.TokenHandle, MAXIMUM_ALLOWED); TJwAutoPointer.Wrap(CopiedToken); //get the token of the logged in user if is2000 then UserToken := TJwSecurityToken.CreateCompatibilityQueryUserToken(MAXIMUM_ALLOWED, 'explorer.exe') else //XP, 2003, Vista, 2008 UserToken := TJwSecurityToken.CreateWTSQueryUserTokenEx(nil, WtsGetActiveConsoleSessionID); TJwAutoPointer.Wrap(UserToken); //give the copied token the same sessionid as the logged in user CopiedToken.TokenSessionId := UserToken.TokenSessionId; //create the environment block using the logged in user JwaWindows.CreateEnvironmentBlock(@pEnv, UserToken.TokenHandle, false); try if not CreateProcessAsUser( CopiedToken.TokenHandle, TJwPChar(App), //__in_opt LPCTSTR lpApplicationName, pCmdLine, //__inout_opt LPTSTR lpCommandLine, nil, //__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, nil, //__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, false, //__in BOOL bInheritHandles, CREATE_NEW_CONSOLE or CREATE_DEFAULT_ERROR_MODE or CREATE_UNICODE_ENVIRONMENT, //__in DWORD dwCreationFlags, pEnv, //__in_opt LPVOID lpEnvironment, pCurDir, //__in_opt LPCTSTR lpCurrentDirectory, StartupInfo, //__in LPSTARTUPINFO lpStartupInfo, ProcInfo //__out LPPROCESS_INFORMATION lpProcessInformation ) then raiseLastOsError; finally DestroyEnvironmentBlock(pEnv); end; CloseHandle(ProcInfo.hProcess); CloseHandle(ProcInfo.hThread); end; |
Re: Programm von Dienst starten lassen (Jetzt aber wirklich
Ja das sieht doch super aus. Kann ich das für den JEDI Blog verwenden als Post?
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:19 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz