|
|
/*++
* File name: * scfuncs.c * Contents: * Functions exported to smclient intepreter * * Copyright (C) 1998-1999 Microsoft Corp. --*/ #include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <process.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <winsock.h>
#include "tclient.h"
#define PROTOCOLAPI
#include "protocol.h"
#include "gdata.h"
#include "queues.h"
#include "misc.h"
#include "rclx.h"
#include "..\..\bmplib\bmplib.h"
// This structure is used by _FindTopWindow
typedef struct _SEARCHWND { TCHAR *szClassName; // The class name of searched window,
// NULL - ignore
TCHAR *szCaption; // Window caption, NULL - ignore
LONG_PTR lProcessId; // Process Id of the owner, 0 - ignore
HWND hWnd; // Found window handle
} SEARCHWND, *PSEARCHWND;
/*
* Internal exported functions */ LPCSTR Wait4Str(PCONNECTINFO, LPCWSTR); LPCSTR Wait4StrTimeout(PCONNECTINFO, LPCWSTR); LPCSTR Wait4MultipleStr(PCONNECTINFO, LPCWSTR); LPCSTR Wait4MultipleStrTimeout(PCONNECTINFO, LPCWSTR); LPCSTR GetWait4MultipleStrResult(PCONNECTINFO, LPCWSTR); LPCSTR GetFeedbackString(PCONNECTINFO, LPSTR result, UINT max); LPCSTR Wait4Disconnect(PCONNECTINFO); LPCSTR Wait4Connect(PCONNECTINFO); LPCSTR RegisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam); LPCSTR UnregisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam); LPCSTR GetDisconnectReason(PCONNECTINFO pCI); PROTOCOLAPI LPCSTR SMCAPI SCSendtextAsMsgs(PCONNECTINFO, LPCWSTR); PROTOCOLAPI LPCSTR SMCAPI SCSwitchToProcess(PCONNECTINFO pCI, LPCWSTR lpszParam); PROTOCOLAPI LPCSTR SMCAPI SCSetClientTopmost(PCONNECTINFO pCI, LPCWSTR lpszParam); PROTOCOLAPI LPCSTR SMCAPI SCSendMouseClick(PCONNECTINFO pCI, UINT xPos, UINT yPos); PROTOCOLAPI LPCSTR SMCAPI SCGetClientScreen(PCONNECTINFO pCI, INT left, INT top, INT right, INT bottom, UINT *puiSize, PVOID *ppDIB); PROTOCOLAPI LPCSTR SMCAPI SCSaveClientScreen(PCONNECTINFO pCI, INT left, INT top, INT right, INT bottom, LPCSTR szFileName);
/*
* Intenal functions definition */ LPCSTR _Wait4ConnectTimeout(PCONNECTINFO pCI, DWORD dwTimeout); LPCSTR _Wait4ClipboardTimeout(PCONNECTINFO pCI, DWORD dwTimeout); LPCSTR _SendRClxData(PCONNECTINFO pCI, PRCLXDATA pRClxData); LPCSTR _Wait4RClxDataTimeout(PCONNECTINFO pCI, DWORD dwTimeout); LPCSTR _Wait4Str(PCONNECTINFO, LPCWSTR, DWORD dwTimeout, WAITTYPE); LPCSTR _WaitSomething(PCONNECTINFO pCI, PWAIT4STRING pWait, DWORD dwTimeout); VOID _CloseConnectInfo(PCONNECTINFO); LPCSTR _Login(PCONNECTINFO, LPCWSTR, LPCWSTR, LPCWSTR); HWND _FindTopWindow(LPTSTR, LPTSTR, LONG_PTR); HWND _FindWindow(HWND, LPTSTR, LPTSTR); BOOL _IsExtendedScanCode(INT scancode);
/*
* Clipboard help functions (clputil.c) */ VOID Clp_GetClipboardData( UINT format, HGLOBAL hClipData, INT *pnClipDataSize, HGLOBAL *phNewData);
BOOL Clp_SetClipboardData( UINT formatID, HGLOBAL hClipData, INT nClipDataSize, BOOL *pbFreeHandle);
UINT Clp_GetClipboardFormat(LPCSTR szFormatLookup);
BOOL Clp_EmptyClipboard(VOID);
BOOL Clp_CheckEmptyClipboard(VOID);
UINT _GetKnownClipboardFormatIDByName(LPCSTR szFormatName);
/*++
* Function: * SCInit * Description: * Called by smclient after the library is loaded. * Passes trace routine * Arguments: * pInitData - contains a trace routine * Called by: * !smclient --*/ PROTOCOLAPI VOID SMCAPI SCInit(SCINITDATA *pInitData) { g_pfnPrintMessage = pInitData->pfnPrintMessage; }
/*++
* Function: * SCConnectEx * Description: * Called by smclient when connect command is interpreted * Arguments: * lpszServerName - server to connect to * lpszUserName - login user name. Empty string means no login * lpszPassword - login password * lpszDomain - login domain, empty string means login to a domain * the same as lpszServerName * xRes, yRes - clients resolution, 0x0 - default * ConnectFlags - * - low speed (compression) option * - cache the bitmaps to the disc option * - connection context allocated in this function * Return value: * Error message. NULL on success * Called by: * SCConnect --*/ PROTOCOLAPI LPCSTR SMCAPI SCConnectEx( LPCWSTR lpszServerName, LPCWSTR lpszUserName, LPCWSTR lpszPassword, LPCWSTR lpszDomain, LPCWSTR lpszShell, IN const int xRes, IN const int yRes, IN const int ConnectionFlags, PCONNECTINFO *ppCI) { HWND hDialog, hClient, hConnect; HWND hContainer, hInput, hOutput; STARTUPINFO si; PROCESS_INFORMATION procinfo; LPCSTR rv = NULL; int trys; CHAR szCommandLine[MAX_STRING_LENGTH]; LPCSTR szDiscon; UINT xxRes, yyRes;
// Correct the resolution
if (xRes >= 1600 && yRes >= 1200) {xxRes = 1600; yyRes = 1200;} else if (xRes >= 1280 && yRes >= 1024) {xxRes = 1280; yyRes = 1024;} else if (xRes >= 1024 && yRes >= 768) {xxRes = 1024; yyRes = 768;} else if (xRes >= 800 && yRes >= 600) {xxRes = 800; yyRes = 600;} else {xxRes = 640; yyRes = 480;}
*ppCI = NULL;
for (trys = 60; trys && !g_hWindow; trys--) Sleep(1000);
if (!g_hWindow) { TRACE((ERROR_MESSAGE, "Panic !!! Feedback window is not created\n")); rv = ERR_WAIT_FAIL_TIMEOUT; goto exitpt; }
*ppCI = (PCONNECTINFO)malloc(sizeof(**ppCI));
if (!*ppCI) { TRACE((ERROR_MESSAGE, "Couldn't allocate %d bytes memory\n", sizeof(**ppCI))); rv = ERR_ALLOCATING_MEMORY; goto exitpt; } memset(*ppCI, 0, sizeof(**ppCI));
(*ppCI)->OwnerThreadId = GetCurrentThreadId();
// Check in what mode the client will be executed
// if the server name starts with '\'
// then tclient.dll will wait until some remote client
// is connected (aka RCLX mode)
// otherwise start the client on the same machine
// running tclient.dll (smclient)
if (*lpszServerName != L'\\') { // This is local mode, start the RDP client process
FillMemory(&si, sizeof(si), 0); si.cb = sizeof(si); si.wShowWindow = SW_SHOWMINIMIZED;
_SetClientRegistry(lpszServerName, lpszShell, xxRes, yyRes, ConnectionFlags);
_snprintf(szCommandLine, sizeof(szCommandLine), #ifdef _WIN64
"%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%I64d " REG_FORMAT, #else // !_WIN64
"%s /CLXDLL=CLXTSHAR.DLL /CLXCMDLINE=%s%d " REG_FORMAT, #endif // _WIN64
g_strClientImg, _HWNDOPT, g_hWindow, GetCurrentProcessId(), GetCurrentThreadId());
(*ppCI)->dead = FALSE;
_AddToClientQ(*ppCI);
if (!CreateProcessA(NULL, szCommandLine, NULL, // Security attribute for process
NULL, // Security attribute for thread
FALSE, // Inheritance - no
0, // Creation flags
NULL, // Environment
NULL, // Current dir
&si, &procinfo)) { TRACE((ERROR_MESSAGE, "Error creating process (szCmdLine=%s), GetLastError=0x%x\n", szCommandLine, GetLastError())); CloseHandle(procinfo.hProcess); CloseHandle(procinfo.hThread); procinfo.hProcess = procinfo.hProcess = NULL;
rv = ERR_CREATING_PROCESS; goto exiterr; }
(*ppCI)->hProcess = procinfo.hProcess; (*ppCI)->hThread = procinfo.hThread; (*ppCI)->lProcessId = procinfo.dwProcessId; (*ppCI)->dwThreadId = procinfo.dwThreadId;
rv = Wait4Connect(*ppCI); if (rv || (*ppCI)->dead) { (*ppCI)->dead = TRUE; TRACE((WARNING_MESSAGE, "Client can't connect\n")); rv = ERR_CONNECTING; goto exiterr; }
hClient = (*ppCI)->hClient; if ( NULL == hClient) { trys = 120; // 2 minutes
do { hClient = _FindTopWindow(NAME_MAINCLASS, NULL, procinfo.dwProcessId); if (!hClient) { Sleep(1000); trys --; } } while(!hClient && trys);
if (!trys) { TRACE((WARNING_MESSAGE, "Can't connect")); rv = ERR_CONNECTING; goto exiterr; } }
// Find the clients child windows
trys = 240; // 2 min
do { hContainer = _FindWindow(hClient, NULL, NAME_CONTAINERCLASS); hInput = _FindWindow(hContainer, NULL, NAME_INPUT); hOutput = _FindWindow(hContainer, NULL, NAME_OUTPUT); if (!hContainer || !hInput || !hOutput) { TRACE((INFO_MESSAGE, "Can't get child windows. Retry")); Sleep(500); trys--; } } while ((!hContainer || !hInput || !hOutput) && trys);
if (!trys) { TRACE((WARNING_MESSAGE, "Can't find child windows")); rv = ERR_CONNECTING; goto exiterr; }
TRACE((INFO_MESSAGE, "hClient = 0x%x\n", hClient)); TRACE((INFO_MESSAGE, "hContainer= 0x%x\n", hContainer)); TRACE((INFO_MESSAGE, "hInput = 0x%x\n", hInput)); TRACE((INFO_MESSAGE, "hOutput = 0x%x\n", hOutput));
(*ppCI)->hClient = hClient; (*ppCI)->hContainer = hContainer; (*ppCI)->hInput = hInput; (*ppCI)->hOutput = hOutput; } else { // Else what !? This is RCLX mode
// Go in wait mode and wait until some client is connected
// remotely
// set flag in context that this connection works only with remote client
// find the valid server name
while (*lpszServerName && (*lpszServerName) == L'\\') lpszServerName ++;
TRACE((INFO_MESSAGE, "A thread in RCLX mode. Wait for some client." "The target is: %S\n", lpszServerName));
(*ppCI)->dead = FALSE; (*ppCI)->RClxMode = TRUE; (*ppCI)->dwThreadId = GetCurrentThreadId(); _AddToClientQ(*ppCI);
rv = _Wait4ConnectTimeout(*ppCI, INFINITE); if (rv || (*ppCI)->dead) { (*ppCI)->dead = TRUE; TRACE((WARNING_MESSAGE, "Client can't connect to the test controler (us)\n")); rv = ERR_CONNECTING; goto exiterr; } else { // dwProcessId contains socket. hClient is pointer to RCLX
// context structure, aren't they ?
ASSERT((*ppCI)->lProcessId != INVALID_SOCKET); ASSERT((*ppCI)->hClient);
TRACE((INFO_MESSAGE, "Client received remote connection\n")); }
// Next, send connection info to the remote client
// like server to connect to, resolution, etc.
if (!RClx_SendConnectInfo( (PRCLXCONTEXT)((*ppCI)->hClient), lpszServerName, xxRes, yyRes, ConnectionFlags)) { (*ppCI)->dead = TRUE; TRACE((WARNING_MESSAGE, "Client can't send connection info\n")); rv = ERR_CONNECTING; goto exiterr; }
// Now again wait for connect event
// this time it will be real
rv = Wait4Connect(*ppCI); if ((*ppCI)->bWillCallAgain) { // if so, now the client is disconnected
TRACE((INFO_MESSAGE, "Wait for second call\n")); (*ppCI)->dead = FALSE;
rv = Wait4Connect(*ppCI); // Wait for second connect
rv = Wait4Connect(*ppCI);
}
if (rv || (*ppCI)->dead) { (*ppCI)->dead = TRUE; TRACE((WARNING_MESSAGE, "Client(mstsc) can't connect to TS\n")); rv = ERR_CONNECTING; goto exiterr; } }
// Save the resolution
(*ppCI)->xRes = xxRes; (*ppCI)->yRes = yyRes;
// If username is present try to login
if (wcslen(lpszUserName)) { rv = _Login(*ppCI, lpszUserName, lpszPassword, lpszDomain); if (rv) goto exiterr; }
exitpt: return rv; exiterr: if (*ppCI) { if ((szDiscon = SCDisconnect(*ppCI))) { TRACE(( WARNING_MESSAGE, "Error disconnecting: %s\n", szDiscon)); }
*ppCI = NULL; }
return rv; }
PROTOCOLAPI LPCSTR SMCAPI SCConnect( LPCWSTR lpszServerName, LPCWSTR lpszUserName, LPCWSTR lpszPassword, LPCWSTR lpszDomain, IN const int xRes, IN const int yRes, PCONNECTINFO *ppCI) { return SCConnectEx( lpszServerName, lpszUserName, lpszPassword, lpszDomain, NULL, // Default shell (MS Explorer)
xRes, yRes, g_ConnectionFlags, // compression, bmp cache, full screen
ppCI);
}
/*++
* Function: * SCDisconnect * Description: * Called by smclient, when disconnect command is interpreted * Arguments: * pCI - connection context * Return value: * Error message. NULL on success * Called by: * !smclient --*/ PROTOCOLAPI LPCSTR SMCAPI SCDisconnect( PCONNECTINFO pCI) { LPCSTR rv = NULL; INT nCloseTime = WAIT4STR_TIMEOUT; INT nCloseTries = 0; DWORD wres; HWND hYesNo = NULL; HWND hDiscBox = NULL; HWND hDialog = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!(pCI->RClxMode)) { // Try to close the client window
if (!pCI->hClient) pCI->hClient = _FindTopWindow(NAME_MAINCLASS, NULL, pCI->lProcessId);
if (pCI->hClient) SendMessage(pCI->hClient, WM_CLOSE, 0, 0);
do { // search for disconnect dialog and close it
if (!hDialog && !hDiscBox && (hDiscBox = _FindTopWindow(NULL, g_strDisconnectDialogBox, pCI->lProcessId))) PostMessage(hDiscBox, WM_CLOSE, 0, 0);
// If the client asks whether to close or not
// Answer with 'Yes'
if (!hYesNo) hYesNo = _FindTopWindow(NULL, g_strYesNoShutdown, pCI->lProcessId);
if (hYesNo && (nCloseTries % 10) == 1) PostMessage(hYesNo, WM_KEYDOWN, VK_RETURN, 0); else if ((nCloseTries % 10) == 5) { // On every 10 attempts retry to close the client
if (!pCI->hClient) pCI->hClient = _FindTopWindow(NAME_MAINCLASS, NULL, pCI->lProcessId);
if (pCI->hClient) PostMessage(pCI->hClient, WM_CLOSE, 0, 0); }
nCloseTries++; nCloseTime -= 3000; } while ( (wres = WaitForSingleObject(pCI->hProcess, 3000)) == WAIT_TIMEOUT && nCloseTime > 0 );
if (wres == WAIT_TIMEOUT) { TRACE((WARNING_MESSAGE, "Can't close process. WaitForSingleObject timeouts\n")); TRACE((WARNING_MESSAGE, "Process #%d will be killed\n", pCI->lProcessId )); if (!TerminateProcess(pCI->hProcess, 1)) { TRACE((WARNING_MESSAGE, "Can't kill process #%p. GetLastError=%d\n", pCI->lProcessId, GetLastError())); } }
}
if (!_RemoveFromClientQ(pCI)) { TRACE(( WARNING_MESSAGE, "Couldn't find CONNECTINFO in the queue\n" )); }
_CloseConnectInfo(pCI);
exitpt: return rv; }
/*++
* Function: * SCLogoff * Description: * Called by smclient, when logoff command is interpreted * Arguments: * pCI - connection context * Return value: * Error message. NULL on success * Called by: * !smclient --*/ PROTOCOLAPI LPCSTR SMCAPI SCLogoff( PCONNECTINFO pCI) { LPCSTR rv = NULL; INT retries = 5;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto disconnectpt; }
do { // Send Ctrl+Esc
SCSenddata(pCI, WM_KEYDOWN, 17, 1900545); SCSenddata(pCI, WM_KEYDOWN, 27, 65537); SCSenddata(pCI, WM_KEYUP, 27, -1073676287); SCSenddata(pCI, WM_KEYUP, 17, -1071841279); // Wait for Run... menu
rv = _Wait4Str(pCI, g_strStartRun, WAIT4STR_TIMEOUT/4, WAIT_STRING);
if (rv) goto next_retry;
// Send three times Key-Up (scan code 72) and <Enter>
SCSendtextAsMsgs(pCI, g_strStartLogoff);
rv = _Wait4Str(pCI, g_strNTSecurity, WAIT4STR_TIMEOUT/4, WAIT_STRING); next_retry: retries --; } while (rv && retries);
if (rv) goto disconnectpt;
for (retries = 5; retries; retries--) { SCSendtextAsMsgs(pCI, g_strNTSecurity_Act);
rv = Wait4Str(pCI, g_strSureLogoff);
if (!rv) break; }
if (rv) goto disconnectpt;
SCSendtextAsMsgs(pCI, g_strSureLogoffAct); // Press enter
rv = Wait4Disconnect(pCI); if (rv) { TRACE((WARNING_MESSAGE, "Can't close the connection\n")); }
disconnectpt: rv = SCDisconnect(pCI);
exitpt: return rv; }
/*++
* Function: * SCStart * Description: * Called by smclient, when start command is interpreted * This functions emulates starting an app from Start->Run menu * on the server side * Arguments: * pCI - connection context * lpszAppName - command line * Return value: * Error message. NULL on success * Called by: * !smclient --*/ PROTOCOLAPI LPCSTR SMCAPI SCStart( PCONNECTINFO pCI, LPCWSTR lpszAppName) { LPCSTR waitres = NULL; // int retries = 5;
// int retries2 = 5;
DWORD dwTimeout; LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
dwTimeout = 10000; // start the timeout of 10 secs
// Try to start run menu
do { // Press Ctrl+Esc
do { TRACE((ALIVE_MESSAGE, "Start: Sending Ctrl+Esc\n")); SCSenddata(pCI, WM_KEYDOWN, 17, 1900545); SCSenddata(pCI, WM_KEYDOWN, 27, 65537); SCSenddata(pCI, WM_KEYUP, 27, -1073676287); SCSenddata(pCI, WM_KEYUP, 17, -1071841279);
// If the last wait was unsuccessfull increase the timeout
if (waitres) dwTimeout += 2000;
// Wait for Run... menu
waitres = _Wait4Str(pCI, g_strStartRun, dwTimeout, WAIT_STRING);
if (waitres) { TRACE((INFO_MESSAGE, "Start: Start menu didn't appear. Retrying\n")); } else { TRACE((ALIVE_MESSAGE, "Start: Got the start menu\n")); } } while (waitres && dwTimeout < WAIT4STR_TIMEOUT);
if (waitres) { TRACE((WARNING_MESSAGE, "Start: Start menu didn't appear. Giving up\n")); rv = ERR_START_MENU_NOT_APPEARED; goto exitpt; }
TRACE((ALIVE_MESSAGE, "Start: Waiting for the \"Run\" box\n")); // press 'R' for Run...
SCSendtextAsMsgs(pCI, g_strStartRun_Act); waitres = _Wait4Str(pCI, g_strRunBox, dwTimeout, WAIT_STRING); if (waitres) // No success, press Esc
{ TRACE((INFO_MESSAGE, "Start: Can't get the \"Run\" box. Retrying\n")); SCSenddata(pCI, WM_KEYDOWN, 27, 65537); SCSenddata(pCI, WM_KEYUP, 27, -1073676287); } } while (waitres && dwTimeout < WAIT4STR_TIMEOUT);
if (waitres) { TRACE((WARNING_MESSAGE, "Start: \"Run\" box didn't appear. Giving up\n")); rv = ERR_COULDNT_OPEN_PROGRAM; goto exitpt; }
TRACE((ALIVE_MESSAGE, "Start: Sending the command line\n")); // Now we have the focus on the "Run" box, send the app name
rv = SCSendtextAsMsgs(pCI, lpszAppName);
// Hit <Enter>
SCSenddata(pCI, WM_KEYDOWN, 13, 1835009); SCSenddata(pCI, WM_KEYUP, 13, -1071906815);
exitpt: return rv; }
// Eventualy, we are going to change the clipboard
// Syncronize this, so no other thread's AV while
// checking the clipboard content
// store 1 for write, 0 for read
static LONG g_ClipOpened = 0;
/*++
* Function: * SCClipbaord * Description: * Called by smclient, when clipboard command is interpreted * when eClipOp is COPY_TO_CLIPBOARD it copies the lpszFileName to * the clipboard. If eClipOp is PASTE_FROM_CLIPBOARD it * checks the clipboard content against the content of lpszFileName * Arguments: * pCI - connection context * eClipOp - clipboard operation. Possible values: * COPY_TO_CLIPBOARD and PASTE_FROM_CLIPBOARD * Return value: * Error message. NULL on success * Called by: * !smclient --*/ PROTOCOLAPI LPCSTR SMCAPI SCClipboard( PCONNECTINFO pCI, const CLIPBOARDOPS eClipOp, LPCSTR lpszFileName) { LPCSTR rv = NULL; INT hFile = -1; LONG clplength = 0; UINT uiFormat = 0; HGLOBAL ghClipData = NULL; HGLOBAL hNewData = NULL; PBYTE pClipData = NULL; BOOL bClipboardOpen = FALSE; BOOL bFreeClipHandle = TRUE;
LONG prevOp = 1;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (lpszFileName == NULL || !(*lpszFileName)) { // No filename specified, work like an empty clipboard is requested
if (eClipOp == COPY_TO_CLIPBOARD) if (pCI->RClxMode) { if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient), NULL, 0, 0)) rv = ERR_COPY_CLIPBOARD; } else { if (!Clp_EmptyClipboard()) rv = ERR_COPY_CLIPBOARD; } else if (eClipOp == PASTE_FROM_CLIPBOARD) { if (pCI->RClxMode) { if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), 0)) { rv = ERR_PASTE_CLIPBOARD; goto exitpt; } if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT)) { rv = ERR_PASTE_CLIPBOARD; goto exitpt; }
// We do not expect to receive clipboard data
// just format ID
if (!pCI->uiClipboardFormat) // if the format is 0, then there's no clipboard
rv = NULL; else rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE; } else { if (Clp_CheckEmptyClipboard()) rv = NULL; else rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE; } } else { TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n")); rv = ERR_INVALID_PARAM; } goto exitpt; }
if (eClipOp == COPY_TO_CLIPBOARD) { // Open the file for reading
hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY); if (hFile == -1) { TRACE((ERROR_MESSAGE, "Error opening file: %s. errno=%d\n", lpszFileName, errno)); rv = ERR_COPY_CLIPBOARD; goto exitpt; }
// Get the clipboard length (in the file)
clplength = _filelength(hFile) - sizeof(uiFormat); // Get the format
if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat)) { TRACE((ERROR_MESSAGE, "Error reading from file. errno=%d\n", errno)); rv = ERR_COPY_CLIPBOARD; goto exitpt; }
ghClipData = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, clplength); if (!ghClipData) { TRACE((ERROR_MESSAGE, "Can't allocate %d bytes\n", clplength)); rv = ERR_COPY_CLIPBOARD; goto exitpt; }
pClipData = GlobalLock(ghClipData); if (!pClipData) { TRACE((ERROR_MESSAGE, "Can't lock handle 0x%x\n", ghClipData)); rv = ERR_COPY_CLIPBOARD; goto exitpt; }
if (_read(hFile, pClipData, clplength) != clplength) { TRACE((ERROR_MESSAGE, "Error reading from file. errno=%d\n", errno)); rv = ERR_COPY_CLIPBOARD; goto exitpt; }
GlobalUnlock(ghClipData);
if (pCI->RClxMode) // RCLX mode, send the data to the client's machine
{ if (!(pClipData = GlobalLock(ghClipData))) { rv = ERR_COPY_CLIPBOARD; goto exitpt; }
if (!RClx_SendClipboard((PRCLXCONTEXT)(pCI->hClient), pClipData, clplength, uiFormat)) { rv = ERR_COPY_CLIPBOARD; goto exitpt; } } else { // Local mode, change the clipboard on this machine
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1))) { rv = ERR_CLIPBOARD_LOCKED; goto exitpt; }
if (!OpenClipboard(NULL)) { TRACE((ERROR_MESSAGE, "Can't open the clipboard. GetLastError=%d\n", GetLastError())); rv = ERR_COPY_CLIPBOARD; goto exitpt; }
bClipboardOpen = TRUE;
// Empty the clipboard, so we'll have only one entry
EmptyClipboard();
if (!Clp_SetClipboardData(uiFormat, ghClipData, clplength, &bFreeClipHandle)) { TRACE((ERROR_MESSAGE, "SetClipboardData failed. GetLastError=%d\n", GetLastError())); rv = ERR_COPY_CLIPBOARD; goto exitpt; } }
} else if (eClipOp == PASTE_FROM_CLIPBOARD) { LONG nClipDataSize;
// Open the file for reading
hFile = _open(lpszFileName, _O_RDONLY|_O_BINARY); if (hFile == -1) { TRACE((ERROR_MESSAGE, "Error opening file: %s. errno=%d\n", lpszFileName, errno)); rv = ERR_PASTE_CLIPBOARD; goto exitpt; }
// Get the clipboard length (in the file)
clplength = _filelength(hFile) - sizeof(uiFormat); // Get the format
if (_read(hFile, &uiFormat, sizeof(uiFormat)) != sizeof(uiFormat)) { TRACE((ERROR_MESSAGE, "Error reading from file. errno=%d\n", errno)); rv = ERR_PASTE_CLIPBOARD; goto exitpt; }
// This piece retrieves the clipboard
if (pCI->RClxMode) // Send request for a clipboard
{ if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), uiFormat)) { rv = ERR_PASTE_CLIPBOARD; goto exitpt; } if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT)) { rv = ERR_PASTE_CLIPBOARD; goto exitpt; }
ghClipData = pCI->ghClipboard; // Get the clipboard size
nClipDataSize = pCI->nClipboardSize; } else { // retrieve the local clipboard
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1))) { rv = ERR_CLIPBOARD_LOCKED; goto exitpt; }
if (!OpenClipboard(NULL)) { TRACE((ERROR_MESSAGE, "Can't open the clipboard. GetLastError=%d\n", GetLastError())); rv = ERR_PASTE_CLIPBOARD; goto exitpt; }
bClipboardOpen = TRUE;
// Retrieve the data
ghClipData = GetClipboardData(uiFormat); if (ghClipData) { Clp_GetClipboardData(uiFormat, ghClipData, &nClipDataSize, &hNewData); bFreeClipHandle = FALSE; }
if (hNewData) ghClipData = hNewData; }
if (!ghClipData) { TRACE((ERROR_MESSAGE, "Can't get clipboard data (empty clipboard ?). GetLastError=%d\n", GetLastError())); rv = ERR_PASTE_CLIPBOARD_EMPTY; goto exitpt; }
if (!nClipDataSize) TRACE((WARNING_MESSAGE, "Clipboard is empty.\n"));
pClipData = GlobalLock(ghClipData); if (!pClipData) { TRACE((ERROR_MESSAGE, "Can't lock global mem. GetLastError=%d\n", GetLastError())); rv = ERR_PASTE_CLIPBOARD; goto exitpt; }
// Check if the client is on Win16 platform
// and the clipboard is paragraph aligned
// the file size is just bellow this size
if (pCI->RClxMode && (strstr(pCI->szClientType, "WIN16") != NULL) && ((nClipDataSize % 16) == 0) && ((nClipDataSize - clplength) < 16) && (nClipDataSize != 0)) { // if so, then cut the clipboard size with the difference
nClipDataSize = clplength; } else if (nClipDataSize != clplength) { TRACE((INFO_MESSAGE, "Different length: file=%d, clipbrd=%d\n", clplength, nClipDataSize)); rv = ERR_PASTE_CLIPBOARD_DIFFERENT_SIZE; goto exitpt; }
// compare the data
{ BYTE pBuff[1024]; PBYTE pClp = pClipData; UINT nBytes; BOOL bEqu = TRUE;
while (bEqu && (nBytes = _read(hFile, pBuff, sizeof(pBuff))) && nBytes != -1) { if (memcmp(pBuff, pClp, nBytes)) bEqu = FALSE;
pClp += nBytes; }
if (!bEqu) { TRACE((INFO_MESSAGE, "Clipboard and file are not equal\n")); rv = ERR_PASTE_CLIPBOARD_NOT_EQUAL; } }
} else rv = ERR_UNKNOWN_CLIPBOARD_OP;
exitpt: // Do the cleanup
// Release the clipboard handle
if (pClipData) GlobalUnlock(ghClipData);
// free any clipboard received in RCLX mode
if (pCI->RClxMode && pCI->ghClipboard) { GlobalFree(pCI->ghClipboard); pCI->ghClipboard = NULL; } else if (ghClipData && eClipOp == COPY_TO_CLIPBOARD && bFreeClipHandle) GlobalFree(ghClipData);
if (hNewData) GlobalFree(hNewData);
// Close the file
if (hFile != -1) _close(hFile);
// Close the clipboard
if (bClipboardOpen) CloseClipboard(); if (!prevOp) InterlockedExchange(&g_ClipOpened, 0);
return rv; }
/*++
* Function: * SCSaveClipboard * Description: * Save the clipboard in file (szFileName) with * format specified in szFormatName * Arguments: * pCI - connection context * szFormatName- format name * szFileName - the name of the file to save to * Return value: * Error message. NULL on success * Called by: * !perlext --*/ PROTOCOLAPI LPCSTR SMCAPI SCSaveClipboard( PCONNECTINFO pCI, LPCSTR szFormatName, LPCSTR szFileName) { LPCSTR rv = ERR_SAVE_CLIPBOARD; BOOL bClipboardOpen = FALSE; UINT nFormatID = 0; HGLOBAL ghClipData = NULL; HGLOBAL hNewData = NULL; INT nClipDataSize; CHAR *pClipData = NULL; INT hFile = -1;
LONG prevOp = 1;
// ++++++ First go thru parameter check
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (szFormatName == NULL || !(*szFormatName)) { TRACE((ERROR_MESSAGE, "SCClipboard: Invalid format name\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
if (szFileName == NULL || !(*szFileName)) { TRACE((ERROR_MESSAGE, "SCClipboard: Invalid filename\n")); rv = ERR_INVALID_PARAM; goto exitpt; } // ------ End of parameter check
//
if (pCI->RClxMode) { nFormatID = _GetKnownClipboardFormatIDByName(szFormatName); if (!nFormatID) { TRACE((ERROR_MESSAGE, "Can't get the clipboard format ID: %s.\n", szFormatName)); goto exitpt; }
// Send request for a clipboard
if (!RClx_SendClipboardRequest((PRCLXCONTEXT)(pCI->hClient), nFormatID)) { rv = ERR_PASTE_CLIPBOARD; goto exitpt; } if (_Wait4ClipboardTimeout(pCI, WAIT4STR_TIMEOUT)) { rv = ERR_PASTE_CLIPBOARD; goto exitpt; }
ghClipData = pCI->ghClipboard; // Get the clipboard size
nClipDataSize = pCI->nClipboardSize;
if (!ghClipData || !nClipDataSize) { TRACE((WARNING_MESSAGE, "Clipboard is empty.\n")); goto exitpt; } } else { // local mode
// Open the clipboard
if ((prevOp = InterlockedExchange(&g_ClipOpened, 1))) { rv = ERR_CLIPBOARD_LOCKED; goto exitpt; }
if (!OpenClipboard(NULL)) { TRACE((ERROR_MESSAGE, "Can't open the clipboard. GetLastError=%d\n", GetLastError())); goto exitpt; }
bClipboardOpen = TRUE;
nFormatID = Clp_GetClipboardFormat(szFormatName);
if (!nFormatID) { TRACE((ERROR_MESSAGE, "Can't get the clipboard format: %s.\n", szFormatName)); goto exitpt; }
TRACE((INFO_MESSAGE, "Format ID: %d(0x%X)\n", nFormatID, nFormatID));
// Retrieve the data
ghClipData = GetClipboardData(nFormatID); if (!ghClipData) { TRACE((ERROR_MESSAGE, "Can't get clipboard data. GetLastError=%d\n", GetLastError())); goto exitpt; }
Clp_GetClipboardData(nFormatID, ghClipData, &nClipDataSize, &hNewData); if (hNewData) ghClipData = hNewData;
if (!nClipDataSize) { TRACE((WARNING_MESSAGE, "Clipboard is empty.\n")); goto exitpt; } }
pClipData = GlobalLock(ghClipData); if (!pClipData) { TRACE((ERROR_MESSAGE, "Can't lock global mem. GetLastError=%d\n", GetLastError())); goto exitpt; }
// Open the destination file
hFile = _open(szFileName, _O_RDWR|_O_CREAT|_O_BINARY|_O_TRUNC, _S_IREAD|_S_IWRITE); if (hFile == -1) { TRACE((ERROR_MESSAGE, "Can't open a file: %s\n", szFileName)); goto exitpt; }
// First write the format type
if (_write(hFile, &nFormatID, sizeof(nFormatID)) != sizeof(nFormatID)) { TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno)); goto exitpt; }
if (_write(hFile, pClipData, nClipDataSize) != (INT)nClipDataSize) { TRACE((ERROR_MESSAGE, "_write failed. errno=%d\n", errno)); goto exitpt; }
TRACE((INFO_MESSAGE, "File written successfully. %d bytes written\n", nClipDataSize));
rv = NULL; exitpt: // Do the cleanup
// Close the file
if (hFile != -1) _close(hFile);
// Release the clipboard handle
if (pClipData) GlobalUnlock(ghClipData);
if (hNewData) GlobalFree(hNewData);
// Close the clipboard
if (bClipboardOpen) CloseClipboard(); if (!prevOp) InterlockedExchange(&g_ClipOpened, 0);
// free any clipboard received in RCLX mode
if (pCI && pCI->RClxMode && pCI->ghClipboard) { GlobalFree(pCI->ghClipboard); pCI->ghClipboard = NULL; }
return rv; }
/*++
* Function: * SCSenddata * Description: * Called by smclient, when senddata command is interpreted * Sends an window message to the client * Arguments: * pCI - connection context * uiMessage - the massage Id * wParam - word param of the message * lParam - long param of the message * Return value: * Error message. NULL on success * Called by: * !smclient --*/ PROTOCOLAPI LPCSTR SMCAPI SCSenddata( PCONNECTINFO pCI, const UINT uiMessage, const WPARAM wParam, const LPARAM lParam) { UINT msg = uiMessage; LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
// TRACE((ALIVE_MESSAGE, "Senddata: uMsg=%x wParam=%x lParam=%x\n",
// uiMessage, wParam, lParam));
// Determines whether it will
// send the message to local window
// or thru RCLX
if (!pCI->RClxMode) { // Obsolete, a client registry setting "Allow Background Input" asserts
// that the client will accept the message
// SetFocus(pCI->hInput);
// SendMessage(pCI->hInput, WM_SETFOCUS, 0, 0);
SendMessage(pCI->hInput, msg, wParam, lParam); } else { // RClxMode
ASSERT(pCI->lProcessId != INVALID_SOCKET); ASSERT(pCI->hClient);
if (!RClx_SendMessage((PRCLXCONTEXT)(pCI->hClient), msg, wParam, lParam)) { TRACE((WARNING_MESSAGE, "Can't send message thru RCLX\n")); } }
exitpt: return rv; }
PROTOCOLAPI LPCSTR SMCAPI SCClientTerminate(PCONNECTINFO pCI) { LPCSTR rv = ERR_CLIENTTERMINATE_FAIL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!(pCI->RClxMode)) { if (!TerminateProcess(pCI->hProcess, 1)) { TRACE((WARNING_MESSAGE, "Can't kill process #%p. GetLastError=%d\n", pCI->lProcessId, GetLastError())); goto exitpt; } } else { TRACE((WARNING_MESSAGE, "ClientTerminate is not supported in RCLX mode yet\n")); TRACE((WARNING_MESSAGE, "Using disconnect\n")); }
rv = SCDisconnect(pCI);
exitpt: return rv;
}
/*++
* Function: * SCGetSessionId * Description: * Called by smclient, returns the session ID. 0 is invalid, not logged on * yet * Arguments: * pCI - connection context * Return value: * session id, 0 is invlid value, -1 is returned on NT4 clients * Called by: * !smclient --*/ PROTOCOLAPI UINT SMCAPI SCGetSessionId(PCONNECTINFO pCI) { UINT rv = 0;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); goto exitpt; }
if (pCI->dead) { goto exitpt; }
rv = pCI->uiSessionId;
exitpt:
return rv; }
/*++
* Function: * SCCheck * Description: * Called by smclient, when check command is interpreted * Arguments: * pCI - connection context * lpszCommand - command name * lpszParam - command parameter * Return value: * Error message. NULL on success. Exceptions are GetDisconnectReason and * GetWait4MultipleStrResult * Called by: * !smclient --*/ PROTOCOLAPI LPCSTR SMCAPI SCCheck(PCONNECTINFO pCI, LPCSTR lpszCommand, LPCWSTR lpszParam) { LPCSTR rv = ERR_INVALID_COMMAND;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if ( !_stricmp(lpszCommand, "Wait4Str")) rv = Wait4Str(pCI, lpszParam); else if (!_stricmp(lpszCommand, "Wait4Disconnect")) rv = Wait4Disconnect(pCI); else if (!_stricmp(lpszCommand, "RegisterChat")) rv = RegisterChat(pCI, lpszParam); else if (!_stricmp(lpszCommand, "UnregisterChat")) rv = UnregisterChat(pCI, lpszParam); else if (!_stricmp(lpszCommand, "GetDisconnectReason")) rv = GetDisconnectReason(pCI); else if (!_stricmp(lpszCommand, "Wait4StrTimeout")) rv = Wait4StrTimeout(pCI, lpszParam); else if (!_stricmp(lpszCommand, "Wait4MultipleStr")) rv = Wait4MultipleStr(pCI, lpszParam); else if (!_stricmp(lpszCommand, "Wait4MultipleStrTimeout")) rv = Wait4MultipleStrTimeout(pCI, lpszParam); else if (!_stricmp(lpszCommand, "GetWait4MultipleStrResult")) rv = GetWait4MultipleStrResult(pCI, lpszParam); else if (!_stricmp(lpszCommand, "SwitchToProcess")) rv = SCSwitchToProcess(pCI, lpszParam); /* **New** */ else if (!_stricmp(lpszCommand, "SetClientTopmost")) rv = SCSetClientTopmost(pCI, lpszParam);
exitpt: return rv; }
/*
* Extensions and help functions */
/*++
* Function: * Wait4Disconnect * Description: * Waits until the client is disconnected * Arguments: * pCI - connection context * Return value: * Error message. NULL on success * Called by: * SCCheck, SCLogoff --*/ LPCSTR Wait4Disconnect(PCONNECTINFO pCI) { WAIT4STRING Wait; LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
memset(&Wait, 0, sizeof(Wait)); Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
Wait.lProcessId = pCI->lProcessId; Wait.pOwner = pCI; Wait.WaitType = WAIT_DISC;
rv = _WaitSomething(pCI, &Wait, WAIT4STR_TIMEOUT); if (!rv) { TRACE(( INFO_MESSAGE, "Client is disconnected\n")); }
CloseHandle(Wait.evWait); exitpt: return rv; }
/*++
* Function: * Wait4Connect * Description: * Waits until the client is connect * Arguments: * pCI - connection context * Return value: * Error message, NULL on success * Called by: * SCCOnnect --*/ LPCSTR Wait4Connect(PCONNECTINFO pCI) { return (_Wait4ConnectTimeout(pCI, CONNECT_TIMEOUT)); }
/*++
* Function: * _Wait4ConnectTimeout * Description: * Waits until the client is connect * Arguments: * pCI - connection context * dwTimeout - timeout value * Return value: * Error message, NULL on success * Called by: * SCConnect --*/ LPCSTR _Wait4ConnectTimeout(PCONNECTINFO pCI, DWORD dwTimeout) { WAIT4STRING Wait; LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
memset(&Wait, 0, sizeof(Wait)); Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
Wait.lProcessId = pCI->lProcessId; Wait.pOwner = pCI; Wait.WaitType = WAIT_CONN;
rv = _WaitSomething(pCI, &Wait, dwTimeout); if (!rv) { TRACE(( INFO_MESSAGE, "Client is connected\n")); }
CloseHandle(Wait.evWait); exitpt: return rv; }
/*++
* Function: * _Wait4ClipboardTimeout * Description: * Waits until clipboard response is received from RCLX module * Arguments: * pCI - connection context * dwTimeout - timeout value * Return value: * Error message, NULL on success * Called by: * SCClipboard --*/ LPCSTR _Wait4ClipboardTimeout(PCONNECTINFO pCI, DWORD dwTimeout) { WAIT4STRING Wait; LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!(pCI->RClxMode)) { TRACE((WARNING_MESSAGE, "WaitForClipboard: Not in RCLX mode\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
memset(&Wait, 0, sizeof(Wait)); Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
Wait.lProcessId = pCI->lProcessId; Wait.pOwner = pCI; Wait.WaitType = WAIT_CLIPBOARD;
rv = _WaitSomething(pCI, &Wait, dwTimeout); if (!rv) { TRACE(( INFO_MESSAGE, "Clipboard received\n")); }
CloseHandle(Wait.evWait); exitpt: return rv; }
/*++
* Function: * GetDisconnectReason * Description: * Retrieves, if possible, the client error box * Arguments: * pCI - connection context * Return value: * The error box message. NULL if not available * Called by: * SCCheck --*/ LPCSTR GetDisconnectReason(PCONNECTINFO pCI) { HWND hDiscBox; LPCSTR rv = NULL; HWND hWnd, hwndTop, hwndNext; char classname[128]; char caption[256];
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (strlen(pCI->szDiscReason)) { rv = pCI->szDiscReason; goto exitpt; }
hDiscBox = _FindTopWindow(NULL, DISCONNECT_DIALOG_BOX, pCI->lProcessId);
if (!hDiscBox) { rv = ERR_NORMAL_EXIT; goto exitpt; } else { TRACE((INFO_MESSAGE, "Found hDiscBox=0x%x", hDiscBox)); }
pCI->szDiscReason[0] = 0; hWnd = NULL;
hwndTop = GetWindow(hDiscBox, GW_CHILD); if (!hwndTop) { TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hDiscBox)); goto exitpt; }
hwndNext = hwndTop; do { hWnd = hwndNext; if (!GetClassName(hWnd, classname, sizeof(classname))) { TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n", hWnd)); goto nextwindow; } if (!GetWindowText(hWnd, caption, sizeof(caption))) { TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n")); goto nextwindow; }
if (!strcmp(classname, STATIC_CLASS) && strlen(classname) < sizeof(pCI->szDiscReason) - strlen(pCI->szDiscReason) - 3) { strcat(pCI->szDiscReason, caption); strcat(pCI->szDiscReason, "\n"); } nextwindow: hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT); } while (hWnd && hwndNext != hwndTop);
rv = (LPCSTR)pCI->szDiscReason;
exitpt: return rv; }
/*++
* Function: * Wait4Str * Description: * Waits for a specific string to come from clients feedback * Arguments: * pCI - connection context * lpszParam - waited string * Return value: * Error message, NULL on success * Called by: * SCCheck --*/ LPCSTR Wait4Str(PCONNECTINFO pCI, LPCWSTR lpszParam) { return _Wait4Str(pCI, lpszParam, WAIT4STR_TIMEOUT, WAIT_STRING); }
/*++
* Function: * Wait4StrTimeout * Description: * Waits for a specific string to come from clients feedback * The timeout is different than default and is specified in * lpszParam argument, like: * waited_string<->timeout_value * Arguments: * pCI - connection context * lpszParam - waited string and timeout * Return value: * Error message, NULL on success * Called by: * SCCheck --*/ LPCSTR Wait4StrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam) { WCHAR waitstr[MAX_STRING_LENGTH]; WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR); DWORD dwTimeout; LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (!sep) { TRACE((WARNING_MESSAGE, "Wait4StrTiemout: No timeout value. Default applying\n")); rv = Wait4Str(pCI, lpszParam); } else { LONG_PTR len = sep - lpszParam;
if (len > sizeof(waitstr) - 1) len = sizeof(waitstr) - 1;
wcsncpy(waitstr, lpszParam, len); waitstr[len] = 0; sep += wcslen(CHAT_SEPARATOR); dwTimeout = _wtoi(sep);
if (!dwTimeout) { TRACE((WARNING_MESSAGE, "Wait4StrTiemout: No timeout value(%s). Default applying\n", sep)); dwTimeout = WAIT4STR_TIMEOUT; }
rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_STRING); }
exitpt: return rv; }
/*++
* Function: * Wait4MultipleStr * Description: * Same as Wait4Str, but waits for several strings at once * the strings are separated by '|' character * Arguments: * pCI - connection context * lpszParam - waited strings * Return value: * Error message, NULL on success * Called by: * SCCheck --*/ LPCSTR Wait4MultipleStr(PCONNECTINFO pCI, LPCWSTR lpszParam) { return _Wait4Str(pCI, lpszParam, WAIT4STR_TIMEOUT, WAIT_MSTRINGS); }
/*++
* Function: * Wait4MultipleStrTimeout * Description: * Combination between Wait4StrTimeout and Wait4MultipleStr * Arguments: * pCI - connection context * lpszParam - waited strings and timeout value. Example * - "string1|string2|...|stringN<->5000" * Return value: * Error message, NULL on success * Called by: * SCCheck --*/ LPCSTR Wait4MultipleStrTimeout(PCONNECTINFO pCI, LPCWSTR lpszParam) { WCHAR waitstr[MAX_STRING_LENGTH]; WCHAR *sep = wcsstr(lpszParam, CHAT_SEPARATOR); DWORD dwTimeout; LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
pCI->nWait4MultipleStrResult = 0; pCI->szWait4MultipleStrResult[0] = 0;
if (!sep) { TRACE((WARNING_MESSAGE, "Wait4MultipleStrTiemout: No timeout value. Default applying")); rv = Wait4MultipleStr(pCI, lpszParam); } else { LONG_PTR len = sep - lpszParam;
if (len > sizeof(waitstr) - 1) len = sizeof(waitstr) - 1;
wcsncpy(waitstr, lpszParam, len); waitstr[len] = 0; sep += wcslen(CHAT_SEPARATOR); dwTimeout = _wtoi(sep);
if (!dwTimeout) { TRACE((WARNING_MESSAGE, "Wait4StrTiemout: No timeout value. Default applying")); dwTimeout = WAIT4STR_TIMEOUT; }
rv = _Wait4Str(pCI, waitstr, dwTimeout, WAIT_MSTRINGS); }
exitpt: return rv; }
/*++
* Function: * GetWait4MultipleStrResult * Description: * Retrieves the result from last Wait4MultipleStr call * Arguments: * pCI - connection context * lpszParam - unused * Return value: * The string, NULL on error * Called by: * SCCheck --*/ LPCSTR GetWait4MultipleStrResult(PCONNECTINFO pCI, LPCWSTR lpszParam) { LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (*pCI->szWait4MultipleStrResult) rv = (LPCSTR)pCI->szWait4MultipleStrResult; else rv = NULL;
exitpt: return rv; }
/*++
* Function: * GetFeedbackString * Description: * Pick a string from connection buffer or wait until * something is received * Arguments: * pCI - connection context * result - the buffer for received string * max - the buffer size * Return value: * Error message, NULL on success * Called by: * * * * EXPORTED * * * --*/ LPCSTR GetFeedbackString(PCONNECTINFO pCI, LPSTR result, UINT max) { LPCSTR rv = NULL; int nFBpos, nFBsize ;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
// Grab the buffer pointers
EnterCriticalSection(g_lpcsGuardWaitQueue); nFBpos = pCI->nFBend + FEEDBACK_SIZE - pCI->nFBsize; nFBsize = pCI->nFBsize; LeaveCriticalSection(g_lpcsGuardWaitQueue);
nFBpos %= FEEDBACK_SIZE;
if (!max) goto exitpt;
*result = 0;
if (!nFBsize) // Empty buffer, wait for feedback to receive
{ rv = _Wait4Str(pCI, L"", WAIT4STR_TIMEOUT, WAIT_STRING); } if (!rv) // Pickup from buffer
{ UINT i;
EnterCriticalSection(g_lpcsGuardWaitQueue);
// Adjust the buffer pointers
pCI->nFBsize = pCI->nFBend + FEEDBACK_SIZE - nFBpos - 1; pCI->nFBsize %= FEEDBACK_SIZE;
// Copy the string but watch out for overflow
if (max > wcslen(pCI->Feedback[nFBpos]) + 1) max = wcslen(pCI->Feedback[nFBpos]);
for (i = 0; i < max; i++) result[i] = (char)(pCI->Feedback[nFBpos][i]); result[max] = 0;
LeaveCriticalSection(g_lpcsGuardWaitQueue); } exitpt: return rv; }
/*++
* Function: * _SendRClxData * Description: * Sends request for data to the client * Arguments: * pCI - connection context * pRClxData - data to send * Return value: * Error message, NULL on success * Called by: * SCGetClientScreen --*/ LPCSTR _SendRClxData(PCONNECTINFO pCI, PRCLXDATA pRClxData) { LPCSTR rv = NULL; PRCLXCONTEXT pRClxCtx; RCLXREQPROLOG Request;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!(pCI->RClxMode)) { TRACE((WARNING_MESSAGE, "_SendRClxData: Not in RCLX mode\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
pRClxCtx = (PRCLXCONTEXT)pCI->hClient; if (!pRClxCtx || pRClxCtx->hSocket == INVALID_SOCKET) { TRACE((ERROR_MESSAGE, "Not connected yet, RCLX context is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!pRClxData) { TRACE((ERROR_MESSAGE, "_SendRClxData: Data block is null\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
Request.ReqType = REQ_DATA; Request.ReqSize = pRClxData->uiSize + sizeof(*pRClxData); if (!RClx_SendBuffer(pRClxCtx->hSocket, &Request, sizeof(Request))) { rv = ERR_CLIENT_DISCONNECTED; goto exitpt; }
if (!RClx_SendBuffer(pRClxCtx->hSocket, pRClxData, Request.ReqSize)) { rv = ERR_CLIENT_DISCONNECTED; goto exitpt; }
exitpt: return rv; }
/*++
* Function: * _Wait4RClxData * Description: * Waits for data response from RCLX client * Arguments: * pCI - connection context * dwTimeout - timeout value * Return value: * Error message, NULL on success * Called by: * SCGetClientScreen --*/ LPCSTR _Wait4RClxDataTimeout(PCONNECTINFO pCI, DWORD dwTimeout) { WAIT4STRING Wait; LPCSTR rv = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!(pCI->RClxMode)) { TRACE((WARNING_MESSAGE, "_Wait4RClxData: Not in RCLX mode\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
memset(&Wait, 0, sizeof(Wait)); Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
Wait.lProcessId = pCI->lProcessId; Wait.pOwner = pCI; Wait.WaitType = WAIT_DATA;
rv = _WaitSomething(pCI, &Wait, dwTimeout); if (!rv) { TRACE(( INFO_MESSAGE, "RCLX data received\n")); }
CloseHandle(Wait.evWait); exitpt: return rv; }
/*++
* Function: * _Wait4Str * Description: * Waits for string(s) with specified timeout * Arguments: * pCI - connection context * lpszParam - waited string(s) * dwTimeout - timeout value * WaitType - WAIT_STRING ot WAIT_MSTRING * Return value: * Error message, NULL on success * Called by: * SCStart, Wait4Str, Wait4StrTimeout, Wait4MultipleStr * Wait4MultipleStrTimeout, GetFeedbackString --*/ LPCSTR _Wait4Str(PCONNECTINFO pCI, LPCWSTR lpszParam, DWORD dwTimeout, WAITTYPE WaitType) { WAIT4STRING Wait; int parlen, i; LPCSTR rv = NULL;
ASSERT(pCI);
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
memset(&Wait, 0, sizeof(Wait));
// Check the parameter
parlen = wcslen(lpszParam);
// Copy the string
if (parlen > sizeof(Wait.waitstr)/sizeof(WCHAR)-1) parlen = sizeof(Wait.waitstr)/sizeof(WCHAR)-1;
wcsncpy(Wait.waitstr, lpszParam, parlen); Wait.waitstr[parlen] = 0; Wait.strsize = parlen;
// Convert delimiters to 0s
if (WaitType == WAIT_MSTRINGS) { WCHAR *p = Wait.waitstr;
while((p = wcschr(p, WAIT_STR_DELIMITER))) { *p = 0; p++; } }
Wait.evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
if (!Wait.evWait) { TRACE((ERROR_MESSAGE, "Couldn't create event\n")); goto exitpt; } Wait.lProcessId = pCI->lProcessId; Wait.pOwner = pCI; Wait.WaitType = WaitType;
TRACE(( INFO_MESSAGE, "Expecting string: %S\n", Wait.waitstr)); rv = _WaitSomething(pCI, &Wait, dwTimeout); TRACE(( INFO_MESSAGE, "String %S received\n", Wait.waitstr));
if (!rv && pCI->dead) { rv = ERR_CLIENT_IS_DEAD; }
CloseHandle(Wait.evWait); exitpt: return rv; }
/*++
* Function: * _WaitSomething * Description: * Wait for some event: string, connect or disconnect * Meanwhile checks for chat sequences * Arguments: * pCI - connection context * pWait - the event function waits for * dwTimeout - timeout value * Return value: * Error message, NULL on success * Called by: * Wait4Connect, Wait4Disconnect, _Wait4Str --*/ LPCSTR _WaitSomething(PCONNECTINFO pCI, PWAIT4STRING pWait, DWORD dwTimeout) { BOOL bDone = FALSE; LPCSTR rv = NULL; DWORD waitres;
ASSERT(pCI || pWait);
_AddToWaitQueue(pCI, pWait); pCI->evWait4Str = pWait->evWait;
do { if ( (waitres = WaitForMultipleObjects( pCI->nChatNum+1, &pCI->evWait4Str, FALSE, dwTimeout)) <= (pCI->nChatNum + WAIT_OBJECT_0)) { if (waitres == WAIT_OBJECT_0) { bDone = TRUE; } else { PWAIT4STRING pNWait;
ASSERT((unsigned)pCI->nChatNum >= waitres - WAIT_OBJECT_0);
// Here we must send response messages
waitres -= WAIT_OBJECT_0 + 1; ResetEvent(pCI->aevChatSeq[waitres]); pNWait = _RetrieveFromWaitQByEvent(pCI->aevChatSeq[waitres]);
ASSERT(pNWait); ASSERT(wcslen(pNWait->respstr)); TRACE((INFO_MESSAGE, "Recieved : [%d]%S\n", pNWait->strsize, pNWait->waitstr )); SCSendtextAsMsgs(pCI, (LPCWSTR)pNWait->respstr); } } else { if (*(pWait->waitstr)) { TRACE((WARNING_MESSAGE, "Wait for \"%S\" failed: TIMEOUT\n", pWait->waitstr)); } else { TRACE((WARNING_MESSAGE, "Wait failed: TIMEOUT\n")); } rv = ERR_WAIT_FAIL_TIMEOUT; bDone = TRUE; } } while(!bDone);
pCI->evWait4Str = NULL;
_RemoveFromWaitQueue(pWait);
if (!rv && pCI->dead) rv = ERR_CLIENT_IS_DEAD;
return rv; }
/*++
* Function: * RegisterChat * Description: * This regiters a wait4str <-> sendtext pair * so when we receive a specific string we will send a proper messages * lpszParam is kind of: XXXXXX<->YYYYYY * XXXXX is the waited string, YYYYY is the respond * These command could be nested up to: MAX_WAITING_EVENTS * Arguments: * pCI - connection context * lpszParam - parameter, example: * "Connect to existing Windows NT session<->\n" * - hit enter when this string is received * Return value: * Error message, NULL on success * Called by: * SCCheck, _Login --*/ LPCSTR RegisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam) { PWAIT4STRING pWait; int parlen, i, resplen; LPCSTR rv = NULL; LPCWSTR resp;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!lpszParam) { TRACE((WARNING_MESSAGE, "Parameter is null\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (pCI->nChatNum >= MAX_WAITING_EVENTS) { TRACE(( WARNING_MESSAGE, "RegisterChat: too much waiting strings\n" )); goto exitpt; }
// Split the parameter
resp = wcsstr(lpszParam, CHAT_SEPARATOR); // Check the strings
if (!resp) { TRACE(( WARNING_MESSAGE, "RegisterChat: invalid parameter\n" )); rv = ERR_INVALID_PARAM; goto exitpt; }
parlen = wcslen(lpszParam) - wcslen(resp); resp += wcslen(CHAT_SEPARATOR);
if (!parlen) { TRACE((WARNING_MESSAGE, "RegisterChat empty parameter\n")); goto exitpt; }
resplen = wcslen(resp); if (!resplen) { TRACE((WARNING_MESSAGE, "RegisterChat: empty respond string\n" )); goto exitpt; }
// Allocate the WAIT4STRING structure
pWait = (PWAIT4STRING)malloc(sizeof(*pWait)); if (!pWait) { TRACE((WARNING_MESSAGE, "RegisterChat: can't allocate %d bytes\n", sizeof(*pWait) )); goto exitpt; } memset(pWait, 0, sizeof(*pWait));
// Copy the waited string
if (parlen > sizeof(pWait->waitstr)/sizeof(WCHAR)-1) parlen = sizeof(pWait->waitstr)/sizeof(WCHAR)-1;
wcsncpy(pWait->waitstr, lpszParam, parlen); pWait->waitstr[parlen] = 0; pWait->strsize = parlen;
// Copy the respond string
if (resplen > sizeof(pWait->respstr)-1) resplen = sizeof(pWait->respstr)-1;
wcsncpy(pWait->respstr, resp, resplen); pWait->respstr[resplen] = 0; pWait->respsize = resplen;
pWait->evWait = CreateEvent(NULL, //security
TRUE, //manual
FALSE, //initial state
NULL); //name
if (!pWait->evWait) { TRACE((ERROR_MESSAGE, "Couldn't create event\n")); free (pWait); goto exitpt; } pWait->lProcessId = pCI->lProcessId; pWait->pOwner = pCI; pWait->WaitType = WAIT_STRING;
// _AddToWaitQNoCheck(pCI, pWait);
_AddToWaitQueue(pCI, pWait);
// Add to connection info array
pCI->aevChatSeq[pCI->nChatNum] = pWait->evWait; pCI->nChatNum++;
exitpt: return rv; }
// Remove a WAIT4STRING from waiting Q
// Param is the waited string
/*++
* Function: * UnregisterChat * Description: * Deallocates and removes from waiting Q everithing * from RegisterChat function * Arguments: * pCI - connection context * lpszParam - waited string * Return value: * Error message, NULL on success * Called by: * SCCheck, _Login --*/ LPCSTR UnregisterChat(PCONNECTINFO pCI, LPCWSTR lpszParam) { PWAIT4STRING pWait; LPCSTR rv = NULL; int i;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!lpszParam) { TRACE((WARNING_MESSAGE, "Parameter is null\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
pWait = _RemoveFromWaitQIndirect(pCI, lpszParam); if (!pWait) { TRACE((WARNING_MESSAGE, "UnregisterChat: can't find waiting string: %S\n", lpszParam )); goto exitpt; }
i = 0; while (i < pCI->nChatNum && pCI->aevChatSeq[i] != pWait->evWait) i++;
ASSERT(i < pCI->nChatNum);
memmove(pCI->aevChatSeq+i, pCI->aevChatSeq+i+1, (pCI->nChatNum-i-1)*sizeof(pCI->aevChatSeq[0])); pCI->nChatNum--;
CloseHandle(pWait->evWait);
free(pWait);
exitpt: return rv; }
/*
* Returns TRUE if the client is dead */ PROTOCOLAPI BOOL SMCAPI SCIsDead(PCONNECTINFO pCI) { if (!pCI) return TRUE;
return pCI->dead; }
/*++
* Function: * _CloseConnectInfo * Description: * Clean all resources for this connection. Close the client * Arguments: * pCI - connection context * Called by: * SCDisconnect --*/ VOID _CloseConnectInfo(PCONNECTINFO pCI) { PRCLXDATACHAIN pRClxDataChain, pNext;
ASSERT(pCI);
_FlushFromWaitQ(pCI);
// Close All handles
EnterCriticalSection(g_lpcsGuardWaitQueue);
/* // not needed, the handle is already closed
if (pCI->evWait4Str) { CloseHandle(pCI->evWait4Str); pCI->evWait4Str = NULL; } */
// Chat events are already closed by FlushFromWaitQ
// no need to close them
pCI->nChatNum = 0;
if (!pCI->RClxMode) { // The client was local, so we have handles opened
if (pCI->hProcess) CloseHandle(pCI->hProcess); if (pCI->hThread); CloseHandle(pCI->hThread);
pCI->hProcess = pCI->hThread =NULL; } else { // Hmmm, RCLX mode. Then disconnect the socket
if (pCI->hClient) RClx_EndRecv((PRCLXCONTEXT)(pCI->hClient));
pCI->hClient = NULL; // Clean the pointer
}
// Clear the clipboard handle (if any)
if (pCI->ghClipboard) { GlobalFree(pCI->ghClipboard); pCI->ghClipboard = NULL; }
// clear any recevied RCLX data
pRClxDataChain = pCI->pRClxDataChain; while(pRClxDataChain) { pNext = pRClxDataChain->pNext; free(pRClxDataChain); pRClxDataChain = pNext; }
LeaveCriticalSection(g_lpcsGuardWaitQueue);
if (!pCI->RClxMode) _DeleteClientRegistry(pCI);
free(pCI); }
/*++
* Function: * _Login * Description: * Emulate login procedure * Arguments: * pCI - connection context * lpszUserName - user name * lpszPassword - password * lpszDomain - domain name * Return value: * Error message, NULL on success * Called by: * SCConnect --*/ LPCSTR _Login(PCONNECTINFO pCI, LPCWSTR lpszUserName, LPCWSTR lpszPassword, LPCWSTR lpszDomain) { LPCSTR waitres; LPCSTR rv = NULL; WCHAR szBuff[MAX_STRING_LENGTH]; INT nLogonRetrys = 5; UINT nLogonWaitTime;
ASSERT(pCI);
retry_logon: _snwprintf(szBuff, MAX_STRING_LENGTH, L"%s|%s|%s", g_strWinlogon, g_strPriorWinlogon, g_strLogonDisabled);
waitres = Wait4MultipleStr(pCI, szBuff); if (!waitres) { if (pCI->nWait4MultipleStrResult == 1) { SCSendtextAsMsgs(pCI, g_strPriorWinlogon_Act); waitres = Wait4Str(pCI, g_strWinlogon); } else if (pCI->nWait4MultipleStrResult == 2) { SCSendtextAsMsgs(pCI, L"\\n"); waitres = Wait4Str(pCI, g_strWinlogon); } }
if (waitres) { TRACE((WARNING_MESSAGE, "Login failed")); rv = waitres; goto exitpt; }
// Hit Alt+U to go to user name field
SCSendtextAsMsgs(pCI, g_strWinlogon_Act);
SCSendtextAsMsgs(pCI, lpszUserName); // Hit <Tab> key
Sleep(300); SCSendtextAsMsgs(pCI, L"\\t");
SCSendtextAsMsgs(pCI, lpszPassword); // Hit <Tab> key
Sleep(300); SCSendtextAsMsgs(pCI, L"\\t");
SCSendtextAsMsgs(pCI, lpszDomain); Sleep(300);
// Retry logon in case of
// 1. Winlogon is on background
// 2. Wrong username/password/domain
// 3. Other
// Hit <Enter>
SCSendtextAsMsgs(pCI, L"\\n");
nLogonWaitTime = 0; while (!pCI->dead && !pCI->uiSessionId && nLogonWaitTime < CONNECT_TIMEOUT) { // Sleep with wait otherwise the chat won't work
// i.e. this is a hack
waitres = _Wait4Str(pCI, g_strLogonErrorMessage, 1000, WAIT_STRING); if (!waitres) // Error message received
{ SCSendtextAsMsgs(pCI, L"\\n"); Sleep(1000); break; } nLogonWaitTime += 1000; }
if (!pCI->dead && !pCI->uiSessionId) { TRACE((WARNING_MESSAGE, "Logon sequence failed. Retrying (%d)", nLogonRetrys)); if (nLogonRetrys--) goto retry_logon; }
if (!pCI->uiSessionId) { // Send Enter, just in case we are not logged yet
SCSendtextAsMsgs(pCI, L"\\n"); rv = ERR_CANTLOGON; }
exitpt: return rv; }
WPARAM _GetVirtualKey(INT scancode) { if (scancode == 29) // L Control
return VK_CONTROL; else if (scancode == 42) // L Shift
return VK_SHIFT; else if (scancode == 56) // L Alt
return VK_MENU; else return MapVirtualKey(scancode, 3); }
/*++
* Function: * SCSendtextAsMsgs * Description: * Converts a string to WM_KEYUP/KEYDOWN messages * And sends them thru client window * Arguments: * pCI - connection context * lpszString - the string to be send * it can contain the following escape character: * \n - Enter, \t - Tab, \^ - Esc, \& - Alt switch up/down * \XXX - scancode XXX is down, \*XXX - scancode XXX is up * Return value: * Error message, NULL on success * Called by: * SCLogoff, SCStart, _WaitSomething, _Login --*/ PROTOCOLAPI LPCSTR SMCAPI SCSendtextAsMsgs(PCONNECTINFO pCI, LPCWSTR lpszString) { LPCSTR rv = NULL; INT scancode = 0; WPARAM vkKey; BOOL bShiftDown = FALSE; BOOL bAltKey = FALSE; BOOL bCtrlKey = FALSE; UINT uiMsg; LPARAM lParam;
#define _SEND_KEY(_c_, _m_, _v_, _l_) {/*Sleep(40); */SCSenddata(_c_, _m_, _v_, _l_);}
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (!lpszString) { TRACE((ERROR_MESSAGE, "NULL pointer passed to SCSendtextAsMsgs")); rv = ERR_INVALID_PARAM; goto exitpt; }
TRACE(( INFO_MESSAGE, "Sending: \"%S\"\n", lpszString));
for (;*lpszString; lpszString++) { if (*lpszString != '\\') { try_again: if ((scancode = OemKeyScan(*lpszString)) == 0xffffffff) { rv = ERR_INVALID_SCANCODE_IN_XLAT; goto exitpt; }
// Check the Shift key state
if ((scancode & SHIFT_DOWN) && !bShiftDown) { uiMsg = (bAltKey)?WM_SYSKEYDOWN:WM_KEYDOWN; _SEND_KEY(pCI, uiMsg, VK_SHIFT, WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 0, 0)); bShiftDown = TRUE; } else if (!(scancode & SHIFT_DOWN) && bShiftDown) { uiMsg = (bAltKey)?WM_SYSKEYUP:WM_KEYUP; _SEND_KEY(pCI, uiMsg, VK_SHIFT, WM_KEY_LPARAM(1, 0x2A, 0, (bAltKey)?1:0, 1, 1)); bShiftDown = FALSE; } } else { // Non printable symbols
lpszString++; switch(*lpszString) { case 'n': scancode = 0x1C; break; // Enter
case 't': scancode = 0x0F; break; // Tab
case '^': scancode = 0x01; break; // Esc
case 'p': Sleep(100); break; // Sleep for 0.1 sec
case 'P': Sleep(1000); break; // Sleep for 1 sec
case 'x': SCSendMouseClick(pCI, pCI->xRes/2, pCI->yRes/2); break; case '&': // Alt key
if (bAltKey) { _SEND_KEY(pCI, WM_KEYUP, VK_MENU, WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1)); } else { _SEND_KEY(pCI, WM_SYSKEYDOWN, VK_MENU, WM_KEY_LPARAM(1, 0x38, 0, 1, 0, 0)); } bAltKey = !bAltKey; continue; case '*': lpszString ++; if (isdigit(*lpszString)) { INT exten;
scancode = _wtoi(lpszString); TRACE((INFO_MESSAGE, "Scancode: %d UP\n", scancode));
vkKey = _GetVirtualKey(scancode);
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP;
if (vkKey == VK_MENU) bAltKey = FALSE; else if (vkKey == VK_CONTROL) bCtrlKey = FALSE; else if (vkKey == VK_SHIFT) bShiftDown = FALSE;
exten = (_IsExtendedScanCode(scancode))?1:0; lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 1, 1); if (uiMsg == WM_KEYUP) { TRACE((INFO_MESSAGE, "WM_KEYUP, 0x%x, 0x%x\n", vkKey, lParam)); } else { TRACE((INFO_MESSAGE, "WM_SYSKEYUP, 0x%x, 0x%x\n", vkKey, lParam)); }
_SEND_KEY(pCI, uiMsg, vkKey, lParam);
while(isdigit(lpszString[1])) lpszString++; } else { lpszString--; } continue; break; case 0: continue; default: if (isdigit(*lpszString)) { INT exten;
scancode = _wtoi(lpszString); TRACE((INFO_MESSAGE, "Scancode: %d DOWN\n", scancode)); vkKey = _GetVirtualKey(scancode);
if (vkKey == VK_MENU) bAltKey = TRUE; else if (vkKey == VK_CONTROL) bCtrlKey = TRUE; else if (vkKey == VK_SHIFT) bShiftDown = TRUE;
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN;
exten = (_IsExtendedScanCode(scancode))?1:0; lParam = WM_KEY_LPARAM(1, scancode, exten, (bAltKey)?1:0, 0, 0);
if (uiMsg == WM_KEYDOWN) { TRACE((INFO_MESSAGE, "WM_KEYDOWN, 0x%x, 0x%x\n", vkKey, lParam)); } else { TRACE((INFO_MESSAGE, "WM_SYSKEYDOWN, 0x%x, 0x%x\n", vkKey, lParam)); }
_SEND_KEY(pCI, uiMsg, vkKey, lParam);
while(isdigit(lpszString[1])) lpszString++;
continue; } goto try_again; }
} vkKey = MapVirtualKey(scancode, 3); // Remove flag fields
scancode &= 0xff;
uiMsg = (!bAltKey || bCtrlKey)?WM_KEYDOWN:WM_SYSKEYDOWN; // Send the scancode
_SEND_KEY(pCI, uiMsg, vkKey, WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 0, 0)); uiMsg = (!bAltKey || bCtrlKey)?WM_KEYUP:WM_SYSKEYUP; _SEND_KEY(pCI, uiMsg, vkKey, WM_KEY_LPARAM(1, scancode, 0, (bAltKey)?1:0, 1, 1)); }
// And Alt key
if (bAltKey) _SEND_KEY(pCI, WM_KEYUP, VK_MENU, WM_KEY_LPARAM(1, 0x38, 0, 0, 1, 1));
// Shift up
if (bShiftDown) _SEND_KEY(pCI, WM_KEYUP, VK_LSHIFT, WM_KEY_LPARAM(1, 0x2A, 0, 0, 1, 1));
// Ctrl key
if (bCtrlKey) _SEND_KEY(pCI, WM_KEYUP, VK_CONTROL, WM_KEY_LPARAM(1, 0x1D, 0, 0, 1, 1)); #undef _SEND_KEY
exitpt: return rv; }
/*++
* Function: * SwitchToProcess * Description: * Use Alt+Tab to switch to a particular process that is already running * Arguments: * pCI - connection context * lpszParam - the text in the alt-tab box that uniquely identifies the * process we should stop at (i.e., end up switching to) * Return value: * Error message, NULL on success * Called by: * SCCheck --*/ PROTOCOLAPI LPCSTR SMCAPI SCSwitchToProcess(PCONNECTINFO pCI, LPCWSTR lpszParam) { #define ALT_TAB_WAIT_TIMEOUT 1000
#define MAX_APPS 20
LPCSTR rv = NULL; LPCSTR waitres = NULL; INT retrys = MAX_APPS;
WCHAR *wszCurrTask = 0;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
// Wait and look for the string, before we do any switching. This makes
// sure we don't hit the string even before we hit alt-tab, and then
// end up switching to the wrong process
while (_Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT/5, WAIT_STRING) == 0) ;
// Press alt down
SCSenddata(pCI, WM_KEYDOWN, 18, 540540929);
// Now loop through the list of applications (assuming there is one),
// stopping at our desired app.
do { SCSenddata(pCI, WM_KEYDOWN, 9, 983041); SCSenddata(pCI, WM_KEYUP, 9, -1072758783);
waitres = _Wait4Str(pCI, lpszParam, ALT_TAB_WAIT_TIMEOUT, WAIT_STRING);
retrys --; } while (waitres && retrys);
SCSenddata(pCI, WM_KEYUP, 18, -1070071807);
rv = waitres;
exitpt: return rv; }
/*++
* Function: * SCSetClientTopmost * Description: * Swithces the focus to this client * Arguments: * pCI - connection context * lpszParam * - "0" will remote the WS_EX_TOPMOST style * - "non_zero" will set it as topmost window * Return value: * Error message, NULL on success * Called by: * SCCheck --*/ PROTOCOLAPI LPCSTR SMCAPI SCSetClientTopmost( PCONNECTINFO pCI, LPCWSTR lpszParam ) { LPCSTR rv = NULL; BOOL bTop = FALSE;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (pCI->RClxMode) { TRACE((ERROR_MESSAGE, "SetClientOnFocus not supported in RCLX mode\n")); rv = ERR_NOTSUPPORTED; goto exitpt; }
if (!pCI->hClient) { TRACE((WARNING_MESSAGE, "Client's window handle is null\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
if (lpszParam) bTop = (_wtoi(lpszParam) != 0); else bTop = 0;
SetWindowPos(pCI->hClient, (bTop)?HWND_TOPMOST:HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE);
ShowWindow(pCI->hClient, SW_SHOWNORMAL);
if (bTop) { TRACE((INFO_MESSAGE, "Client is SET as topmost window\n")); } else { TRACE((INFO_MESSAGE, "Client is RESET as topmost window\n")); }
exitpt: return rv; }
/*++
* Function: * _SendMouseClick * Description: * Sends a messages for a mouse click * Arguments: * pCI - connection context * xPos - mouse position * yPos * Return value: * error string if fails, NULL on success * Called by: * * * * EXPORTED * * * --*/ PROTOCOLAPI LPCSTR SMCAPI SCSendMouseClick( PCONNECTINFO pCI, UINT xPos, UINT yPos) { LPCSTR rv;
rv = SCSenddata(pCI, WM_LBUTTONDOWN, 0, xPos + (yPos << 16)); if (!rv) SCSenddata(pCI, WM_LBUTTONUP, 0, xPos + (yPos << 16));
return rv; }
/*++
* Function: * SCSaveClientScreen * Description: * Saves in a file rectangle of the client's receive screen buffer * ( aka shadow bitmap) * Arguments: * pCI - connection context * left, top, right, bottom - rectangle coordinates * if all == -1 get's the whole screen * szFileName - file to record * Return value: * error string if fails, NULL on success * Called by: * * * * EXPORTED * * * --*/ PROTOCOLAPI LPCSTR SMCAPI SCSaveClientScreen( PCONNECTINFO pCI, INT left, INT top, INT right, INT bottom, LPCSTR szFileName) { LPCSTR rv = NULL; PVOID pDIB = NULL; UINT uiSize = 0;
if (!szFileName) { TRACE((WARNING_MESSAGE, "SCSaveClientScreen: szFileName is NULL\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
// leave the rest of param checking to SCGetClientScreen
rv = SCGetClientScreen(pCI, left, top, right, bottom, &uiSize, &pDIB); if (rv) goto exitpt;
if (!pDIB || !uiSize) { TRACE((ERROR_MESSAGE, "SCSaveClientScreen: failed, no data\n")); rv = ERR_NODATA; goto exitpt; }
if (!SaveDIB(pDIB, szFileName)) { TRACE((ERROR_MESSAGE, "SCSaveClientScreen: save failed\n")); rv = ERR_NODATA; goto exitpt; }
exitpt:
if (pDIB) free(pDIB);
return rv; }
/*++
* Function: * SCGetClientScreen * Description: * Gets rectangle of the client's receive screen buffer * ( aka shadow bitmap) * Arguments: * pCI - connection context * left, top, right, bottom - rectangle coordinates * if all == -1 get's the whole screen * ppDIB - pointer to the received DIB * puiSize - size of allocated data in ppDIB * * !!!!! DON'T FORGET to free() THAT MEMORY !!!!! * * Return value: * error string if fails, NULL on success * Called by: * SCSaveClientScreen * * * * EXPORTED * * * --*/ PROTOCOLAPI LPCSTR SMCAPI SCGetClientScreen( PCONNECTINFO pCI, INT left, INT top, INT right, INT bottom, UINT *puiSize, PVOID *ppDIB) { LPCSTR rv; PRCLXDATA pRClxData; PREQBITMAP pReqBitmap; PRCLXDATACHAIN pIter, pPrev, pNext; PRCLXDATACHAIN pRClxDataChain = NULL;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (!pCI->RClxMode) { TRACE((WARNING_MESSAGE, "SCGetClientScreen is not supported in non-RCLX mode\n")); rv = ERR_NOTSUPPORTED; goto exitpt; }
if (!ppDIB || !puiSize) { TRACE((WARNING_MESSAGE, "ppDIB and/or puiSize parameter is NULL\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
// Remove all recieved DATA_BITMAP from the recieve buffer
EnterCriticalSection(g_lpcsGuardWaitQueue); { pIter = pCI->pRClxDataChain; pPrev = NULL;
while (pIter) { pNext = pIter->pNext;
if (pIter->RClxData.uiType == DATA_BITMAP) { // dispose this entry
if (pPrev) pPrev->pNext = pIter->pNext; else pCI->pRClxDataChain = pIter->pNext;
if (!pIter->pNext) pCI->pRClxLastDataChain = pPrev;
free(pIter); } else pPrev = pIter;
pIter = pNext; } } LeaveCriticalSection(g_lpcsGuardWaitQueue);
pRClxData = alloca(sizeof(*pRClxData) + sizeof(*pReqBitmap)); pRClxData->uiType = DATA_BITMAP; pRClxData->uiSize = sizeof(*pReqBitmap); pReqBitmap = (PREQBITMAP)pRClxData->Data; pReqBitmap->left = left; pReqBitmap->top = top; pReqBitmap->right = right; pReqBitmap->bottom = bottom;
TRACE((INFO_MESSAGE, "Getting client's DIB (%d, %d, %d, %d)\n", left, top, right, bottom)); rv = _SendRClxData(pCI, pRClxData);
if (rv) goto exitpt;
do { rv = _Wait4RClxDataTimeout(pCI, WAIT4STR_TIMEOUT); if (rv) goto exitpt;
if (!pCI->pRClxDataChain) { TRACE((ERROR_MESSAGE, "RClxData is not received\n")); rv = ERR_WAIT_FAIL_TIMEOUT; goto exitpt; }
EnterCriticalSection(g_lpcsGuardWaitQueue); // Get any received DATA_BITMAP
{ pIter = pCI->pRClxDataChain; pPrev = NULL;
while (pIter) { pNext = pIter->pNext;
if (pIter->RClxData.uiType == DATA_BITMAP) { // dispose this entry from the chain
if (pPrev) pPrev->pNext = pIter->pNext; else pCI->pRClxDataChain = pIter->pNext;
if (!pIter->pNext) pCI->pRClxLastDataChain = pPrev;
goto entry_is_found; } else pPrev = pIter;
pIter = pNext; }
entry_is_found: pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_BITMAP)? pIter:NULL; } LeaveCriticalSection(g_lpcsGuardWaitQueue); } while (!pRClxDataChain && !pCI->dead);
if (!pRClxDataChain) { TRACE((WARNING_MESSAGE, "SCGetClientScreen: client died\n")); goto exitpt; }
*ppDIB = malloc(pRClxDataChain->RClxData.uiSize); if (!(*ppDIB)) { TRACE((WARNING_MESSAGE, "Can't allocate %d bytes\n", pRClxDataChain->RClxData.uiSize)); rv = ERR_ALLOCATING_MEMORY; goto exitpt; }
memcpy(*ppDIB, pRClxDataChain->RClxData.Data, pRClxDataChain->RClxData.uiSize); *puiSize = pRClxDataChain->RClxData.uiSize;
exitpt:
if (pRClxDataChain) free(pRClxDataChain);
return rv; }
/*++
* Function: * SCSendVCData * Description: * Sends data to a virtual channel * Arguments: * pCI - connection context * szVCName - the virtual channel name * pData - data * uiSize - data size * Return value: * error string if fails, NULL on success * Called by: * * * * EXPORTED * * * --*/ PROTOCOLAPI LPCSTR SMCAPI SCSendVCData( PCONNECTINFO pCI, LPCSTR szVCName, PVOID pData, UINT uiSize ) { LPCSTR rv; PRCLXDATA pRClxData = NULL; CHAR *szName2Send; PVOID pData2Send; UINT uiPacketSize;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (!pCI->RClxMode) { TRACE((WARNING_MESSAGE, "SCSendVCData is not supported in non-RCLXmode\n")); rv = ERR_NOTSUPPORTED; goto exitpt; }
if (!pData || !uiSize) { TRACE((WARNING_MESSAGE, "pData and/or uiSize parameter are NULL\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
if (strlen(szVCName) > MAX_VCNAME_LEN - 1) { TRACE((WARNING_MESSAGE, "channel name too long\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
uiPacketSize = sizeof(*pRClxData) + MAX_VCNAME_LEN + uiSize;
pRClxData = malloc(uiPacketSize); if (!pRClxData) { TRACE((ERROR_MESSAGE, "SCSendVCData: can't allocate %d bytes\n", uiPacketSize)); rv = ERR_ALLOCATING_MEMORY; goto exitpt; }
pRClxData->uiType = DATA_VC; pRClxData->uiSize = uiPacketSize - sizeof(*pRClxData);
szName2Send = (CHAR *)pRClxData->Data; strcpy(szName2Send, szVCName);
pData2Send = szName2Send + MAX_VCNAME_LEN; memcpy(pData2Send, pData, uiSize);
rv = _SendRClxData(pCI, pRClxData);
exitpt: if (pRClxData) free(pRClxData);
return rv; }
/*++
* Function: * SCRecvVCData * Description: * Receives data from virtual channel * Arguments: * pCI - connection context * szVCName - the virtual channel name * ppData - data pointer * * !!!!! DON'T FORGET to free() THAT MEMORY !!!!! * * puiSize - pointer to the data size * Return value: * error string if fails, NULL on success * Called by: * * * * EXPORTED * * * --*/ PROTOCOLAPI LPCSTR SMCAPI SCRecvVCData( PCONNECTINFO pCI, LPCSTR szVCName, PVOID pData, UINT uiBlockSize, UINT *puiBytesRead ) { LPCSTR rv; LPSTR szRecvVCName; PVOID pChanData; PRCLXDATACHAIN pIter, pPrev, pNext; PRCLXDATACHAIN pRClxDataChain = NULL; UINT uiBytesRead = 0; BOOL bBlockFree = FALSE;
if (!pCI) { TRACE((WARNING_MESSAGE, "Connection info is null\n")); rv = ERR_NULL_CONNECTINFO; goto exitpt; }
if (pCI->dead) { rv = ERR_CLIENT_IS_DEAD; goto exitpt; }
if (!pCI->RClxMode) { TRACE((WARNING_MESSAGE, "SCRecvVCData is not supported in non-RCLXmode\n")); rv = ERR_NOTSUPPORTED; goto exitpt; }
if (!pData || !uiBlockSize || !puiBytesRead) { TRACE((WARNING_MESSAGE, "Invalid parameters\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
if (strlen(szVCName) > MAX_VCNAME_LEN - 1) { TRACE((WARNING_MESSAGE, "channel name too long\n")); rv = ERR_INVALID_PARAM; goto exitpt; }
// Extract data entry from this channel
do { if (!pCI->pRClxDataChain) { rv = _Wait4RClxDataTimeout(pCI, WAIT4STR_TIMEOUT); if (rv) goto exitpt; } EnterCriticalSection(g_lpcsGuardWaitQueue);
// Search for data from this channel
{ pIter = pCI->pRClxDataChain; pPrev = NULL;
while (pIter) { pNext = pIter->pNext;
if (pIter->RClxData.uiType == DATA_VC && !_stricmp(pIter->RClxData.Data, szVCName)) {
if (pIter->RClxData.uiSize - pIter->uiOffset - MAX_VCNAME_LEN <= uiBlockSize) { // will read the whole block
// dispose this entry
if (pPrev) pPrev->pNext = pIter->pNext; else pCI->pRClxDataChain = pIter->pNext;
if (!pIter->pNext) pCI->pRClxLastDataChain = pPrev;
bBlockFree = TRUE; }
goto entry_is_found; } else pPrev = pIter;
pIter = pNext; } entry_is_found:
pRClxDataChain = (pIter && pIter->RClxData.uiType == DATA_VC)? pIter:NULL; } LeaveCriticalSection(g_lpcsGuardWaitQueue); } while (!pRClxDataChain && !pCI->dead);
ASSERT(pRClxDataChain->RClxData.uiType == DATA_VC);
szRecvVCName = pRClxDataChain->RClxData.Data; if (_stricmp(szRecvVCName, szVCName)) { TRACE((ERROR_MESSAGE, "SCRecvVCData: received from different channel: %s\n", szRecvVCName)); ASSERT(0); }
pChanData = (BYTE *)(pRClxDataChain->RClxData.Data) + pRClxDataChain->uiOffset + MAX_VCNAME_LEN; uiBytesRead = pRClxDataChain->RClxData.uiSize - pRClxDataChain->uiOffset - MAX_VCNAME_LEN; if (uiBytesRead > uiBlockSize) uiBytesRead = uiBlockSize;
memcpy(pData, pChanData, uiBytesRead);
pRClxDataChain->uiOffset += uiBytesRead;
rv = NULL;
exitpt:
if (pRClxDataChain && bBlockFree) { ASSERT(pRClxDataChain->uiOffset + MAX_VCNAME_LEN == pRClxDataChain->RClxData.uiSize); free(pRClxDataChain); }
if (puiBytesRead) { *puiBytesRead = uiBytesRead; TRACE((INFO_MESSAGE, "SCRecvVCData: %d bytes read\n", uiBytesRead)); }
return rv; }
/*++
* Function: * _EnumWindowsProc * Description: * Used to find a specific window * Arguments: * hWnd - current enumerated window handle * lParam - pointer to SEARCHWND passed from * _FindTopWindow * Return value: * TRUE on success but window is not found * FALSE if the window is found * Called by: * _FindTopWindow thru EnumWindows --*/ BOOL CALLBACK _EnumWindowsProc( HWND hWnd, LPARAM lParam ) { TCHAR classname[128]; TCHAR caption[128]; BOOL rv = TRUE; DWORD dwProcessId; LONG_PTR lProcessId; PSEARCHWND pSearch = (PSEARCHWND)lParam;
if (pSearch->szClassName && !GetClassName(hWnd, classname, sizeof(classname))) { goto exitpt; }
if (pSearch->szCaption && !GetWindowText(hWnd, caption, sizeof(caption))) { goto exitpt; }
GetWindowThreadProcessId(hWnd, &dwProcessId); lProcessId = dwProcessId; if ( (!pSearch->szClassName || ! // Check for classname
#ifdef UNICODE
wcscmp #else
strcmp #endif
(classname, pSearch->szClassName)) && (!pSearch->szCaption || ! #ifdef UNICODE
wcscmp #else
strcmp #endif
(caption, pSearch->szCaption)) && lProcessId == pSearch->lProcessId) { ((PSEARCHWND)lParam)->hWnd = hWnd; rv = FALSE; }
exitpt: return rv; }
/*++
* Function: * _FindTopWindow * Description: * Find specific window by classname and/or caption and/or process Id * Arguments: * classname - class name to search for, NULL ignore * caption - caption to search for, NULL ignore * dwProcessId - process Id, 0 ignore * Return value: * window handle found, NULL otherwise * Called by: * SCConnect, SCDisconnect, GetDisconnectResult --*/ HWND _FindTopWindow(LPTSTR classname, LPTSTR caption, LONG_PTR lProcessId) { SEARCHWND search;
search.szClassName = classname; search.szCaption = caption; search.hWnd = NULL; search.lProcessId = lProcessId;
EnumWindows(_EnumWindowsProc, (LPARAM)&search);
return search.hWnd; }
/*++
* Function: * _FindWindow * Description: * Find child window by caption and/or classname * Arguments: * hwndParent - the parent window handle * srchcaption - caption to search for, NULL - ignore * srchclass - class name to search for, NULL - ignore * Return value: * window handle found, NULL otherwise * Called by: * SCConnect --*/ HWND _FindWindow(HWND hwndParent, LPTSTR srchcaption, LPTSTR srchclass) { HWND hWnd, hwndTop, hwndNext; BOOL bFound; TCHAR classname[128]; TCHAR caption[128];
hWnd = NULL;
hwndTop = GetWindow(hwndParent, GW_CHILD); if (!hwndTop) { TRACE((INFO_MESSAGE, "GetWindow failed. hwnd=0x%x\n", hwndParent)); goto exiterr; }
bFound = FALSE; hwndNext = hwndTop; do { hWnd = hwndNext; if (srchclass && !GetClassName(hWnd, classname, sizeof(classname))) { TRACE((INFO_MESSAGE, "GetClassName failed. hwnd=0x%x\n")); goto nextwindow; } if (srchcaption && !GetWindowText(hWnd, caption, sizeof(caption))) { TRACE((INFO_MESSAGE, "GetWindowText failed. hwnd=0x%x\n")); goto nextwindow; }
if ( (!srchclass || ! #ifdef UNICODE
wcscmp #else
strcmp #endif
(classname, srchclass)) && (!srchcaption || ! #ifdef UNICODE
wcscmp #else
strcmp #endif
(caption, srchcaption)) ) bFound = TRUE; nextwindow: hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT); } while (hWnd && hwndNext != hwndTop && !bFound);
if (!bFound) goto exiterr;
return hWnd; exiterr: return NULL; }
BOOL _IsExtendedScanCode(INT scancode) { static BYTE extscans[] = \ {28, 29, 53, 55, 56, 71, 72, 73, 75, 77, 79, 80, 81, 82, 83, 87, 88}; INT idx;
for (idx = 0; idx < sizeof(extscans); idx++) { if (scancode == (INT)extscans[idx]) return TRUE; } return FALSE; }
PROTOCOLAPI BOOL SMCAPI SCOpenClipboard(HWND hwnd) { return OpenClipboard(hwnd); }
PROTOCOLAPI BOOL SMCAPI SCCloseClipboard(VOID) { return CloseClipboard(); }
|