/*+ * File name: * clxtshar.c * Contents: * Client extension loaded by RDP client * * Copyright (C) 1998-1999 Microsoft Corp. --*/ #include #include #include #include #include #include #include #include #ifndef OS_WINCE #include #endif // OS_WINCE #ifndef OS_WINCE #ifdef OS_WIN32 #include #endif // OS_WIN32 #endif // !OS_WINCE #include "clxexport.h" #include "clxtshar.h" #define WM_CLIPBOARD (WM_USER) // Internal notifcation to send // our clipboard #ifdef OS_WIN32 #ifndef OS_WINCE /*++ * Function: * DllMain * Description: * Dll entry point for win32 (no WinCE) --*/ int APIENTRY DllMain(HINSTANCE hDllInst, DWORD dwReason, LPVOID fImpLoad) { if (dwReason == DLL_PROCESS_ATTACH) { g_hInstance = hDllInst; TRACE((INFO_MESSAGE, TEXT("Clx attached\n"))); // Check the key "Allow Background Input" // If not set pop a message for that if (!_CheckRegistrySettings()) MessageBox(NULL, "CLXTSHAR.DLL: Can't find registry key:\n" "HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client\\" "Allow Background Input.\n" "In order to work properly " "CLX needs this key to be set to 1", "Warning", MB_OK); _GetIniSettings(); } if (dwReason == DLL_PROCESS_DETACH) { TRACE((INFO_MESSAGE, TEXT("Clx detached\n"))); } return TRUE; } #endif // !OS_WINCE #endif // OS_WIN32 #ifdef OS_WINCE /*++ * Function: * dllentry * Description: * Dll entry point for wince --*/ BOOL __stdcall dllentry(HINSTANCE hDllInst, DWORD dwReason, LPVOID fImpLoad) { if (dwReason == DLL_PROCESS_ATTACH) { g_hInstance = hDllInst; TRACE((INFO_MESSAGE, TEXT("Clx attached\n"))); if (!_StartAsyncThread()) TRACE((ERROR_MESSAGE, TEXT("Can't start AsyncThread. TCP unusable\n"))); _GetIniSettings(); } if (dwReason == DLL_PROCESS_DETACH) { TRACE((INFO_MESSAGE, TEXT("Clx detached\n"))); _CloseAsyncThread(); } return TRUE; } #endif // OS_WIN32 #ifdef OS_WIN16 /*++ * Function: * LibMain * Description: * Dll entry point for win16 --*/ int CALLBACK LibMain(HINSTANCE hInstance, WORD dataSeg, WORD heapSize, LPSTR pCmdLine) { // Check if we are already initialized // Only one client is allowed in Win16 environment // so, only one dll can be loaded at a time if (g_hInstance) goto exitpt; g_hInstance = hInstance; // Check the key "Allow Background Input" // If not set pop a message for that if (!_CheckIniSettings()) MessageBox(NULL, "CLXTSHAR.DLL: Can't find key: " "Allow Background Input in mstsc.ini, section \"\"\n" "In order to work properly " "CLX needs this key to be set to 1", "Warning", MB_OK); _GetIniSettings(); exitpt: return TRUE; } #endif // OS_WIN16 /*++ * Function: * ClxInitialize * Description: * Initilizes a context for the current session * reads the command line paramters and determines * the mode wich will run the extension: local or RCLX (Remote CLient * eXecution) * Win32/Win16/WinCE * Arguments: * pClInfo - RDP client info * ppClx - context info * Return value: * TRUE on success * Called by: * !mstsc after the dll is loaded --*/ BOOL CLXAPI ClxInitialize(PCLINFO pClInfo, PCLXINFO *ppClx) { BOOL rv = FALSE; HWND hwndSMC; #ifdef OS_WIN32 #ifndef OS_WINCE // We have enough problems in stress with early unloaded // dll, reference it now and keep it up until the process // dies LoadLibrary("clxtshar.dll"); #endif // !OS_WINCE #endif // OS_WIN32 hwndSMC = _ParseCmdLine(pClInfo->pszCmdLine); if (IS_RCLX && !WS_Init()) { TRACE((ERROR_MESSAGE, TEXT("Can't init winsock\n"))); goto exitpt; } if (g_pClx) // Should not be called twice { TRACE((WARNING_MESSAGE, TEXT("g_pClx is not null. Reentered ?!\n"))); goto exitpt; } *ppClx = (PCLXINFO)_CLXALLOC(sizeof(**ppClx)); g_pClx = (*ppClx); if (!*ppClx) goto exitpt; // Clear the structure memset(*ppClx, 0, sizeof(**ppClx)); // put init of g_pClx here g_pClx->bClipboardReenter = (ULONG)-1; // // Remember client's input window (*ppClx)->hwndMain = pClInfo->hwndMain; if (pClInfo->hwndMain) #ifdef OS_WINCE g_hRDPInst = GetCurrentProcessId(); #else // !OS_WINCE #ifdef _WIN64 g_hRDPInst = (HINSTANCE)GetWindowLongPtr((*ppClx)->hwndMain, GWLP_HINSTANCE); #else // !_WIN64 #ifdef OS_WIN32 g_hRDPInst = (HINSTANCE)GetWindowLong((*ppClx)->hwndMain, GWL_HINSTANCE); #endif // OS_WIN32 #endif // _WIN64 #ifdef OS_WIN16 g_hRDPInst = (HINSTANCE)GetWindowWord((*ppClx)->hwndMain, GWW_HINSTANCE); #endif // OS_WIN16 #endif // !OS_WINCE #ifndef OS_WINCE #ifdef OS_WIN32 // and dwProcessId (*ppClx)->dwProcessId = GetCurrentProcessId(); #endif // OS_WIN32 #endif // !OS_WINCE if (IS_RCLX) { (*ppClx)->hSocket = INVALID_SOCKET; RClx_CreateWindow(g_hInstance); } #ifdef OS_WIN32 #ifndef OS_WINCE else { if (!((*ppClx)->hwndSMC = hwndSMC)) (*ppClx)->hwndSMC = _FindSMCWindow(*ppClx); } #endif // !OS_WINCE #endif // OS_WIN32 if (g_hWindow) // REMOVED: && g_nMyReconId) PostMessage(g_hWindow, WM_TIMER, 0, 0); rv = TRUE; exitpt: return rv; } /*++ * Function: * ClxEvent * Description: * Notifies tclient.dll that some event happend. * Connect/disconnect. * Win32/Win16/WinCE * Arguments: * pClx - context * Event - can be one of the following: * CLX_EVENT_CONNECT * CLX_EVENT_DISCONNECT * CLX_EVENT_LOGON * Called by: * !mstsc on event * alse some of the internal functions call this, especialy * to notify that the client can't connect: * ClxTerminate * _GarbageCollecting when an error box is popped --*/ VOID CLXAPI ClxEvent(PCLXINFO pClx, CLXEVENT Event, ULONG ulResult) { UINT uiMessage = 0; #ifdef OS_WIN16 ULONG lResult = ulResult; #else // !OS_WIN16 LONG_PTR lResult = ulResult; #endif if (!pClx) goto exitpt; #ifdef VLADIMIS #pragma message("Disable this peace before checkin") if (Event == CLX_EVENT_SHADOWBITMAPDC) { pClx->hdcShadowBitmap = (HDC)lResult; goto exitpt; } else if (Event == CLX_EVENT_SHADOWBITMAP) { pClx->hShadowBitmap = (HBITMAP)lResult; goto exitpt; } #endif // VLADIMIS if (IS_RCLX) { RClx_SendEvent(pClx, Event, ulResult); } #ifndef OS_WINCE else { if (!_CheckWindow(pClx)) goto exitpt; if (Event == CLX_EVENT_DISCONNECT) uiMessage = WM_FB_DISCONNECT; else if (Event == CLX_EVENT_CONNECT) { uiMessage = WM_FB_CONNECT; lResult = (LONG_PTR)pClx->hwndMain; } else if (Event == CLX_EVENT_LOGON) // lResult contains the session ID uiMessage = WM_FB_LOGON; if (uiMessage) { #ifdef OS_WIN32 _ClxSendMessage(pClx->hwndSMC, uiMessage, (WPARAM)lResult, pClx->dwProcessId); #endif // OS_WIN32 #ifdef OS_WIN16 if (g_hRDPInst) SendMessage(pClx->hwndSMC, uiMessage, g_hRDPInst, (LPARAM)lResult); #endif // OS_WIN16 } } #endif // !OS_WINCE exitpt: ; } /*++ * Function: * ClxTextOut * Description: * Notifies tclient.dll that TEXTOUT order is recieved. * Passes the string to the dll. Supported only in Win32 * Win32/Win16/WinCE * Arguments: * pClx - context * pText - buffer containing the string * textLength - string length * Called by: * !mstsc on receiving textout order --*/ VOID CLXAPI ClxTextOut(PCLXINFO pClx, PVOID pText, INT textLength) { if (!pClx || !(*((UINT16 *)pText))) goto exitpt; if (IS_RCLX) { RClx_SendTextOut(pClx, pText, textLength); goto exitpt; } #ifdef OS_WIN32 #ifndef OS_WINCE if (!_CheckWindow(pClx)) goto exitpt; if (!pClx->hMapF) if (!_OpenMapFile(pClx, 0)) goto exitpt; if (_SaveInMapFile(pClx->hMapF, pText, textLength, pClx->dwProcessId)) _ClxSendMessage(pClx->hwndSMC, WM_FB_TEXTOUT, (WPARAM)pClx->dwProcessId, (LPARAM)pClx->hMapF); #endif // !OS_WINCE #endif // OS_WIN32 exitpt: ; } /*++ * Function: * ClxTerminate * Description: * Frees all alocations from ClxInitialize * Win32/Win16/WinCE * Arguments: * pClx - context * Called by: * !mstsc before the dll is unloaded and client exit --*/ VOID CLXAPI ClxTerminate(PCLXINFO pClx) { PCLXVCHANNEL pNext; if (!pClx) goto exitpt; ClxEvent(pClx, CLX_EVENT_DISCONNECT, 0); if (IS_RCLX) { pClx->alive = FALSE; RClx_Disconnect(pClx); } #ifdef OS_WIN32 #ifndef OS_WINCE else { if(pClx->hMapF) CloseHandle(pClx->hMapF); _ClxDestroySendMsgThread(g_pClx); } #endif // !OS_WINCE #endif // OS_WIN32 if (pClx->uiReconnectTimer) { KillTimer(g_hWindow, pClx->uiReconnectTimer); pClx->uiReconnectTimer = 0; } if (pClx->pRequest) _CLXFREE(pClx->pRequest); _CLXFREE(pClx); g_pClx = NULL; // dispose g_pVChannels; while(g_pVChannels) { pNext = g_pVChannels->pNext; free(g_pVChannels); g_pVChannels = pNext; } RClx_DestroyWindow(); if (IS_RCLX) WSACleanup(); exitpt: ; } /* * Void functions exported to the RDP client */ VOID CLXAPI ClxConnect(PCLXINFO pClx, LPTSTR lpsz) { } VOID CLXAPI ClxDisconnect(PCLXINFO pClx) { } /*++ * Function: * ClxDialog * Description: * The RDP client is ready with the connect dialog. * In RCLX mode this means that we can connect to the test controler * Win32/Win16/WinCE * Arguments: * pClx - connection context * hwnd - handle to the dialog window * Called by: * !mstsc when the connect dialog is ready --*/ VOID CLXAPI ClxDialog(PCLXINFO pClx, HWND hwnd) { if (!pClx) goto exitpt; pClx->hwndDialog = hwnd; if (hwnd == NULL) // Dialog disappears goto exitpt; if (g_hWindow) PostMessage(g_hWindow, WM_TIMER, 0, 0); else TRACE((ERROR_MESSAGE, TEXT("No g_hWindow in ClxDialog\n"))); exitpt: ; } /*++ * Function: * ClxBitmap * Description: * Send a received bitmap to tclient.dll * Works on Win16/Win32/WinCE in RCLX mode * and on Win32 for local mode * Arguments: * pClx - context * cxSize, cySize - size of the bitmap * pBuffer - bitmap bits * nBmiSize - size of BITMAPINFO * pBmi - BITMAPINFO * Called by: * UHDrawMemBltOrder!mstsc * ClxGlyphOut --*/ VOID CLXAPI ClxBitmap( PCLXINFO pClx, UINT cxSize, UINT cySize, PVOID pBuffer, UINT nBmiSize, PVOID pBmi) { #ifndef OS_WINCE #ifdef OS_WIN32 UINT nSize, nBmpSize; PBMPFEEDBACK pView; #endif // OS_WIN32 #endif // !OS_WINCE if (!g_GlyphEnable) goto exitpt; if (!pClx) goto exitpt; if (nBmiSize && !pBmi) goto exitpt; if (IS_RCLX) { RClx_SendBitmap(pClx, cxSize, cySize, pBuffer, nBmiSize, pBmi); goto exitpt; } #ifdef OS_WIN32 #ifndef OS_WINCE if (!_CheckWindow(pClx)) goto exitpt; if (!nBmiSize) nBmpSize = (cxSize * cySize ) >> 3; else { nBmpSize = ((PBITMAPINFO)pBmi)->bmiHeader.biSizeImage; if (!nBmpSize) nBmpSize = (cxSize * cySize * ((PBITMAPINFO)pBmi)->bmiHeader.biBitCount) >> 3; } nSize = nBmpSize + nBmiSize + sizeof(*pView); if (!nSize) goto exitpt; if (!pClx->hMapF) if (!_OpenMapFile(pClx, nSize)) goto exitpt; if (nSize > pClx->nMapSize) if (!_ReOpenMapFile(pClx, nSize)) goto exitpt; pView = MapViewOfFile(pClx->hMapF, FILE_MAP_ALL_ACCESS, 0, 0, nSize); if (!pView) goto exitpt; pView->lProcessId = pClx->dwProcessId; pView->bmpsize = nBmpSize; pView->bmiSize = nBmiSize; pView->xSize = cxSize; pView->ySize = cySize; if (pBmi) CopyMemory(&(pView->BitmapInfo), pBmi, nBmiSize); CopyMemory((BYTE *)(&(pView->BitmapInfo)) + nBmiSize, pBuffer, nBmpSize); if (!nBmiSize) { // This is glyph, strip it to the skin _StripGlyph((BYTE *)(&pView->BitmapInfo), &cxSize, cySize); nBmpSize = (cxSize * cySize ) >> 3; pView->bmpsize = nBmpSize; pView->xSize = cxSize; } UnmapViewOfFile(pView); _ClxSendMessage(pClx->hwndSMC, WM_FB_BITMAP, (WPARAM)pClx->dwProcessId, (LPARAM)pClx->hMapF); #endif // !OS_WINCE #endif // OS_WIN32 exitpt: ; } /*++ * Function: * ClxGlyphOut * Description: * Send a glyph to tclient.dll * Win32/Win16/WinCE * Arguments: * pClx - context * cxBits,cyBits - glyph size * pBuffer - the glyph * Called by: * GHOutputBuffer!mstsc --*/ VOID CLXAPI ClxGlyphOut( PCLXINFO pClx, UINT cxBits, UINT cyBits, PVOID pBuffer) { if (g_GlyphEnable) ClxBitmap(pClx, cxBits, cyBits, pBuffer, 0, NULL); } /*++ * Function: * ClxGlyphOut * Description: * Send a glyph to tclient.dll * Win32/Win16/WinCE * Arguments: * pClx - context * cxBits,cyBits - glyph size * pBuffer - the glyph * Called by: * GHOutputBuffer!mstsc --*/ BOOL CLXAPI ClxGetClientData( PCLX_CLIENT_DATA pClntData ) { BOOL rv = FALSE; if (!pClntData) { TRACE((ERROR_MESSAGE, TEXT("ClxGetClientData: parameter is NULL\n"))); goto exitpt; } memset(pClntData, 0, sizeof(*pClntData)); if (!g_pClx) { TRACE((ERROR_MESSAGE, TEXT("ClxGetClientData: Clx has no context\n"))); goto exitpt; } pClntData->hScreenDC = g_pClx->hdcShadowBitmap; pClntData->hScreenBitmap = g_pClx->hShadowBitmap; pClntData->hwndMain = g_pClx->hwndMain; pClntData->hwndDialog = g_pClx->hwndDialog; pClntData->hwndInput = g_pClx->hwndInput; rv = TRUE; exitpt: return rv; } /*++ * Function: * _ParseCmdLine * Description: * Retreives WHND of tclient.dll feedback window * passed by the command line * or retreives TestServer name if clxtshar will be * executed in RCLX mode * Win32/Win16/WinCE * Arguments: * szCmdLine - command line * Return value: * The window handle * Called by: * ClxInitialize --*/ HWND _ParseCmdLine(LPCTSTR szCmdLine) { HWND hwnd = NULL; LPCTSTR pszwnd, pszdot, pszend; INT nCounter; if (!szCmdLine) goto exitpt; TRACE((INFO_MESSAGE, TEXT("Command line: %s\n"), szCmdLine)); // Check for _RECONIDOPT(ReconID) option pszwnd = _CLX_strstr(szCmdLine, TEXT(_RECONIDOPT)); if (!pszwnd) goto skip_reconidopt; pszwnd += _CLX_strlen(TEXT(_RECONIDOPT)); g_nMyReconId = _CLX_atol(pszwnd); skip_reconidopt: // Check for _HWNDOPT(hSMC) option pszwnd = _CLX_strstr(szCmdLine, TEXT(_HWNDOPT)); if (!pszwnd) goto exitpt; // Goto the parameter pszwnd += _CLX_strlen(TEXT(_HWNDOPT)); // Find the end of the paramter pszend = _CLX_strchr(pszwnd, TEXT(' ')); if (!pszend) pszend = pszwnd + _CLX_strlen(pszwnd); // Check if paramter is valid host name, i.e. not a number pszdot = _CLX_strchr(pszwnd, TEXT('.')); if (isalpha(*pszwnd) || (pszdot && (pszdot < pszend))) { // This is RCLX mode, grab the TestServer name #ifdef OS_WIN16 INT len; #else // !OS_WIN16 LONG_PTR len; #endif // OS_WIN16 len = pszend - pszwnd; if (!len || len >= sizeof(g_szTestServer)) { TRACE((WARNING_MESSAGE, TEXT("TestServer name is too long\n"))); goto exitpt; } for (nCounter = 0; nCounter < len; g_szTestServer[nCounter] = (CHAR)(pszwnd[nCounter]), nCounter++) ; g_szTestServer[len] = 0; #ifdef UNICODE TRACE((INFO_MESSAGE, L"RCLX mode, connecting to test controler: %S\n", g_szTestServer)); #else // !UNICODE TRACE((INFO_MESSAGE, "RCLX mode, connecting to test controler: %s\n", g_szTestServer)); #endif // !UNICODE } else { // local execution, hwnd passed #ifdef _WIN64 hwnd = (HWND) _atoi64(pszwnd); #else // !_WIN64 hwnd = (HWND) _CLX_atol(pszwnd); #endif // !_WIN64 TRACE((INFO_MESSAGE, TEXT("Local mode. Sending messages to smclient. HWND=0x%x\n"), hwnd)); } exitpt: return hwnd; } #ifndef OS_WINCE /*++ * Function: * _EnumWindowsProcForSMC * Description: * Searches for the feedback window by class name * When found, sends a WM_FB_ACCEPTME to ensure that * this is the right window handle * Win32/Win16/!WinCE * Arguments: * hWnd - current window * lParam - unused * Return value: * FALSE if found * Called by: * _FindSMCWindow thru EnumWindows --*/ BOOL CALLBACK LOADDS _EnumWindowsProcForSMC( HWND hWnd, LPARAM lParam ) { TCHAR classname[128]; BOOL bCont = TRUE; if (GetClassName(hWnd, classname, sizeof(classname))) { if (! _CLX_strcmp(classname, TEXT(_TSTNAMEOFCLAS)) && #ifdef OS_WIN32 SendMessage(hWnd, WM_FB_ACCEPTME, 0, GetCurrentProcessId())) #endif #ifdef OS_WIN16 SendMessage(hWnd, WM_FB_ACCEPTME, (WPARAM)g_hRDPInst, 0)) #endif { *((HWND*)lParam) = hWnd; bCont = FALSE; } } return bCont; } /*++ * Function: * _FindSMCWindow * Description: * Finds the tclient feedback window * Win32/Win16/!WinCE * Arguments: * pClx - context * Return value: * The window handle * Called by: * ClxInitialize, _CheckWindow --*/ HWND _FindSMCWindow(PCLXINFO pClx) { HWND hwndFound = NULL; EnumWindows(_EnumWindowsProcForSMC, (LPARAM)&hwndFound); return hwndFound; } /*++ * Function: * _CheckWindow * Description: * Checks the feedback window and if neccessary finds it * Win32/Win16/!WinCE * Arguments: * pClx - context * Return value: * Feedback window handle * Called by: * ClxEvetm ClxTextOut, ClxBitmap --*/ HWND _CheckWindow(PCLXINFO pClx) { if (!pClx->hwndSMC) { pClx->hwndSMC = _FindSMCWindow(pClx); if (pClx->hwndSMC) { TRACE((INFO_MESSAGE, TEXT("SMC window found:0x%x\n"), pClx->hwndSMC)); } } else { #ifdef _WIN64 if (!GetWindowLongPtr(pClx->hwndSMC, GWLP_HINSTANCE)) #else // !_WIN64 #ifdef OS_WIN32 if (!GetWindowLong(pClx->hwndSMC, GWL_HINSTANCE)) #endif #ifdef OS_WIN16 if (!GetWindowWord(pClx->hwndSMC, GWW_HINSTANCE)) #endif #endif // _WIN64 { TRACE((WARNING_MESSAGE, TEXT("SMC window lost\n"))); pClx->hwndSMC = NULL; } } return (pClx->hwndSMC); } #endif // !OS_WINCE #ifdef OS_WIN32 #ifndef OS_WINCE /*++ * Function: * _OpenMapFile * Description: * Opens a shared memeory for passing feedback to tclient.dll * Win32/!Win16/!WinCE * Return value: * TRUE if handle is allocated successfully * Called by: * ClxTextOut, ClxBitmap --*/ BOOL _OpenMapFile(PCLXINFO pClx, UINT nSize) { HANDLE hMapF; UINT nPageAligned; if (!nSize) nPageAligned = ((sizeof(FEEDBACKINFO) / CLX_ONE_PAGE) + 1) * CLX_ONE_PAGE; else nPageAligned = ((nSize / CLX_ONE_PAGE) + 1) * CLX_ONE_PAGE; hMapF = CreateFileMapping(INVALID_HANDLE_VALUE, //PG.SYS NULL, // no security PAGE_READWRITE, 0, // Size high nPageAligned, // Size low (1 page) NULL); pClx->nMapSize = (hMapF)?nPageAligned:0; pClx->hMapF = hMapF; return (hMapF != NULL); } /*++ * Function: * _ReOpenMapFile * Description: * Closes and opens a new shared memory with larger size * Win32/!Win16/!WinCE * Arguments: * pClx - context * newSize - size of the new memory * Return value: * TRUE on success * Called by: * ClxBitmap --*/ BOOL _ReOpenMapFile(PCLXINFO pClx, UINT newSize) { HANDLE hNewMapF; UINT nPageAligned; nPageAligned = ((newSize / CLX_ONE_PAGE) + 1) * CLX_ONE_PAGE; if (pClx->hMapF) CloseHandle(pClx->hMapF); hNewMapF = CreateFileMapping(INVALID_HANDLE_VALUE, //PG.SYS NULL, // no security PAGE_READWRITE, 0, // Size high nPageAligned, // Size low NULL); pClx->nMapSize = (hNewMapF)?nPageAligned:0; pClx->hMapF = hNewMapF; return (hNewMapF != NULL); } /*++ * Function: * _SaveinMapFile * Description: * Saves a string into the shared memory * Win32/!Win16/!WinCE * Arguments: * hMapF - handle to the map file * str - the string * strsize - size of the string * dwProcessId - our process Id * Return value: * TRUE on success * Called by: * ClxTextOut --*/ BOOL _SaveInMapFile(HANDLE hMapF, LPVOID *str, int strsize, DWORD dwProcessId) { BOOL rv = FALSE, count = 0; PFEEDBACKINFO pView; DWORD laste; pView = MapViewOfFile(hMapF, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(*pView)); if (!pView) goto exitpt; pView->dwProcessId = dwProcessId; strsize = (strsize > sizeof(pView->string)/sizeof(WCHAR))? sizeof(pView->string)/sizeof(WCHAR): strsize; CopyMemory(pView->string, str, strsize*sizeof(WCHAR)); ((WCHAR *)(pView->string))[strsize] = 0; pView->strsize = strsize; UnmapViewOfFile(pView); rv = TRUE; exitpt: return rv; } /*++ * Function: * _CheckRegistrySettings * Description: * Checks if the registry settings are OK for running clxtshar * "Allow Background Input" must be set to 1 for proper work * Win32/!Win16/!WinCE * Return value: * TRUE if the settings are OK * Called by: * DllMain --*/ BOOL _CheckRegistrySettings(VOID) { HKEY key; DWORD disposition; DWORD keyType; DWORD value; DWORD cbData; BOOL rv = FALSE; LONG sysrc; sysrc = RegCreateKeyEx(HKEY_CURRENT_USER, REG_BASE, 0, /* reserved */ NULL, /* class */ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, /* security attributes */ &key, &disposition); cbData = sizeof(value); sysrc = RegQueryValueEx(key, ALLOW_BACKGROUND_INPUT, 0, // reserved &keyType, // returned type (LPBYTE)&value, // data pointer &cbData); if (sysrc != ERROR_SUCCESS) { TRACE((WARNING_MESSAGE, TEXT("RegQueryValueEx failed, status = %d\n"), sysrc)); goto exitpt; } if (keyType != REG_DWORD || cbData != sizeof(value)) { TRACE((WARNING_MESSAGE, TEXT("Mismatch in type/size of registry entry\n"))); goto exitpt; } rv = (value == 1); exitpt: return rv; } #endif // !OS_WINCE #endif // OS_WIN32 #ifdef OS_WIN16 /*++ * Function: * _CheckRegistrySettings * Description: * Checks if the ini settings are OK for running clxtshar * "Allow Background Input" must be set to 1 for proper work * !Win32/Win16/!WinCE * Return value: * TRUE if the settings are OK * Called by: * DllMain --*/ BOOL _CheckIniSettings(VOID) { UINT nABI; nABI = GetPrivateProfileInt("", ALLOW_BACKGROUND_INPUT, 0, "mstsc.ini"); return (nABI == 1); } #endif // OS_WIN16 /*++ * Function: * _GetIniSettings * Description: * Gets the verbose level for printing debug messages * ini file: smclient.ini * section : clx * key : verbose, value: 0-4 (0-(default) no debug spew, 4 all debug) * key : GlyphEnable, value: 0(default), 1 - Enables/Disables glyph sending * Win32/Win16/WinCE * Called by: * DllMain, dllentry, LibMain --*/ VOID _GetIniSettings(VOID) { #ifdef OS_WINCE g_VerboseLevel = 4; g_GlyphEnable = 1; #else // !OS_WINCE CHAR szIniFileName[_MAX_PATH]; const CHAR smclient_ini[] = "\\smclient.ini"; const CHAR clx_ini_section[] = "clx"; *szIniFileName = 0; if (!_getcwd ( szIniFileName, sizeof(szIniFileName) - strlen(smclient_ini) - 1) ) { TRACE((ERROR_MESSAGE, TEXT("Current directory length too long.\n"))); } strcat(szIniFileName, smclient_ini); // Get the timeout value g_VerboseLevel = GetPrivateProfileInt( clx_ini_section, "verbose", g_VerboseLevel, szIniFileName); g_GlyphEnable = GetPrivateProfileInt( clx_ini_section, "GlyphEnable", g_GlyphEnable, szIniFileName); #endif // !OS_WINCE GetPrivateProfileString( TEXT("tclient"), TEXT("UIYesNoDisconnect"), TEXT(YES_NO_SHUTDOWN), g_strYesNoShutdown, sizeof(g_strYesNoShutdown), szIniFileName ); GetPrivateProfileString( TEXT("tclient"), TEXT("UIDisconnectDialogBox"), TEXT(DISCONNECT_DIALOG_BOX), g_strDisconnectDialogBox, sizeof(g_strDisconnectDialogBox), szIniFileName ); GetPrivateProfileString( TEXT("tclient"), TEXT("UIClientCaption"), TEXT(CLIENT_CAPTION), g_strClientCaption, sizeof(g_strClientCaption), szIniFileName ); } /*++ * Function: * _StripGlyph * Description: * Strips leading and trailing blank ... BITS * Yes, bits. The glyph must be aligned from left and right on bit * And glyph width must be aligned on word * Win32/Win16/WinCE * Arguments: * pData - the glyph bits * pxSize - glyph width * ySize - glyph height * Called by: * ClxBitmap --*/ VOID _StripGlyph(LPBYTE pData, UINT *pxSize, UINT ySize) { UINT xSize = *pxSize; UINT leftBytes, leftBits; UINT riteBytes, riteBits; UINT xBytes = xSize >> 3; UINT xScan, yScan, xFinal; BOOL bScan, bAddByte; BYTE mask; BYTE *pSrc, *pDst; if (!pData || !xBytes || !ySize) goto exitpt; leftBytes = riteBytes = 0; leftBits = riteBits = 0; *pxSize = 0; // Insurance for bad exit // Scan from left for first nonzero byte bScan = TRUE; while(bScan) { for (yScan = 0; yScan < ySize && bScan; yScan ++) bScan = (pData[yScan*xBytes + leftBytes] == 0); if (bScan) { leftBytes++; bScan = (leftBytes < xBytes); } } // Trash if blank if (leftBytes == xBytes) goto exitpt; // Scan from left for most left nonzero bit for(yScan = 0; yScan < ySize; yScan ++) { UINT bitc = 0; BYTE b = pData[yScan*xBytes + leftBytes]; while (b) { b >>= 1; bitc ++; } if (bitc > leftBits) leftBits = bitc; } if (!leftBits) // There's something wrong goto exitpt; leftBits = 8 - leftBits; // So far so good. Check the ri(gh)te side bScan = TRUE; while(bScan) { for(yScan = 0 ; yScan < ySize && bScan; yScan ++) bScan = (pData[(yScan + 1)*xBytes - 1 - riteBytes] == 0); if (bScan) { riteBytes ++; bScan = (riteBytes < xBytes); } } // Scan from rite for most rite nonzero bit for(yScan = 0; yScan < ySize; yScan ++) { UINT bitc = 0; BYTE b = pData[(yScan+1)*xBytes - 1 - riteBytes]; while(b) { b <<= 1; bitc ++; } if (bitc > riteBits) riteBits = bitc; } riteBits = 8 - riteBits; // Cool, now get the final width xFinal = xSize - riteBits - leftBits - ((leftBytes + riteBytes) << 3); // align it and get bytes xFinal = (xFinal + 8) >> 3; // Now smoothly move the bitmap to the new location pDst = pData; mask = BitMask[leftBits]; bAddByte = xFinal & 1; for (yScan = 0; yScan < ySize; yScan ++) { pSrc = pData + yScan*xBytes + leftBytes; for(xScan = 0; xScan < xFinal; xScan ++, pDst++, pSrc++) { BYTE b = *pSrc; BYTE r; r = (pSrc[1] & mask) >> (8 - leftBits); b <<= leftBits; b |= r; (*pDst) = b; } pDst[-1] &= BitMask[8 - (riteBits + leftBits) % 8]; if (bAddByte) { (*pDst) = 0; pDst++; } } // BUG: Yes, this is a real bug. But removing it means to // rerecord all glyph database and the impact for // glyph recognition is not so bad //if (bAddByte) // xFinal++; *pxSize = xFinal << 3; exitpt: ; } /*++ * Function: * LocalPrintMessage * Description: * Prints debugging and warning/error messages * Win32/Win16/WinCE * Arguments: * errlevel - level of the message to print * format - print format * Called by: * every TRACE line --*/ VOID __cdecl LocalPrintMessage(INT errlevel, LPCTSTR format, ...) { TCHAR szBuffer[256]; TCHAR *type; va_list arglist; int nchr; if (errlevel >= g_VerboseLevel) goto exitpt; va_start (arglist, format); nchr = _CLX_vsnprintf (szBuffer, sizeof(szBuffer), format, arglist); va_end (arglist); switch(errlevel) { case INFO_MESSAGE: type = TEXT("CLX INF:"); break; case ALIVE_MESSAGE: type = TEXT("CLX ALV:"); break; case WARNING_MESSAGE: type = TEXT("CLX WRN:"); break; case ERROR_MESSAGE: type = TEXT("CLX ERR:"); break; default: type = TEXT("UNKNOWN:"); } OutputDebugString(type); OutputDebugString(szBuffer); exitpt: ; } /*++ * Function: * _ClxAssert * Description: * Asserts boolean expression * Win32/Win16/WinCE * Arguments: * bCond - boolean condition * filename - source file of the assertion * line - line of the assertion * Called by: * every ASSERT line --*/ VOID _ClxAssert( LPCTSTR filename, INT line) { TRACE((ERROR_MESSAGE, TEXT("ASSERT: %s line %d\n"), filename, line)); DebugBreak(); } /* * RCLX (Remote CLient eXecution) functions */ /*++ * Function: * RClx_SendClientInfo * Description: * Sends platform specific information to the test controller * Allows reconnection to a previous thread on the test ctrler * Win32/Win16/WinCE * Arguments: * pClx - context * Return value: * TRUE on success * Called by: * _ClxWndProc on WM_TIMER message --*/ BOOL RClx_SendClientInfo(PCLXINFO pClx) { LPCSTR szClientInfo; RCLXCLIENTINFOFEED ClntInfo; RCLXFEEDPROLOG FeedProlog; BOOL rv = FALSE; ASSERT(pClx->hSocket != INVALID_SOCKET); TRACE((ALIVE_MESSAGE, TEXT("Sending Client info\n"))); #ifdef OS_WIN16 szClientInfo = "WIN16"; #endif // OS_WIN16 #ifdef OS_WIN32 #ifndef OS_WINCE szClientInfo = "WIN32"; #else szClientInfo = "WINCE"; #endif // OS_WINCE #endif // OS_WIN32 strcpy(ClntInfo.szClientInfo, szClientInfo); if (!g_nMyReconId) { ClntInfo.nReconnectAct = 0; ClntInfo.ReconnectID = 0; } else { ClntInfo.nReconnectAct = 1; ClntInfo.ReconnectID = g_nMyReconId; } FeedProlog.FeedType = FEED_CLIENTINFO; FeedProlog.HeadSize = sizeof(ClntInfo); FeedProlog.TailSize = 0; rv = RClx_SendBuffer(pClx->hSocket, &FeedProlog, sizeof(FeedProlog)); rv = RClx_SendBuffer(pClx->hSocket, &ClntInfo, sizeof(ClntInfo)); if (rv) pClx->bClientInfoSent = TRUE; return rv; } /*++ * Function: * RClx_Connect * Description: * Connects to the TestServer. The connect call is blocking * After the connection succeeds "selects" the socket for * async read, write is blocking * Win32/Win16/WinCE * Arguments: * pClx - context * Return value: * TRUE on success * Called by: * _ClxWndProc on WM_TIMER message --*/ BOOL RClx_Connect(PCLXINFO pClx) { SOCKET hRemote; BOOL rv = FALSE; INT optval; ASSERT(pClx); if (pClx->hSocket != INVALID_SOCKET) { TRACE((WARNING_MESSAGE, TEXT("RClx_Connect called more than once\n"))); rv = TRUE; goto exitpt; } #ifdef UNICODE TRACE((INFO_MESSAGE, L"Connecting to: %S:%d\n", g_szTestServer, g_nPort)); #else // !UNICODE TRACE((INFO_MESSAGE, "Connecting to: %s:%d\n", g_szTestServer, g_nPort)); #endif // !UNICODE hRemote = socket(AF_INET, SOCK_STREAM, 0); if (hRemote == INVALID_SOCKET) { TRACE((ERROR_MESSAGE, TEXT("Can't create socket: %d\n"), WSAGetLastError())); goto exitpt; } optval = 1; setsockopt(hRemote, IPPROTO_TCP, TCP_NODELAY, (const char *)&optval, sizeof(optval)); setsockopt(hRemote, SOL_SOCKET, SO_DONTLINGER, (const char *)&optval, sizeof(optval)); if ((!pClx->sinTestSrv.sin_addr.s_addr || pClx->sinTestSrv.sin_addr.s_addr == INADDR_NONE) && (pClx->sinTestSrv.sin_addr.s_addr = inet_addr(g_szTestServer)) == INADDR_NONE) { struct hostent *phostent; if ((phostent = gethostbyname(g_szTestServer)) == NULL) { #ifdef UNICODE TRACE((ERROR_MESSAGE, L"gethostbyname for %S failed: %d\n", g_szTestServer, WSAGetLastError())); #else // !UNICODE TRACE((ERROR_MESSAGE, "gethostbyname for %s failed: %d\n", g_szTestServer, WSAGetLastError())); #endif // !UNICODE goto cleanup; } pClx->sinTestSrv.sin_addr.s_addr = *(u_long*)(phostent->h_addr); } pClx->sinTestSrv.sin_family = PF_INET; pClx->sinTestSrv.sin_port = htons(g_nPort); if (connect(hRemote, (SOCKADDR *)&(pClx->sinTestSrv), sizeof(pClx->sinTestSrv)) == SOCKET_ERROR) { #ifdef UNICODE TRACE((WARNING_MESSAGE, L"Can't connect to %S: %d\n", g_szTestServer, WSAGetLastError())); #else // !UNICODE TRACE((WARNING_MESSAGE, "Can't connect to %s: %d\n", g_szTestServer, WSAGetLastError())); #endif // !UNICODE goto cleanup; } pClx->bClientInfoSent = FALSE; pClx->hSocket = hRemote; if (WSAAsyncSelect(hRemote, g_hWindow, WM_WSOCK, FD_CLOSE|FD_READ) == SOCKET_ERROR) { TRACE((ERROR_MESSAGE, TEXT("WSAAsyncSelect failed: %d\n"), WSAGetLastError())); goto cleanup; } // If the socket is disconnected somewhere between connect and select // we'll miss the notification, so try to read // and have chance to fail PostMessage(g_hWindow, WM_WSOCK, hRemote, FD_READ); rv = TRUE; exitpt: return rv; cleanup: pClx->hSocket = hRemote; RClx_Disconnect(pClx); return rv; } /*++ * Function: * RClx_Disconnect * Description: * Gracefully closes the socket to the TestServer. Deinitializes * some vars in the context. Removes garbage windows * Win32/Win16/WinCE * Arguments: * pClx - context * Called by: * ClxTerminate * RClx_Connect on error * RClx_SendEvent on EVENT_DIACONNECT * _ClxWndProc on WM_WSOCK and error --*/ VOID RClx_Disconnect(PCLXINFO pClx) { INT recvresult; CHAR tBuf[128]; if (pClx && pClx->hSocket != INVALID_SOCKET) { WSAAsyncSelect(pClx->hSocket, g_hWindow, 0, 0); shutdown(pClx->hSocket, SD_SEND); do { recvresult = recv(pClx->hSocket, tBuf, sizeof(tBuf), 0); } while (recvresult && recvresult != SOCKET_ERROR); closesocket(pClx->hSocket); pClx->hSocket = INVALID_SOCKET; } // Zero some vars pClx->hwndInput = NULL; pClx->RClxInfo.szHydraServer[0] = 0; if (pClx->alive) { // attempt to close the client _AttemptToCloseTheClient(); g_pClx->bCloseTrys = TRUE; } else if (g_hWindow && !g_nMyReconId) // Retry to connect { TRACE((INFO_MESSAGE, TEXT("Disconnected from test server.Trying to reconnect\n"))); // If there's no timer looping // Start connecting by posting a message if (!pClx->uiReconnectTimer) pClx->uiReconnectTimer = SetTimer(g_hWindow, RCLX_RECONNECT_TIMERID, RCLX_RECONNECTELAPSETIME, NULL); if (!pClx->uiReconnectTimer) PostMessage(g_hWindow, WM_TIMER, 0, 0); } } /*++ * Function: * RClx_SendBuffer * Description: * Sends a buffer thru socket. The socket must be BLOCKING * so, all the buffer is sent after this function exits * Win32/Win16/WinCE * Arguments: * hSocket - the socket * pBuffer - the buffer * nSize - buffer size * Return value: * TRUE on success, FALSE if the connection failed * Called by: * RClx_SendEvent * RClx_SendBitmap * --*/ BOOL RClx_SendBuffer(SOCKET hSocket, PVOID pBuffer, DWORD nSize) { INT result = 0; DWORD nBytesToSend = nSize; UINT nBytesToSend2; BYTE HUGEMOD *pBuffPtr = pBuffer; ASSERT(hSocket != INVALID_SOCKET); ASSERT(pBuffer); if (!nSize) goto exitpt; do { #ifdef OS_WIN16 nBytesToSend2 = (UINT)((nBytesToSend > 0x1000)?0x1000:nBytesToSend); #else nBytesToSend2 = nBytesToSend; #endif // OS_WIN16 result = send(hSocket, pBuffPtr, nBytesToSend2, 0); if (result != SOCKET_ERROR) { nBytesToSend -= result; pBuffPtr += result; } else if (WSAGetLastError() == WSAEWOULDBLOCK) { // The socket is blocked, wait on select until it's writable FD_SET fd; FD_ZERO(&fd); FD_SET(hSocket, &fd); select(-1, NULL, &fd, NULL, NULL); } } while ((result != SOCKET_ERROR || WSAGetLastError() == WSAEWOULDBLOCK) && nBytesToSend); exitpt: return (result != SOCKET_ERROR); } /*++ * Function: * RClx_SendEvent * Description: * Sends an event to the TestServer * Win32/Win16/WinCE * Arguments: * pClx - context * Event - the event * Return value: * TRUE on success * Called by: * ClxEvent --*/ BOOL RClx_SendEvent(PCLXINFO pClx, CLXEVENT Event, DWORD dwParam) { BOOL rv = FALSE; if (Event == CLX_EVENT_DISCONNECT) { pClx->alive = FALSE; RClx_Disconnect(pClx); } if (Event == CLX_EVENT_CONNECT) { RCLXFEEDPROLOG FeedProlog; pClx->alive = TRUE; FeedProlog.FeedType = FEED_CONNECT; FeedProlog.HeadSize = 0; FeedProlog.TailSize = 0; rv = RClx_SendBuffer(pClx->hSocket, &FeedProlog, sizeof(FeedProlog)); } if (Event == CLX_EVENT_LOGON) { RCLXFEEDPROLOG FeedProlog; UINT32 uSessionID = dwParam; pClx->alive = TRUE; FeedProlog.FeedType = FEED_LOGON; FeedProlog.HeadSize = sizeof(uSessionID); FeedProlog.TailSize = 0; rv = RClx_SendBuffer(pClx->hSocket, &FeedProlog, sizeof(FeedProlog)); rv = RClx_SendBuffer(pClx->hSocket, &uSessionID, sizeof(uSessionID)); } return rv; } /*++ * Function: * RClx_SendClipbaord * Description: * Sends the current clipbaord content to the test controller * Win32/Win16/WinCE * Arguments: * pClx - context * uiFormat - desired clipboard format * nSize - size of the data * pClipboard - the clipboard data * Return value: * TRUE on success * Called by: * _ClxWndProc on WM_CLIPBOARD message --*/ BOOL RClx_SendClipboard( PCLXINFO pClx, UINT uiFormat, UINT32 nSize, VOID HUGEMOD *pClipboard) { BOOL rv = FALSE; RCLXFEEDPROLOG FeedProlog; RCLXCLIPBOARDFEED RClxClipboard; ASSERT(pClx); // if nSize == 0, then pClipboard == 0 ASSERT((nSize && pClipboard) || (!nSize && !pClipboard)); RClxClipboard.uiFormat = uiFormat; RClxClipboard.nClipBoardSize = nSize; FeedProlog.FeedType = FEED_CLIPBOARD; FeedProlog.HeadSize = sizeof(RClxClipboard); FeedProlog.TailSize = nSize; rv = RClx_SendBuffer(pClx->hSocket, &FeedProlog, sizeof(FeedProlog)); if (!rv) goto exitpt; TRACE((INFO_MESSAGE, TEXT("Sending the clipboard, FormatID=%d, Size=%ld\n"), uiFormat, nSize)); rv = RClx_SendBuffer(pClx->hSocket, &RClxClipboard, sizeof(RClxClipboard)); if (!rv) goto exitpt; if (pClipboard) rv = RClx_SendBuffer(pClx->hSocket, pClipboard, nSize); exitpt: return rv; } /*++ * Function: * RClx_SendTextOut * Description: * Sends text out order to the smclient in RCLX mode * Win32/Win16/WinCE * Arguments: * pClx - context * pText - unicode string * textLength - the string length * Return value: * TRUE on success * Called by: * ClxTextOut --*/ BOOL RClx_SendTextOut(PCLXINFO pClx, PVOID pText, INT textLength) { BOOL rv = FALSE; RCLXFEEDPROLOG FeedProlog; RCLXTEXTFEED FeedText; UINT16 *szString; ASSERT(pClx); if (!pText || !textLength) goto exitpt; FeedProlog.FeedType = FEED_TEXTOUT; FeedProlog.HeadSize = sizeof(FeedText); FeedProlog.TailSize = (textLength + 1) * sizeof(UINT16); szString = _alloca((textLength + 1) * sizeof(UINT16)); if (!szString) goto exitpt; memcpy(szString, pText, textLength * sizeof(UINT16)); szString[textLength] = 0; rv = RClx_SendBuffer(pClx->hSocket, &FeedProlog, sizeof(FeedProlog)); if (!rv) goto exitpt; rv = RClx_SendBuffer(pClx->hSocket, &FeedText, sizeof(FeedText)); if (!rv) goto exitpt; rv = RClx_SendBuffer(pClx->hSocket, szString, FeedProlog.TailSize); exitpt: return rv; } /*++ * Function: * RClx_SendBitmap * Description: * Sends a bitmap to the TestController * Win32/Win16/WinCE * Arguments: * pClx - context * cxSize - bitmap width * cySize - bitmap height * pBuffer - pointer to bitmap bits * nBmiSize- BitmapInfo length * pBmi - pointer to BITMAPINFO * Return value: * TRUE on success * Called by: * ClxBitmap --*/ BOOL RClx_SendBitmap( PCLXINFO pClx, UINT cxSize, UINT cySize, PVOID pBuffer, UINT nBmiSize, PVOID pBmi) { BOOL rv = FALSE; RCLXFEEDPROLOG FeedProlog; RCLXBITMAPFEED FeedBitmap; UINT nExtraBmi; UINT nBmpSize; BYTE *pLocalBits; ASSERT(pClx); ASSERT(pClx->hSocket != INVALID_SOCKET); if (nBmiSize > sizeof(FeedBitmap.BitmapInfo)) { TRACE((WARNING_MESSAGE, TEXT("BitmapInfo size larger than expected. Ignoring\n"))); goto exitpt; } // Get bitmap size if (!nBmiSize) { nBmpSize = (cxSize * cySize ) >> 3; } else { nBmpSize = (UINT)(((PBITMAPINFO)pBmi)->bmiHeader.biSizeImage); if (!nBmpSize) nBmpSize = (cxSize * cySize * ((PBITMAPINFO)pBmi)->bmiHeader.biBitCount) >> 3; } pLocalBits = _alloca(nBmpSize); if (!pLocalBits) goto exitpt; memcpy(pLocalBits, pBuffer, nBmpSize); if (!nBmiSize) { // this is glyph, strip it ! _StripGlyph(pLocalBits, &cxSize, cySize); nBmpSize = (cxSize * cySize ) >> 3; } // Prepare the prolog FeedProlog.FeedType = FEED_BITMAP; nExtraBmi = sizeof(FeedBitmap.BitmapInfo) - nBmiSize; FeedProlog.HeadSize = sizeof(FeedBitmap) - nExtraBmi; FeedProlog.TailSize = nBmpSize; FeedBitmap.bmpsize = nBmpSize; FeedBitmap.bmisize = nBmiSize; FeedBitmap.xSize = cxSize; FeedBitmap.ySize = cySize; memcpy(&FeedBitmap.BitmapInfo, pBmi, sizeof(FeedBitmap.BitmapInfo) - nExtraBmi); if (!RClx_SendBuffer(pClx->hSocket, &FeedProlog, sizeof(FeedProlog))) { TRACE((ERROR_MESSAGE, TEXT("FEED_BITMAP:Can't send the prolog. WSAGetLastError=%d\n"), WSAGetLastError())); goto exitpt; } if (!RClx_SendBuffer(pClx->hSocket, &FeedBitmap, FeedProlog.HeadSize)) { TRACE((ERROR_MESSAGE, TEXT("FEED_BITMAP:Can't send the header.\n"))); goto exitpt; } if (!RClx_SendBuffer(pClx->hSocket, pLocalBits, FeedProlog.TailSize)) { TRACE((ERROR_MESSAGE, TEXT("FEED_BITMAP:Can't send the bits.\n"))); goto exitpt; } rv = TRUE; exitpt: return rv; } /*++ * Function: * _ClxWndProc * Description: * Dispatche messages procedure. Dispatches WM_TIMER and * WM_WSOCK - socket message. WM_TIMER drives _OnBackground * and connection retrys * Win32/Win16/WinCE * Arguments: * hwnd - window handle, same as g_hWindow * uiMessage - message Id * wParam - word param * lParam - long param * Return value: * LRESULT - standard for window procs --*/ LRESULT CALLBACK LOADDS _ClxWndProc( HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam) { SOCKET hSocket; PCLXINFO pClx = g_pClx; switch (uiMessage) { case WM_WSOCK: if (!pClx) { TRACE((WARNING_MESSAGE, TEXT("Winsock message before context initialization\n"))); goto exitpt; } hSocket = (SOCKET)wParam; if (hSocket != pClx->hSocket) { TRACE((WARNING_MESSAGE, TEXT("Notification for unknown socket\n"))); goto exitpt; } if (WSAGETSELECTERROR(lParam)) { TRACE((WARNING_MESSAGE, TEXT("Winsock error: %d\n"), WSAGETSELECTERROR(lParam))); RClx_Disconnect(pClx); goto exitpt; } if (WSAGETSELECTEVENT(lParam) == FD_CLOSE) { TRACE((INFO_MESSAGE, TEXT("Connection to the test server lost\n"))); RClx_Disconnect(pClx); } else if (WSAGETSELECTEVENT(lParam) == FD_READ) { if (!RClx_ReadRequest(pClx) && WSAGetLastError() != WSAEWOULDBLOCK) { TRACE((WARNING_MESSAGE, TEXT("Socket read error: %d\n"), WSAGetLastError())); RClx_Disconnect(pClx); } } else { TRACE((WARNING_MESSAGE, TEXT("Unexpected winsock notification #%d\n"), WSAGETSELECTEVENT(lParam))); goto exitpt; } break; case WM_TIMER: // Start connection or background process { BOOL bRunRdp = FALSE; HWND hConBtn, hServerBox; #ifndef OS_WINCE // no resolution box in CE HWND hResolutionBox; #endif CHAR szResSelect[20]; if (!pClx) { TRACE((WARNING_MESSAGE, TEXT("Timer message before context initialization\n"))); goto exitpt; } if (g_uiBackgroundTimer == (UINT_PTR)wParam) // This is our background thread { _OnBackground(pClx); goto exitpt; } if (!g_nMyReconId && !pClx->hwndDialog) { pClx->hwndDialog = _FindTopWindow(NULL, g_strClientCaption, g_hRDPInst); goto check_timer; } if (!g_nMyReconId && pClx->hwndDialog && !GetDlgItem(pClx->hwndDialog, UI_IDC_CONNECT)) { TRACE((INFO_MESSAGE, TEXT("No dialog box yet. Waiting...\n"))); goto check_timer; } if (pClx->alive && pClx->hSocket == INVALID_SOCKET) { TRACE((INFO_MESSAGE, TEXT("Client is alive, no socket to the test server\n"))); //_AttemptToCloseTheClient(); goto exitpt; } // Check if we are connected if (pClx->hSocket == INVALID_SOCKET && (!RClx_Connect(pClx))) goto check_timer; // We are connected, send the client info if (!pClx->bClientInfoSent) RClx_SendClientInfo(pClx); // if we are reconnecting we don't have UI to set params if (g_nMyReconId) break; // Check if connect dialog is OK if (!pClx->hwndDialog) goto check_timer; if (!strlen(pClx->RClxInfo.szHydraServer)) { TRACE((INFO_MESSAGE, TEXT("Connect info is not received yet\n"))); goto check_timer; } // set something in server listbox, to enable connect button { HWND hwndCombo = GetDlgItem(pClx->hwndDialog, UI_IDC_SERVER); SetDlgItemText(pClx->hwndDialog, UI_IDC_SERVER, TEXT("vladimis")); #ifdef OS_WIN32 PostMessage(pClx->hwndDialog, WM_COMMAND, MAKEWPARAM(UI_IDC_SERVER, CBN_EDITCHANGE), (LPARAM)hwndCombo); #endif #ifdef OS_WIN16 PostMessage(pClx->hwndDialog, WM_COMMAND, UI_IDC_SERVER, MAKELONG(hwndCombo, CBN_EDITCHANGE)); #endif } // Check the connect button state hConBtn = GetDlgItem(pClx->hwndDialog, UI_IDC_CONNECT); if (!hConBtn) { TRACE((WARNING_MESSAGE, TEXT("Can't get Connect button\n"))); goto check_timer; } if (!IsWindowEnabled(hConBtn)) { TRACE((INFO_MESSAGE, TEXT("Connect button is not enabled yet\n"))); goto check_timer; } if ( // Check for valid controls (hServerBox = GetDlgItem(pClx->hwndDialog, UI_IDC_SERVER)) #ifndef OS_WINCE // no resolution box in WinCE && (hResolutionBox = GetDlgItem(pClx->hwndDialog, UI_IDC_RESOLUTION)) #endif // !OS_WINCE ) { TRACE((INFO_MESSAGE, TEXT("The client is ready to launch.\n"))); } else goto check_timer; bRunRdp = TRUE; check_timer: if (!bRunRdp) { TRACE((INFO_MESSAGE, TEXT("Can't start the client yet. Waiting\n"))); if (!pClx->uiReconnectTimer) pClx->uiReconnectTimer = SetTimer(hwnd, RCLX_RECONNECT_TIMERID, RCLX_RECONNECTELAPSETIME, NULL); if (!pClx->uiReconnectTimer) { TRACE((WARNING_MESSAGE, TEXT("Can't create timer. Start timer simulation\n"))); PostMessage(hwnd, WM_TIMER, 0, 0); } goto exitpt; } else { if (pClx->uiReconnectTimer) { KillTimer(g_hWindow, pClx->uiReconnectTimer); pClx->uiReconnectTimer = 0; } } // Check that we have clear view for launch // no error boxes laying arround if (_GarbageCollecting(pClx, TRUE)) goto exitpt; #ifdef UNICODE TRACE((INFO_MESSAGE, TEXT("Trying to connect to Hydra server: %S\n"), pClx->RClxInfo.szHydraServer)); #else TRACE((INFO_MESSAGE, TEXT("Trying to connect to Hydra server: %s\n"), pClx->RClxInfo.szHydraServer)); #endif // UNICODE // Set server name #ifdef UNICODE _CLX_SetDlgItemTextA #else // !UNICODE SetDlgItemText #endif // !UNICODE (pClx->hwndDialog, UI_IDC_SERVER, pClx->RClxInfo.szHydraServer); // Set the resolution _snprintf(szResSelect, sizeof(szResSelect), "%dx%d", pClx->RClxInfo.xResolution, pClx->RClxInfo.yResolution); #ifndef OS_WINCE // no resolution box SendMessage(hResolutionBox, CB_SELECTSTRING, 0, (LPARAM)szResSelect); #endif // !OS_WINCE // set the low speed option CheckDlgButton(pClx->hwndDialog, UI_IDC_CONNECTION, (pClx->RClxInfo.bLowSpeed)?BST_CHECKED:BST_UNCHECKED ); // set the persistent cache option CheckDlgButton(pClx->hwndDialog, UI_IDC_BITMAP_PERSISTENCE, (pClx->RClxInfo.bPersistentCache)?BST_CHECKED :BST_UNCHECKED ); // Now connect PostMessage(pClx->hwndDialog, WM_COMMAND, UI_IDC_CONNECT, 0); } break; case WM_CLIPBOARD: { HGLOBAL ghClipboard = NULL; HGLOBAL ghNewData = NULL; BOOL bOpened = FALSE; BOOL bRetry = TRUE; VOID HUGEMOD *pClipboard = NULL; UINT32 nSize = 0; UINT uiFormat = 0; BOOL bReentered; #ifndef OS_WIN32 pClx->bClipboardReenter++; bReentered = pClx->bClipboardReenter != 0; #else bReentered = InterlockedIncrement(&pClx->bClipboardReenter) != 0; #endif // OS_WIN32 if (bReentered) { TRACE((WARNING_MESSAGE, TEXT("WM_CLIPBOARD reentered\n"))); bRetry = FALSE; goto clpcleanup; } // lParam contains the desired format uiFormat = (UINT)lParam; // WinCE version doesn't support clipboard #ifndef OS_WINCE if (!OpenClipboard(hwnd)) { TRACE((ERROR_MESSAGE, TEXT("Can't open the clipboard. retrying\n"))); goto clpcleanup; } bOpened = TRUE; // if uiFormat is zero then return empty clipbrd body // with the first available format if (!uiFormat) { uiFormat = EnumClipboardFormats(uiFormat); TRACE((INFO_MESSAGE, TEXT("Responging on format request. FormatID=%d\n"), uiFormat)); goto send_clipboard; } ghClipboard = GetClipboardData((UINT)uiFormat); if (!ghClipboard) { TRACE((WARNING_MESSAGE, TEXT("Clipboard is empty.\n"))); } else { Clp_GetClipboardData(uiFormat, ghClipboard, &nSize, &ghNewData); if (ghNewData) ghClipboard = ghNewData; pClipboard = GlobalLock(ghClipboard); if (!pClipboard) { TRACE((ERROR_MESSAGE, TEXT("Can't lock the clipboard. retrying\n"))); goto clpcleanup; } } send_clipboard: #else // !OS_WINCE TRACE((WARNING_MESSAGE, TEXT("WinCE: clipboard not supported\n"))); #endif // !OS_WINCE if (!RClx_SendClipboard(pClx, uiFormat, nSize, pClipboard)) { TRACE((ERROR_MESSAGE, TEXT("Can't send the clipboard\n"))); bRetry = FALSE; goto clpcleanup; } bRetry = FALSE; clpcleanup: #ifndef OS_WINCE if (pClipboard) GlobalUnlock(ghClipboard); if (bOpened) CloseClipboard(); if (ghNewData) GlobalFree(ghNewData); #endif // !OS_WINCE #ifndef OS_WIN32 pClx->bClipboardReenter--; #else InterlockedDecrement(&pClx->bClipboardReenter); #endif // OS_WIN32 if (bRetry) // Retry to send the clipboard PostMessage(hwnd, WM_CLIPBOARD, 0, lParam); } break; case WM_CLOSE: if (!DestroyWindow(hwnd)) { TRACE((ERROR_MESSAGE, TEXT("Can't destroy window. GetLastError: %d\n"), _CLXWINDOW_CLASS, GetLastError())); } break; default: return DefWindowProc(hwnd, uiMessage, wParam, lParam); } exitpt: return 0; } /*++ * Function: * RClx_CreateWindow * Description: * Creates g_hWindow for dispatching winsocket and timer * messages * Win32/Win16/WinCE * Arguments: * hInstance - Dll instance handle * Called by: * ClxInitialize --*/ VOID RClx_CreateWindow(HINSTANCE hInstance) { WNDCLASS wc; #ifdef OS_WIN32 DWORD dwLastErr; #endif memset(&wc, 0, sizeof(wc)); wc.lpfnWndProc = _ClxWndProc; wc.hInstance = hInstance; wc.lpszClassName = TEXT(_CLXWINDOW_CLASS); if (!RegisterClass (&wc) #ifdef OS_WIN32 && (dwLastErr = GetLastError()) && dwLastErr != ERROR_CLASS_ALREADY_EXISTS #endif // OS_WIN32 ) { TRACE((WARNING_MESSAGE, TEXT("Can't register class. GetLastError=%d\n"), GetLastError())); goto exitpt; } g_hWindow = CreateWindow( TEXT(_CLXWINDOW_CLASS), NULL, // Window name 0, // dwStyle 0, // x 0, // y 0, // nWidth 0, // nHeight NULL, // hWndParent NULL, // hMenu hInstance, NULL); // lpParam if (!g_hWindow) { TRACE((WARNING_MESSAGE, TEXT("Can't create window to handle socket messages\n"))); goto exitpt; } g_uiBackgroundTimer = SetTimer(g_hWindow, RCLX_BACKGNDTIMERID, RCLX_TIMERELAPSETIME, NULL); exitpt: ; } /*++ * Function: * RClx_DestroyWindow * Description: * Destroys g_hWindow created in RClx_CreateWindow * Win32/Win16/WinCE * Called by: * ClxTerminate --*/ VOID RClx_DestroyWindow(VOID) { if (g_hWindow) { if (g_uiBackgroundTimer) { KillTimer(g_hWindow, g_uiBackgroundTimer); g_uiBackgroundTimer = 0; } PostMessage(g_hWindow, WM_CLOSE, 0, 0); if (!UnregisterClass(TEXT(_CLXWINDOW_CLASS), g_hInstance)) { TRACE((WARNING_MESSAGE, TEXT("Can't unregister class: %s. GetLastError: %d\n"), _CLXWINDOW_CLASS, GetLastError())); } g_hWindow = NULL; } } /*++ * Function: * RClx_ReadRequest * Description: * CLXINFO contains a buffer for incoming requests * this function trys to receive it all. If the socket blocks * the function exits with OK and next time FD_READ is received will * be called again. If a whole request is received it calls * RClx_ProcessRequest * Win32/Win16/WinCE * Arguments: * pClx - the context * Return value: * TRUE if reading doesn't fail * Called by: * _ClxWndProc on FD_READ event --*/ BOOL RClx_ReadRequest(PCLXINFO pClx) { INT recvres; INT nErrorCode = 0; BYTE HUGEMOD *pRecv = NULL; UINT32 nRecv = 0; BYTE TempBuff[256]; ASSERT(pClx); ASSERT(pClx->hSocket != INVALID_SOCKET); do { if (!pClx->bPrologReceived) { pRecv = (BYTE HUGEMOD *)&(pClx->RClxReqProlog) + pClx->nBytesReceived; nRecv = sizeof(pClx->RClxReqProlog) - pClx->nBytesReceived; #ifndef OS_WINCE recvres = recv(pClx->hSocket, pRecv, (int)nRecv, 0); nErrorCode = WSAGetLastError(); #else // OS_WINCE recvres = AsyncRecv(pClx->hSocket, pRecv, nRecv, &nErrorCode); #endif // OS_WINCE if (recvres != SOCKET_ERROR) pClx->nBytesReceived += recvres; else if (nErrorCode != WSAEWOULDBLOCK) TRACE((ERROR_MESSAGE, TEXT("recv error: %d, request for 0x%lx bytes\n"), nErrorCode, nRecv )); if (pClx->nBytesReceived == sizeof(pClx->RClxReqProlog)) { UINT32 nReqSize = pClx->RClxReqProlog.ReqSize; pClx->nBytesReceived = 0; pClx->bPrologReceived = TRUE; if (nReqSize) // If request body is empty, don't allocate { if (!pClx->pRequest) retry_alloc: { pClx->pRequest = _CLXALLOC(nReqSize); if (pClx->pRequest) pClx->nReqAllocSize = nReqSize; else pClx->nReqAllocSize = 0; } else if (nReqSize > pClx->nReqAllocSize) { pClx->pRequest = _CLXREALLOC(pClx->pRequest, nReqSize); if (pClx->pRequest) pClx->nReqAllocSize = nReqSize; else { pClx->nReqAllocSize = 0; goto retry_alloc; } } if (!pClx->pRequest) { TRACE((WARNING_MESSAGE, TEXT("Can't alloc 0x%lx bytes for receiving a request. GetLastError=%d. Skipping\n"), nReqSize, GetLastError())); } } } } else { if (pClx->nBytesReceived == pClx->RClxReqProlog.ReqSize) goto process_req; if (pClx->pRequest) { pRecv = (BYTE HUGEMOD *)pClx->pRequest + pClx->nBytesReceived; nRecv = pClx->RClxReqProlog.ReqSize - pClx->nBytesReceived; } else { // point to a temp buffer if pReques is not allocated pRecv = TempBuff; nRecv = sizeof(TempBuff); } #ifdef OS_WIN16 // WFW has problems with receiving big buffers if (nRecv >= 0x1000) nRecv = 0x1000; #endif // OS_WIN16 #ifndef OS_WINCE recvres = recv(pClx->hSocket, pRecv, (int)nRecv, 0); nErrorCode = WSAGetLastError(); #else // OS_WINCE recvres = AsyncRecv(pClx->hSocket, pRecv, nRecv, &nErrorCode); #endif // OS_WINCE if (recvres != SOCKET_ERROR) { pClx->nBytesReceived += recvres; } else { if (nErrorCode != WSAEWOULDBLOCK) { TRACE((ERROR_MESSAGE, TEXT("recv error: %d, request for 0x%lx bytes\n"), nErrorCode, nRecv )); TRACE((ERROR_MESSAGE, TEXT("ReqProlog was received. Type=%d, Size=%d\n"), pClx->RClxReqProlog.ReqType, pClx->RClxReqProlog.ReqSize )); } } process_req: if (pClx->nBytesReceived == pClx->RClxReqProlog.ReqSize) { pClx->nBytesReceived = 0; pClx->bPrologReceived = FALSE; RClx_ProcessRequest(pClx); } } } while ((recvres != 0 || nRecv == 0) && // recvres will be 0 if nRecv is 0 recvres != SOCKET_ERROR); // return FALSE if error is occured if (!recvres) return FALSE; // connection was gracefully closed if (recvres == SOCKET_ERROR) { if (nErrorCode == WSAEWOULDBLOCK) return TRUE; // the call will block, but ok else return FALSE; // other SOCKET_ERROR } return TRUE; // it is ok } /*++ * Function: * RClx_DataArrived * Description: * Processes request for data * Win32/Win16/WinCE * Arguments: * pClx - context * pRClxData - the request * Called by: * RClx_ProcessRequest --*/ VOID RClx_DataArrived(PCLXINFO pClx, PRCLXDATA pRClxData) { ASSERT(pRClxData); switch(pRClxData->uiType) { case DATA_BITMAP: { PREQBITMAP pReqBitmap = (PREQBITMAP)pRClxData->Data; RCLXDATA Response; RCLXFEEDPROLOG RespProlog; HANDLE hDIB = NULL; DWORD dwDIBSize = 0; TRACE((INFO_MESSAGE, TEXT("REQDATA_BITMAP arrived\n"))); if (pRClxData->uiSize != sizeof(*pReqBitmap)) ASSERT(0); // process differently on WINCE (no shadow bitmap) // if (pClx->hdcShadowBitmap) _GetDIBFromBitmap(pClx->hdcShadowBitmap, pClx->hShadowBitmap, &hDIB, (INT)pReqBitmap->left, (INT)pReqBitmap->top, (INT)pReqBitmap->right, (INT)pReqBitmap->bottom); else { TRACE((WARNING_MESSAGE, TEXT("Shadow bitmap is NULL\n"))); } if (!hDIB) dwDIBSize = 0; else dwDIBSize = (DWORD)GlobalSize(hDIB); RespProlog.FeedType = FEED_DATA; RespProlog.HeadSize = sizeof(Response) + dwDIBSize; RespProlog.TailSize = 0; Response.uiType = DATA_BITMAP; Response.uiSize = dwDIBSize; RClx_SendBuffer(pClx->hSocket, &RespProlog, sizeof(RespProlog)); RClx_SendBuffer(pClx->hSocket, &Response, sizeof(Response)); if (hDIB) { LPVOID pDIB = GlobalLock(hDIB); if (pDIB) { RClx_SendBuffer(pClx->hSocket, pDIB, dwDIBSize); GlobalUnlock(hDIB); } GlobalFree(hDIB); } } break; case DATA_VC: { LPSTR szChannelName; LPVOID pData; DWORD dwSize; szChannelName = (LPSTR)(pRClxData->Data); pData = ((BYTE *)(pRClxData->Data)) + MAX_VCNAME_LEN; dwSize = pRClxData->uiSize - MAX_VCNAME_LEN; if (pRClxData->uiSize < MAX_VCNAME_LEN) { TRACE((ERROR_MESSAGE, TEXT("DATA_VC: uiSize too small\n"))); goto exitpt; } if (strlen(szChannelName) > MAX_VCNAME_LEN - 1) { TRACE((ERROR_MESSAGE, TEXT("DATA_VC: channel name too long\n"))); goto exitpt; } _CLXSendDataVC(szChannelName, pData, dwSize); } break; default: TRACE((WARNING_MESSAGE, TEXT("Unknown data request type. Ignoring\n"))); } exitpt: ; } /*++ * Function: * RClx_ProcessRequest * Description: * Dispatches a request received by RClx_ReadRequest * Win32/Win16/WinCE * Arguments: * pClx - context * Called by: * RClx_ReadRequest --*/ VOID RClx_ProcessRequest(PCLXINFO pClx) { PRCLXMSG pClxMsg; DWORD ReqType; HWND hContainer; PRCLXREQPROLOG pReqProlog; LPVOID pReq; ASSERT(pClx); pReqProlog = &(pClx->RClxReqProlog); ASSERT(pReqProlog); pReq = pClx->pRequest; ReqType = pReqProlog->ReqType; switch(ReqType) { case REQ_MESSAGE: if (!pReq || pReqProlog->ReqSize != sizeof(*pClxMsg)) { TRACE((WARNING_MESSAGE, TEXT("REQ_MESSAGE with different size. Ignoring\n"))); goto exitpt; } pClxMsg = (PRCLXMSG)pReq; if(!pClx->hwndInput) { hContainer = _FindWindow(pClx->hwndMain, TEXT(NAME_CONTAINERCLASS)); if (hContainer) pClx->hwndInput = _FindWindow(hContainer, TEXT(NAME_INPUT)); else pClx->hwndInput = NULL; if (!pClx->hwndInput) { TRACE((WARNING_MESSAGE, TEXT("Can't find input window.") TEXT(" Discarding any user input\n" ))); goto exitpt; } } SendMessage(pClx->hwndInput, (UINT)pClxMsg->message, (WPARAM)pClxMsg->wParam, (LPARAM)pClxMsg->lParam); break; case REQ_CONNECTINFO: if (!pReq || pReqProlog->ReqSize != sizeof(RCLXCONNECTINFO)) { TRACE((WARNING_MESSAGE, TEXT("REQ_CONNECTINFO with different size. Ignoring\n"))); goto exitpt; } TRACE((INFO_MESSAGE, TEXT("CONNECTINFO received\n"))); memcpy(&pClx->RClxInfo, pReq, sizeof(pClx->RClxInfo)); // Kick _ClxWndProc to connect PostMessage(g_hWindow, WM_TIMER, 0, 0); break; case REQ_GETCLIPBOARD: if (!pReq || pReqProlog->ReqSize != sizeof(RCLXCLIPBOARD)) { TRACE((WARNING_MESSAGE, TEXT("REQ_GETCLIPBOARD with different size. Ignoring\n"))); goto exitpt; } TRACE((INFO_MESSAGE, TEXT("REQ_GETCLIPBOARD received. FormatId=%d\n"), ((PRCLXCLIPBOARD)pReq)->uiFormat )); // Kick _ClxWndProc to send our clipboard PostMessage(g_hWindow, WM_CLIPBOARD, 0, (LPARAM)((PRCLXCLIPBOARD)pReq)->uiFormat); // lParam contains the required format break; case REQ_SETCLIPBOARD: { UINT32 nClipSize; if (!pReq || pReqProlog->ReqSize < sizeof(RCLXCLIPBOARD)) { TRACE((WARNING_MESSAGE, TEXT("REQ_SETCLIPBOARD with wrong size. Ignoring\n"))); goto exitpt; } TRACE((INFO_MESSAGE, TEXT("REQ_SETCLIPBOARD received. FormatId=%d, Clipboard size = %d\n"), ((PRCLXCLIPBOARD)pReq)->uiFormat, pReqProlog->ReqSize - sizeof(UINT) )); nClipSize = pReqProlog->ReqSize - sizeof(((PRCLXCLIPBOARD)pReq)->uiFormat); _SetClipboard( (UINT)((PRCLXCLIPBOARD)pReq)->uiFormat, ((PRCLXCLIPBOARD)pReq)->pNewClipboard, nClipSize); } break; case REQ_DATA: { PRCLXDATA pRClxData = (PRCLXDATA)pReq; ASSERT(pRClxData); if (pReqProlog->ReqSize != pRClxData->uiSize + (UINT32)sizeof(*pRClxData)) ASSERT(0); RClx_DataArrived(pClx, pRClxData); } break; default: TRACE((WARNING_MESSAGE, TEXT("Unknown request type. Ignoring\n"))); } exitpt: ; } /*++ * Function: * _EnumWindowsProc * Description: * Used to find a specific window * Win32/Win16/WinCE * 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 LOADDS _EnumWindowsProc( HWND hWnd, LPARAM lParam ) { TCHAR classname[128]; TCHAR caption[128]; BOOL rv = TRUE; _CLXWINDOWOWNER hInst; PSEARCHWND pSearch = (PSEARCHWND)lParam; if (pSearch->szClassName && !GetClassName(hWnd, classname, sizeof(classname)/sizeof(TCHAR))) { goto exitpt; } if (pSearch->szCaption && !GetWindowText(hWnd, caption, sizeof(caption)/sizeof(TCHAR))) { goto exitpt; } #ifdef OS_WINCE { DWORD procId = 0; GetWindowThreadProcessId(hWnd, &procId); hInst = procId; } #else // !OS_WINCE #ifdef _WIN64 hInst = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE); #else // !_WIN64 #ifdef OS_WIN32 hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); #endif // OS_WIN32 #endif // !OS_WINCE #ifdef OS_WIN16 hInst = (HINSTANCE)GetWindowWord(hWnd, GWW_HINSTANCE); #endif #endif // _WIN64 if ( (!pSearch->szClassName || ! // Check for classname _CLX_strcmp(classname, pSearch->szClassName)) && (!pSearch->szCaption || ! _CLX_strcmp(caption, pSearch->szCaption)) && hInst == pSearch->hInstance) { ((PSEARCHWND)lParam)->hWnd = hWnd; rv = FALSE; } exitpt: return rv; } /*++ * Function: * _FindTopWindow * Description: * Find specific window by classname and/or caption and/or process Id * Win32/Win16/WinCE * Arguments: * classname - class name to search for, NULL ignore * caption - caption to search for, NULL ignore * hInst - instance handle, NULL ignore * Return value: * window handle found, NULL otherwise * Called by: * SCConnect, SCDisconnect, GetDisconnectResult --*/ HWND _FindTopWindow(LPCTSTR classname, LPCTSTR caption, _CLXWINDOWOWNER hInst) { SEARCHWND search; search.szClassName = classname; search.szCaption = caption; search.hWnd = NULL; search.hInstance = hInst; EnumWindows(_EnumWindowsProc, (LPARAM)&search); return search.hWnd; } /*++ * Function: * _FindWindow * Description: * Find child window by classname * Win32/Win16/WinCE * Arguments: * hwndParent - the parent window handle * srchclass - class name to search for, NULL - ignore * Return value: * window handle found, NULL otherwise * Called by: * --*/ HWND _FindWindow(HWND hwndParent, LPCTSTR srchclass) { HWND hWnd, hwndTop, hwndNext; BOOL bFound; TCHAR classname[128]; hWnd = NULL; hwndTop = GetWindow(hwndParent, GW_CHILD); if (!hwndTop) { TRACE((INFO_MESSAGE, TEXT("GetWindow failed. hwnd=0x%x\n"), hwndParent)); goto exiterr; } bFound = FALSE; hwndNext = hwndTop; do { hWnd = hwndNext; if (srchclass && !GetClassName(hWnd, classname, sizeof(classname)/sizeof(TCHAR))) { TRACE((INFO_MESSAGE, TEXT("GetClassName failed. hwnd=0x%x\n"))); goto nextwindow; } if (!srchclass || !_CLX_strcmp(classname, srchclass)) bFound = TRUE; nextwindow: #ifndef OS_WINCE hwndNext = GetNextWindow(hWnd, GW_HWNDNEXT); #else // OS_WINCE hwndNext = GetWindow(hWnd, GW_HWNDNEXT); #endif // OS_WINCE } while (hWnd && hwndNext != hwndTop && !bFound); if (!bFound) goto exiterr; return hWnd; exiterr: return NULL; } /*++ * Function: * _OnBackground * Description: * Simulates a background thread. Called when a message from the * background timer is received * Win32/Win16/WinCE * Arguments: * pClx - the context * Called by: * _ClxWndProc on WM_TIMER message from g_uiBackgroundTimer is received --*/ VOID _OnBackground(PCLXINFO pClx) { // Try to limit this call, eats lot of the CPU _GarbageCollecting(pClx, TRUE); } /* * GarbageCollecting - closes all message boxes asking this and that * like "shall i close the connection" or "there's some error" * This function is forced by g_uiBackgroundTimer message */ /*++ * Function: * _GarbageCollecting * Description: * Closes all message boxes asking this and that * like "shall i close the connection" or "there's some error" * This function is forced by g_uiBackgroundTimer message * Win32/Win16/WinCE * Arguments: * pClx - the context * bNotifyForErrorBox - if TRUE calls ClxEvent with * "disconnected" event * Return value: * TRUE if error boxes are found * Called by: * --*/ BOOL _GarbageCollecting(PCLXINFO pClx, BOOL bNotifyForErrorBox) { HWND hBox; BOOL rv = FALSE; // Clean all extra message boxes, like saying that // we cannot connect because of this and that if (!g_hRDPInst) goto exitpt; hBox = _FindTopWindow(NULL, g_strDisconnectDialogBox, g_hRDPInst); if (hBox) { rv = TRUE; PostMessage(hBox, WM_CLOSE, 0, 0); if (bNotifyForErrorBox) // Notifiy that we are disconnected ClxEvent(pClx, CLX_EVENT_DISCONNECT, 0); } hBox = _FindTopWindow(NULL, TEXT(FATAL_ERROR_5), g_hRDPInst); if (hBox) { rv = TRUE; PostMessage(hBox, WM_CLOSE, 0, 0); if (bNotifyForErrorBox) // Notifiy that we are disconnected ClxEvent(pClx, CLX_EVENT_DISCONNECT, 0); } if (pClx->bCloseTrys) { _AttemptToCloseTheClient(); } exitpt: if (rv) TRACE((INFO_MESSAGE, "Error boxes found\n")); return rv; } /*++ * Function: * _SetClipboard * Description: * Sets the clipboard content in RCLX mode * Win32/Win16/WinCE * Arguments: * uiFormat - clipboard format * pClipboard - new clipboard content * nSize - the clipboard size * Called by: * RClx_ProcessRequest on REQ_SETCLIPBOARD --*/ VOID _SetClipboard(UINT uiFormat, PVOID pClipboard, UINT32 nSize) { HGLOBAL ghNewClipboard = NULL; BOOL bOpened = FALSE; BOOL bFreeClipHandle = TRUE; LPVOID pNewClipboard = NULL; // WinCE - no clipboard #ifndef OS_WINCE if (!nSize) { // Just empty the clipboard if (OpenClipboard(NULL)) { bOpened = TRUE; EmptyClipboard(); } else { TRACE((ERROR_MESSAGE, TEXT("Can't lock the clipbord. GetLastError=%d\n"), GetLastError())); } goto exitpt; } if (!pClipboard) { TRACE((ERROR_MESSAGE, TEXT("_SetClipboard: pClipboard is NULL\n"))); goto exitpt; } ghNewClipboard = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, nSize); if (!ghNewClipboard) { TRACE((ERROR_MESSAGE, TEXT("Can't alloc(GlobalAlloc) %d bytes\n"), nSize)); goto exitpt; } pNewClipboard = GlobalLock(ghNewClipboard); if (!pNewClipboard) { TRACE((ERROR_MESSAGE, TEXT("Can't lock the clipbord. GetLastError=%d\n"), GetLastError())); goto exitpt; } // Copy the data HUGEMEMCPY(pNewClipboard, pClipboard, nSize); if (!OpenClipboard(NULL)) { TRACE((ERROR_MESSAGE, TEXT("Can't open the clipboard. GetLastError=%d\n"), GetLastError())); goto exitpt; } bOpened = TRUE; // Empty the clipboard, so we'll have only one entry EmptyClipboard(); GlobalUnlock(ghNewClipboard); pNewClipboard = NULL; if (!Clp_SetClipboardData(uiFormat, ghNewClipboard, nSize, &bFreeClipHandle)) TRACE((ERROR_MESSAGE, TEXT("SetClipboardData failed. GetLastError=%d\n"), GetLastError())); else TRACE((INFO_MESSAGE, TEXT("Clipboard is loaded successfuly. %ld bytes\n"), nSize)); exitpt: if (pNewClipboard) GlobalUnlock(ghNewClipboard); if (bOpened) CloseClipboard(); // Do not free already set clipboard if (ghNewClipboard && bFreeClipHandle) GlobalFree(ghNewClipboard); #else // !OS_WINCE TRACE((WARNING_MESSAGE, TEXT("WinCE: clipboard not supported\n"))); #endif // !OS_WINCE } #ifdef OS_WINCE SOCKET g_hSocket = 0; LONG g_lEvent = 0; HWND g_hNotifyWindow = NULL; UINT g_uiMessage = 0; HANDLE g_hAsyncThread = NULL; BYTE g_pRecvBuffer[1024]; INT g_nRecvStart, g_nRecvLength; BOOL g_bGoAsync = FALSE; CRITICAL_SECTION g_AsyncCS; /*++ * Function: * WSAAsyncSelect * Description: * Windows CE doesn't have this function. Here we are using extra thread * for implementation of this mechanism * WinCE only * Arguments: * s - socket handle * hWnd - notification window * wMsg - message to send when event occured * lEvent - event mask, on what event this will work * Return value: * On error returns SOCKET_ERROR * Called by: * RClx_Connect, RClx_Disconnect --*/ INT WSAAsyncSelect (SOCKET s, HWND hWnd, UINT uiMsg, LONG lEvent) { INT rv = SOCKET_ERROR; if (!g_hAsyncThread) { TRACE((ERROR_MESSAGE, TEXT("WSAAsyncSelect: no AsyncThread\n"))); goto exitpt; } if (s == INVALID_SOCKET) { TRACE((ERROR_MESSAGE, TEXT("WSAAsyncSelect: INVALID_SOCKET passed\n"))); goto exitpt; } if ((lEvent & FD_WRITE) || (lEvent & FD_CONNECT)) { TRACE((ERROR_MESSAGE, TEXT("WSAAsyncSelec: FD_WRITE & FD_CONNECT not supported\n"))); goto exitpt; } EnterCriticalSection(&g_AsyncCS); g_hSocket = s; g_lEvent = lEvent; g_hNotifyWindow = hWnd; g_uiMessage = uiMsg; LeaveCriticalSection(&g_AsyncCS); ResumeThread(g_hAsyncThread); rv = 0; exitpt: return rv; } /*++ * Function: * AsyncRecv * Description: * Used to receive w/o blocking. WSAAsync Select must be called * with FD_READ flag * WinCE only * Arguments: * s - socket handle * pBuffer - buffer for received bytes * nBytesToRead - how many bytes to receive * pnErrorCode - pointer to error code * Return value: * On error returns SOCKET_ERROR * Called by: * RClx_ReadRequest --*/ INT AsyncRecv(SOCKET s, PVOID pBuffer, INT nBytesToRead, INT *pnErrorCode) { INT rv = SOCKET_ERROR; if (!g_hAsyncThread) { TRACE((ERROR_MESSAGE, TEXT("WSAAsyncSelect: no AsyncThread\n"))); goto exitpt; } ASSERT(s == g_hSocket); (*pnErrorCode) = 0; EnterCriticalSection(&g_AsyncCS); if (!g_nRecvLength) { (*pnErrorCode) = WSAEWOULDBLOCK; } else { INT nToCopy; nToCopy = (nBytesToRead < g_nRecvLength)?nBytesToRead:g_nRecvLength; memcpy(pBuffer, g_pRecvBuffer + g_nRecvStart, nToCopy); rv = nToCopy; g_nRecvLength -= nToCopy; g_nRecvStart += nToCopy; } // Resume the thread if (!g_nRecvLength) ResumeThread(g_hAsyncThread); LeaveCriticalSection(&g_AsyncCS); exitpt: return rv; } /*++ * Function: * _SelectWorker * Description: * Simulates WSAAsyncSelect message notification * Lifetime: between ClxInitialize and ClxTerminate * WinCE only * Arguments: * lParam - unused parameter * Return value: * allways 0 * Called by: * _StartAsyncThread as a thread function --*/ UINT _SelectWorker(LPVOID lpParam) { SOCKET hSocket; LONG lEvent; FD_SET fdRead; HWND hwndNotify; UINT uiMsg; INT Status; while(g_bGoAsync) { FD_ZERO(&fdRead); EnterCriticalSection(&g_AsyncCS); hSocket = g_hSocket; lEvent = g_lEvent; hwndNotify = g_hNotifyWindow; uiMsg = g_uiMessage; LeaveCriticalSection(&g_AsyncCS); if (hSocket == INVALID_SOCKET || !lEvent || !hwndNotify || !uiMsg) goto wait; if (lEvent & FD_READ) FD_SET(hSocket, &fdRead); Status = select(-1, &fdRead, NULL, NULL, NULL); if (Status == SOCKET_ERROR && (lEvent & FD_CLOSE)) PostMessage(hwndNotify, uiMsg, hSocket, FD_CLOSE); if (FD_ISSET(hSocket, &fdRead) && (lEvent & FD_READ)) { EnterCriticalSection(&g_AsyncCS); if (!g_nRecvLength) // Read into the buffer { g_nRecvStart = 0; g_nRecvLength = recv(hSocket, g_pRecvBuffer, sizeof(g_pRecvBuffer), 0); if (g_nRecvLength == SOCKET_ERROR) g_nRecvLength = 0; } LeaveCriticalSection(&g_AsyncCS); if (g_nRecvLength) PostMessage(hwndNotify, uiMsg, hSocket, FD_READ); else if (lEvent & FD_CLOSE) PostMessage(hwndNotify, uiMsg, hSocket, FD_CLOSE); } wait: ASSERT(g_hAsyncThread); SuspendThread(g_hAsyncThread); } return 0; } /*++ * Function: * _StartAsyncThread * Description: * Starts thread for simulating WSAAsyncSelect * WinCE only * Return value: * TRUE on success * Called by: * dllentry on DLL_ATTACH_PROCESS --*/ BOOL _StartAsyncThread(VOID) { DWORD dwThreadId; InitializeCriticalSection(&g_AsyncCS); g_bGoAsync = TRUE; g_hAsyncThread = CreateThread( NULL, // security 0, // stack size (default) _SelectWorker, NULL, // parameter 0, // flags &dwThreadId); return (g_hAsyncThread != NULL); } /*++ * Function: * _CloseAsyncThread * Description: * Destroys the thread created in _StartAsyncThread * WinCE only * Called by: * dllentry on DLL_DETTACH_PROCESS --*/ VOID _CloseAsyncThread(VOID) { if (g_hAsyncThread) { g_bGoAsync = FALSE; ResumeThread(g_hAsyncThread); TRACE((INFO_MESSAGE, TEXT("Closing Async Thread\n"))); if (WaitForSingleObject(g_hAsyncThread, 15000) == WAIT_TIMEOUT) { TRACE((WARNING_MESSAGE, TEXT("Async Thread is still alive. Retrying once more time\n"))); ResumeThread(g_hAsyncThread); if (WaitForSingleObject(g_hAsyncThread, 30000) == WAIT_TIMEOUT) { TRACE((ERROR_MESSAGE, TEXT("Async thread is again alive. KILL THE THREAD !!!\n"))); TerminateThread(g_hAsyncThread, 1); } } g_hAsyncThread = NULL; } DeleteCriticalSection(&g_AsyncCS); } BOOL CheckDlgButton( HWND hDlg, INT nIDButton, UINT uCheck) { LONG lres = SendDlgItemMessage(hDlg, nIDButton, BM_SETCHECK, uCheck, 0); return (lres == 0); } #endif // OS_WINCE #ifdef UNICODE /*++ * Function: * _CLX_SetDlgItemTextA * Description: * Ascii version for SetDlgItemText * WinCE only, UNICODE only * Arguments: * hDlg - dialog handle * nDlgItem - dialog item * lpString - item text * Return value: * TRUE on success * Called by: * _ClxWndProc on WM_TIMER message --*/ BOOL _CLX_SetDlgItemTextA(HWND hDlg, INT nDlgItem, LPCSTR lpString) { WCHAR lpStringW[128]; INT ccLen = strlen(lpString); lpStringW[0] = 0; MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, lpString, -1, lpStringW, ccLen + 1); return SetDlgItemText(hDlg, nDlgItem, lpStringW); } #endif // UNICODE #ifndef OS_WINCE /* * * Clipboard functions * */ HGLOBAL Clp_GetMFData(HANDLE hData, UINT32 *pDataLen); HGLOBAL Clp_SetMFData(UINT32 dataLen, PVOID pData); // next is directly cut & paste from clputil.c typedef struct { UINT32 mm; UINT32 xExt; UINT32 yExt; } CLIPBOARD_MFPICT, *PCLIPBOARD_MFPICT; VOID Clp_GetClipboardData( UINT format, HGLOBAL hClipData, UINT32 *pnClipDataSize, HGLOBAL *phNewData) { HGLOBAL hData = hClipData; UINT32 dataLen = 0; WORD numEntries; DWORD dwEntries; PVOID pData; *phNewData = NULL; *pnClipDataSize = 0; if (format == CF_PALETTE) { /****************************************************************/ /* Find out how many entries there are in the palette and */ /* allocate enough memory to hold them all. */ /****************************************************************/ if (GetObject(hData, sizeof(numEntries), (LPSTR)&numEntries) == 0) { numEntries = 256; } dataLen = sizeof(LOGPALETTE) + (((UINT32)numEntries - 1) * sizeof(PALETTEENTRY)); *phNewData = GlobalAlloc(GHND, dataLen); if (*phNewData == 0) { TRACE((ERROR_MESSAGE, "Failed to get %d bytes for palette", dataLen)); goto exitpt; } else { /************************************************************/ /* now get the palette entries into the new buffer */ /************************************************************/ pData = GlobalLock(*phNewData); dwEntries = GetPaletteEntries((HPALETTE)hData, 0, numEntries, (PALETTEENTRY*)pData); GlobalUnlock(*phNewData); if (dwEntries == 0) { TRACE((ERROR_MESSAGE, "Failed to get any palette entries")); goto exitpt; } dataLen = (UINT32)dwEntries * sizeof(PALETTEENTRY); } } else if (format == CF_METAFILEPICT) { *phNewData = Clp_GetMFData(hData, &dataLen); if (!*phNewData) { TRACE((ERROR_MESSAGE, "Failed to set MF data")); goto exitpt; } } else { if (format == CF_DIB) { // Get the exact DIB size BITMAPINFOHEADER *pBMI = (BITMAPINFOHEADER *)GlobalLock(hData); if (pBMI) { if (pBMI->biSizeImage) dataLen = pBMI->biSize + pBMI->biSizeImage; GlobalUnlock(hData); } } /****************************************************************/ /* just get the length of the block */ /****************************************************************/ if (!dataLen) dataLen = (DWORD)GlobalSize(hData); } *pnClipDataSize = dataLen; exitpt: ; } BOOL Clp_SetClipboardData( UINT formatID, HGLOBAL hClipData, UINT32 nClipDataSize, BOOL *pbFreeHandle) { BOOL rv = FALSE; PVOID pData = NULL; HGLOBAL hData = NULL; LOGPALETTE *pLogPalette = NULL; UINT numEntries; UINT memLen; ASSERT(pbFreeHandle); *pbFreeHandle = TRUE; if (formatID == CF_METAFILEPICT) { /********************************************************************/ /* We have to put a handle to the metafile on the clipboard - which */ /* means creating a metafile from the received data first */ /********************************************************************/ pData = GlobalLock(hClipData); if (!pData) { TRACE((ERROR_MESSAGE, "Failed to lock buffer\n")); goto exitpt; } hData = Clp_SetMFData(nClipDataSize, pData); if (!hData) { TRACE((ERROR_MESSAGE, "Failed to set MF data\n")); } else if (SetClipboardData(formatID, hData) != hData) { TRACE((ERROR_MESSAGE, "SetClipboardData. GetLastError=%d\n", GetLastError())); } GlobalUnlock(hClipData); } else if (formatID == CF_PALETTE) { /********************************************************************/ /* We have to put a handle to the palette on the clipboard - again */ /* this means creating one from the received data first */ /* */ /* Allocate memory for a LOGPALETTE structure large enough to hold */ /* all the PALETTE ENTRY structures, and fill it in. */ /********************************************************************/ numEntries = (UINT)(nClipDataSize / sizeof(PALETTEENTRY)); memLen = (sizeof(LOGPALETTE) + ((numEntries - 1) * sizeof(PALETTEENTRY))); pLogPalette = malloc(memLen); if (!pLogPalette) { TRACE((ERROR_MESSAGE, "Failed to get %d bytes", memLen)); goto exitpt; } pLogPalette->palVersion = 0x300; pLogPalette->palNumEntries = (WORD)numEntries; /********************************************************************/ /* get a pointer to the data and copy it to the palette */ /********************************************************************/ pData = GlobalLock(hClipData); if (pData == NULL) { TRACE((ERROR_MESSAGE, "Failed to lock buffer")); goto exitpt; } HUGEMEMCPY(pLogPalette->palPalEntry, pData, nClipDataSize); /********************************************************************/ /* unlock the buffer */ /********************************************************************/ GlobalUnlock(hClipData); /********************************************************************/ /* now create a palette */ /********************************************************************/ hData = CreatePalette(pLogPalette); if (!hData) { TRACE((ERROR_MESSAGE, "CreatePalette failed\n")); goto exitpt; } /********************************************************************/ /* and set the palette handle to the Clipboard */ /********************************************************************/ if (SetClipboardData(formatID, hData) != hData) { TRACE((ERROR_MESSAGE, "SetClipboardData. GetLastError=%d\n", GetLastError())); } } else { /****************************************************************/ /* Just set it onto the clipboard */ /****************************************************************/ if (SetClipboardData(formatID, hClipData) != hClipData) { TRACE((ERROR_MESSAGE, "SetClipboardData. GetLastError=%d, hClipData=0x%x\n", GetLastError(), hClipData)); goto exitpt; } // Only in this case we don't need to free the handle *pbFreeHandle = FALSE; } rv = TRUE; exitpt: if (!pLogPalette) { free(pLogPalette); } return rv; } HGLOBAL Clp_GetMFData(HANDLE hData, UINT32 *pDataLen) { UINT32 lenMFBits = 0; BOOL rc = FALSE; LPMETAFILEPICT pMFP = NULL; HDC hMFDC = NULL; HMETAFILE hMF = NULL; HGLOBAL hMFBits = NULL; HANDLE hNewData = NULL; CHAR *pNewData = NULL; PVOID pBits = NULL; /************************************************************************/ /* Lock the memory to get a pointer to a METAFILEPICT header structure */ /* and create a METAFILEPICT DC. */ /************************************************************************/ pMFP = (LPMETAFILEPICT)GlobalLock(hData); if (pMFP == NULL) goto exitpt; hMFDC = CreateMetaFile(NULL); if (hMFDC == NULL) goto exitpt; /************************************************************************/ /* Copy the MFP by playing it into the DC and closing it. */ /************************************************************************/ if (!PlayMetaFile(hMFDC, pMFP->hMF)) { CloseMetaFile(hMFDC); goto exitpt; } hMF = CloseMetaFile(hMFDC); if (hMF == NULL) goto exitpt; /************************************************************************/ /* Get the MF bits and determine how long they are. */ /************************************************************************/ #ifdef OS_WIN16 hMFBits = GetMetaFileBits(hMF); lenMFBits = GlobalSize(hMFBits); #else lenMFBits = GetMetaFileBitsEx(hMF, 0, NULL); #endif if (lenMFBits == 0) goto exitpt; /************************************************************************/ /* Work out how much memory we need and get a buffer */ /************************************************************************/ *pDataLen = sizeof(CLIPBOARD_MFPICT) + lenMFBits; hNewData = GlobalAlloc(GHND, *pDataLen); if (hNewData == NULL) goto exitpt; pNewData = GlobalLock(hNewData); /************************************************************************/ /* Copy the MF header and bits into the buffer. */ /************************************************************************/ ((PCLIPBOARD_MFPICT)pNewData)->mm = pMFP->mm; ((PCLIPBOARD_MFPICT)pNewData)->xExt = pMFP->xExt; ((PCLIPBOARD_MFPICT)pNewData)->yExt = pMFP->yExt; #ifdef OS_WIN16 pBits = GlobalLock(hMFBits); HUGEMEMCPY((pNewData + sizeof(CLIPBOARD_MFPICT)), pBits, lenMFBits); GlobalUnlock(hMFBits); #else lenMFBits = GetMetaFileBitsEx(hMF, lenMFBits, (pNewData + sizeof(CLIPBOARD_MFPICT))); if (lenMFBits == 0) goto exitpt; #endif /************************************************************************/ /* all OK */ /************************************************************************/ rc = TRUE; exitpt: /************************************************************************/ /* Unlock any global mem. */ /************************************************************************/ if (pMFP) { GlobalUnlock(hData); } if (pNewData) { GlobalUnlock(hNewData); } /************************************************************************/ /* if things went wrong, then free the new data */ /************************************************************************/ if ((rc == FALSE) && (hNewData != NULL)) { GlobalFree(hNewData); hNewData = NULL; } return(hNewData); } HGLOBAL Clp_SetMFData(UINT32 dataLen, PVOID pData) { BOOL rc = FALSE; HGLOBAL hMFBits = NULL; PVOID pMFMem = NULL; HMETAFILE hMF = NULL; HGLOBAL hMFPict = NULL; LPMETAFILEPICT pMFPict = NULL; /************************************************************************/ /* Allocate memory to hold the MF bits (we need the handle to pass to */ /* SetMetaFileBits). */ /************************************************************************/ hMFBits = GlobalAlloc(GHND, dataLen - sizeof(CLIPBOARD_MFPICT)); if (hMFBits == NULL) goto exitpt; /************************************************************************/ /* Lock the handle and copy in the MF header. */ /************************************************************************/ pMFMem = GlobalLock(hMFBits); if (pMFMem == NULL) goto exitpt; HUGEMEMCPY(pMFMem, (PVOID)((CHAR *)pData + sizeof(CLIPBOARD_MFPICT)), dataLen - sizeof(CLIPBOARD_MFPICT) ); GlobalUnlock(hMFBits); /************************************************************************/ /* Now use the copied MF bits to create the actual MF bits and get a */ /* handle to the MF. */ /************************************************************************/ #ifdef OS_WIN16 hMF = SetMetaFileBits(hMFBits); #else hMF = SetMetaFileBitsEx(dataLen - sizeof(CLIPBOARD_MFPICT), pMFMem); #endif if (hMF == NULL) goto exitpt; /************************************************************************/ /* Allocate a new METAFILEPICT structure, and use the data from the */ /* header. */ /************************************************************************/ hMFPict = GlobalAlloc(GHND, sizeof(METAFILEPICT)); pMFPict = (LPMETAFILEPICT)GlobalLock(hMFPict); if (!pMFPict) goto exitpt; pMFPict->mm = (int)((PCLIPBOARD_MFPICT)pData)->mm; pMFPict->xExt = (int)((PCLIPBOARD_MFPICT)pData)->xExt; pMFPict->yExt = (int)((PCLIPBOARD_MFPICT)pData)->yExt; pMFPict->hMF = hMF; GlobalUnlock(hMFPict); rc = TRUE; exitpt: /************************************************************************/ /* tidy up */ /************************************************************************/ if (!rc) { if (hMFPict) { GlobalFree(hMFPict); } if (hMFBits) { GlobalFree(hMFBits); } } return(hMFPict); } #endif // !OS_WINCE BOOL WS_Init(VOID) { WORD versionRequested; WSADATA wsaData; INT intRC; BOOL rv = FALSE; versionRequested = MAKEWORD(1, 1); intRC = WSAStartup(versionRequested, &wsaData); if (intRC != 0) goto exitpt; rv = TRUE; exitpt: return rv; } VOID _AttemptToCloseTheClient(VOID) { HWND hYesNo = NULL; static BOOL bSpeedupTimer = FALSE; if (!bSpeedupTimer) { KillTimer(g_hWindow, g_uiBackgroundTimer); g_uiBackgroundTimer = SetTimer(g_hWindow, RCLX_BACKGNDTIMERID, RCLX_TIMERELAPSETIME/15+1000, NULL); bSpeedupTimer = TRUE; } hYesNo = _FindTopWindow(NULL, g_strYesNoShutdown, g_hRDPInst); if (hYesNo) { PostMessage(hYesNo, WM_KEYDOWN, VK_RETURN, 0); } else { PostMessage(g_pClx->hwndMain, WM_CLOSE, 0, 0); // Don't know how, but this helps for NT4 client PostMessage(g_pClx->hwndMain, WM_LBUTTONDOWN, 0, 0); } } /* * This came from: \\index1\src\nt\private\samples\wincap32\dibutil.c */ #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) #define IS_WIN30_DIB(lpbi) ((*(LPDWORD)(lpbi)) == sizeof(BITMAPINFOHEADER)) WORD DIBNumColors(LPSTR lpDIB) { WORD wBitCount; // DIB bit count // If this is a Windows-style DIB, the number of colors in the // color table can be less than the number of bits per pixel // allows for (i.e. lpbi->biClrUsed can be set to some value). // If this is the case, return the appropriate value. if (IS_WIN30_DIB(lpDIB)) { DWORD dwClrUsed; dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed; if (dwClrUsed) return (WORD)dwClrUsed; } // Calculate the number of colors in the color table based on // the number of bits per pixel for the DIB. if (IS_WIN30_DIB(lpDIB)) wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount; else wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount; // return number of colors based on bits per pixel switch (wBitCount) { case 1: return 2; case 4: return 16; case 8: return 256; default: return 0; } } WORD PaletteSize(LPSTR lpDIB) { // calculate the size required by the palette if (IS_WIN30_DIB (lpDIB)) return (DIBNumColors(lpDIB) * sizeof(RGBQUAD)); else return (DIBNumColors(lpDIB) * sizeof(RGBTRIPLE)); } /************************************************************************* * * BitmapToDIB() * * Parameters: * * HBITMAP hBitmap - specifies the bitmap to convert * * HPALETTE hPal - specifies the palette to use with the bitmap * * Return Value: * * HANDLE - identifies the device-dependent bitmap * * Description: * * This function creates a DIB from a bitmap using the specified palette. * ************************************************************************/ HANDLE BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal) { BITMAP bm; // bitmap structure BITMAPINFOHEADER bi; // bitmap header LPBITMAPINFOHEADER lpbi; // pointer to BITMAPINFOHEADER DWORD dwLen; // size of memory block HANDLE hDIB, h; // handle to DIB, temp handle HDC hDC; // handle to DC WORD biBits; // bits per pixel // check if bitmap handle is valid if (!hBitmap) return NULL; // fill in BITMAP structure, return NULL if it didn't work if (!GetObject(hBitmap, sizeof(bm), (LPSTR)&bm)) return NULL; // if no palette is specified, use default palette if (hPal == NULL) hPal = GetStockObject(DEFAULT_PALETTE); // calculate bits per pixel biBits = bm.bmPlanes * bm.bmBitsPixel; // make sure bits per pixel is valid if (biBits <= 1) biBits = 1; else if (biBits <= 4) biBits = 4; else if (biBits <= 8) biBits = 8; else // if greater than 8-bit, force to 24-bit biBits = 24; // initialize BITMAPINFOHEADER bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = bm.bmWidth; bi.biHeight = bm.bmHeight; bi.biPlanes = 1; bi.biBitCount = biBits; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; // calculate size of memory block required to store BITMAPINFO dwLen = bi.biSize + PaletteSize((LPSTR)&bi); // get a DC hDC = GetDC(NULL); if ( !hDC ) return NULL; // select and realize our palette hPal = SelectPalette(hDC, hPal, FALSE); RealizePalette(hDC); // alloc memory block to store our bitmap hDIB = GlobalAlloc(GHND, dwLen); // if we couldn't get memory block if (!hDIB) { // clean up and return NULL SelectPalette(hDC, hPal, TRUE); RealizePalette(hDC); ReleaseDC(NULL, hDC); return NULL; } // lock memory and get pointer to it lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); /// use our bitmap info. to fill BITMAPINFOHEADER *lpbi = bi; // call GetDIBits with a NULL lpBits param, so it will calculate the // biSizeImage field for us GetDIBits(hDC, hBitmap, 0, (UINT)bi.biHeight, NULL, (LPBITMAPINFO)lpbi, DIB_RGB_COLORS); // get the info. returned by GetDIBits and unlock memory block bi = *lpbi; GlobalUnlock(hDIB); // if the driver did not fill in the biSizeImage field, make one up if (bi.biSizeImage == 0) bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight; // realloc the buffer big enough to hold all the bits dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + bi.biSizeImage; if (h = GlobalReAlloc(hDIB, dwLen, 0)) hDIB = h; else { // clean up and return NULL GlobalFree(hDIB); hDIB = NULL; SelectPalette(hDC, hPal, TRUE); RealizePalette(hDC); ReleaseDC(NULL, hDC); return NULL; } // lock memory block and get pointer to it */ lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); // call GetDIBits with a NON-NULL lpBits param, and actualy get the // bits this time if (GetDIBits(hDC, hBitmap, 0, (UINT)bi.biHeight, (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), (LPBITMAPINFO)lpbi, DIB_RGB_COLORS) == 0) { // clean up and return NULL GlobalUnlock(hDIB); hDIB = NULL; SelectPalette(hDC, hPal, TRUE); RealizePalette(hDC); ReleaseDC(NULL, hDC); return NULL; } bi = *lpbi; // clean up GlobalUnlock(hDIB); SelectPalette(hDC, hPal, TRUE); RealizePalette(hDC); ReleaseDC(NULL, hDC); // return handle to the DIB return hDIB; } /*++ * Function: * _GetDIBFromBitmap * Description: * Copies a rectangle from hBitmap and converts it to DIB data * * Arguments: * hBitmap - the main bitmap * ppDIB - pointer to DIB data * left, top, right, bottom - describes the rectangle * - if all are == -1, returns the whole bitmap * Return value: * TRUE on success * Called by: * _ClxWndProc on WM_TIMER message --*/ VOID _GetDIBFromBitmap( HDC hdcMemSrc, HBITMAP hBitmap, HANDLE *phDIB, INT left, INT top, INT right, INT bottom) { HANDLE hDIB = NULL; HDC hdcMemDst = NULL; HDC hdcScreen = NULL; HBITMAP hDstBitmap = NULL; HBITMAP hOldDstBmp = NULL; if (!hdcMemSrc) goto exitpt; if (left == -1 && right == -1 && top == -1 && bottom == -1 && hBitmap) { BITMAP bitmapInfo; if (sizeof(bitmapInfo) != GetObject(hBitmap, sizeof(bitmapInfo), &bitmapInfo)) goto exitpt; left = top = 0; right = bitmapInfo.bmWidth; bottom = bitmapInfo.bmHeight; } // reorder left...bottom if needed if (left > right) { INT change = left; left = right; right = change; } if (top > bottom) { INT change = top; top = bottom; bottom = change; } hdcScreen = GetDC(NULL); hdcMemDst = CreateCompatibleDC(hdcScreen); hDstBitmap = CreateCompatibleBitmap(hdcScreen, right - left, bottom - top); if (!hdcMemDst || !hDstBitmap) { TRACE(( ERROR_MESSAGE, TEXT("Can't create destination DC to get client's DIB\n"))); goto exitpt; } hOldDstBmp = SelectObject(hdcMemDst, hDstBitmap); if (!BitBlt( hdcMemDst, 0, 0, // dest x,y right - left, // dest width bottom - top, // dest height hdcMemSrc, left, top, // source coordinates SRCCOPY)) goto exitpt; TRACE((INFO_MESSAGE, TEXT("Getting DIB (%d, %d, %d, %d)\n"), left, top, right, bottom)); hDIB = BitmapToDIB(hDstBitmap, NULL); exitpt: if (hdcMemDst) { if (hOldDstBmp) SelectObject(hdcMemDst, hOldDstBmp); DeleteDC(hdcMemDst); } if (hdcScreen) ReleaseDC(NULL, hdcScreen); if (hDstBitmap) DeleteObject(hDstBitmap); *phDIB = hDIB; if (!hDIB) TRACE((ERROR_MESSAGE, TEXT("Can't get client's DIB. GetLastError=%d\n"), GetLastError())); } #ifndef OS_WINCE #ifdef OS_WIN32 DWORD __stdcall _ClxSendMsgThread(VOID *param) { while(1) { if (!g_pClx || WaitForSingleObject(g_pClx->semSendReady, INFINITE) != WAIT_OBJECT_0) goto exitpt; if (!g_pClx || g_pClx->bSendMsgThreadExit) goto exitpt; SendMessage(g_pClx->msg.hwnd, g_pClx->msg.message, g_pClx->msg.wParam, g_pClx->msg.lParam); // release the owner of the message ReleaseSemaphore(g_pClx->semSendCompleted, 1, NULL); // release next waiting worker ReleaseSemaphore(g_pClx->semSendDone, 1, NULL); } exitpt: return 0; } /*++ * Function: * _ClxSendMessage * Description: * Calls SendMessage from separate thread * prevents deadlock on SendMessage (#319816) * * Arguments: * hBitmap - the main bitmap * ppDIB - pointer to DIB data * left, top, right, bottom - describes the rectangle * - if all are == -1, returns the whole bitmap * Return value: * TRUE on success * Called by: * _ClxWndProc on WM_TIMER message --*/ LRESULT _ClxSendMessage( HWND hWnd, // handle of destination window UINT Msg, // message to send WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { LRESULT rv = 0; PCLXINFO pClx = g_pClx; DWORD dwThreadId; if (!pClx) goto exitpt; if (!pClx->semSendDone) pClx->semSendDone = CreateSemaphore(NULL, 1, 10, NULL); if (!pClx->semSendReady) pClx->semSendReady = CreateSemaphore(NULL, 0, 10, NULL); if (!pClx->semSendCompleted) pClx->semSendCompleted = CreateSemaphore(NULL, 0, 10, NULL); if (!pClx->semSendDone || !pClx->semSendReady || !pClx->semSendCompleted) goto exitpt; if (!pClx->hSendMsgThread) { pClx->hSendMsgThread = CreateThread( NULL, 0, _ClxSendMsgThread, NULL, 0, &dwThreadId); } if (!pClx->hSendMsgThread) goto exitpt; // Wait 10 mins send to complete if (WaitForSingleObject(pClx->semSendDone, 600000) != WAIT_OBJECT_0) goto exitpt; pClx->msg.hwnd = hWnd; pClx->msg.message = Msg; pClx->msg.wParam = wParam; pClx->msg.lParam = lParam; // Signal the thread for available message ReleaseSemaphore(pClx->semSendReady, 1, NULL); // Wait for the send to complete WaitForSingleObject(pClx->semSendCompleted, 600000); exitpt: return rv; } VOID _ClxDestroySendMsgThread(PCLXINFO pClx) { if (!pClx) goto exitpt; if (!pClx->semSendDone || !pClx->semSendReady || !pClx->hSendMsgThread || !pClx->semSendCompleted) goto exitpt; // Wait 10 mins send to complete WaitForSingleObject(pClx->semSendDone, 600000); pClx->bSendMsgThreadExit = TRUE; // signal the thread to exit ReleaseSemaphore(pClx->semSendReady, 1, NULL); // wait for the thread to exit if (WaitForSingleObject(pClx->hSendMsgThread, 1200000) != WAIT_OBJECT_0) { TRACE((ERROR_MESSAGE, TEXT("SendThread can't exit, calling TerminateThread\n"))); TerminateThread(pClx->hSendMsgThread, 0); } CloseHandle(pClx->hSendMsgThread); exitpt: if (pClx->semSendCompleted) { CloseHandle(pClx->semSendCompleted); pClx->semSendCompleted = NULL; } if (pClx->semSendDone) { CloseHandle(pClx->semSendDone); pClx->semSendDone = NULL; } if (pClx->semSendReady) { CloseHandle(pClx->semSendReady); pClx->semSendReady = NULL; } pClx->hSendMsgThread = 0; ; } #endif // OS_WIN32 #endif // !OS_WINCE ////////////////////////////////////////////////////////////////////// // // VC Channel support for RCLX mode // DWORD CLXAPI CLXDataReceivedVC( LPCSTR szChannelName, LPVOID pData, DWORD dwSize ) { DWORD rv = (DWORD)-1; PCLXINFO pClx; CHAR szName[MAX_VCNAME_LEN]; PCLXVCHANNEL pVChannel; RCLXDATA Response; RCLXFEEDPROLOG Prolog; BOOL rc; // Check if this channel is already registered pVChannel = g_pVChannels; // find the channel entry while(pVChannel && _stricmp(pVChannel->szName, szChannelName)) { pVChannel = pVChannel->pNext; } if (!pVChannel) { TRACE((WARNING_MESSAGE, TEXT("Channel %s is not registered\n"), szChannelName)); goto exitpt; } if (!IS_RCLX) { TRACE((WARNING_MESSAGE, TEXT("CLXDataReceivedVC: not in RCLX mode\n"))); goto exitpt; } if (!g_pClx) { TRACE((ERROR_MESSAGE, TEXT("CLXDataReceivedVC: pClx is NULL\n"))); goto exitpt; } if (strlen(szChannelName) > MAX_VCNAME_LEN - 1) { TRACE((ERROR_MESSAGE, TEXT("Channel name \"%s\"bigger than %d chars\n"), szChannelName, MAX_VCNAME_LEN)); goto exitpt; } pClx = g_pClx; Prolog.FeedType = FEED_DATA; Prolog.HeadSize = sizeof(Response) + sizeof(szName) + dwSize; Prolog.TailSize = 0; Response.uiType = DATA_VC; Response.uiSize = sizeof(szName) + dwSize; strcpy(szName, szChannelName); TRACE((ALIVE_MESSAGE, TEXT("Sending VC data, DataSize=%d, HeadSize=%d, TailSize=%d, Name=%s\n"), dwSize, Prolog.HeadSize, Prolog.TailSize, szName)); rc = RClx_SendBuffer(pClx->hSocket, &Prolog, sizeof(Prolog)); rc = rc && RClx_SendBuffer(pClx->hSocket, &Response, sizeof(Response)); rc = rc && RClx_SendBuffer(pClx->hSocket, szName, sizeof(szName)); rc = rc && RClx_SendBuffer(pClx->hSocket, pData, dwSize); if (!rc) { TRACE((WARNING_MESSAGE, TEXT("Unable to sent message\n"))); goto exitpt; } rv = 0; exitpt: return rv; } DWORD _CLXSendDataVC( LPCSTR szChannelName, LPVOID pData, DWORD dwSize ) { DWORD rv = (DWORD)-1; // Check if this channel is already registered PCLXVCHANNEL pVChannel = g_pVChannels; // find the channel entry while(pVChannel && _stricmp(pVChannel->szName, szChannelName)) { pVChannel = pVChannel->pNext; } if (!pVChannel) { TRACE((WARNING_MESSAGE, TEXT("Channel %s is not registered\n"), szChannelName)); goto exitpt; } ASSERT(pVChannel->pSendDataFn); rv = (pVChannel->pSendDataFn)(pData, dwSize); exitpt: return rv; } BOOL CLXAPI ClxRegisterVC( LPCSTR szChannelName, PCLXVC_SENDDATA pSendData, PCLXVC_RECVDATA *ppRecvData ) { BOOL rv = FALSE; PCLXVCHANNEL pVChannel; UINT counter; if (!szChannelName || !pSendData || !ppRecvData || strlen(szChannelName) > MAX_VCNAME_LEN - 1) { TRACE((ERROR_MESSAGE, TEXT("ClxRegisterVC: invalid parameters\n"))); goto exitpt; } pVChannel = malloc(sizeof(*pVChannel)); if (!pVChannel) goto exitpt; strcpy(pVChannel->szName, szChannelName); // zero the rest of the name for(counter = strlen(szChannelName); counter < MAX_VCNAME_LEN; counter++) pVChannel->szName[counter] = 0; pVChannel->pSendDataFn = pSendData; *ppRecvData = CLXDataReceivedVC; // Push this in the queue pVChannel->pNext = g_pVChannels; g_pVChannels = pVChannel; rv = TRUE; exitpt: return rv; } VOID CLXAPI ClxUnregisterVC( LPCSTR szChannelName ) { PCLXVCHANNEL pVChannel = g_pVChannels; PCLXVCHANNEL pPrev = NULL; // find the channel and remove it from the queue while(pVChannel && _stricmp(pVChannel->szName, szChannelName)) { pPrev = pVChannel; pVChannel = pVChannel->pNext; } if (!pVChannel) { TRACE((WARNING_MESSAGE, TEXT("Can't find channel name: %s\n"), szChannelName)); goto exitpt; } if (!pPrev) g_pVChannels = pVChannel->pNext; else pPrev->pNext = pVChannel->pNext; exitpt: ; } //////////////////////////////////////////////////////////////////////