/*++ * File name: * tclient.c * Contents: * Initialization code. Global feedback thread * * Copyright (C) 1998-1999 Microsoft Corp. * --*/ #include #include #include #include #include #include #include #include #include #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 // 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(); }