Windows Vista: Interaktive Prozesse durch Dienst starten



  • Hallo zusammen!

    Ich habe mir einen Dienst geschrieben, der Programme starten und verwalten kann (neu starten u.ä.).
    Diese Programme werden wahlweise auf dem Benutzer-Desktop (also interaktiv) und/oder anderen Benutzerangaben ausgeführt, was auch bisher sehr gut funktioniert hat (zumindest auf Windows 2000 und XP).

    Da in Windows Vista die Session 0, in der die Dienste laufen, vom Benutzer abgeschottet ist, funktioniert mein Dienst nicht mehr richtig, wenn ein Programm interaktiv gestartet werden soll.
    In der MSDN gibt es einen Artikel dazu, in dem steht, man soll CreateProcessAsUser verwenden, was ich bereits mache. Allerdings scheint noch etwas zu fehlen, denn bisher schaffe ich es, den Prozess zwar in der Benutzer-Session zu starten, allerdings wird entweder die GUI nicht vollständig gezeichnet oder es erscheint dieses "Session 0 GUI Detection"-Fenster, was allerdings nicht Ziel des Dienstes ist.

    Der folgende Code stammt hauptsächlich aus dem Microsoft-Artikel KB165194 und würde diesen Thread sprengen.
    Ich beschränke mich deshalb vor allem auf das, was ich selber hinzugefügt oder geändert habe:

    BOOL MyCreateProcessWithLogon(string userName,                        // Benutzername
                                  string password,                        // Passwort
                                  string cmdLine,                         // Befehlszeile
                                  string workingDir,                      // Standard-Arbeitsverzeichnis
                                  BOOL useDefDesk,                        // Starte Prozess auf "WinSta0\Default"
                                  LPPROCESS_INFORMATION lpProcessInfo,    // Rückgabe der PROCESS_INFORMATION
                                  BOOL loadUserProfile,                   // Benutzerprofil laden
                                  int sessionID)                          // Session, in der der Prozess gestartet werden soll
    {
      HANDLE              hToken;
      HDESK               hdesk;
      HWINSTA             hwinsta;
      HWINSTA             hwinstaold;
      PSID                psid;
      STARTUPINFO         si;
    
      HANDLE hTokenSelf = NULL;
      if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hTokenSelf))
        return FALSE;
    
      if(!EnablePrivilege(hTokenSelf, SE_TCB_NAME))
        return FALSE;
    
      // 
      // obtain an access token for the user fester
      // 
      if (!LogonUser(
            userName.c_str(),
            NULL,
            password.c_str(),
            LOGON32_LOGON_INTERACTIVE,
            LOGON32_PROVIDER_DEFAULT,
            &hToken
            ))
            return FALSE;
    
      // 
      // initialize STARTUPINFO structure
      // 
      ZeroMemory(&si, sizeof(STARTUPINFO));
      si.cb        = sizeof(STARTUPINFO);
    
      //
      // Execute in another session
      //
      if(sessionID != -2)
        SetTokenSessionId(hToken, sessionID);
    
      //
      // Load the user profile
      //
      if(loadUserProfile)
      {
        PROFILEINFO profInfo;
        ZeroMemory(&profInfo, sizeof(PROFILEINFO));
    
        profInfo.dwSize = sizeof(PROFILEINFO);
        profInfo.lpUserName = const_cast<LPTSTR>(userName.c_str());
    
        if(!LoadUserProfile(hToken, &profInfo))
          return FALSE;
      }
    
      if(useDefDesk)
      {
        //
        // Dieser Teil ist genau so in dem genannten Artikel.
        // Hier wird der Desktop "WinSta0\Default" geöffnet und die Rechte darauf angepasst.
        //
      }
    
      //
      // Impersonate User
      //
      ImpersonateLoggedOnUser(hToken);
    
      bool ret;
    
      // 
      // start the process
      // 
      ret = CreateProcessAsUser(hToken, 
                                NULL, 
                                const_cast<LPSTR>(cmdLine.c_str()), 
                                NULL, 
                                NULL, 
                                FALSE, 
                                NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, 
                                NULL, 
                                workingDir.c_str(), 
                                &si, lpProcessInfo);
    
      //
      // End impersonation
      //
      RevertToSelf();
    
      DWORD createProcError;
      if(!ret)
        createProcError = GetLastError();
    
      if(useDefDesk)
      {
        SetProcessWindowStation(hwinstaold); //set it back
    
        // close the handles
        CloseHandle(hwinstaold);
        CloseWindowStation(hwinsta);
        CloseDesktop(hdesk);
      }
    
      CloseHandle(hToken);
    
      if(!ret)
        SetLastError(createProcError);
    
      return ret;
    }
    
    typedef DWORD (WINAPI *WTSGACSI)(void);
    
    BOOL SetTokenSessionId(HANDLE hToken, int sessID)
    {
      DWORD TokenInfo;
    
      if(sessID == -1)
      {
        // Benutze die ID der Session, die gerade aktiv ist.
        // Die zugehörige Funktion wird dynamisch geladen, um mit Windows 2000 kompatibel zu bleiben
        HMODULE hLib = LoadLibrary("kernel32.dll");
        if(hLib == NULL)
          return FALSE;
    
        WTSGACSI MyWTSGetActiveConsoleSessionId = reinterpret_cast<WTSGACSI>(GetProcAddress(hLib, "WTSGetActiveConsoleSessionId"));
        if(MyWTSGetActiveConsoleSessionId == NULL)
        {
          FreeLibrary(hLib);
          return TRUE;       // We seem not to be on WinXP or higher
        }
    
        TokenInfo = MyWTSGetActiveConsoleSessionId();
        FreeLibrary(hLib);
      }
      else if(sessID >= 0)
        TokenInfo = sessID;
      else
        return FALSE;
    
      HANDLE hProcessToken;
      if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) 
        return FALSE;
    
      EnablePrivilege(hProcessToken, SE_TCB_NAME);
    
      if(!SetTokenInformation(hToken, TokenSessionId, &TokenInfo, sizeof(DWORD)))
      {
        return FALSE; 
      }
    
      return TRUE;
    }
    

    Wie bereits erwähnt funktioniert das auf Windows XP (allerdings ohne schnelle Benutzerumschaltung getestet) und 2000.
    Es ist hierbei egal, ob useDefDesk gesetzt ist oder nicht, das Ergebnis auf Vista ist immer das gleiche: der Prozess kann keine GUI zeichnen, wenn er in der interaktiven Session gestartet wird.

    Ich wäre froh, wenn ihr mir helfen könntet.

    Gruß
    thommy88

    PS: Entschuldigt bitte das Gemisch aus deutschen und englischen Kommentaren 🙂 .



  • AFAIK ist dies nicht mehr möglich. Du kommst um das "Session 0 GUI Detection" nicht mehr drum rum, wenn Du dem Benutzer was sagen willst...



  • Erst mal danke für die Antwort!

    Meiner Erfahrung nach sollte es aber trotzdem möglich sein, da dieses "Session 0 GUI Detection" auch von einem Prozess kommt, der von einem Dienst gestartet wurde (hab mal mit ProcessExplorer nachgeschaut).
    Außerdem schreibt Microsoft in seinem eigenen Artikel:

    MSDN schrieb:

    [...]
    - For more complex UI, use the CreateProcessAsUser function to create a process in the user's session.
    [...]

    Leider steht nirgends, was man dabei beachten muss...



  • Warum willst Du das überhaupt machen!?
    Es ist ein sehr schlechtes Design und fordert von Dir immesen Aufwand dies hinzubekommen. Ich "hasse" CreateProcessAsUser, da dies wirklich sehr komplex und mit via ACLs verbunden ist...

    Warum machst Du nicht einen Dienst und ein Programm, welches via Autostart bei (jedem) Benutzer im Hintergrund läuft und via InterProcess-Kommunikation mit dem Dienst kommuniziert. Simpel/einfach und nix mit ACLs...



  • Jochen Kalmbach schrieb:

    Warum willst Du das überhaupt machen!?

    Mein Ziel ist es, einen Dienst zu schreiben, der sozusagen externe Programme als "Dienst" startet, sie also nach Möglichkeit nicht beendet werden, wenn sich der Benutzer abmeldet. Wenn das externe Programm abstürzt oder "unerwartet" beendet wird, soll es neu gestartet werden.
    Das Benutzerkonto für das externe Programm soll einstellbar sein, deshalb vor allem CreateProcessAsUser.

    Der angemeldete Benutzer soll allerdings auch mit den Programmen interagieren können, was ja leider durch die verschiedenen Sessions ab Windows Vista nicht mehr direkt möglich zu sein scheint.

    Das soll eine Art Verbesserung von SrvAny sein.



  • Eine Lösung hierzu würde mich auch interessieren, Danke!


  • Mod

    thommy88 schrieb:

    Der angemeldete Benutzer soll allerdings auch mit den Programmen interagieren können, was ja leider durch die verschiedenen Sessions ab Windows Vista nicht mehr direkt möglich zu sein scheint.

    Wenn es Service ist es keine Verbindung zum Desktop.
    Hat es Verbindung zum Desktop wird es beendet beim abmelden.

    Das eine schließt das andere IMHO aus.




Anmelden zum Antworten