Examples Delphi

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.