Source code of Windows XP (NT5)
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.

1075 lines
30 KiB

  1. /*++
  2. * File name:
  3. * tclient.c
  4. * Contents:
  5. * Initialization code. Global feedback thread
  6. *
  7. * Copyright (C) 1998-1999 Microsoft Corp.
  8. *
  9. --*/
  10. #include <windows.h>
  11. #include <stdio.h>
  12. #include <malloc.h>
  13. #include <process.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16. #include <ctype.h>
  17. #include <direct.h>
  18. #include <winsock.h>
  19. #include "tclient.h"
  20. #define PROTOCOLAPI __declspec(dllexport)
  21. #include "protocol.h"
  22. #include "queues.h"
  23. #include "bmpcache.h"
  24. #include "rclx.h"
  25. #include "extraexp.h"
  26. /*
  27. * Internal functions definitions
  28. */
  29. BOOL _RegisterWindow(VOID);
  30. LRESULT CALLBACK _FeedbackWndProc( HWND , UINT, WPARAM, LPARAM);
  31. BOOL _CreateFeedbackThread(VOID);
  32. VOID _DestroyFeedbackThread(VOID);
  33. VOID _CleanStuff(VOID);
  34. VOID _ReadINIValues(VOID);
  35. /*
  36. * Global data
  37. */
  38. HWND g_hWindow = NULL; // Window handle for the feedback thread
  39. HINSTANCE g_hInstance = NULL; // Dll instance
  40. PWAIT4STRING g_pWaitQHead = NULL; // Linked list for waited events
  41. PFNPRINTMESSAGE g_pfnPrintMessage= NULL;// Trace function (from smclient)
  42. PCONNECTINFO g_pClientQHead = NULL; // LL of all threads
  43. HANDLE g_hThread = NULL; // Feedback Thread handle
  44. UINT WAIT4STR_TIMEOUT= 600000;
  45. // Global timeout value. Default:10 mins
  46. // Optional from smclient.ini,
  47. // tclient section
  48. // timeout=600 (in seconds)
  49. UINT CONNECT_TIMEOUT = 35000;
  50. // Connect timeout value
  51. // Default is 35 seconds
  52. // This value can be changed from
  53. // smclient.ini [tclient]
  54. // contimeout=XXX seconds
  55. LPCRITICAL_SECTION g_lpcsGuardWaitQueue = NULL;
  56. // Guards the access to all
  57. // global variables
  58. // Some strings we are expecting and response actions
  59. // Those are used in SCConnect, _Logon and SCStart
  60. WCHAR g_strStartRun[MAX_STRING_LENGTH]; // Indicates that start menu is up
  61. WCHAR g_strStartRun_Act[MAX_STRING_LENGTH]; // Chooses "Run..." from start menu
  62. WCHAR g_strRunBox[MAX_STRING_LENGTH]; // Indication for Run... box
  63. WCHAR g_strWinlogon[MAX_STRING_LENGTH]; // Indication that winlogon is up
  64. WCHAR g_strWinlogon_Act[MAX_STRING_LENGTH]; // Action when winlogon appears (chooses username)
  65. WCHAR g_strPriorWinlogon[MAX_STRING_LENGTH]; // Idication before winlogon (for example
  66. // if Options >> appears, i.e domain
  67. // box is hidden
  68. WCHAR g_strPriorWinlogon_Act[MAX_STRING_LENGTH]; // Shows the domain box (Alt+O)
  69. WCHAR g_strNTSecurity[MAX_STRING_LENGTH]; // Indication of NT Security box
  70. WCHAR g_strNTSecurity_Act[MAX_STRING_LENGTH]; // Action on that box (logoff)
  71. WCHAR g_strSureLogoff[MAX_STRING_LENGTH]; // Inidcation of "Are you sure" box
  72. WCHAR g_strSureLogoffAct[MAX_STRING_LENGTH]; // Action on "Are you sure"
  73. WCHAR g_strStartLogoff[MAX_STRING_LENGTH]; // How to invode Windows Security dialog from the start menu
  74. WCHAR g_strLogonErrorMessage[MAX_STRING_LENGTH];
  75. // Caption of an error box
  76. // which appears while logging in
  77. // responce is <Enter>
  78. // while loging off
  79. WCHAR g_strLogonDisabled[MAX_STRING_LENGTH];
  80. // Caption of the box when
  81. // logon is disabled
  82. CHAR g_strClientCaption[MAX_STRING_LENGTH];
  83. CHAR g_strDisconnectDialogBox[MAX_STRING_LENGTH];
  84. CHAR g_strYesNoShutdown[MAX_STRING_LENGTH];
  85. CHAR g_strClientImg[MAX_STRING_LENGTH];
  86. // Low Speed option
  87. // Cache Bitmaps on disc option
  88. // by default, client will not run
  89. // in full screen
  90. INT g_ConnectionFlags = TSFLAG_COMPRESSION|TSFLAG_BITMAPCACHE;
  91. /*++
  92. * Function:
  93. * InitDone
  94. *
  95. * Description:
  96. * Initialize/delete global data. Create/destroy
  97. * feedback thread
  98. *
  99. * Arguments:
  100. * hDllInst - Instance to the DLL
  101. * bInit - TRUE if initialize
  102. *
  103. * Return value:
  104. * TRUE if succeeds
  105. *
  106. --*/
  107. int InitDone(HINSTANCE hDllInst, int bInit)
  108. {
  109. int rv = TRUE;
  110. if (bInit)
  111. {
  112. CHAR szMyLibName[_MAX_PATH];
  113. g_lpcsGuardWaitQueue = malloc(sizeof(*g_lpcsGuardWaitQueue));
  114. if (!g_lpcsGuardWaitQueue)
  115. {
  116. rv = FALSE;
  117. goto exitpt;
  118. }
  119. // Overreference the library
  120. // The reason for that is beacuse an internal thread is created.
  121. // When the library trys to unload it can't kill that thread
  122. // and wait for its handle to get signaled, because
  123. // the thread itself wants to go to DllEntry and this
  124. // causes a deadlock. The best solution is to overreference the
  125. // handle so the library is unload at the end of the process
  126. if (!GetModuleFileName(hDllInst, szMyLibName, sizeof(szMyLibName)))
  127. {
  128. TRACE((ERROR_MESSAGE, "Can't overref the dll. Exit.\n"));
  129. free(g_lpcsGuardWaitQueue);
  130. rv = FALSE;
  131. goto exitpt;
  132. }
  133. if (!LoadLibrary(szMyLibName))
  134. {
  135. TRACE((ERROR_MESSAGE, "Can't overref the dll. Exit.\n"));
  136. free(g_lpcsGuardWaitQueue);
  137. rv = FALSE;
  138. goto exitpt;
  139. }
  140. g_hInstance = hDllInst;
  141. InitializeCriticalSection(g_lpcsGuardWaitQueue);
  142. InitCache();
  143. _ReadINIValues();
  144. if (_RegisterWindow()) // If failed to register the window,
  145. _CreateFeedbackThread(); // means the feedback thread will
  146. // not work
  147. } else
  148. {
  149. if (g_pWaitQHead || g_pClientQHead)
  150. {
  151. TRACE((ERROR_MESSAGE,
  152. "The Library unload is unclean. Will try to fix this\n"));
  153. _CleanStuff();
  154. }
  155. _DestroyFeedbackThread();
  156. DeleteCache();
  157. if (g_lpcsGuardWaitQueue)
  158. {
  159. DeleteCriticalSection(g_lpcsGuardWaitQueue);
  160. free(g_lpcsGuardWaitQueue);
  161. }
  162. g_lpcsGuardWaitQueue = NULL;
  163. g_hInstance = NULL;
  164. g_pfnPrintMessage = NULL;
  165. }
  166. exitpt:
  167. return rv;
  168. }
  169. /*
  170. * Used by perl script to break into the kernel debugger
  171. */
  172. void MyBreak(void)
  173. {
  174. TRACE((INFO_MESSAGE, "Break is called\n"));
  175. DebugBreak();
  176. }
  177. VOID
  178. _ConvertAnsiToUnicode( LPWSTR wszDst, LPWSTR wszSrc )
  179. {
  180. #define _TOHEX(_d_) ((_d_ <= '9' && _d_ >= '0')?_d_ - '0': \
  181. (_d_ <= 'f' && _d_ >= 'a')?_d_ - 'a' + 10: \
  182. (_d_ <= 'F' && _d_ >= 'F')?_d_ - 'A' + 10:0)
  183. while( wszSrc[0] && wszSrc[1] && wszSrc[2] && wszSrc[3] )
  184. {
  185. *wszDst = (_TOHEX(wszSrc[0]) << 4) + _TOHEX(wszSrc[1]) +
  186. (((_TOHEX(wszSrc[2]) << 4) + _TOHEX(wszSrc[3])) << 8);
  187. wszDst ++;
  188. wszSrc += 4;
  189. }
  190. *wszDst = 0;
  191. #undef _TOHEX
  192. }
  193. /*
  194. *
  195. * Wrappers for GetPrivateProfileW, on Win95 there's no UNICODE veriosn
  196. * of this function
  197. *
  198. */
  199. DWORD
  200. _WrpGetPrivateProfileStringW(
  201. LPCWSTR lpAppName,
  202. LPCWSTR lpKeyName,
  203. LPCWSTR lpDefault,
  204. LPWSTR lpReturnedString,
  205. DWORD nSize,
  206. LPCWSTR lpFileName)
  207. {
  208. DWORD rv = 0;
  209. CHAR szAppName[MAX_STRING_LENGTH];
  210. CHAR szKeyName[MAX_STRING_LENGTH];
  211. CHAR szDefault[MAX_STRING_LENGTH];
  212. CHAR szReturnedString[MAX_STRING_LENGTH];
  213. CHAR szFileName[MAX_STRING_LENGTH];
  214. rv = GetPrivateProfileStringW(
  215. lpAppName,
  216. lpKeyName,
  217. lpDefault,
  218. lpReturnedString,
  219. nSize,
  220. lpFileName);
  221. if (rv)
  222. goto exitpt;
  223. // Call the ANSI version
  224. _snprintf(szAppName, MAX_STRING_LENGTH, "%S", lpAppName);
  225. _snprintf(szKeyName, MAX_STRING_LENGTH, "%S", lpKeyName);
  226. _snprintf(szFileName, MAX_STRING_LENGTH, "%S", lpFileName);
  227. _snprintf(szDefault, MAX_STRING_LENGTH, "%S", lpDefault);
  228. rv = GetPrivateProfileString(
  229. szAppName,
  230. szKeyName,
  231. szDefault,
  232. szReturnedString,
  233. sizeof(szReturnedString),
  234. szFileName);
  235. _snwprintf(lpReturnedString, nSize, L"%S", szReturnedString);
  236. exitpt:
  237. if ( L'\\' == lpReturnedString[0] &&
  238. L'U' == towupper(lpReturnedString[1]))
  239. _ConvertAnsiToUnicode( lpReturnedString, lpReturnedString + 2 );
  240. return rv;
  241. }
  242. UINT
  243. _WrpGetPrivateProfileIntW(
  244. LPCWSTR lpAppName,
  245. LPCWSTR lpKeyName,
  246. INT nDefault,
  247. LPCWSTR lpFileName)
  248. {
  249. UINT rv = (UINT)-1;
  250. CHAR szAppName[MAX_STRING_LENGTH];
  251. CHAR szKeyName[MAX_STRING_LENGTH];
  252. CHAR szFileName[MAX_STRING_LENGTH];
  253. rv = GetPrivateProfileIntW(
  254. lpAppName,
  255. lpKeyName,
  256. nDefault,
  257. lpFileName);
  258. if (rv != (UINT)-1 && rv)
  259. goto exitpt;
  260. // Call the ANSI version
  261. _snprintf(szAppName, MAX_STRING_LENGTH, "%S", lpAppName);
  262. _snprintf(szKeyName, MAX_STRING_LENGTH, "%S", lpKeyName);
  263. _snprintf(szFileName, MAX_STRING_LENGTH, "%S", lpFileName);
  264. rv = GetPrivateProfileInt(
  265. szAppName,
  266. szKeyName,
  267. nDefault,
  268. szFileName);
  269. exitpt:
  270. return rv;
  271. }
  272. /*++
  273. * Function:
  274. * _ReadINIValues
  275. *
  276. * Description:
  277. * Reads smclient.ini, section [tclient], variable "timeout"
  278. * This is a global timeout for Wait4Str etc
  279. * Also read some other values
  280. * Arguments:
  281. * none
  282. * Return value:
  283. * none
  284. *
  285. --*/
  286. VOID _ReadINIValues(VOID)
  287. {
  288. UINT nNew;
  289. WCHAR szIniFileName[_MAX_PATH];
  290. WCHAR szBuff[ 4 * MAX_STRING_LENGTH];
  291. WCHAR szBuffDef[MAX_STRING_LENGTH];
  292. BOOL bFlag;
  293. // Construct INI path
  294. *szIniFileName = 0;
  295. if (!_wgetcwd (
  296. szIniFileName,
  297. (int)(sizeof(szIniFileName)/sizeof(WCHAR) - wcslen(SMCLIENT_INI) - 1))
  298. )
  299. {
  300. TRACE((ERROR_MESSAGE, "Current directory length too long.\n"));
  301. }
  302. wcscat(szIniFileName, SMCLIENT_INI);
  303. // Get the timeout value
  304. nNew = _WrpGetPrivateProfileIntW(
  305. TCLIENT_INI_SECTION,
  306. L"timeout",
  307. 600,
  308. szIniFileName);
  309. if (nNew)
  310. {
  311. WAIT4STR_TIMEOUT = nNew * 1000;
  312. TRACE((INFO_MESSAGE, "New timeout: %d seconds\n", nNew));
  313. }
  314. nNew = _WrpGetPrivateProfileIntW(
  315. TCLIENT_INI_SECTION,
  316. L"contimeout",
  317. 35,
  318. szIniFileName);
  319. if (nNew)
  320. {
  321. CONNECT_TIMEOUT = nNew * 1000;
  322. TRACE((INFO_MESSAGE, "New timeout: %d seconds\n", nNew));
  323. }
  324. g_ConnectionFlags = 0;
  325. bFlag =
  326. _WrpGetPrivateProfileIntW(
  327. TCLIENT_INI_SECTION,
  328. L"LowSpeed",
  329. 0,
  330. szIniFileName);
  331. if (bFlag)
  332. g_ConnectionFlags |=TSFLAG_COMPRESSION;
  333. bFlag =
  334. _WrpGetPrivateProfileIntW(
  335. TCLIENT_INI_SECTION,
  336. L"PersistentCache",
  337. 0,
  338. szIniFileName);
  339. if (bFlag)
  340. g_ConnectionFlags |=TSFLAG_BITMAPCACHE;
  341. bFlag =
  342. _WrpGetPrivateProfileIntW(
  343. TCLIENT_INI_SECTION,
  344. L"FullScreen",
  345. 0,
  346. szIniFileName);
  347. if (bFlag)
  348. g_ConnectionFlags |=TSFLAG_FULLSCREEN;
  349. // read the strings
  350. _WrpGetPrivateProfileStringW(
  351. TCLIENT_INI_SECTION,
  352. L"StartRun",
  353. RUN_MENU,
  354. g_strStartRun,
  355. MAX_STRING_LENGTH,
  356. szIniFileName);
  357. _WrpGetPrivateProfileStringW(
  358. TCLIENT_INI_SECTION,
  359. L"StartLogoff",
  360. START_LOGOFF,
  361. g_strStartLogoff,
  362. MAX_STRING_LENGTH,
  363. szIniFileName);
  364. _WrpGetPrivateProfileStringW(
  365. TCLIENT_INI_SECTION,
  366. L"StartRunAct",
  367. RUN_ACT,
  368. g_strStartRun_Act,
  369. MAX_STRING_LENGTH,
  370. szIniFileName);
  371. _WrpGetPrivateProfileStringW(
  372. TCLIENT_INI_SECTION,
  373. L"RunBox",
  374. RUN_BOX,
  375. g_strRunBox,
  376. MAX_STRING_LENGTH,
  377. szIniFileName);
  378. _WrpGetPrivateProfileStringW(
  379. TCLIENT_INI_SECTION,
  380. L"WinLogon",
  381. WINLOGON_USERNAME,
  382. g_strWinlogon,
  383. MAX_STRING_LENGTH,
  384. szIniFileName);
  385. _WrpGetPrivateProfileStringW(
  386. TCLIENT_INI_SECTION,
  387. L"WinLogonAct",
  388. WINLOGON_ACT,
  389. g_strWinlogon_Act,
  390. MAX_STRING_LENGTH,
  391. szIniFileName);
  392. _WrpGetPrivateProfileStringW(
  393. TCLIENT_INI_SECTION,
  394. L"PriorWinLogon",
  395. PRIOR_WINLOGON,
  396. g_strPriorWinlogon,
  397. MAX_STRING_LENGTH,
  398. szIniFileName);
  399. _WrpGetPrivateProfileStringW(
  400. TCLIENT_INI_SECTION,
  401. L"PriorWinLogonAct",
  402. PRIOR_WINLOGON_ACT,
  403. g_strPriorWinlogon_Act,
  404. MAX_STRING_LENGTH,
  405. szIniFileName);
  406. _WrpGetPrivateProfileStringW(
  407. TCLIENT_INI_SECTION,
  408. L"NTSecurity",
  409. WINDOWS_NT_SECURITY,
  410. g_strNTSecurity,
  411. MAX_STRING_LENGTH,
  412. szIniFileName);
  413. _WrpGetPrivateProfileStringW(
  414. TCLIENT_INI_SECTION,
  415. L"NTSecurityAct",
  416. WINDOWS_NT_SECURITY_ACT,
  417. g_strNTSecurity_Act,
  418. MAX_STRING_LENGTH,
  419. szIniFileName);
  420. _WrpGetPrivateProfileStringW(
  421. TCLIENT_INI_SECTION,
  422. L"SureLogoff",
  423. ARE_YOU_SURE,
  424. g_strSureLogoff,
  425. MAX_STRING_LENGTH,
  426. szIniFileName);
  427. _WrpGetPrivateProfileStringW(
  428. TCLIENT_INI_SECTION,
  429. L"SureLogoffAct",
  430. SURE_LOGOFF_ACT,
  431. g_strSureLogoffAct,
  432. MAX_STRING_LENGTH,
  433. szIniFileName);
  434. _WrpGetPrivateProfileStringW(
  435. TCLIENT_INI_SECTION,
  436. L"LogonErrorMessage",
  437. LOGON_ERROR_MESSAGE,
  438. g_strLogonErrorMessage,
  439. MAX_STRING_LENGTH,
  440. szIniFileName);
  441. _WrpGetPrivateProfileStringW(
  442. TCLIENT_INI_SECTION,
  443. L"LogonDisabled",
  444. LOGON_DISABLED_MESSAGE,
  445. g_strLogonDisabled,
  446. MAX_STRING_LENGTH,
  447. szIniFileName);
  448. _snwprintf(szBuffDef, sizeof(szBuffDef), L"%S", CLIENT_CAPTION);
  449. _WrpGetPrivateProfileStringW(
  450. TCLIENT_INI_SECTION,
  451. L"UIClientCaption",
  452. szBuffDef,
  453. szBuff,
  454. MAX_STRING_LENGTH,
  455. szIniFileName);
  456. _snprintf(g_strClientCaption, MAX_STRING_LENGTH, "%S", szBuff);
  457. _snwprintf(szBuffDef, sizeof(szBuffDef), L"%S", DISCONNECT_DIALOG_BOX);
  458. _WrpGetPrivateProfileStringW(
  459. TCLIENT_INI_SECTION,
  460. L"UIDisconnectDialogBox",
  461. szBuffDef,
  462. szBuff,
  463. MAX_STRING_LENGTH,
  464. szIniFileName);
  465. _snprintf(g_strDisconnectDialogBox, MAX_STRING_LENGTH, "%S", szBuff);
  466. _snwprintf(szBuffDef, sizeof(szBuffDef), L"%S", YES_NO_SHUTDOWN);
  467. _WrpGetPrivateProfileStringW(
  468. TCLIENT_INI_SECTION,
  469. L"UIYesNoDisconnect",
  470. szBuffDef,
  471. szBuff,
  472. MAX_STRING_LENGTH,
  473. szIniFileName);
  474. _snprintf(g_strYesNoShutdown, MAX_STRING_LENGTH, "%S", szBuff);
  475. _snwprintf(szBuffDef, sizeof(szBuffDef), L"%S", CLIENT_EXE);
  476. _WrpGetPrivateProfileStringW(
  477. TCLIENT_INI_SECTION,
  478. L"ClientImage",
  479. szBuffDef,
  480. szBuff,
  481. MAX_STRING_LENGTH,
  482. szIniFileName);
  483. _snprintf(g_strClientImg, MAX_STRING_LENGTH, "%S", szBuff);
  484. }
  485. /*++
  486. * Function:
  487. * _FeedbackWndProc
  488. * Description:
  489. * Window proc wich dispatches messages containing feedback
  490. * The messages are usualy sent by RDP clients
  491. *
  492. --*/
  493. LRESULT CALLBACK _FeedbackWndProc( HWND hwnd,
  494. UINT uiMessage,
  495. WPARAM wParam,
  496. LPARAM lParam)
  497. {
  498. HANDLE hMapF = NULL;
  499. switch (uiMessage)
  500. {
  501. case WM_FB_TEXTOUT:
  502. _TextOutReceived((DWORD)wParam, (HANDLE)lParam);
  503. break;
  504. case WM_FB_GLYPHOUT:
  505. _GlyphReceived((DWORD)wParam, (HANDLE)lParam);
  506. break;
  507. case WM_FB_DISCONNECT:
  508. _SetClientDead(lParam);
  509. _CheckForWorkerWaitingDisconnect(lParam);
  510. _CancelWaitingWorker(lParam);
  511. break;
  512. case WM_FB_CONNECT:
  513. _CheckForWorkerWaitingConnect((HWND)wParam, lParam);
  514. break;
  515. case WM_FB_LOGON:
  516. TRACE((INFO_MESSAGE, "LOGON event, session ID=%d\n",
  517. wParam));
  518. _SetSessionID(lParam, (UINT)wParam);
  519. break;
  520. break;
  521. case WM_FB_ACCEPTME:
  522. return (_CheckIsAcceptable(lParam, FALSE) != NULL);
  523. case WM_WSOCK: // Windows socket messages
  524. RClx_DispatchWSockEvent((SOCKET)wParam, lParam);
  525. break;
  526. case WM_DESTROY:
  527. PostQuitMessage(0);
  528. break;
  529. default:
  530. return DefWindowProc(hwnd, uiMessage, wParam, lParam);
  531. }
  532. return 0;
  533. }
  534. /*++
  535. * Function:
  536. * _RegisterWindow
  537. * Description:
  538. * Resgisters window class for the feedback dispatcher
  539. * Arguments:
  540. * none
  541. * Return value:
  542. * TRUE on success
  543. *
  544. --*/
  545. BOOL _RegisterWindow(VOID)
  546. {
  547. WNDCLASS wc;
  548. BOOL rv;
  549. DWORD dwLastErr;
  550. memset(&wc, 0, sizeof(wc));
  551. wc.lpfnWndProc = _FeedbackWndProc;
  552. wc.hInstance = g_hInstance;
  553. wc.lpszClassName = _TSTNAMEOFCLAS;
  554. if (!RegisterClass (&wc) &&
  555. (dwLastErr = GetLastError()) &&
  556. dwLastErr != ERROR_CLASS_ALREADY_EXISTS)
  557. {
  558. TRACE((ERROR_MESSAGE,
  559. "Can't register class. GetLastError=%d\n",
  560. GetLastError()));
  561. goto exitpt;
  562. }
  563. rv = TRUE;
  564. exitpt:
  565. return rv;
  566. }
  567. /*++
  568. * Function:
  569. * _GoFeedback
  570. * Description:
  571. * Main function for the feedback thread. The thread is created for the
  572. * lifetime of the DLL
  573. * Arguments:
  574. * lpParam is unused
  575. * Return value:
  576. * Thread exit code
  577. --*/
  578. DWORD WINAPI _GoFeedback(LPVOID lpParam)
  579. {
  580. MSG msg;
  581. g_hWindow = CreateWindow(
  582. _TSTNAMEOFCLAS,
  583. NULL, // Window name
  584. 0, // dwStyle
  585. 0, // x
  586. 0, // y
  587. 0, // nWidth
  588. 0, // nHeight
  589. NULL, // hWndParent
  590. NULL, // hMenu
  591. g_hInstance,
  592. NULL); // lpParam
  593. if (!g_hWindow)
  594. {
  595. TRACE((ERROR_MESSAGE, "No feedback window handle"));
  596. goto exitpt;
  597. } else {
  598. if (!RClx_Init())
  599. TRACE((ERROR_MESSAGE, "Can't initialize RCLX\n"));
  600. while (GetMessage (&msg, NULL, 0, 0) && msg.message != WM_FB_END)
  601. {
  602. DispatchMessage (&msg);
  603. }
  604. RClx_Done();
  605. }
  606. TRACE((INFO_MESSAGE, "Window/Thread destroyed\n"));
  607. FreeLibraryAndExitThread(g_hInstance, 0);
  608. exitpt:
  609. return 1;
  610. }
  611. /*++
  612. * Function:
  613. * _SetClientRegistry
  614. * Description:
  615. * Sets the registry prior running RDP client
  616. * The format of the key is: smclient_PID_TID
  617. * PID is the process ID and TID is the thread ID
  618. * This key is deleated after the client disconnects
  619. * Arguments:
  620. * lpszServerName - server to which the client will connect
  621. * xRes, yRes - clients resolution
  622. * bLowSpeed - low speed (compression) option
  623. * bCacheBitmaps - cache the bitmaps to the disc option
  624. * bFullScreen - the client will be in full screen mode
  625. * Called by:
  626. * SCConnect
  627. --*/
  628. VOID
  629. _SetClientRegistry(
  630. LPCWSTR lpszServerName,
  631. LPCWSTR lpszShell,
  632. INT xRes,
  633. INT yRes,
  634. INT ConnectionFlags)
  635. {
  636. const CHAR *pData;
  637. CHAR szServer[MAX_STRING_LENGTH];
  638. register int i;
  639. LONG sysrc;
  640. HKEY key;
  641. DWORD disposition;
  642. DWORD dataSize;
  643. DWORD ResId;
  644. CHAR lpszRegistryEntry[4*MAX_STRING_LENGTH];
  645. RECT rcDesktop = {0, 0, 0, 0};
  646. INT desktopX, desktopY;
  647. _snprintf(lpszRegistryEntry, sizeof(lpszRegistryEntry),
  648. "%s\\" REG_FORMAT,
  649. REG_BASE, GetCurrentProcessId(), GetCurrentThreadId());
  650. // Get desktop size
  651. GetWindowRect(GetDesktopWindow(), &rcDesktop);
  652. desktopX = rcDesktop.right;
  653. desktopY = rcDesktop.bottom;
  654. // Adjust the resolution
  655. if (desktopX < xRes || desktopY < yRes)
  656. {
  657. xRes = desktopX;
  658. yRes = desktopY;
  659. }
  660. // Convert lpszServerName to proper format
  661. for (i=0; i < sizeof(szServer)/sizeof(TCHAR)-1 && lpszServerName[i]; i++)
  662. szServer[i] = (CHAR)lpszServerName[i];
  663. szServer[i] = 0;
  664. pData = szServer;
  665. dataSize = (strlen(pData)+1);
  666. // Before starting ducati client set registry with server name
  667. sysrc = RegCreateKeyEx(HKEY_CURRENT_USER,
  668. lpszRegistryEntry,
  669. 0, /* reserved */
  670. NULL, /* class */
  671. REG_OPTION_NON_VOLATILE,
  672. KEY_ALL_ACCESS,
  673. NULL, /* security attributes */
  674. &key,
  675. &disposition);
  676. if (sysrc != ERROR_SUCCESS)
  677. {
  678. TRACE((WARNING_MESSAGE, "RegCreateKeyEx failed, sysrc = %d\n", sysrc));
  679. goto exitpt;
  680. }
  681. sysrc = RegSetValueEx(key,
  682. TEXT("MRU0"),
  683. 0, // reserved
  684. REG_SZ,
  685. (LPBYTE)pData,
  686. dataSize);
  687. if (sysrc != ERROR_SUCCESS)
  688. {
  689. TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
  690. }
  691. // Set alternative shell (if specified
  692. if (lpszShell)
  693. {
  694. sysrc = RegSetValueEx(key,
  695. TEXT("Alternate Shell"),
  696. 0, // reserved
  697. REG_BINARY,
  698. (LPBYTE)lpszShell,
  699. wcslen(lpszShell) * sizeof(*lpszShell));
  700. if (sysrc != ERROR_SUCCESS)
  701. {
  702. TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
  703. }
  704. }
  705. // Set the resolution
  706. if (xRes >= 1600 && yRes >= 1200) ResId = 4;
  707. else if (xRes >= 1280 && yRes >= 1024) ResId = 3;
  708. else if (xRes >= 1024 && yRes >= 768) ResId = 2;
  709. else if (xRes >= 800 && yRes >= 600) ResId = 1;
  710. else ResId = 0; // 640x480
  711. sysrc = RegSetValueEx(key,
  712. "Desktop Size ID",
  713. 0,
  714. REG_DWORD,
  715. (LPBYTE)&ResId,
  716. sizeof(ResId));
  717. if (sysrc != ERROR_SUCCESS)
  718. {
  719. TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
  720. }
  721. ResId = 1;
  722. sysrc = RegSetValueEx(key,
  723. "Auto Connect",
  724. 0, // reserved
  725. REG_DWORD,
  726. (LPBYTE)&ResId,
  727. sizeof(ResId));
  728. if (sysrc != ERROR_SUCCESS)
  729. {
  730. TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
  731. }
  732. ResId = (ConnectionFlags & TSFLAG_BITMAPCACHE)?1:0;
  733. sysrc = RegSetValueEx(key,
  734. "BitmapCachePersistEnable",
  735. 0, // reserved
  736. REG_DWORD,
  737. (LPBYTE)&ResId,
  738. sizeof(ResId));
  739. if (sysrc != ERROR_SUCCESS)
  740. {
  741. TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
  742. }
  743. ResId = (ConnectionFlags & TSFLAG_COMPRESSION)?1:0;
  744. sysrc = RegSetValueEx(key,
  745. "Compression",
  746. 0, // reserved
  747. REG_DWORD,
  748. (LPBYTE)&ResId,
  749. sizeof(ResId));
  750. if (sysrc != ERROR_SUCCESS)
  751. {
  752. TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
  753. }
  754. if (ConnectionFlags & TSFLAG_FULLSCREEN)
  755. {
  756. ResId = 2;
  757. sysrc = RegSetValueEx(key,
  758. "Screen Mode ID",
  759. 0, // reserved
  760. REG_DWORD,
  761. (LPBYTE)&ResId,
  762. sizeof(ResId));
  763. if (sysrc != ERROR_SUCCESS)
  764. {
  765. TRACE((WARNING_MESSAGE,
  766. "RegSetValue failed, status = %d\n", sysrc));
  767. }
  768. }
  769. RegCloseKey(key);
  770. ResId = 1;
  771. sysrc = RegCreateKeyEx(HKEY_CURRENT_USER,
  772. REG_BASE,
  773. 0, /* reserved */
  774. NULL, /* class */
  775. REG_OPTION_NON_VOLATILE,
  776. KEY_ALL_ACCESS,
  777. NULL, /* security attributes */
  778. &key,
  779. &disposition);
  780. if (sysrc != ERROR_SUCCESS)
  781. {
  782. TRACE((WARNING_MESSAGE, "RegCreateKeyEx failed, sysrc = %d\n", sysrc));
  783. goto exitpt;
  784. }
  785. sysrc = RegSetValueEx(key,
  786. ALLOW_BACKGROUND_INPUT,
  787. 0,
  788. REG_DWORD,
  789. (LPBYTE)&ResId,
  790. sizeof(ResId));
  791. if (sysrc != ERROR_SUCCESS)
  792. {
  793. TRACE((WARNING_MESSAGE, "RegSetValue failed, status = %d\n", sysrc));
  794. }
  795. RegCloseKey(key);
  796. exitpt:
  797. ;
  798. }
  799. /*++
  800. * Function:
  801. * _DeleteClientRegistry
  802. * Description:
  803. * Deletes the key set by _SetClientRegistry
  804. * Called by:
  805. * SCDisconnect
  806. --*/
  807. VOID _DeleteClientRegistry(PCONNECTINFO pCI)
  808. {
  809. CHAR lpszRegistryEntry[4*MAX_STRING_LENGTH];
  810. LONG sysrc;
  811. _snprintf(lpszRegistryEntry, sizeof(lpszRegistryEntry),
  812. "%s\\" REG_FORMAT,
  813. REG_BASE, GetCurrentProcessId(), pCI->OwnerThreadId);
  814. sysrc = RegDeleteKey(HKEY_CURRENT_USER, lpszRegistryEntry);
  815. if (sysrc != ERROR_SUCCESS)
  816. {
  817. TRACE((WARNING_MESSAGE, "RegDeleteKey failed, status = %d\n", sysrc));
  818. }
  819. }
  820. /*++
  821. * Function:
  822. * _CreateFeedbackThread
  823. * Description:
  824. * Creates the feedback thread
  825. * Called by:
  826. * InitDone
  827. --*/
  828. BOOL _CreateFeedbackThread(VOID)
  829. {
  830. BOOL rv = TRUE;
  831. // Register feedback window class
  832. WNDCLASS wc;
  833. DWORD dwThreadId, dwLastErr;
  834. g_hThread = (HANDLE)
  835. _beginthreadex
  836. (NULL,
  837. 0,
  838. (unsigned (__stdcall *)(void*))_GoFeedback,
  839. NULL,
  840. 0,
  841. &dwThreadId);
  842. if (!g_hThread) {
  843. TRACE((ERROR_MESSAGE, "Couldn't create thread\n"));
  844. rv = FALSE;
  845. }
  846. return rv;
  847. }
  848. /*++
  849. * Function:
  850. * _DestroyFeedbackThread
  851. * Description:
  852. * Destroys the thread created by _CreateFeedbackThread
  853. * Called by:
  854. * InitDone
  855. --*/
  856. VOID _DestroyFeedbackThread(VOID)
  857. {
  858. if (g_hThread)
  859. {
  860. DWORD dwWait;
  861. CHAR szMyLibName[_MAX_PATH];
  862. // Closing feedback thread
  863. PostMessage(g_hWindow, WM_FB_END, 0, 0);
  864. TRACE((INFO_MESSAGE, "Closing DLL thread\n"));
  865. // Dedstroy the window
  866. DestroyWindow(g_hWindow);
  867. // CloseHandle(g_hThread);
  868. g_hThread = NULL;
  869. }
  870. }
  871. /*++
  872. * Function:
  873. * _CleanStuff
  874. * Description:
  875. * Cleans the global queues. Closes any resources
  876. * Called by:
  877. * InitDone
  878. --*/
  879. VOID _CleanStuff(VOID)
  880. {
  881. // Thread safe, bacause is executed from DllEntry
  882. while (g_pClientQHead)
  883. {
  884. TRACE((WARNING_MESSAGE, "Cleaning connection info: 0x%x\n",
  885. g_pClientQHead));
  886. SCDisconnect(g_pClientQHead);
  887. }
  888. #if 0
  889. if (g_pClientQHead)
  890. {
  891. PCONNECTINFO pNext, pIter = g_pClientQHead;
  892. while (pIter)
  893. {
  894. int nEv;
  895. DWORD wres;
  896. TRACE((WARNING_MESSAGE, "Cleaning connection info: 0x%x\n", pIter));
  897. // Clear Events
  898. if (pIter->evWait4Str)
  899. {
  900. CloseHandle(pIter->evWait4Str);
  901. pIter->evWait4Str = NULL;
  902. }
  903. for (nEv = 0; nEv < pIter->nChatNum; nEv ++)
  904. CloseHandle(pIter->aevChatSeq[nEv]);
  905. pIter->nChatNum = 0;
  906. // Clear Processes
  907. do {
  908. SendMessage(pIter->hClient, WM_CLOSE, 0, 0);
  909. } while((wres = WaitForSingleObject(pIter->hProcess, WAIT4STR_TIMEOUT/4) == WAIT_TIMEOUT));
  910. if (wres == WAIT_TIMEOUT)
  911. {
  912. TRACE((WARNING_MESSAGE,
  913. "Can't close process. WaitForSingleObject timeouts\n"));
  914. TRACE((WARNING_MESSAGE,
  915. "Process #%d will be killed\n",
  916. pIter->dwProcessId ));
  917. if (!TerminateProcess(pIter->hProcess, 1))
  918. {
  919. TRACE((WARNING_MESSAGE,
  920. "Can't kill process #%d. GetLastError=%d\n",
  921. pIter->dwProcessId, GetLastError()));
  922. }
  923. }
  924. TRACE((WARNING_MESSAGE, "Closing process\n"));
  925. if (pIter->hProcess)
  926. CloseHandle(pIter->hProcess);
  927. if (pIter->hThread)
  928. CloseHandle(pIter->hThread);
  929. pIter->hProcess = pIter->hThread = NULL;
  930. // Free the structures
  931. pNext = pIter->pNext;
  932. free(pNext);
  933. pIter = pNext;
  934. }
  935. }
  936. #endif // 0
  937. }
  938. VOID _TClientAssert( LPCTSTR filename, INT line)
  939. {
  940. TRACE(( ERROR_MESSAGE, "ASSERT %s line: %d\n", filename, line));
  941. DebugBreak();
  942. }