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.

Einen Kommentar schreiben

Time limit is exhausted. Please reload the CAPTCHA.