Mega Code Archive

 
Categories / Delphi / Examples
 

Use Undocumented Terminal Server APIs

Title: Use Undocumented Terminal Server API's Question: How to use Undocumented Terminal Server API's? Winsta.dll provides functions to Extract Idle Time and Login time for Terminal Sessions as well as API's to connect to another session or Shadow it. Answer: {******************************************************************} { This Unit provides Delphi translations of some functions from } { WinSta.dll. The functions are not documented by Microsoft and } { were tested only with Windows 2003 standard. Functions were not } { tested with Windows 2000, but are expected to work. } { } { Author: Remko Weijnen (r dot weijnen at gmail dot com) } { Version: 0.3 } { Date: 03-01-2007 } { } { The contents of this file are subject to } { the Mozilla Public License Version 1.1 (the "License"); you may } { not use this file except in compliance with the License. You may } { obtain a copy of the License at } { http://www.mozilla.org/MPL/MPL-1.1.html } { } { Software distributed under the License is distributed on an } { "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or } { implied. See the License for the specific language governing } { rights and limitations under the License. } {******************************************************************} // $Id: JwaWinSta.pas,v 1.2 2007/01/07 18:53:06 assarbad Exp $ unit JwaWinSta; interface Uses SysUtils, Windows, DateUtils, JwaNative; type HANDLE = THandle; PVOID = Pointer; PWINSTATIONNAMEW = PWideChar; _WINSTATIONQUERYINFORMATIONW = record Reserved1: array[0..71] of byte; // Res1: array[0..3] of byte; // WinStationName: array[0..12] of WideChar; // Res2: array[0..13] of byte; // ClientName: array[0..12] of WideChar; // Res3: array[0..1] of byte; // Reserved1: array[0..35] of WideChar; SessionId: Longint; Reserved2: array[0..3] of byte; ConnectTime: FILETIME; DisconnectTime: FILETIME; LastInputTime: FILETIME; LoginTime: FILETIME; Reserved3: array[0..1095] of byte; // Reserved3: array[0..548] of byte; CurrentTime: FILETIME; end; function _WinStationNotifyNewSession(hServer: HANDLE; SessionID: ULONG): Boolean; stdcall; function WinStationNameFromLogonIdW(hServer: HANDLE; SessionID: ULONG; pWinStationName: PWINSTATIONNAMEW): boolean; stdcall; {***********************************************************} { WinStationShadow: Shadow another user's session } { hServer : Handle to Terminal Server } { Use WTSOpenServer to obtain or pass } { SERVERNAME_CURRENT } { } { pServerName: ServerName (or IP), can be nil or empty } { string for local server } { } { SessionID : The session you want to shadow } { } { Hotkey : The hotkey to end the remote control. } { must be a Virtual-Key Code. } { Supply VK_MULTIPLY for the default (*) } { } { HKModifier: Key to press in combination with Hotkey, } { also a Virtual-Key code. Supply MOD_CONTROL } { for the default (CTRL) } { } { v0.2: } { Changed param2 Unknown: ULONG to pServerName: PWideChar } {***********************************************************} function WinStationShadow(hServer: Handle; pServerName: PWideChar; SessionID: ULONG; HotKey: ULong; HKModifier: ULong): Boolean; stdcall; {***********************************************************} { WinStationShadowStop: Not needed, is called for you when } { pressing the hotkey supplied with WinStationShadow } { v0.2: } { Added param 3, Unknown: Integer } { (note: possibly pServerName: PWideChar; } { Not tested! {***********************************************************} function WinStationShadowStop(hServer: Handle; SessionID: ULONG; Unknown: Integer): Boolean; stdcall; {***********************************************************} { WinStationConnect: Connect to a session } { Target session will be disconnected. When connecting to } { another users session, you have to provide password } { Password must not be nil, empty string '' is allowed to } { connect to owned session } { hServer: Handle to Terminal Server } { Use WTSOpenServer to obtain or pass } { SERVERNAME_CURRENT } { } { SessionID: The session you want to connect to } { } { TargetSessionID: The Session which is connected to the } { SessionID (LOGINID_CURRENT) } { } { pPassword: Password for the disconnected session, it's } { the Windows password for the user that owns } { the session. Supply PWideChar('') for no } { password, nil is invalid. } { bWait: Boolean, wait until the connect has completed } { } { v0.3: } { Changed to stdcall, changed Unknown to boolean bWait } { changed name ActiveSessionID to TargetSessionID } { Function tested and working on Windows 2003 } {***********************************************************} function WinStationConnectW(hServer: Handle; SessionID: ULong; TargetSessionID: ULong; pPassword: PWideChar; bWait:Boolean): Boolean; stdcall; {***********************************************************} { WinStationQueryInformation: Query Terminal Sessions Info } { When using WTSAPI function, this function is called } { supply WinStationInformationClass 8 to retrieve Idle Time } { and logon time, see helper function GetWTSIdleTime } { } { hServer: Handle to Terminal Server } { Use WTSOpenServer to obtain handle or pass } { SERVERNAME_CURRENT } { } { SessionID: The session you want query } {***********************************************************} function WinStationQueryInformationW(hServer: HANDLE; SessionId: ULONG; WinStationInformationClass: Cardinal; pWinStationInformation: PVOID; WinStationInformationLength: ULONG; var pReturnLength: ULONG): Boolean; stdcall; function GetWTSLogonIdleTime(hServer: Handle; SessionID: ULong; var sLogonTime: String; var sIdleTime: String): Boolean; function FileTime2DateTime(FileTime: FileTime): TDateTime; const SERVERNAME_CURRENT = HANDLE(0); LOGONID_CURRENT = ULONG(-1); implementation function _WinStationNotifyNewSession; external 'winsta.dll' name '_WinStationNotifyNewSession'; function WinStationNameFromLogonIdW; external 'winsta.dll' name 'WinStationNameFromLogonIdW'; function WinStationShadow; external 'winsta.dll' Name 'WinStationShadow'; function WinStationShadowStop; external 'winsta.dll' Name 'WinStationShadowStop'; function WinStationConnectW; external 'winsta.dll' Name 'WinStationConnectW'; function WinStationQueryInformationW; external 'winsta.dll' Name 'WinStationQueryInformationW'; function FileTime2DateTime(FileTime: FileTime): TDateTime; var LocalFileTime: TFileTime; SystemTime: TSystemTime; begin FileTimeToLocalFileTime(FileTime, LocalFileTime) ; FileTimeToSystemTime(LocalFileTime, SystemTime) ; Result := SystemTimeToDateTime(SystemTime) ; end; function GetWTSLogonIdleTime(hServer: HANDLE; SessionID: ULong; var sLogonTime: String; var sIdleTime: String): Boolean; var uReturnLength: ULONG; info: _WINSTATIONQUERYINFORMATIONW; CurrentTime: TDateTime; LastInputTime: TDateTime; IdleTime: TDateTime; LogonTime: TDateTime; Days, Hours, Minutes: Word; fs: TFormatSettings; begin GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, fs); uReturnLength := 0; try Result := WinStationQueryInformationW(hServer, SessionID, 8, @info, sizeof(info), uReturnLength); if Result then begin LogonTime := FileTime2DateTime(Info.LoginTime); if YearOf(LogonTime) = 1601 then begin sLogonTime := ''; end else begin sLogonTime := DateTimeToStr(LogonTime, fs); end; { from Usenet post by Chuck Chopp http://groups.google.com/group/microsoft.public.win32.programmer.kernel/browse_thread/thread/c6dd86e7df6d26e4/3cf53e12a3246e25?lnk=st&q=WinStationQueryInformationa+group:microsoft.public.*&rnum=1&hl=en#3cf53e12a3246e25 2) The system console session cannot go into an idle/disconnected state. As such, the LastInputTime value will always math CurrentTime for the console session. 3) The LastInputTime value will be zero if the session has gone disconnected. In that case, use the DisconnectTime value in place of LastInputTime when calculating the current idle time for a disconnected session. 4) All of these time values are GMT time values. 5) The disconnect time value will be zero if the sesson has never been disconnected.} CurrentTime := FileTime2DateTime(Info.CurrentTime); LastInputTime := FileTime2DateTime(Info.LastInputTime); // Disconnected session = idle since DisconnectTime if YearOf(LastInputTime) = 1601 then begin LastInputTime := FileTime2DateTime(Info.DisconnectTime); end; // IdleTime := LastInputTime - CurrentTime; IdleTime := CurrentTime - LastInputTime; Days := Trunc(IdleTime); Hours := HourOf(IdleTime); Minutes := MinuteOf(IdleTime); if Days 0 then begin sIdleTime := Format('%d + %d:%1.2d', [Days, Hours, Minutes]); end else if Hours 0 then begin sIdleTime := Format('%d:%1.2d', [Hours, Minutes]); end else if Minutes 0 then begin sIdleTime := IntToStr(Minutes); end else begin sIdleTime := '-'; end; end; except on E: Exception do begin Result := False; end; end; end; end.