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:
- Der Aufrufer braucht administrative Rechte für WTSQuerySessionInformation und für WinStationConnect ohne Passwort.
- Der aufrufende Prozess muss in der Logon-Sitzung laufen, die momentan auf dem Bildschirm zu sehen (mit dem Terminal verbunden) ist.
Am 6. November 2019 um 16:44 Uhr
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);`
Am 7. November 2019 um 08:47 Uhr
Ein BOOL ist ein BYTE in c++ (Visual Studio).
(https://docs.microsoft.com/de-de/cpp/cpp/data-type-ranges)
Am 8. Dezember 2019 um 19:40 Uhr
Ein `bool` schon, der typ `BOOL` nicht! Am Besten ist es `BOOLEAN` zu verwenden, welches ein typedef fuer `BYTE` ist…
Am 21. Juni 2021 um 12:12 Uhr
1. How to switch to the session of signed-out user with WinStationConnectW().
Am 21. Juni 2021 um 13:44 Uhr
That’s not possible.