Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

836 lines
24 KiB

//
// key.c
//
// Handles all keyboard input messages sent to the server.
//
// Copyright (C) 2001 Microsoft Corporation
//
// Author: a-devjen (Devin Jenson)
//
#include "apihandl.h"
#include "tclient2.h"
// Average number of characters per word
#define AVG_CHARS_PER_WORD 6
// Max WPM before setting delay to 0
#define MAX_WORDS_PER_MIN 1000
// This macro calculates the delay between each character
// (in milliseconds) according to the specified WPM
#define CALC_DELAY_BETWEEN_CHARS(WPM) \
(60000 / (WPM * AVG_CHARS_PER_WORD))
// This constant simply defines the default delay
// between each character according to the default
// words per minute specified in TCLIENT2.H
static const UINT DEFAULT_DELAY_BETWEEN_CHARS =
CALC_DELAY_BETWEEN_CHARS(T2_DEFAULT_WORDS_PER_MIN);
//
// The Foucher Formula
// (Defining MS. Per Char)
//
// 60000
// ---------
// WPM * CPW
//
// Internal Helper Function Prototypes
static LPARAM KeyGenParam(UINT Message, BOOL IsAltDown, int vKeyCode);
static BOOL KeyCharToVirt(WCHAR KeyChar, int *vKeyCode,
BOOL *RequiresShift);
static BOOL KeyVirtToChar(int vKeyCode, WCHAR *KeyChar);
static LPCSTR KeySendVirtMessage(HANDLE Connection, UINT Message,
int vKeyCode);
static LPCSTR KeySendCharMessage(HANDLE Connection, UINT Message,
WCHAR KeyChar);
// Macros to quick return the specified keystate
#define ISALTDOWN ((TSAPIHANDLE *)Connection)->IsAltDown
#define ISSHIFTDOWN ((TSAPIHANDLE *)Connection)->IsShiftDown
#define ISCTRLDOWN ((TSAPIHANDLE *)Connection)->IsCtrlDown
/*
R - Specifies the repeat count for the current message. The value is
the number of times the keystroke is autorepeated as a result
of the user holding down the key. If the keystroke is held long
enough, multiple messages are sent. However, the repeat count
is not cumulative. This value is always 1 for WM_SYSKEYUP and
WM_KEYUP.
S - Specifies the scan code. The value depends on the original
equipment manufacturer (OEM).
E - Specifies whether the key is an extended key, such as the
right-hand ALT and CTRL keys that appear on an enhanced 101- or
102-key keyboard. The value is 1 if it is an extended key;
otherwise, it is 0.
X - Reserved; do not use.
C - Specifies the context code. The value is always 0 for a
WM_KEYDOWN and WM_KEYUP message or if the message is posted to
the active window because no window has the keyboard focus.
For WM_SYSKEYDOWN and WM_SYSKEYUP, the value is 1 if the ALT
key is down while the message is activated.
P - Specifies the previous key state. The value is 1 if the key is down
before the message is sent, or it is zero if the key is up.
The value is always 1 for a WM_KEYUP or WM_SYSKEYUP message.
T - Specifies the transition state. The value is always 0 for a
WM_KEYDOWN or WM_SYSKEYDOWN message, and 1 for a
WM_KEYUP or a WM_SYSKEYUP message.
TPCXXXXESSSSSSSSRRRRRRRRRRRRRRRR
10987654321098765432109876543210
| | | |
30 20 10 0
*/
// KeyGenParam
//
// Based on the specified information, this generates
// a valid LPARAM as documented by Windows keyboard
// messages. Quick documentation is provided above.
//
// Returns the generated LPARAM, no failure code.
static LPARAM KeyGenParam(
UINT Message,
BOOL IsAltDown,
int vKeyCode)
{
// Set the repeat count for all key messages (1)
DWORD Param = 0x00000001; // Set zBit 0-15 (WORD) as value 1
// Next set the OEM Scan code for the virtual key code
DWORD ScanCode = (DWORD)((BYTE)MapVirtualKey((UINT)vKeyCode, 0));
if (ScanCode != 0)
Param |= (ScanCode << 16); // Set zBits 16-23
// Set the extended flag for extended keys
switch (vKeyCode) {
// Add more keys here
case VK_DELETE:
case VK_LWIN:
case VK_RWIN:
Param |= 0x01000000;
break;
}
// Is the ALT Key down for SYS_ messages?
if (IsAltDown)
Param |= 0x20000000; // Enable zBit 29
// Set the previous key and transition state
if (Message == WM_SYSKEYUP || Message == WM_KEYUP)
Param |= 0xC0000000; // Enable zBit 30 and 31
// Convert the DWORD to an LPARAM and return
return (LPARAM)Param;
}
// KeyCharToVirt
//
// Maps the specified character to a virtual key code.
// This will also specify whether or not shift is required for
// the virtual key code have the same effect. For example:
// the letter 'K'. Sending VK_K is not enough shift must be
// down to get the capital 'K'.
//
// Returns TRUE of the data has been successfully
// translated, FALSE otherwise. If FALSE, pointers have
// been unmodified.
static BOOL KeyCharToVirt(
WCHAR KeyChar,
int *vKeyCode,
BOOL *RequiresShift)
{
// Get the key data
SHORT ScanData = VkKeyScanW(KeyChar);
// Check Success/Failure of API call
if (LOBYTE(ScanData) == -1 && HIBYTE(ScanData) == -1)
return FALSE;
// Set the data
if (vKeyCode != NULL)
*vKeyCode = LOBYTE(ScanData);
if (RequiresShift != NULL)
*RequiresShift = (HIBYTE(ScanData) & 0x01);
return TRUE;
}
// KeyVirtToChar
//
// Translates the specified virtual key code into a character.
// There are some virtual key codes that do not have
// character representations, such as the arrow keys. In this
// case, the function fails.
//
// Returns TRUE if the virtual key code was successfully
// translated, FALSE otherwise. If FALSE, the KeyChar
// pointer has not been modified.
static BOOL KeyVirtToChar(
int vKeyCode,
WCHAR *KeyChar)
{
// Use Win32 API to map, two is the value used for
// vKey->Char, no type is defined for easier
// readability there :-( [as of Jan 2001]
UINT Result = MapVirtualKeyW((UINT)vKeyCode, 2);
if (Result == 0)
return FALSE;
// Set the data
if (KeyChar != NULL)
*KeyChar = (WCHAR)Result;
return TRUE;
}
// KeySendVirtMessage
//
// This routine corrects and sends a key message to the server.
// WM_SYSKEY messages are converted over to WM_KEY messages so
// that they can be transferred over the wire correctly - they
// still work though. An LPARAM is automatically generated.
// Support is only implemented for the messages:
// WM_KEYDOWN and WM_KEYUP (and the SYS versions).
//
// The return value is a string specifying an error if one
// occured, otherwise the process was successful and NULL
// is returned.
static LPCSTR KeySendVirtMessage(
HANDLE Connection,
UINT Message,
int vKeyCode)
{
// Wait until the user unpauses the script (if paused)
T2WaitForPauseInput(Connection);
// Filter out invalid messages, and convert SYS messages
switch (Message) {
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
Message = WM_KEYDOWN;
break;
case WM_SYSKEYUP:
case WM_KEYUP:
Message = WM_KEYUP;
break;
default:
return "Unsupported keyboard message";
}
// Trigger specific actions for special virtual key codes
switch (vKeyCode) {
// CTRL Key message
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL: {
switch (Message) {
case WM_KEYDOWN:
// Flag CTRL as down
if (ISCTRLDOWN == TRUE)
return NULL;
ISCTRLDOWN = TRUE;
break;
case WM_KEYUP:
// Flag CTRL as up
if (ISCTRLDOWN == FALSE)
return NULL;
ISCTRLDOWN = FALSE;
break;
}
}
// SHIFT Key message
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT: {
switch (Message) {
case WM_KEYDOWN:
// Flag SHIFT as down
if (ISSHIFTDOWN == TRUE)
return NULL;
ISSHIFTDOWN = TRUE;
break;
case WM_KEYUP:
// Flag SHIFT as up
if (ISSHIFTDOWN == FALSE)
return NULL;
ISSHIFTDOWN = FALSE;
break;
}
break;
}
// ALT Key message
case VK_MENU:
case VK_LMENU:
case VK_RMENU: {
switch (Message) {
case WM_KEYDOWN:
// Flag ALT as down
if (ISALTDOWN == TRUE)
return NULL;
ISALTDOWN = TRUE;
break;
case WM_KEYUP:
// Flag ALT as up
if (ISALTDOWN == FALSE)
return NULL;
ISALTDOWN = FALSE;
break;
}
break;
}
}
// Send the message over the wire
return T2SendData(Connection, Message, (WPARAM)vKeyCode,
KeyGenParam(Message, ISALTDOWN, vKeyCode));
}
// KeySendCharMessage
//
// This routine automatically translates a charater into its
// virtual key code counterpart and passes the code onto the
// KeySendVirtMessage. Shift reliant characters will NOT
// have the SHIFT key automatically be sent over as well,
// because this would be more difficult to manage the shift key
// locally.
//
// Only WM_KEYDOWN and WM_KEYUP messages (and their SYS versions)
// are supported. Unsupported keyboard messages will cause the
// routine to fail.
//
// The return value is a string specifying an error if one
// occured, otherwise the process was successful and NULL
// is returned.
static LPCSTR KeySendCharMessage(HANDLE Connection, UINT Message,
WCHAR KeyChar)
{
// Wait until the user unpauses the script (if paused)
T2WaitForPauseInput(Connection);
// Only the following messages are supported
if (Message == WM_KEYDOWN || Message == WM_KEYUP ||
Message == WM_SYSKEYDOWN || Message == WM_SYSKEYUP) {
int vKeyCode = 0;
// Translate the character to a virtual key code
if (KeyCharToVirt(KeyChar, &vKeyCode, NULL) == FALSE)
return "Failed to map character to a virtual key code";
// Submit the virtual key code
return KeySendVirtMessage(Connection, Message, vKeyCode);
}
// WM_CHAR and WM_SYSCHAR is not used over this tclient connection
return "Unsupported keyboard message";
}
// T2SetDefaultWPM
//
// Every TCLIENT connection has it's own special properties, and this
// includes the "Words Per Minute" property. The property represents
// the default speed in which TCLIENT is to type text to the server.
// The scripter can override this by using the optional parameter
// after TypeText() to indicate the temporary words per minute.
//
// This routine sets the default words per minute as described above.
//
// The return value is NULL if successful, otherwise a string
// describing the error.
TSAPI LPCSTR T2SetDefaultWPM(HANDLE Connection, DWORD WordsPerMinute)
{
// Validate the handle
if (T2IsHandle(Connection) == FALSE)
return "Not a valid connection";
// If WPM is set to 0, use the default
if (WordsPerMinute == 0) {
WordsPerMinute = T2_DEFAULT_WORDS_PER_MIN;
((TSAPIHANDLE *)Connection)->DelayPerChar =
DEFAULT_DELAY_BETWEEN_CHARS;
}
// If WPM is suggested to be insanely high, set the delay to
// 0 instead of trying to calculate it
else if (WordsPerMinute > MAX_WORDS_PER_MIN)
((TSAPIHANDLE *)Connection)->DelayPerChar = 0;
// Otherwise, calculate the words per minute into
// the delay value used in Sleep() calls
else
((TSAPIHANDLE *)Connection)->DelayPerChar =
CALC_DELAY_BETWEEN_CHARS(WordsPerMinute);
// Record the words per minute if the user wants this value back
((TSAPIHANDLE *)Connection)->WordsPerMinute = WordsPerMinute;
return NULL;
}
// T2GetDefaultWPM
//
// Every TCLIENT connection has it's own special properties, and this
// includes the "Words Per Minute" property. The property represents
// the default speed in which TCLIENT is to type text to the server.
// The scripter can override this by using the optional parameter
// after TypeText() to indicate the temporary words per minute.
//
// This routine retrieves the current default words per minute
// as described above.
//
// The return value is NULL if successful, otherwise a string
// describing the error.
TSAPI LPCSTR T2GetDefaultWPM(HANDLE Connection, DWORD *WordsPerMinute)
{
// Validate the handle
if (T2IsHandle(Connection) == FALSE)
return "Not a valid connection";
__try {
// Attempt to set a value at the specified pointer
*WordsPerMinute = ((TSAPIHANDLE *)Connection)->WordsPerMinute;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
_ASSERT(FALSE);
// Nope, it failed.
return "Invalid WordsPerMinute pointer";
}
return NULL;
}
//
// Keyboard Routines
//
// These functions are used to make keyboard handling easier.
// They come in two flavors, character version, and
// virtual keycode version. The virtual keycode functions
// are prefixed with a "V" after the T2 to indicate so.
//
// T2KeyAlt
//
// Allows for easy ability to do an ALT + Key combonation.
// For example, ALT-F in a typical Windows application will
// open up the File menu.
//
// Returns NULL if successful, otherwise a string describing
// the failure.
// Character Version
TSAPI LPCSTR T2KeyAlt(HANDLE Connection, WCHAR KeyChar)
{
// Don't validate the connection handle here,
// it is done within T2KeyDown, and no reason
// two double check it.
// First press the ALT key
LPCSTR Result = T2VKeyDown(Connection, VK_MENU);
if (Result != NULL)
return Result;
// Be realistic
T2WaitForLatency(Connection);
// Next press and release the specified custom key
Result = T2KeyPress(Connection, KeyChar);
// Be realistic
T2WaitForLatency(Connection);
// Finally, let up on the ALT key
T2VKeyUp(Connection, VK_MENU);
return Result;
}
// Virtual Key Code Version
TSAPI LPCSTR T2VKeyAlt(HANDLE Connection, INT vKeyCode)
{
// Don't validate the connection handle here,
// it is done within T2VKeyDown, and no reason
// two double check it.
// First press the ALT key
LPCSTR Result = T2VKeyDown(Connection, VK_MENU);
if (Result != NULL)
return Result;
// Be realistic
T2WaitForLatency(Connection);
// Next press and release the specified custom key
Result = T2VKeyPress(Connection, vKeyCode);
// Be realistic
T2WaitForLatency(Connection);
// Finally, let up on the ALT key
T2VKeyUp(Connection, VK_MENU);
return Result;
}
// T2KeyCtrl
//
// Allows for easy ability to do a CTRL + Key combonation.
// For example, CTRL-C in a typical Windows application will
// copy a selected item to the clipboard.
//
// Returns NULL if successful, otherwise a string describing
// the failure.
// Character Version
TSAPI LPCSTR T2KeyCtrl(HANDLE Connection, WCHAR KeyChar)
{
// Don't validate the connection handle here,
// it is done within T2KeyDown, and no reason
// two double check it.
// First press the CTRL key
LPCSTR Result = T2VKeyDown(Connection, VK_CONTROL);
if (Result != NULL)
return Result;
// Be realistic
T2WaitForLatency(Connection);
// Next press and release the specified custom key
Result = T2KeyPress(Connection, KeyChar);
// Be realistic
T2WaitForLatency(Connection);
// Finally, let up on the CTRL key
T2VKeyUp(Connection, VK_CONTROL);
return Result;
}
// Virtual Key Code Version
TSAPI LPCSTR T2VKeyCtrl(HANDLE Connection, INT vKeyCode)
{
// Don't validate the connection handle here,
// it is done within T2VKeyDown, and no reason
// two double check it.
// First press the CTRL key
LPCSTR Result = T2VKeyDown(Connection, VK_CONTROL);
if (Result != NULL)
return Result;
// Be realistic
T2WaitForLatency(Connection);
// Next press and release the specified custom key
Result = T2VKeyPress(Connection, vKeyCode);
// Be realistic
T2WaitForLatency(Connection);
// Finally, let up on the CTRL key
T2VKeyUp(Connection, VK_CONTROL);
return Result;
}
// T2KeyDown
//
// Presses and a key down, and holds it down until
// T2KeyUp is called. This is useful for holding down SHIFT
// to type several letters in caps, etc. NOTE: For character
// versions of this function, SHIFT will NOT automatically be
// pressed. If you do a T2KeyDown(hCon, L'T'), a lowercase
// key may be the output! You will need to manually press the
// SHIFT key using T2VKeyDown(hCon, VK_SHIFT). Remember,
// these are low level calls. TypeText() on the other hand,
// will automatically press/release SHIFT as needed.
//
// Returns NULL if successful, otherwise a string describing
// the failure.
// Character Version
TSAPI LPCSTR T2KeyDown(HANDLE Connection, WCHAR KeyChar)
{
// Validate the handle
if (T2IsHandle(Connection) == FALSE)
return "Not a valid connection";
// Simply send the WM_KEYDOWN message over the wire
return KeySendCharMessage(Connection, WM_KEYDOWN, KeyChar);
}
// Virtual Key Code Version
TSAPI LPCSTR T2VKeyDown(HANDLE Connection, INT vKeyCode)
{
// Validate the handle
if (T2IsHandle(Connection) == FALSE)
return "Not a valid connection";
// Simply send the WM_KEYDOWN message over the wire
return KeySendVirtMessage(Connection, WM_KEYDOWN, vKeyCode);
}
// T2KeyPress
//
// Presses and releases a key. NOTE: For character
// versions of this function, SHIFT will NOT automatically be
// pressed. If you do a T2KeyDown(hCon, L'T'), a lowercase
// key may be the output! You will need to manually press the
// SHIFT key using T2VKeyDown(hCon, VK_SHIFT). Remember,
// these are low level calls. TypeText() on the other hand,
// will automatically press/release SHIFT as needed.
//
// Returns NULL if successful, otherwise a string describing
// the failure.
// Character Version
TSAPI LPCSTR T2KeyPress(HANDLE Connection, WCHAR KeyChar)
{
// Don't validate the connection handle here,
// it is done within T2VKeyDown, and no reason
// two double check it.
// First press the key
LPCSTR Result = T2KeyDown(Connection, KeyChar);
if (Result != NULL)
return Result;
// Be realistic
T2WaitForLatency(Connection);
// Then release it
return T2KeyUp(Connection, KeyChar);
}
// Virtual Key Code Version
TSAPI LPCSTR T2VKeyPress(HANDLE Connection, INT vKeyCode)
{
// Don't validate the connection handle here,
// it is done within T2VKeyDown, and no reason
// two double check it.
// First press the key
LPCSTR Result = T2VKeyDown(Connection, vKeyCode);
if (Result != NULL)
return Result;
// Be realistic
T2WaitForLatency(Connection);
// Then release it
return T2VKeyUp(Connection, vKeyCode);
}
// T2KeyUp
//
// Releases a key that has been pressed by the T2KeyDown
// function. If the key is not down, behavior is undefined.
//
// Returns NULL if successful, otherwise a string describing
// the failure.
// Character Version
TSAPI LPCSTR T2KeyUp(HANDLE Connection, WCHAR KeyChar)
{
// Simply send the WM_KEYUP message over the wire
return KeySendCharMessage(Connection, WM_KEYUP, KeyChar);
}
// Virtual Key Code Version
TSAPI LPCSTR T2VKeyUp(HANDLE Connection, INT vKeyCode)
{
// Simply send the WM_KEYUP message over the wire
return KeySendVirtMessage(Connection, WM_KEYUP, vKeyCode);
}
// T2TypeText
//
// This handy function enumerates each character specified in the text
// and sends the required key messages over the wire to end up with
// the proper result. The shift key is automatically pressed/depressed
// as needed for capital letters and acts as real user action.
// Additionally, the speed of typed text is indicated by the
// (optional) WordsPerMinute parameter. If WordsPerMinute is 0 (zero),
// the default WordsPerMinute for the handle is used.
//
// Returns NULL if successful, otherwise a string describing
// the failure.
TSAPI LPCSTR T2TypeText(HANDLE Connection, LPCWSTR Text, UINT WordsPerMin)
{
LPCSTR Result = NULL;
int vKeyCode = 0;
BOOL RequiresShift = FALSE;
UINT DelayBetweenChars;
BOOL ShiftToggler;
// Validate the handle
if (T2IsHandle(Connection) == FALSE)
return "Not a valid connection";
// First get the default delay between each character
DelayBetweenChars = ((TSAPIHANDLE *)Connection)->DelayPerChar;
// Get the current state of the shift key
ShiftToggler = ISSHIFTDOWN;
// If specified, use the custom WordsPerMinute
if (WordsPerMin > 0)
DelayBetweenChars = CALC_DELAY_BETWEEN_CHARS(WordsPerMin);
// Enter the exception clause in case we have some bad
// Text pointer, and we attempt to continue forever...
__try {
// Loop between each character until a null character is hit
for (; *Text != 0; ++Text) {
// First get the key code associated with the current character
if (KeyCharToVirt(*Text, &vKeyCode, &RequiresShift) == FALSE) {
// This should never happen, but Roseanne is still being
// aired on channel 11, so you never know...
_ASSERT(FALSE);
return "Failed to map a character to a virtual key code";
}
// Press shift if we need
if (RequiresShift == TRUE && ShiftToggler == FALSE)
Result = KeySendVirtMessage(Connection, WM_KEYDOWN, VK_SHIFT);
// Release shift if we need
else if (RequiresShift == FALSE && ShiftToggler == TRUE)
Result = KeySendVirtMessage(Connection, WM_KEYUP, VK_SHIFT);
// Set the current shift state now
ShiftToggler = RequiresShift;
// We are not using T2VKeyPress() here because we need want
// to prevent as many performance hits as possible, and in this
// case we would be checking the Connection handle over and
// over, in addition to making and new stacks etc,
// which is quite pointless.
// Press the current key down
Result = KeySendVirtMessage(Connection, WM_KEYDOWN, vKeyCode);
if (Result != NULL)
return Result;
// Release the current key
Result = KeySendVirtMessage(Connection, WM_KEYUP, vKeyCode);
if (Result != NULL)
return Result;
// Note we are not releasing shift if it is down, this is because
// the next key may be caps as well, and normal users don't
// press and release shift for each character (assuming they
// didn't use the caps key...)
// Delay for the specified amount of time to get accurate
// count for WordsPerMinute
Sleep(DelayBetweenChars);
}
// We are done going through all the text. If we still have
// shift down, release it at this point.
if (ShiftToggler == TRUE)
return KeySendVirtMessage(Connection, WM_KEYUP, VK_SHIFT);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
// Uhm, shouldn't happen?
_ASSERT(FALSE);
return "Exception occured";
}
return NULL;
}