Einzelnen Beitrag anzeigen

CodeX

Registriert seit: 30. Okt 2004
475 Beiträge
 
Delphi 12 Athens
 
#24

Re: Programm von Dienst starten lassen (Jetzt aber wirklich

  Alt 25. Mai 2008, 18:06
Sure, this is my final code (so far ):

Delphi-Quellcode:
      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);
Getestet mit 2000, XP und Vista und für gut befunden.
"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.
  Mit Zitat antworten Zitat