4796 lines
122 KiB
C
4796 lines
122 KiB
C
|
/*+
|
||
|
* File name:
|
||
|
* clxtshar.c
|
||
|
* Contents:
|
||
|
* Client extension loaded by RDP client
|
||
|
*
|
||
|
* Copyright (C) 1998-1999 Microsoft Corp.
|
||
|
--*/
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <windowsx.h>
|
||
|
#include <winsock.h>
|
||
|
#include <string.h>
|
||
|
#include <malloc.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdarg.h>
|
||
|
#ifndef OS_WINCE
|
||
|
#include <direct.h>
|
||
|
#endif // OS_WINCE
|
||
|
|
||
|
#ifndef OS_WINCE
|
||
|
#ifdef OS_WIN32
|
||
|
#include <process.h>
|
||
|
#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:
|
||
|
;
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|