1076 lines
30 KiB
C
1076 lines
30 KiB
C
/*++
|
|
* File name:
|
|
* tclient.c
|
|
* Contents:
|
|
* Initialization code. Global feedback thread
|
|
*
|
|
* Copyright (C) 1998-1999 Microsoft Corp.
|
|
*
|
|
--*/
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <malloc.h>
|
|
#include <process.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <direct.h>
|
|
#include <winsock.h>
|
|
|
|
#include "tclient.h"
|
|
#define PROTOCOLAPI __declspec(dllexport)
|
|
#include "protocol.h"
|
|
#include "queues.h"
|
|
#include "bmpcache.h"
|
|
#include "rclx.h"
|
|
#include "extraexp.h"
|
|
|
|
/*
|
|
* Internal functions definitions
|
|
*/
|
|
BOOL _RegisterWindow(VOID);
|
|
LRESULT CALLBACK _FeedbackWndProc( HWND , UINT, WPARAM, LPARAM);
|
|
BOOL _CreateFeedbackThread(VOID);
|
|
VOID _DestroyFeedbackThread(VOID);
|
|
VOID _CleanStuff(VOID);
|
|
VOID _ReadINIValues(VOID);
|
|
|
|
/*
|
|
* Global data
|
|
*/
|
|
HWND g_hWindow = NULL; // Window handle for the feedback thread
|
|
HINSTANCE g_hInstance = NULL; // Dll instance
|
|
PWAIT4STRING g_pWaitQHead = NULL; // Linked list for waited events
|
|
PFNPRINTMESSAGE g_pfnPrintMessage= NULL;// Trace function (from smclient)
|
|
PCONNECTINFO g_pClientQHead = NULL; // LL of all threads
|
|
HANDLE g_hThread = NULL; // Feedback Thread handle
|
|
UINT WAIT4STR_TIMEOUT= 600000;
|
|
// Global timeout value. Default:10 mins
|
|
// Optional from smclient.ini,
|
|
// tclient section
|
|
// timeout=600 (in seconds)
|
|
UINT CONNECT_TIMEOUT = 35000;
|
|
// Connect timeout value
|
|
// Default is 35 seconds
|
|
// This value can be changed from
|
|
// smclient.ini [tclient]
|
|
// contimeout=XXX seconds
|
|
|
|
LPCRITICAL_SECTION g_lpcsGuardWaitQueue = NULL;
|
|
// Guards the access to all
|
|
// global variables
|
|
|
|
// Some strings we are expecting and response actions
|
|
// Those are used in SCConnect, _Logon and SCStart
|
|
WCHAR g_strStartRun[MAX_STRING_LENGTH]; // Indicates that start menu is up
|
|
WCHAR g_strStartRun_Act[MAX_STRING_LENGTH]; // Chooses "Run..." from start menu
|
|
WCHAR g_strRunBox[MAX_STRING_LENGTH]; // Indication for Run... box
|
|
WCHAR g_strWinlogon[MAX_STRING_LENGTH]; // Indication that winlogon is up
|
|
WCHAR g_strWinlogon_Act[MAX_STRING_LENGTH]; // Action when winlogon appears (chooses username)
|
|
WCHAR g_strPriorWinlogon[MAX_STRING_LENGTH]; // Idication before winlogon (for example
|
|
// if Options >> appears, i.e domain
|
|
// box is hidden
|
|
WCHAR g_strPriorWinlogon_Act[MAX_STRING_LENGTH]; // Shows the domain box (Alt+O)
|
|
WCHAR g_strNTSecurity[MAX_STRING_LENGTH]; // Indication of NT Security box
|
|
WCHAR g_strNTSecurity_Act[MAX_STRING_LENGTH]; // Action on that box (logoff)
|
|
WCHAR g_strSureLogoff[MAX_STRING_LENGTH]; // Inidcation of "Are you sure" box
|
|
WCHAR g_strSureLogoffAct[MAX_STRING_LENGTH]; // Action on "Are you sure"
|
|
WCHAR g_strStartLogoff[MAX_STRING_LENGTH]; // How to invode Windows Security dialog from the start menu
|
|
WCHAR g_strLogonErrorMessage[MAX_STRING_LENGTH];
|
|
// Caption of an error box
|
|
// which appears while logging in
|
|
// responce is <Enter>
|
|
// while loging off
|
|
WCHAR g_strLogonDisabled[MAX_STRING_LENGTH];
|
|
// Caption of the box when
|
|
// logon is disabled
|
|
|
|
CHAR g_strClientCaption[MAX_STRING_LENGTH];
|
|
CHAR g_strDisconnectDialogBox[MAX_STRING_LENGTH];
|
|
CHAR g_strYesNoShutdown[MAX_STRING_LENGTH];
|
|
CHAR g_strClientImg[MAX_STRING_LENGTH];
|
|
|
|
// Low Speed option
|
|
// Cache Bitmaps on disc option
|
|
// by default, client will not run
|
|
// in full screen
|
|
INT g_ConnectionFlags = TSFLAG_COMPRESSION|TSFLAG_BITMAPCACHE;
|
|
|
|
/*++
|
|
* Function:
|
|
* InitDone
|
|
*
|
|
* Description:
|
|
* Initialize/delete global data. Create/destroy
|
|
* feedback thread
|
|
*
|
|
* Arguments:
|
|
* hDllInst - Instance to the DLL
|
|
* bInit - TRUE if initialize
|
|
*
|
|
* Return value:
|
|
* TRUE if succeeds
|
|
*
|
|
--*/
|
|
int InitDone(HINSTANCE hDllInst, int bInit)
|
|
{
|
|
int rv = TRUE;
|
|
|
|
if (bInit)
|
|
{
|
|
CHAR szMyLibName[_MAX_PATH];
|
|
|
|
g_lpcsGuardWaitQueue = malloc(sizeof(*g_lpcsGuardWaitQueue));
|
|
if (!g_lpcsGuardWaitQueue)
|
|
{
|
|
rv = FALSE;
|
|
goto exitpt;
|
|
}
|
|
|
|
// Overreference the library
|
|
// The reason for that is beacuse an internal thread is created.
|
|
// When the library trys to unload it can't kill that thread
|
|
// and wait for its handle to get signaled, because
|
|
// the thread itself wants to go to DllEntry and this
|
|
// causes a deadlock. The best solution is to overreference the
|
|
// handle so the library is unload at the end of the process
|
|
if (!GetModuleFileName(hDllInst, szMyLibName, sizeof(szMyLibName)))
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't overref the dll. Exit.\n"));
|
|
free(g_lpcsGuardWaitQueue);
|
|
rv = FALSE;
|
|
goto exitpt;
|
|
}
|
|
|
|
if (!LoadLibrary(szMyLibName))
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Can't overref the dll. Exit.\n"));
|
|
free(g_lpcsGuardWaitQueue);
|
|
rv = FALSE;
|
|
goto exitpt;
|
|
}
|
|
|
|
g_hInstance = hDllInst;
|
|
InitializeCriticalSection(g_lpcsGuardWaitQueue);
|
|
InitCache();
|
|
_ReadINIValues();
|
|
if (_RegisterWindow()) // If failed to register the window,
|
|
_CreateFeedbackThread(); // means the feedback thread will
|
|
// not work
|
|
} else
|
|
{
|
|
if (g_pWaitQHead || g_pClientQHead)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"The Library unload is unclean. Will try to fix this\n"));
|
|
_CleanStuff();
|
|
}
|
|
_DestroyFeedbackThread();
|
|
DeleteCache();
|
|
if (g_lpcsGuardWaitQueue)
|
|
{
|
|
DeleteCriticalSection(g_lpcsGuardWaitQueue);
|
|
free(g_lpcsGuardWaitQueue);
|
|
}
|
|
g_lpcsGuardWaitQueue = NULL;
|
|
g_hInstance = NULL;
|
|
g_pfnPrintMessage = NULL;
|
|
}
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Used by perl script to break into the kernel debugger
|
|
*/
|
|
void MyBreak(void)
|
|
{
|
|
TRACE((INFO_MESSAGE, "Break is called\n"));
|
|
DebugBreak();
|
|
}
|
|
|
|
VOID
|
|
_ConvertAnsiToUnicode( LPWSTR wszDst, LPWSTR wszSrc )
|
|
{
|
|
#define _TOHEX(_d_) ((_d_ <= '9' && _d_ >= '0')?_d_ - '0': \
|
|
(_d_ <= 'f' && _d_ >= 'a')?_d_ - 'a' + 10: \
|
|
(_d_ <= 'F' && _d_ >= 'F')?_d_ - 'A' + 10:0)
|
|
|
|
while( wszSrc[0] && wszSrc[1] && wszSrc[2] && wszSrc[3] )
|
|
{
|
|
*wszDst = (_TOHEX(wszSrc[0]) << 4) + _TOHEX(wszSrc[1]) +
|
|
(((_TOHEX(wszSrc[2]) << 4) + _TOHEX(wszSrc[3])) << 8);
|
|
wszDst ++;
|
|
wszSrc += 4;
|
|
}
|
|
*wszDst = 0;
|
|
#undef _TOHEX
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Wrappers for GetPrivateProfileW, on Win95 there's no UNICODE veriosn
|
|
* of this function
|
|
*
|
|
*/
|
|
DWORD
|
|
_WrpGetPrivateProfileStringW(
|
|
LPCWSTR lpAppName,
|
|
LPCWSTR lpKeyName,
|
|
LPCWSTR lpDefault,
|
|
LPWSTR lpReturnedString,
|
|
DWORD nSize,
|
|
LPCWSTR lpFileName)
|
|
{
|
|
DWORD rv = 0;
|
|
CHAR szAppName[MAX_STRING_LENGTH];
|
|
CHAR szKeyName[MAX_STRING_LENGTH];
|
|
CHAR szDefault[MAX_STRING_LENGTH];
|
|
CHAR szReturnedString[MAX_STRING_LENGTH];
|
|
CHAR szFileName[MAX_STRING_LENGTH];
|
|
|
|
rv = GetPrivateProfileStringW(
|
|
lpAppName,
|
|
lpKeyName,
|
|
lpDefault,
|
|
lpReturnedString,
|
|
nSize,
|
|
lpFileName);
|
|
|
|
if (rv)
|
|
goto exitpt;
|
|
|
|
// Call the ANSI version
|
|
_snprintf(szAppName, MAX_STRING_LENGTH, "%S", lpAppName);
|
|
_snprintf(szKeyName, MAX_STRING_LENGTH, "%S", lpKeyName);
|
|
_snprintf(szFileName, MAX_STRING_LENGTH, "%S", lpFileName);
|
|
_snprintf(szDefault, MAX_STRING_LENGTH, "%S", lpDefault);
|
|
|
|
rv = GetPrivateProfileString(
|
|
szAppName,
|
|
szKeyName,
|
|
szDefault,
|
|
szReturnedString,
|
|
sizeof(szReturnedString),
|
|
szFileName);
|
|
|
|
_snwprintf(lpReturnedString, nSize, L"%S", szReturnedString);
|
|
|
|
exitpt:
|
|
if ( L'\\' == lpReturnedString[0] &&
|
|
L'U' == towupper(lpReturnedString[1]))
|
|
_ConvertAnsiToUnicode( lpReturnedString, lpReturnedString + 2 );
|
|
|
|
return rv;
|
|
}
|
|
|
|
UINT
|
|
_WrpGetPrivateProfileIntW(
|
|
LPCWSTR lpAppName,
|
|
LPCWSTR lpKeyName,
|
|
INT nDefault,
|
|
LPCWSTR lpFileName)
|
|
{
|
|
UINT rv = (UINT)-1;
|
|
CHAR szAppName[MAX_STRING_LENGTH];
|
|
CHAR szKeyName[MAX_STRING_LENGTH];
|
|
CHAR szFileName[MAX_STRING_LENGTH];
|
|
|
|
rv = GetPrivateProfileIntW(
|
|
lpAppName,
|
|
lpKeyName,
|
|
nDefault,
|
|
lpFileName);
|
|
|
|
if (rv != (UINT)-1 && rv)
|
|
goto exitpt;
|
|
|
|
// Call the ANSI version
|
|
_snprintf(szAppName, MAX_STRING_LENGTH, "%S", lpAppName);
|
|
_snprintf(szKeyName, MAX_STRING_LENGTH, "%S", lpKeyName);
|
|
_snprintf(szFileName, MAX_STRING_LENGTH, "%S", lpFileName);
|
|
|
|
rv = GetPrivateProfileInt(
|
|
szAppName,
|
|
szKeyName,
|
|
nDefault,
|
|
szFileName);
|
|
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
|
|
/*++
|
|
* Function:
|
|
* _ReadINIValues
|
|
*
|
|
* Description:
|
|
* Reads smclient.ini, section [tclient], variable "timeout"
|
|
* This is a global timeout for Wait4Str etc
|
|
* Also read some other values
|
|
* Arguments:
|
|
* none
|
|
* Return value:
|
|
* none
|
|
*
|
|
--*/
|
|
VOID _ReadINIValues(VOID)
|
|
{
|
|
UINT nNew;
|
|
WCHAR szIniFileName[_MAX_PATH];
|
|
WCHAR szBuff[ 4 * MAX_STRING_LENGTH];
|
|
WCHAR szBuffDef[MAX_STRING_LENGTH];
|
|
BOOL bFlag;
|
|
|
|
// Construct INI path
|
|
*szIniFileName = 0;
|
|
if (!_wgetcwd (
|
|
szIniFileName,
|
|
(int)(sizeof(szIniFileName)/sizeof(WCHAR) - wcslen(SMCLIENT_INI) - 1))
|
|
)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "Current directory length too long.\n"));
|
|
}
|
|
wcscat(szIniFileName, SMCLIENT_INI);
|
|
|
|
// Get the timeout value
|
|
nNew = _WrpGetPrivateProfileIntW(
|
|
TCLIENT_INI_SECTION,
|
|
L"timeout",
|
|
600,
|
|
szIniFileName);
|
|
|
|
if (nNew)
|
|
{
|
|
WAIT4STR_TIMEOUT = nNew * 1000;
|
|
TRACE((INFO_MESSAGE, "New timeout: %d seconds\n", nNew));
|
|
}
|
|
|
|
nNew = _WrpGetPrivateProfileIntW(
|
|
TCLIENT_INI_SECTION,
|
|
L"contimeout",
|
|
35,
|
|
szIniFileName);
|
|
|
|
if (nNew)
|
|
{
|
|
CONNECT_TIMEOUT = nNew * 1000;
|
|
TRACE((INFO_MESSAGE, "New timeout: %d seconds\n", nNew));
|
|
}
|
|
|
|
g_ConnectionFlags = 0;
|
|
bFlag =
|
|
_WrpGetPrivateProfileIntW(
|
|
TCLIENT_INI_SECTION,
|
|
L"LowSpeed",
|
|
0,
|
|
szIniFileName);
|
|
if (bFlag)
|
|
g_ConnectionFlags |=TSFLAG_COMPRESSION;
|
|
|
|
bFlag =
|
|
_WrpGetPrivateProfileIntW(
|
|
TCLIENT_INI_SECTION,
|
|
L"PersistentCache",
|
|
0,
|
|
szIniFileName);
|
|
if (bFlag)
|
|
g_ConnectionFlags |=TSFLAG_BITMAPCACHE;
|
|
|
|
bFlag =
|
|
_WrpGetPrivateProfileIntW(
|
|
TCLIENT_INI_SECTION,
|
|
L"FullScreen",
|
|
0,
|
|
szIniFileName);
|
|
if (bFlag)
|
|
g_ConnectionFlags |=TSFLAG_FULLSCREEN;
|
|
|
|
// read the strings
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"StartRun",
|
|
RUN_MENU,
|
|
g_strStartRun,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"StartLogoff",
|
|
START_LOGOFF,
|
|
g_strStartLogoff,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"StartRunAct",
|
|
RUN_ACT,
|
|
g_strStartRun_Act,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"RunBox",
|
|
RUN_BOX,
|
|
g_strRunBox,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"WinLogon",
|
|
WINLOGON_USERNAME,
|
|
g_strWinlogon,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"WinLogonAct",
|
|
WINLOGON_ACT,
|
|
g_strWinlogon_Act,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"PriorWinLogon",
|
|
PRIOR_WINLOGON,
|
|
g_strPriorWinlogon,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"PriorWinLogonAct",
|
|
PRIOR_WINLOGON_ACT,
|
|
g_strPriorWinlogon_Act,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"NTSecurity",
|
|
WINDOWS_NT_SECURITY,
|
|
g_strNTSecurity,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"NTSecurityAct",
|
|
WINDOWS_NT_SECURITY_ACT,
|
|
g_strNTSecurity_Act,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"SureLogoff",
|
|
ARE_YOU_SURE,
|
|
g_strSureLogoff,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"SureLogoffAct",
|
|
SURE_LOGOFF_ACT,
|
|
g_strSureLogoffAct,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"LogonErrorMessage",
|
|
LOGON_ERROR_MESSAGE,
|
|
g_strLogonErrorMessage,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"LogonDisabled",
|
|
LOGON_DISABLED_MESSAGE,
|
|
g_strLogonDisabled,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
|
|
_snwprintf(szBuffDef, sizeof(szBuffDef), L"%S", CLIENT_CAPTION);
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"UIClientCaption",
|
|
szBuffDef,
|
|
szBuff,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
_snprintf(g_strClientCaption, MAX_STRING_LENGTH, "%S", szBuff);
|
|
|
|
_snwprintf(szBuffDef, sizeof(szBuffDef), L"%S", DISCONNECT_DIALOG_BOX);
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"UIDisconnectDialogBox",
|
|
szBuffDef,
|
|
szBuff,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
_snprintf(g_strDisconnectDialogBox, MAX_STRING_LENGTH, "%S", szBuff);
|
|
|
|
_snwprintf(szBuffDef, sizeof(szBuffDef), L"%S", YES_NO_SHUTDOWN);
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"UIYesNoDisconnect",
|
|
szBuffDef,
|
|
szBuff,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
_snprintf(g_strYesNoShutdown, MAX_STRING_LENGTH, "%S", szBuff);
|
|
|
|
_snwprintf(szBuffDef, sizeof(szBuffDef), L"%S", CLIENT_EXE);
|
|
_WrpGetPrivateProfileStringW(
|
|
TCLIENT_INI_SECTION,
|
|
L"ClientImage",
|
|
szBuffDef,
|
|
szBuff,
|
|
MAX_STRING_LENGTH,
|
|
szIniFileName);
|
|
_snprintf(g_strClientImg, MAX_STRING_LENGTH, "%S", szBuff);
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _FeedbackWndProc
|
|
* Description:
|
|
* Window proc wich dispatches messages containing feedback
|
|
* The messages are usualy sent by RDP clients
|
|
*
|
|
--*/
|
|
LRESULT CALLBACK _FeedbackWndProc( HWND hwnd,
|
|
UINT uiMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
HANDLE hMapF = NULL;
|
|
|
|
switch (uiMessage)
|
|
{
|
|
case WM_FB_TEXTOUT:
|
|
_TextOutReceived((DWORD)wParam, (HANDLE)lParam);
|
|
break;
|
|
case WM_FB_GLYPHOUT:
|
|
_GlyphReceived((DWORD)wParam, (HANDLE)lParam);
|
|
break;
|
|
case WM_FB_DISCONNECT:
|
|
_SetClientDead(lParam);
|
|
_CheckForWorkerWaitingDisconnect(lParam);
|
|
_CancelWaitingWorker(lParam);
|
|
break;
|
|
case WM_FB_CONNECT:
|
|
_CheckForWorkerWaitingConnect((HWND)wParam, lParam);
|
|
break;
|
|
case WM_FB_LOGON:
|
|
TRACE((INFO_MESSAGE, "LOGON event, session ID=%d\n",
|
|
wParam));
|
|
_SetSessionID(lParam, (UINT)wParam);
|
|
break;
|
|
break;
|
|
case WM_FB_ACCEPTME:
|
|
return (_CheckIsAcceptable(lParam, FALSE) != NULL);
|
|
case WM_WSOCK: // Windows socket messages
|
|
RClx_DispatchWSockEvent((SOCKET)wParam, lParam);
|
|
break;
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
break;
|
|
default:
|
|
return DefWindowProc(hwnd, uiMessage, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _RegisterWindow
|
|
* Description:
|
|
* Resgisters window class for the feedback dispatcher
|
|
* Arguments:
|
|
* none
|
|
* Return value:
|
|
* TRUE on success
|
|
*
|
|
--*/
|
|
BOOL _RegisterWindow(VOID)
|
|
{
|
|
WNDCLASS wc;
|
|
BOOL rv;
|
|
DWORD dwLastErr;
|
|
|
|
memset(&wc, 0, sizeof(wc));
|
|
|
|
wc.lpfnWndProc = _FeedbackWndProc;
|
|
wc.hInstance = g_hInstance;
|
|
wc.lpszClassName = _TSTNAMEOFCLAS;
|
|
|
|
if (!RegisterClass (&wc) &&
|
|
(dwLastErr = GetLastError()) &&
|
|
dwLastErr != ERROR_CLASS_ALREADY_EXISTS)
|
|
{
|
|
TRACE((ERROR_MESSAGE,
|
|
"Can't register class. GetLastError=%d\n",
|
|
GetLastError()));
|
|
goto exitpt;
|
|
}
|
|
|
|
rv = TRUE;
|
|
exitpt:
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _GoFeedback
|
|
* Description:
|
|
* Main function for the feedback thread. The thread is created for the
|
|
* lifetime of the DLL
|
|
* Arguments:
|
|
* lpParam is unused
|
|
* Return value:
|
|
* Thread exit code
|
|
--*/
|
|
DWORD WINAPI _GoFeedback(LPVOID lpParam)
|
|
{
|
|
MSG msg;
|
|
|
|
g_hWindow = CreateWindow(
|
|
_TSTNAMEOFCLAS,
|
|
NULL, // Window name
|
|
0, // dwStyle
|
|
0, // x
|
|
0, // y
|
|
0, // nWidth
|
|
0, // nHeight
|
|
NULL, // hWndParent
|
|
NULL, // hMenu
|
|
g_hInstance,
|
|
NULL); // lpParam
|
|
|
|
if (!g_hWindow)
|
|
{
|
|
TRACE((ERROR_MESSAGE, "No feedback window handle"));
|
|
goto exitpt;
|
|
} else {
|
|
|
|
if (!RClx_Init())
|
|
TRACE((ERROR_MESSAGE, "Can't initialize RCLX\n"));
|
|
|
|
while (GetMessage (&msg, NULL, 0, 0) && msg.message != WM_FB_END)
|
|
{
|
|
DispatchMessage (&msg);
|
|
}
|
|
|
|
RClx_Done();
|
|
}
|
|
|
|
TRACE((INFO_MESSAGE, "Window/Thread destroyed\n"));
|
|
FreeLibraryAndExitThread(g_hInstance, 0);
|
|
exitpt:
|
|
return 1;
|
|
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _SetClientRegistry
|
|
* Description:
|
|
* Sets the registry prior running RDP client
|
|
* The format of the key is: smclient_PID_TID
|
|
* PID is the process ID and TID is the thread ID
|
|
* This key is deleated after the client disconnects
|
|
* Arguments:
|
|
* lpszServerName - server to which the client will connect
|
|
* xRes, yRes - clients resolution
|
|
* bLowSpeed - low speed (compression) option
|
|
* bCacheBitmaps - cache the bitmaps to the disc option
|
|
* bFullScreen - the client will be in full screen mode
|
|
* Called by:
|
|
* SCConnect
|
|
--*/
|
|
VOID
|
|
_SetClientRegistry(
|
|
LPCWSTR lpszServerName,
|
|
LPCWSTR lpszShell,
|
|
INT xRes,
|
|
INT yRes,
|
|
INT ConnectionFlags)
|
|
{
|
|
const CHAR *pData;
|
|
CHAR szServer[MAX_STRING_LENGTH];
|
|
register int i;
|
|
LONG sysrc;
|
|
HKEY key;
|
|
DWORD disposition;
|
|
DWORD dataSize;
|
|
DWORD ResId;
|
|
CHAR lpszRegistryEntry[4*MAX_STRING_LENGTH];
|
|
RECT rcDesktop = {0, 0, 0, 0};
|
|
INT desktopX, desktopY;
|
|
|
|
_snprintf(lpszRegistryEntry, sizeof(lpszRegistryEntry),
|
|
"%s\\" REG_FORMAT,
|
|
REG_BASE, GetCurrentProcessId(), GetCurrentThreadId());
|
|
|
|
// Get desktop size
|
|
GetWindowRect(GetDesktopWindow(), &rcDesktop);
|
|
desktopX = rcDesktop.right;
|
|
desktopY = rcDesktop.bottom;
|
|
|
|
// Adjust the resolution
|
|
if (desktopX < xRes || desktopY < yRes)
|
|
{
|
|
xRes = desktopX;
|
|
yRes = desktopY;
|
|
}
|
|
|
|
// Convert lpszServerName to proper format
|
|
|
|
for (i=0; i < sizeof(szServer)/sizeof(TCHAR)-1 && lpszServerName[i]; i++)
|
|
szServer[i] = (CHAR)lpszServerName[i];
|
|
|
|
szServer[i] = 0;
|
|
pData = szServer;
|
|
dataSize = (strlen(pData)+1);
|
|
|
|
// Before starting ducati client set registry with server name
|
|
|
|
sysrc = RegCreateKeyEx(HKEY_CURRENT_USER,
|
|
lpszRegistryEntry,
|
|
0, /* reserved */
|
|
NULL, /* class */
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL, /* security attributes */
|
|
&key,
|
|
&disposition);
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegCreateKeyEx failed, sysrc = %d\n", sysrc));
|
|
goto exitpt;
|
|
}
|
|
|
|
sysrc = RegSetValueEx(key,
|
|
TEXT("MRU0"),
|
|
0, // reserved
|
|
REG_SZ,
|
|
(LPBYTE)pData,
|
|
dataSize);
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
|
|
}
|
|
|
|
// Set alternative shell (if specified
|
|
if (lpszShell)
|
|
{
|
|
sysrc = RegSetValueEx(key,
|
|
TEXT("Alternate Shell"),
|
|
0, // reserved
|
|
REG_BINARY,
|
|
(LPBYTE)lpszShell,
|
|
wcslen(lpszShell) * sizeof(*lpszShell));
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
|
|
}
|
|
}
|
|
|
|
// Set the resolution
|
|
if (xRes >= 1600 && yRes >= 1200) ResId = 4;
|
|
else if (xRes >= 1280 && yRes >= 1024) ResId = 3;
|
|
else if (xRes >= 1024 && yRes >= 768) ResId = 2;
|
|
else if (xRes >= 800 && yRes >= 600) ResId = 1;
|
|
else ResId = 0; // 640x480
|
|
|
|
sysrc = RegSetValueEx(key,
|
|
"Desktop Size ID",
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&ResId,
|
|
sizeof(ResId));
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
|
|
}
|
|
|
|
ResId = 1;
|
|
sysrc = RegSetValueEx(key,
|
|
"Auto Connect",
|
|
0, // reserved
|
|
REG_DWORD,
|
|
(LPBYTE)&ResId,
|
|
sizeof(ResId));
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
|
|
}
|
|
|
|
ResId = (ConnectionFlags & TSFLAG_BITMAPCACHE)?1:0;
|
|
sysrc = RegSetValueEx(key,
|
|
"BitmapCachePersistEnable",
|
|
0, // reserved
|
|
REG_DWORD,
|
|
(LPBYTE)&ResId,
|
|
sizeof(ResId));
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
|
|
}
|
|
|
|
ResId = (ConnectionFlags & TSFLAG_COMPRESSION)?1:0;
|
|
sysrc = RegSetValueEx(key,
|
|
"Compression",
|
|
0, // reserved
|
|
REG_DWORD,
|
|
(LPBYTE)&ResId,
|
|
sizeof(ResId));
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
|
|
}
|
|
|
|
if (ConnectionFlags & TSFLAG_FULLSCREEN)
|
|
{
|
|
ResId = 2;
|
|
sysrc = RegSetValueEx(key,
|
|
"Screen Mode ID",
|
|
0, // reserved
|
|
REG_DWORD,
|
|
(LPBYTE)&ResId,
|
|
sizeof(ResId));
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"RegSetValue failed, status = %d\n", sysrc));
|
|
}
|
|
}
|
|
|
|
RegCloseKey(key);
|
|
|
|
ResId = 1;
|
|
|
|
sysrc = RegCreateKeyEx(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, "RegCreateKeyEx failed, sysrc = %d\n", sysrc));
|
|
goto exitpt;
|
|
}
|
|
|
|
sysrc = RegSetValueEx(key,
|
|
ALLOW_BACKGROUND_INPUT,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&ResId,
|
|
sizeof(ResId));
|
|
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
|
|
}
|
|
|
|
RegCloseKey(key);
|
|
|
|
exitpt:
|
|
;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _DeleteClientRegistry
|
|
* Description:
|
|
* Deletes the key set by _SetClientRegistry
|
|
* Called by:
|
|
* SCDisconnect
|
|
--*/
|
|
VOID _DeleteClientRegistry(PCONNECTINFO pCI)
|
|
{
|
|
CHAR lpszRegistryEntry[4*MAX_STRING_LENGTH];
|
|
LONG sysrc;
|
|
|
|
_snprintf(lpszRegistryEntry, sizeof(lpszRegistryEntry),
|
|
"%s\\" REG_FORMAT,
|
|
REG_BASE, GetCurrentProcessId(), pCI->OwnerThreadId);
|
|
|
|
sysrc = RegDeleteKey(HKEY_CURRENT_USER, lpszRegistryEntry);
|
|
if (sysrc != ERROR_SUCCESS)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "RegDeleteKey failed, status = %d\n", sysrc));
|
|
}
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _CreateFeedbackThread
|
|
* Description:
|
|
* Creates the feedback thread
|
|
* Called by:
|
|
* InitDone
|
|
--*/
|
|
BOOL _CreateFeedbackThread(VOID)
|
|
{
|
|
BOOL rv = TRUE;
|
|
// Register feedback window class
|
|
WNDCLASS wc;
|
|
DWORD dwThreadId, dwLastErr;
|
|
|
|
g_hThread = (HANDLE)
|
|
_beginthreadex
|
|
(NULL,
|
|
0,
|
|
(unsigned (__stdcall *)(void*))_GoFeedback,
|
|
NULL,
|
|
0,
|
|
&dwThreadId);
|
|
|
|
if (!g_hThread) {
|
|
TRACE((ERROR_MESSAGE, "Couldn't create thread\n"));
|
|
rv = FALSE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _DestroyFeedbackThread
|
|
* Description:
|
|
* Destroys the thread created by _CreateFeedbackThread
|
|
* Called by:
|
|
* InitDone
|
|
--*/
|
|
VOID _DestroyFeedbackThread(VOID)
|
|
{
|
|
|
|
if (g_hThread)
|
|
{
|
|
DWORD dwWait;
|
|
CHAR szMyLibName[_MAX_PATH];
|
|
|
|
// Closing feedback thread
|
|
|
|
PostMessage(g_hWindow, WM_FB_END, 0, 0);
|
|
TRACE((INFO_MESSAGE, "Closing DLL thread\n"));
|
|
|
|
// Dedstroy the window
|
|
DestroyWindow(g_hWindow);
|
|
|
|
// CloseHandle(g_hThread);
|
|
g_hThread = NULL;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
* Function:
|
|
* _CleanStuff
|
|
* Description:
|
|
* Cleans the global queues. Closes any resources
|
|
* Called by:
|
|
* InitDone
|
|
--*/
|
|
VOID _CleanStuff(VOID)
|
|
{
|
|
|
|
// Thread safe, bacause is executed from DllEntry
|
|
|
|
while (g_pClientQHead)
|
|
{
|
|
TRACE((WARNING_MESSAGE, "Cleaning connection info: 0x%x\n",
|
|
g_pClientQHead));
|
|
SCDisconnect(g_pClientQHead);
|
|
}
|
|
#if 0
|
|
if (g_pClientQHead)
|
|
{
|
|
PCONNECTINFO pNext, pIter = g_pClientQHead;
|
|
while (pIter)
|
|
{
|
|
int nEv;
|
|
DWORD wres;
|
|
|
|
TRACE((WARNING_MESSAGE, "Cleaning connection info: 0x%x\n", pIter));
|
|
// Clear Events
|
|
if (pIter->evWait4Str)
|
|
{
|
|
CloseHandle(pIter->evWait4Str);
|
|
pIter->evWait4Str = NULL;
|
|
}
|
|
|
|
for (nEv = 0; nEv < pIter->nChatNum; nEv ++)
|
|
CloseHandle(pIter->aevChatSeq[nEv]);
|
|
|
|
pIter->nChatNum = 0;
|
|
|
|
// Clear Processes
|
|
do {
|
|
SendMessage(pIter->hClient, WM_CLOSE, 0, 0);
|
|
} while((wres = WaitForSingleObject(pIter->hProcess, WAIT4STR_TIMEOUT/4) == WAIT_TIMEOUT));
|
|
|
|
if (wres == WAIT_TIMEOUT)
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't close process. WaitForSingleObject timeouts\n"));
|
|
TRACE((WARNING_MESSAGE,
|
|
"Process #%d will be killed\n",
|
|
pIter->dwProcessId ));
|
|
if (!TerminateProcess(pIter->hProcess, 1))
|
|
{
|
|
TRACE((WARNING_MESSAGE,
|
|
"Can't kill process #%d. GetLastError=%d\n",
|
|
pIter->dwProcessId, GetLastError()));
|
|
}
|
|
}
|
|
|
|
TRACE((WARNING_MESSAGE, "Closing process\n"));
|
|
|
|
if (pIter->hProcess)
|
|
CloseHandle(pIter->hProcess);
|
|
if (pIter->hThread)
|
|
CloseHandle(pIter->hThread);
|
|
|
|
pIter->hProcess = pIter->hThread = NULL;
|
|
|
|
// Free the structures
|
|
pNext = pIter->pNext;
|
|
free(pNext);
|
|
pIter = pNext;
|
|
}
|
|
}
|
|
|
|
#endif // 0
|
|
}
|
|
|
|
VOID _TClientAssert( LPCTSTR filename, INT line)
|
|
{
|
|
TRACE(( ERROR_MESSAGE, "ASSERT %s line: %d\n", filename, line));
|
|
DebugBreak();
|
|
}
|