Benutzer wechsle Dich

Aufgrund eines Feature-Requests (wow wass’n das in deutsch?) im SuRun Forum, entschloss ich mich, herauszufinden, wie man programmatisch die Sitzung eines anderen angemeldeten Benutzers auf den Bildschirm bekommt. Das ist einfacher, als ich erst dachte und funktioniert mit dem undokumentierten Windows API WinStationConnect (und zwar mindestens in Windows XP, Vista und dem RC1 von Windows 7):

BOOL SwitchToSession(DWORD SessionId)
{
  HMODULE hWinSta = LoadLibraryW(L"winsta.dll");
  if (!hWinSta)
    return FALSE;
  typedef BOOL (WINAPI *WSCW)(HANDLE,DWORD,DWORD,PWCHAR,BOOL);
  WSCW WinStationConnectW=(WSCW)GetProcAddress(hWinSta,"WinStationConnectW");
  if (!WinStationConnectW)
    return FALSE;
  return WinStationConnectW(0,SessionId,-1,L"",TRUE);
}

Die Funktion hat vier Parameter:

  • HANDLE hServer
    WTS_CURRENT_SERVER_HANDLE(==0) bezeichnet den lokalen Computer
  • DWORD TargetSessionID
    Das ist die ID der Sitzung, die man auf dem Bildschirm haben will. Die bekommt man mit WTSEnumerateSessions und die entsprechenden Benutzernamen mit WTSQuerySessionInformation.
  • DWORD SessionID
    Das ist die ID der Terminal Server Konsole, die TargetSessionID anzeigen soll. Auf dem lokalen System gibt es nur einen Terminal und damit nur eine SessionID: WTS_CURRENT_SESSION(==-1)
  • LPTSTR Password
    Das Passwort der Sitzung, die angezeigt werden soll. Ist der Aufrufer der Funktion berechtigt, die Sitzung anzuzeigen, kann Password auf einen leeren String zeigen, sonst muss das korrekte Passwort angegeben werden.
  • BOOL bWait
    Legt fest, ob WinStationConnect sofort zum Aufrufer zurückkehrt oder erst, nachdem auf die andere Sitzung umgeschaltet wurde.

SuRun ab 1.2.0.7 Beta 9 benutzt etwa folgende Funktion:

BOOL DoFUS(LPCTSTR ToUser) //Do Fast User Switching
{
  //ToUser can be 'UserName' or 'DomainName\\UserName'
  DWORD SessID=-1;
  DWORD n=0;
  WTS_SESSION_INFO* si=0;
  WTSEnumerateSessions(0,0,1,&si,&n);
  //get Session ID for ToUser
  if (si && n)
  {
    for (DWORD i=0;i<n;i++)
    {
      TCHAR* un=0;
      TCHAR* dn=0;
      DWORD unl=0;
      DWORD dnl=0;
      WTSQuerySessionInformation(0,si[i].SessionId,WTSUserName,&un,&unl);
      WTSQuerySessionInformation(0,si[i].SessionId,WTSDomainName,&dn,&dnl);
      CBigResStr undn(L"%s\\%s",dn,un); //this just combines domain\\user
      WTSFreeMemory(dn);
      dn=0;
      if ((_tcsicmp(un,ToUser)==0)||(_tcsicmp(undn,ToUser)==0))
      {
        WTSFreeMemory(un);
        SessID=si[i].SessionId;
        break;
      }
      WTSFreeMemory(un);
      un=0;
    }
    WTSFreeMemory(si);
  }
  if (SessID==-1)
    return FALSE;
  return SwitchToSession(SessID);
}

So! Das ist einfach… aber auch nicht ganz:

  1. Der Aufrufer braucht administrative Rechte für WTSQuerySessionInformation und für WinStationConnect ohne Passwort.
  2. Der aufrufende Prozess muss in der Logon-Sitzung laufen, die momentan auf dem Bildschirm zu sehen (mit dem Terminal verbunden) ist.

5 Reaktionen zu “Benutzer wechsle Dich”

  1. klaus

    Der Rückgabewert von WinStationConnectW ist nur ein Byte – das Debuggen zeigt, dass die Funktion nur das `al`-Register setzt.

    Die Deklaration muss richtig lauten (UCHAR oder BYTE):
    `typedef UCHAR (WINAPI *WSCW)(HANDLE, DWORD, DWORD, PWCHAR, BOOL);`

  2. Kay

    Ein BOOL ist ein BYTE in c++ (Visual Studio).
    (https://docs.microsoft.com/de-de/cpp/cpp/data-type-ranges)

  3. klaus

    Ein `bool` schon, der typ `BOOL` nicht! Am Besten ist es `BOOLEAN` zu verwenden, welches ein typedef fuer `BYTE` ist…

  4. Sravan

    1. How to switch to the session of signed-out user with WinStationConnectW().

  5. Kay

    That’s not possible.

Einen Kommentar schreiben

Time limit is exhausted. Please reload the CAPTCHA.