|
|
//
// CTBShell.cpp
//
// Contains the methods and properties for the shell object used in TBScript.
// In scripting, to access any members you must prefix the member with "TS.".
//
// Copyright (C) 2001 Microsoft Corporation
//
// Author: a-devjen (Devin Jenson)
//
#include <crtdbg.h>
#include "CTBShell.h"
#define CTBOBJECT CTBShell
#include "virtualdefs.h"
// CTBShell::CTBShell
//
// The constructor.. just initializes data.
//
// No return value.
CTBShell::CTBShell(void) { // Initialize base object stuff
Init(IID_ITBShell);
Connection = NULL;
// Clean up local structures
ZeroMemory(&CurrentUser, sizeof(CurrentUser)); ZeroMemory(&LastErrorString, sizeof(LastErrorString)); ZeroMemory(&DesiredData, sizeof(DesiredData));
// Set default resolution
DesiredData.xRes = SCP_DEFAULT_RES_X; DesiredData.yRes = SCP_DEFAULT_RES_Y;
// Set default words per minute
SetDefaultWPM(T2_DEFAULT_WORDS_PER_MIN); SetLatency(T2_DEFAULT_LATENCY); }
// CTBShell::~CTBShell
//
// The destructor.. just unitializes data.
//
// No return value.
CTBShell::~CTBShell(void) { UnInit(); }
// CTBShell::RecordLastError
//
// This method simply records the last error string, and
// if specified, records the TRUE/FALSE success state according
// to the specified string.
//
// Returns S_OK to prevent script exceptions.
HRESULT CTBShell::RecordLastError(LPCSTR Error, BOOL *Result) { // Just terminate our error string if no error is passed in
if (Error == NULL) *LastErrorString = OLECHAR('\0');
// Otherwise, convert the string from ASCII to multibyte
else mbstowcs(LastErrorString, Error, sizeof(LastErrorString) / sizeof(*LastErrorString));
// If we want the result, enter that in as well
if (Result != NULL) *Result = (Error == NULL) ? TRUE : FALSE;
// Return the proper HRESULT
return S_OK; }
// CTBShell::RecordOrThrow
//
// This first calls RecordLastError to complete the
// recording operation. Then if Error is non-NULL,
// a return value is returned to indicate to OLE that
// an exception should be thrown
//
// Returns S_OK if the string is NULL, and E_FAIL if not.
HRESULT CTBShell::RecordOrThrow(LPCSTR Error, BOOL *Result, HRESULT ErrReturn) { // Do the normal record operation
RecordLastError(Error, Result);
// If we have failure indication, return E_FAIL which causes OLE
// to cause an error in the script.
return (Error == NULL) ? S_OK : ErrReturn; }
// CTBShell::SetParam
//
// Sets a user defined LPARAM value needed for callback purposes.
//
// No return value.
void CTBShell::SetParam(LPARAM lParam) { this->lParam = lParam; }
// CTBShell::SetDesiredData
//
// Allows for the class to reference to access
// user-desired data passed to the app.
//
// No return value.
void CTBShell::SetDesiredData(TSClientData *DesiredDataPtr) { // Simply copy over the structure
if (DesiredDataPtr != NULL) DesiredData = *DesiredDataPtr;
// Validate the resolution.. note we don't have to go too
// far here because TCLIENT does some better checks.
if (DesiredData.xRes == 0) DesiredData.xRes = SCP_DEFAULT_RES_X;
if (DesiredData.yRes == 0) DesiredData.yRes = SCP_DEFAULT_RES_Y;
// We have this data, now modify the Words Per Minute value.
SetDefaultWPM(DesiredData.WordsPerMinute); }
// CTBShell::SetDefaultWPM
//
// Sets the default WPM for the shell.
//
// No return value.
void CTBShell::SetDefaultWPM(DWORD WordsPerMinute) { // If WordsPerMinute is 0 (which in essence, would not be typing
// at all), change it to the default.
if (WordsPerMinute == 0) WordsPerMinute = T2_DEFAULT_WORDS_PER_MIN;
// Change global desired data structure to reflect the new value.
DesiredData.WordsPerMinute = WordsPerMinute;
// And change the value over on TCLIENT2 as well
if (Connection != NULL) T2SetDefaultWPM(Connection, WordsPerMinute); }
// CTBShell::GetDefaultWPM
//
// Retrieves the default WPM for the shell.
//
// Returns the default words per minute.
DWORD CTBShell::GetDefaultWPM(void) { return DesiredData.WordsPerMinute; }
// CTBGlobal::GetLatency
//
// Retreives the current latency for multi-action commands.
//
// Returns the current latency.
DWORD CTBShell::GetLatency(void) { return CurrentLatency; }
// CTBShell::SetLatency
//
// Changes the current latency for multi-action commands.
//
// No return value.
void CTBShell::SetLatency(DWORD Latency) { // Change it locally
CurrentLatency = Latency;
// And also via the TCLIENT2 API
if (Connection != NULL) T2SetLatency(Connection, Latency); }
// CTBShell::GetArguments
//
// Retrieves arguments which the shell was originally started with.
// Do not modify this value!! It is only used for copying. The
// only way to modify this value is during creation of the ScriptEngine
// within the DesiredData structure - you pass in an argument string.
//
// Returns a pointer to the arguments string.
LPCWSTR CTBShell::GetArguments(void) { return DesiredData.Arguments; }
// CTBShell::GetDesiredUserName
//
// Retrieves the name in which the app initially wanted to login with.
// Do not modify this value!! It is only used for copying. The
// only way to modify this value is during creation of the ScriptEngine
// within the DesiredData structure - you set the user name there.
//
// Returns a pointer to a string containing a user name.
LPCWSTR CTBShell::GetDesiredUserName(void) { return DesiredData.User; }
//
//
// Begin methods which are directly exported through COM into script.
//
//
// CTBShell::Connect
//
// Simply way to connect to the desired server.
STDMETHODIMP CTBShell::Connect(BOOL *Result) { return ConnectEx( DesiredData.Server, DesiredData.User, DesiredData.Pass, DesiredData.Domain, DesiredData.xRes, DesiredData.yRes, DesiredData.Flags, DesiredData.BPP, DesiredData.AudioFlags, Result); }
// CTBShell::Connect
//
// Extended way to connect to a server.
STDMETHODIMP CTBShell::ConnectEx(BSTR ServerName, BSTR UserName, BSTR Password, BSTR Domain, INT xRes, INT yRes, INT Flags, INT BPP, INT AudioFlags, BOOL *Result) { LPCSTR LastError;
// Make sure we don't have a connection yet
if (Connection != NULL) return RecordLastError("Already connected", Result);
// Use the T2ConnectEx function in TCLIENT2 API to connect
LastError = T2ConnectEx(ServerName, UserName, Password, Domain, L"explorer", xRes, yRes, Flags, BPP, AudioFlags, &Connection);
// Verify connection...
if (LastError == NULL) {
// Successful, save the current user
wcscpy(CurrentUser, UserName);
// And default data for the connection
T2SetParam(Connection, lParam); T2SetDefaultWPM(Connection, DesiredData.WordsPerMinute); T2SetLatency(Connection, CurrentLatency); }
return RecordLastError(LastError, Result); }
// CTBShell::Disconnect
//
// Disconnect from an active server.
STDMETHODIMP CTBShell::Disconnect(BOOL *Result) { LPCSTR LastError;
// Sanity check the connection
if (Connection == NULL) return RecordLastError("Not connected", Result);
// Disconnect
if ((LastError = T2Disconnect(Connection)) == NULL) Connection = NULL;
return RecordLastError(LastError, Result); }
// CTBShell::GetBuildNumber
//
// Retrieves the build number if retrieved while
// connecting. If no build number has been retreived,
// 0 (zero) is the result.
STDMETHODIMP CTBShell::GetBuildNumber(DWORD *BuildNum) { LPCSTR LastError;
// Sanity check the connection
if (Connection == NULL) {
*BuildNum = 0; return RecordLastError("Not connected"); }
// Get the build number and return
LastError = T2GetBuildNumber(Connection, BuildNum);
return RecordLastError(LastError, NULL); }
// CTBShell::GetCurrentUserName
//
// If connected, retreives the logged on name.
STDMETHODIMP CTBShell::GetCurrentUserName(BSTR *UserName) { // Sanity check the connection
if (Connection == NULL) {
*UserName = SysAllocString(L""); return RecordLastError("Not connected"); }
// Copy the username
*UserName = SysAllocString(CurrentUser);
// Check the result
if (*UserName == NULL) return RecordOrThrow("Not enough memory", NULL, E_OUTOFMEMORY);
return S_OK; }
// CTBShell::GetLastError
//
// Retreives a description of the last error that occured.
STDMETHODIMP CTBShell::GetLastError(BSTR *LastError) { // Copy the string over OLE
*LastError = SysAllocString(LastErrorString);
// Check the result
if (*LastError == NULL) return RecordOrThrow("Not enough memory", NULL, E_OUTOFMEMORY);
return S_OK; }
// CTBShell::IsConnected
//
// Retreives a boolean indicating whether the handle is fully
// connected or not.
STDMETHODIMP CTBShell::IsConnected(BOOL *Result) { *Result = (Connection == NULL) ? FALSE : TRUE;
return S_OK; }
// CTBShell::Logoff
//
// Attempts to have the active connection logoff.
STDMETHODIMP CTBShell::Logoff(BOOL *Result) { LPCSTR LastError;
// Sanity check the connection
if (Connection == NULL) return RecordLastError("Not connected", Result);
// Use the TCLIENT2 API to logoff
if ((LastError = T2Logoff(Connection)) == NULL) Connection = NULL;
// Return success state
return RecordLastError(LastError, Result); }
// CTBShell::WaitForText
//
// Puts the current thread into a wait state until the specified
// text is passed from the active connection. Optionally, you can set
// a timeout value which will make the function fail over the specified
// number of milliseconds.
STDMETHODIMP CTBShell::WaitForText(BSTR Text, INT Timeout, BOOL *Result) { // Sanity check the connection
if (Connection == NULL) return RecordLastError("Not connected", Result);
// Call the API
LPCSTR LastError = T2WaitForText(Connection, Text, Timeout);
// Retirm success state
return RecordLastError(LastError, Result); }
// CTBShell::WaitForTextAndSleep
//
// This is exactly the same as a combonation of two calls:
//
// TS.WaitForText();
// TS.Sleep();
//
// but put into one function. This is because this combonation is
// used so frequently, using this method drastically shrinks the size
// of a script.
STDMETHODIMP CTBShell::WaitForTextAndSleep(BSTR Text, INT Time, BOOL *Result) { // Call TS.WaitForText()
HRESULT OLEResult = WaitForText(Text, -1, Result);
// Call Sleep()
if (OLEResult == S_OK) Sleep(Time);
return OLEResult; }
// CTBShell::SendMessage
//
// Sends a Windows Message the active terminal connection.
STDMETHODIMP CTBShell::SendMessage(UINT Message, WPARAM wParam, LPARAM lParam, BOOL *Result) { LPCSTR LastError;
// Sanity check the connection
if (Connection == NULL) return RecordLastError("Not connected", Result);
// Call the TCLIENT2 API
LastError = T2SendData(Connection, Message, wParam, lParam);
return RecordLastError(LastError, Result); }
// CTBShell::TypeText
//
// Types text at a specified rate.
STDMETHODIMP CTBShell::TypeText(BSTR Text, UINT WordsPerMin, BOOL *Result) { LPCSTR LastError;
// Sanity check the connection
if (Connection == NULL) return RecordLastError("Not connected", Result);
// Call the TCLIENT2 API
LastError = T2TypeText(Connection, Text, WordsPerMin);
return RecordLastError(LastError, Result); }
// CTBShell::OpenStartMenu
//
// Does a CTRL-ESC on the remote client to bring up the start menu.
STDMETHODIMP CTBShell::OpenStartMenu(BOOL *Result) { // CTRL+ESC for the Start Menu
VKeyCtrl(VK_ESCAPE, Result);
if (Result == FALSE) return RecordLastError("Failed to CTRL-ESC", NULL);
// Wait for "Shut Down" on the start menu to appear
return WaitForText(OLESTR("Shut Down"), T2INFINITE, Result); }
// CTBShell::OpenSystemMenu
//
// Does an ALT-SPACE on the remote client to bring up the system menu.
STDMETHODIMP CTBShell::OpenSystemMenu(BOOL *Result) { // ALT+SPACE to open the system menu
VKeyAlt(VK_SPACE, Result);
if (Result == FALSE) return RecordLastError("Failed to ALT-SPACE", NULL);
// Wait for "Close" on the system menu to appear
return WaitForText(OLESTR("Close"), T2INFINITE, Result); }
// CTBShell::Maximize
//
// Attempts to use the system menu to maximize the active window.
STDMETHODIMP CTBShell::Maximize(BOOL *Result) { // Open the system menu
HRESULT OLEResult = OpenSystemMenu(Result);
// Hit 'x' for maximize
if (Result != FALSE) OLEResult = KeyPress(OLESTR("x"), Result);
return OLEResult; }
// CTBShell::Minimize
//
// Attempts to use the system menu to minimize the active window.
STDMETHODIMP CTBShell::Minimize(BOOL *Result) { // Open the system menu
HRESULT OLEResult = OpenSystemMenu(Result);
// Hit 'x' for maximize
if (Result != FALSE) OLEResult = KeyPress(OLESTR("n"), Result);
return OLEResult; }
// CTBShell::Start
//
// Uses the TCLIENT2 function to open the start menu,
// hit r (for run), and type the specified name to run
// a program.
STDMETHODIMP CTBShell::Start(BSTR Name, BOOL *Result) { LPCSTR LastError;
// Sanity check the connection.
if (Connection == NULL) return RecordLastError("Not connected", Result);
// Call the API
LastError = T2Start(Connection, Name);
return RecordLastError(LastError, Result); }
// CTBShell::SwitchToProcess
//
// Uses the TCLIENT2 function to ALT-TAB between programs until the
// specified text is found, which then the current application is opened.
STDMETHODIMP CTBShell::SwitchToProcess(BSTR Name, BOOL *Result) { LPCSTR LastError;
// Sanity check the connection.
if (Connection == NULL) return RecordLastError("Not connected", Result);
// Call the API
LastError = T2SwitchToProcess(Connection, Name);
return RecordLastError(LastError, Result); }
// This macros allows to quickly define the key methods.
// Because they are so similar, this macro is nice as it
// only makes you to change code once if need be.
#define CTBSHELL_ENABLEPTR *
#define CTBSHELL_DISABLEPTR
#define CTBSHELL_KEYFUNCTYPE(Name, Type, Ptr) \
STDMETHODIMP CTBShell::Name(Type Key, BOOL *Result) \ { \ LPCSTR LastError = T2##Name(Connection, Ptr Key); \ if (Connection == NULL) \ return RecordLastError("Not connected", Result); \ return RecordLastError(LastError, Result); \ }
// This quick macro allows for declaring both the ASCII
// version and the virtual key code one both in one swipe.
#define CTBSHELL_KEYFUNCS(Name) \
CTBSHELL_KEYFUNCTYPE(Name, BSTR, CTBSHELL_ENABLEPTR); \ CTBSHELL_KEYFUNCTYPE(V##Name, INT, CTBSHELL_DISABLEPTR);
// Key function defintions
CTBSHELL_KEYFUNCS(KeyAlt); CTBSHELL_KEYFUNCS(KeyCtrl); CTBSHELL_KEYFUNCS(KeyDown); CTBSHELL_KEYFUNCS(KeyPress); CTBSHELL_KEYFUNCS(KeyUp);
|