You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1786 lines
42 KiB
1786 lines
42 KiB
/*+
|
|
* 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 "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")));
|
|
|
|
#if 0
|
|
// 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);
|
|
#endif
|
|
_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
|
|
* 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;
|
|
TCHAR szTempBuf[_MAX_PATH];
|
|
PCLXINFO pClx = NULL;
|
|
|
|
#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
|
|
|
|
if ( NULL == ppClx )
|
|
{
|
|
TRACE((ERROR_MESSAGE, TEXT("ppClx is NULL\n")));
|
|
goto exitpt;
|
|
}
|
|
|
|
pClx = (PCLXINFO)_CLXALLOC(sizeof(**ppClx));
|
|
|
|
if (!pClx)
|
|
{
|
|
TRACE((ERROR_MESSAGE, TEXT("Can't allocate CLX context\n")));
|
|
goto exitpt;
|
|
}
|
|
|
|
// Clear the structure
|
|
memset(pClx, 0, sizeof(*pClx));
|
|
|
|
if ( !_ClxInitSendMessageThread( pClx ))
|
|
{
|
|
TRACE(( ERROR_MESSAGE, TEXT("Failed to init SendMessageThread\n" )));
|
|
goto exitpt;
|
|
}
|
|
|
|
hwndSMC = _ParseCmdLine(pClInfo->pszCmdLine, pClx);
|
|
|
|
#if 0
|
|
if (g_pClx)
|
|
// Should not be called twice
|
|
{
|
|
TRACE((WARNING_MESSAGE, TEXT("g_pClx is not null. Reentered ?!\n")));
|
|
goto exitpt;
|
|
}
|
|
#endif
|
|
|
|
g_pClx = (pClx);
|
|
|
|
// Remember client's input window
|
|
szTempBuf[0] = 0;
|
|
GetClassName( pClInfo->hwndMain, szTempBuf, sizeof( szTempBuf )/sizeof( szTempBuf[0] ));
|
|
|
|
if (!_CLX_strcmp(g_strMainWindowClass, szTempBuf))
|
|
// not our window
|
|
//
|
|
pClx->hwndMain = NULL;
|
|
else
|
|
pClx->hwndMain = pClInfo->hwndMain;
|
|
|
|
if (pClInfo->hwndMain)
|
|
#ifdef OS_WINCE
|
|
g_hRDPInst = GetCurrentProcessId();
|
|
#else // !OS_WINCE
|
|
#ifdef _WIN64
|
|
g_hRDPInst = (HINSTANCE)GetWindowLongPtr(pClx->hwndMain, GWLP_HINSTANCE);
|
|
#else // !_WIN64
|
|
#ifdef OS_WIN32
|
|
g_hRDPInst = (HINSTANCE)GetWindowLong(pClx->hwndMain, GWL_HINSTANCE);
|
|
#endif // OS_WIN32
|
|
#endif // _WIN64
|
|
#ifdef OS_WIN16
|
|
g_hRDPInst = (HINSTANCE)GetWindowWord(pClx->hwndMain, GWW_HINSTANCE);
|
|
#endif // OS_WIN16
|
|
#endif // !OS_WINCE
|
|
|
|
#ifndef OS_WINCE
|
|
#ifdef OS_WIN32
|
|
// and dwProcessId
|
|
if ( 0 == pClx->dwProcessId )
|
|
pClx->dwProcessId = GetCurrentProcessId();
|
|
#endif // OS_WIN32
|
|
#endif // !OS_WINCE
|
|
|
|
#ifdef OS_WIN32
|
|
#ifndef OS_WINCE
|
|
else {
|
|
if (!(pClx->hwndSMC = hwndSMC))
|
|
pClx->hwndSMC = _FindSMCWindow(pClx, 0);
|
|
}
|
|
#endif // !OS_WINCE
|
|
#endif // OS_WIN32
|
|
|
|
rv = TRUE;
|
|
exitpt:
|
|
|
|
if ( !rv && NULL != pClx )
|
|
{
|
|
_CLXFREE( pClx );
|
|
g_pClx = NULL;
|
|
pClx = NULL;
|
|
}
|
|
|
|
if ( NULL != ppClx )
|
|
{
|
|
*ppClx = pClx;
|
|
}
|
|
|
|
if ( !rv )
|
|
TRACE((ERROR_MESSAGE, TEXT("ClxInitialzie failed\n")));
|
|
|
|
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, WPARAM wResult)
|
|
{
|
|
UINT uiMessage = 0;
|
|
|
|
if (!pClx)
|
|
goto exitpt;
|
|
|
|
#ifdef VLADIMIS_NEW_CHANGE
|
|
if (Event == CLX_EVENT_SHADOWBITMAPDC)
|
|
{
|
|
pClx->hdcShadowBitmap = (HDC)wResult;
|
|
goto exitpt;
|
|
} else if (Event == CLX_EVENT_SHADOWBITMAP)
|
|
{
|
|
pClx->hShadowBitmap = (HBITMAP)wResult;
|
|
goto exitpt;
|
|
} else if (Event == CLX_EVENT_PALETTE)
|
|
{
|
|
pClx->hShadowPalette = (HPALETTE)wResult;
|
|
}
|
|
#endif // VLADIMIS
|
|
|
|
#ifndef OS_WINCE
|
|
{
|
|
|
|
if (!_CheckWindow(pClx))
|
|
goto exitpt;
|
|
|
|
if (Event == CLX_EVENT_DISCONNECT)
|
|
uiMessage = WM_FB_DISCONNECT;
|
|
else if (Event == CLX_EVENT_CONNECT)
|
|
{
|
|
uiMessage = WM_FB_CONNECT;
|
|
wResult = (WPARAM)pClx->hwndMain;
|
|
}
|
|
else if (Event == CLX_EVENT_LOGON)
|
|
// wResult contains the session ID
|
|
uiMessage = WM_FB_LOGON;
|
|
|
|
if (uiMessage)
|
|
{
|
|
#ifdef OS_WIN32
|
|
if (!_ClxAcquireSendMessageThread(pClx))
|
|
goto exitpt;
|
|
|
|
_ClxSendMessage(
|
|
pClx,
|
|
pClx->hwndSMC,
|
|
uiMessage,
|
|
wResult,
|
|
pClx->dwProcessId);
|
|
_ClxReleaseSendMessageThread(pClx);
|
|
|
|
#endif // OS_WIN32
|
|
#ifdef OS_WIN16
|
|
if (g_hRDPInst)
|
|
SendMessage(pClx->hwndSMC,
|
|
uiMessage,
|
|
g_hRDPInst,
|
|
(LRESULT)wResult);
|
|
#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)
|
|
{
|
|
BOOL bMsgThreadAcquired = FALSE;
|
|
|
|
if (!pClx || !(*((UINT16 *)pText)))
|
|
goto exitpt;
|
|
|
|
#ifdef OS_WIN32
|
|
#ifndef OS_WINCE
|
|
if (!_CheckWindow(pClx))
|
|
goto exitpt;
|
|
|
|
if (!_ClxAcquireSendMessageThread(pClx))
|
|
goto exitpt;
|
|
|
|
bMsgThreadAcquired = TRUE;
|
|
if (!pClx->hMapF)
|
|
if (!_OpenMapFile(0, &(pClx->hMapF), &(pClx->nMapSize)))
|
|
goto exitpt;
|
|
|
|
if (_SaveInMapFile(pClx->hMapF, pText, textLength, pClx->dwProcessId))
|
|
_ClxSendMessage(
|
|
pClx,
|
|
pClx->hwndSMC,
|
|
WM_FB_TEXTOUT,
|
|
(WPARAM)pClx->dwProcessId,
|
|
(LPARAM)pClx->hMapF);
|
|
#endif // !OS_WINCE
|
|
#endif // OS_WIN32
|
|
|
|
exitpt:
|
|
if ( bMsgThreadAcquired )
|
|
_ClxReleaseSendMessageThread(pClx);
|
|
}
|
|
|
|
/*++
|
|
* 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)
|
|
{
|
|
if (!pClx)
|
|
goto exitpt;
|
|
|
|
ClxEvent(pClx, CLX_EVENT_DISCONNECT, 0);
|
|
|
|
#ifdef OS_WIN32
|
|
#ifndef OS_WINCE
|
|
{
|
|
if(pClx->hMapF)
|
|
CloseHandle(pClx->hMapF);
|
|
if(pClx->hBMPMapF)
|
|
CloseHandle(pClx->hBMPMapF);
|
|
|
|
_ClxDestroySendMsgThread(pClx);
|
|
}
|
|
#endif // !OS_WINCE
|
|
#endif // OS_WIN32
|
|
|
|
_CLXFREE(pClx);
|
|
g_pClx = NULL;
|
|
|
|
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.
|
|
* 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;
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* ClxBitmap
|
|
* Description:
|
|
* Send a received bitmap to tclient.dll
|
|
* Works on Win16/Win32/WinCE
|
|
* 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
|
|
BOOL bMsgThreadAcquired = FALSE;
|
|
|
|
if (!g_GlyphEnable)
|
|
goto exitpt;
|
|
|
|
if (!pClx)
|
|
goto exitpt;
|
|
|
|
if (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 (!_ClxAcquireSendMessageThread(pClx))
|
|
goto exitpt;
|
|
|
|
bMsgThreadAcquired = TRUE;
|
|
|
|
if (!pClx->hBMPMapF)
|
|
if (!_OpenMapFile(nSize, &(pClx->hBMPMapF), &(pClx->nBMPMapSize)))
|
|
goto exitpt;
|
|
|
|
if (nSize > pClx->nBMPMapSize)
|
|
if (!_ReOpenMapFile( nSize, &(pClx->hBMPMapF), &(pClx->nBMPMapSize) ))
|
|
goto exitpt;
|
|
|
|
pView = (PBMPFEEDBACK)MapViewOfFile(pClx->hBMPMapF,
|
|
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,
|
|
pClx->hwndSMC,
|
|
WM_FB_BITMAP,
|
|
(WPARAM)pClx->dwProcessId,
|
|
(LPARAM)pClx->hBMPMapF);
|
|
|
|
#endif // !OS_WINCE
|
|
#endif // OS_WIN32
|
|
|
|
exitpt:
|
|
if ( bMsgThreadAcquired )
|
|
_ClxReleaseSendMessageThread(pClx);
|
|
}
|
|
|
|
/*++
|
|
* 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->hScreenPalette = g_pClx->hShadowPalette;
|
|
|
|
rv = TRUE;
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _ParseCmdLine
|
|
* Description:
|
|
* Retreives WHND of tclient.dll feedback window
|
|
* passed by the command line
|
|
* Win32/Win16/WinCE
|
|
* Arguments:
|
|
* szCmdLine - command line
|
|
* Return value:
|
|
* The window handle
|
|
* Called by:
|
|
* ClxInitialize
|
|
--*/
|
|
HWND _ParseCmdLine(LPCTSTR szCmdLine, PCLXINFO pClx)
|
|
{
|
|
HWND hwnd = NULL;
|
|
LPCTSTR pszwnd, pszdot, pszend;
|
|
INT nCounter;
|
|
|
|
if (!szCmdLine)
|
|
goto exitpt;
|
|
|
|
TRACE((INFO_MESSAGE, TEXT("Command line: %s\n"), szCmdLine));
|
|
|
|
pszwnd = _CLX_strstr(szCmdLine, TEXT(_COOKIE));
|
|
if (!pszwnd)
|
|
goto skip_cookie;
|
|
|
|
pszwnd += _CLX_strlen(TEXT(_COOKIE));
|
|
pClx->dwProcessId = (DWORD_PTR)_atoi64( pszwnd );
|
|
|
|
skip_cookie:
|
|
|
|
// Check for _HWNDOPT(hSMC) option
|
|
pszwnd = _CLX_strstr(szCmdLine, TEXT(_HWNDOPT));
|
|
|
|
if (!pszwnd)
|
|
goto findnext;
|
|
|
|
// 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('.'));
|
|
|
|
{
|
|
// 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));
|
|
}
|
|
|
|
findnext:
|
|
|
|
#ifdef OS_WIN32
|
|
// check for replace pid command
|
|
pszwnd = szCmdLine;
|
|
pszwnd = _CLX_strstr(szCmdLine, TEXT("pid="));
|
|
if ( NULL != pszwnd )
|
|
{
|
|
WPARAM wParam;
|
|
LPARAM lParam;
|
|
HWND hClxWnd = hwnd;
|
|
|
|
pszwnd += 4 * sizeof(pszwnd[0]);
|
|
|
|
#ifdef _WIN64
|
|
wParam = _atoi64( pszwnd );
|
|
#else // !_WIN64
|
|
wParam = _CLX_atol( pszwnd );
|
|
#endif
|
|
lParam = GetCurrentProcessId();
|
|
|
|
if ( NULL == hClxWnd )
|
|
hClxWnd = _FindSMCWindow( pClx, (LPARAM)wParam );
|
|
|
|
if ( NULL != hClxWnd )
|
|
{
|
|
PostMessage(hClxWnd,
|
|
WM_FB_REPLACEPID,
|
|
wParam, lParam);
|
|
|
|
pClx->hwndSMC = hClxWnd;
|
|
}
|
|
}
|
|
#endif // OS_WIN32
|
|
|
|
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, *(LPARAM *)lParam))
|
|
#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
|
|
* lParam - if non zero override current process id
|
|
* in the query
|
|
* Return value:
|
|
* The window handle
|
|
* Called by:
|
|
* ClxInitialize, _CheckWindow
|
|
--*/
|
|
HWND _FindSMCWindow(PCLXINFO pClx, LPARAM lParam)
|
|
{
|
|
HWND hwndFound = NULL;
|
|
|
|
#ifdef OS_WIN32
|
|
if ( 0 == lParam )
|
|
lParam = pClx->dwProcessId;
|
|
#endif // OS_WIN32
|
|
|
|
if (!EnumWindows(_EnumWindowsProcForSMC, (LPARAM)&lParam))
|
|
hwndFound = (HWND)lParam;
|
|
|
|
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, 0);
|
|
|
|
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(
|
|
UINT nSize,
|
|
HANDLE *phNewMapF,
|
|
UINT *pnMapSize
|
|
)
|
|
{
|
|
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);
|
|
|
|
*pnMapSize = (hMapF)?nPageAligned:0;
|
|
|
|
*phNewMapF = 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(
|
|
UINT newSize,
|
|
HANDLE *phNewMapF,
|
|
UINT *pnMapSize
|
|
)
|
|
{
|
|
HANDLE hNewMapF;
|
|
UINT nPageAligned;
|
|
|
|
nPageAligned = ((newSize / CLX_ONE_PAGE) + 1) * CLX_ONE_PAGE;
|
|
if (*phNewMapF)
|
|
CloseHandle(*phNewMapF);
|
|
hNewMapF = CreateFileMapping(INVALID_HANDLE_VALUE, //PG.SYS
|
|
NULL, // no security
|
|
PAGE_READWRITE,
|
|
0, // Size high
|
|
nPageAligned, // Size low
|
|
NULL);
|
|
|
|
*pnMapSize = (hNewMapF)?nPageAligned:0;
|
|
*phNewMapF = 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_PTR dwProcessId)
|
|
{
|
|
BOOL rv = FALSE, count = 0;
|
|
PFEEDBACKINFO pView;
|
|
DWORD laste;
|
|
|
|
pView = (PFEEDBACKINFO)MapViewOfFile(hMapF,
|
|
FILE_MAP_ALL_ACCESS,
|
|
0,
|
|
0,
|
|
sizeof(*pView));
|
|
|
|
if (!pView)
|
|
goto exitpt;
|
|
|
|
pView->dwProcessId = dwProcessId;
|
|
|
|
strsize = (strsize > sizeof(pView->string)/sizeof(WCHAR) - 1)?
|
|
PtrToInt( (PVOID)(sizeof(pView->string)/sizeof(WCHAR) - 1)):
|
|
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 = NULL;
|
|
DWORD disposition;
|
|
DWORD keyType;
|
|
DWORD value;
|
|
DWORD cbData;
|
|
BOOL rv = FALSE;
|
|
LONG sysrc;
|
|
|
|
sysrc = RegCreateKeyExW(HKEY_CURRENT_USER,
|
|
REG_BASE,
|
|
0, /* reserved */
|
|
NULL, /* class */
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL, /* security attributes */
|
|
&key,
|
|
&disposition);
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
TEXT("RegCreateKeyEx failed, status = %d\n"), sysrc));
|
|
goto exitpt;
|
|
}
|
|
|
|
cbData = sizeof(value);
|
|
sysrc = RegQueryValueExW(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";
|
|
|
|
memset( szIniFileName, 0, sizeof( szIniFileName ));
|
|
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
|
|
);
|
|
|
|
GetPrivateProfileString(
|
|
TEXT("tclient"),
|
|
TEXT("UIMainWindowClass"),
|
|
TEXT("UIMainClass"),
|
|
g_strMainWindowClass,
|
|
sizeof(g_strMainWindowClass),
|
|
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)/sizeof( szBuffer[0] ), format, arglist);
|
|
va_end (arglist);
|
|
szBuffer[sizeof( szBuffer )/sizeof( szBuffer[0] ) - 1] = 0;
|
|
|
|
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(BOOL bCond, LPCTSTR filename, INT line)
|
|
{
|
|
if (!bCond)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
TEXT("ASSERT: %s line %d\n"), filename, line));
|
|
|
|
DebugBreak();
|
|
}
|
|
}
|
|
|
|
/*++
|
|
* 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;
|
|
}
|
|
|
|
#ifndef OS_WINCE
|
|
#ifdef OS_WIN32
|
|
|
|
DWORD
|
|
__stdcall
|
|
_ClxSendMsgThread(VOID *param)
|
|
{
|
|
PCLXINFO pClx = (PCLXINFO)param;
|
|
while(1)
|
|
{
|
|
if (!pClx || WaitForSingleObject(pClx->semSendReady, INFINITE) !=
|
|
WAIT_OBJECT_0)
|
|
goto exitpt;
|
|
|
|
if (!pClx || pClx->bSendMsgThreadExit)
|
|
goto exitpt;
|
|
|
|
SendMessage(pClx->msg.hwnd,
|
|
pClx->msg.message,
|
|
pClx->msg.wParam,
|
|
pClx->msg.lParam);
|
|
|
|
// release next waiting worker
|
|
ReleaseSemaphore(pClx->semSendDone, 1, NULL);
|
|
|
|
}
|
|
|
|
exitpt:
|
|
return 0;
|
|
}
|
|
|
|
BOOL
|
|
_ClxInitSendMessageThread( PCLXINFO pClx )
|
|
{
|
|
BOOL rv = FALSE;
|
|
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->semSendDone || !pClx->semSendReady)
|
|
goto exitpt;
|
|
|
|
if (!pClx->hSendMsgThread)
|
|
{
|
|
pClx->hSendMsgThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
_ClxSendMsgThread,
|
|
pClx,
|
|
0,
|
|
&dwThreadId);
|
|
}
|
|
if (!pClx->hSendMsgThread)
|
|
goto exitpt;
|
|
|
|
rv = TRUE;
|
|
exitpt:
|
|
if ( !rv )
|
|
{
|
|
_ClxDestroySendMsgThread( pClx );
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
_ClxAcquireSendMessageThread( PCLXINFO pClx )
|
|
{
|
|
BOOL rv = FALSE;
|
|
|
|
if (!pClx)
|
|
goto exitpt;
|
|
|
|
if (!pClx->hSendMsgThread)
|
|
goto exitpt;
|
|
|
|
// Wait 10 mins send to complete
|
|
if (WaitForSingleObject(pClx->semSendDone, 600000) !=
|
|
WAIT_OBJECT_0)
|
|
goto exitpt;
|
|
|
|
rv = TRUE;
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
VOID
|
|
_ClxReleaseSendMessageThread( PCLXINFO pClx )
|
|
{
|
|
ASSERT( pClx->semSendReady );
|
|
|
|
// Signal the thread for available message
|
|
ReleaseSemaphore(pClx->semSendReady, 1, NULL);
|
|
}
|
|
/*++
|
|
* 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(
|
|
PCLXINFO pClx,
|
|
HWND hWnd, // handle of destination window
|
|
UINT Msg, // message to send
|
|
WPARAM wParam, // first message parameter
|
|
LPARAM lParam // second message parameter
|
|
)
|
|
{
|
|
LRESULT rv = 0;
|
|
|
|
ASSERT(pClx->semSendDone);
|
|
ASSERT(pClx->semSendReady);
|
|
ASSERT(pClx->hSendMsgThread);
|
|
|
|
pClx->msg.hwnd = hWnd;
|
|
pClx->msg.message = Msg;
|
|
pClx->msg.wParam = wParam;
|
|
pClx->msg.lParam = lParam;
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
VOID
|
|
_ClxDestroySendMsgThread(PCLXINFO pClx)
|
|
{
|
|
|
|
if (!pClx)
|
|
goto exitpt1;
|
|
|
|
if (!pClx->semSendDone || !pClx->semSendReady || !pClx->hSendMsgThread)
|
|
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->semSendDone)
|
|
{
|
|
CloseHandle(pClx->semSendDone);
|
|
pClx->semSendDone = NULL;
|
|
}
|
|
|
|
if (pClx->semSendReady)
|
|
{
|
|
CloseHandle(pClx->semSendReady);
|
|
pClx->semSendReady = NULL;
|
|
}
|
|
|
|
pClx->hSendMsgThread = 0;
|
|
|
|
exitpt1:
|
|
;
|
|
}
|
|
|
|
#endif // OS_WIN32
|
|
#endif // !OS_WINCE
|
|
|