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
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|