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.

1147 lines
29 KiB

  1. //============================================================================
  2. // Copyright (c) 1995, Microsoft Corporation
  3. //
  4. // File: init.c
  5. //
  6. // History:
  7. // Abolade Gbadegesin July-24-1995 Created
  8. //
  9. // Server routines for tracing dll.
  10. // All functions invoked by the server thread are code-page independent.
  11. //============================================================================
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <windows.h>
  16. #include <rtutils.h>
  17. #include "trace.h"
  18. // waits on
  19. // lpserver->hConsole
  20. // lpserver->hStopEvent
  21. // lpserver->hTableEvent
  22. // lpclient->hConfigEvent for each client
  23. #define POS_CONSOLE 0
  24. #define POS_STOP 1
  25. #define POS_TABLE 2
  26. #define POS_CLIENT_0 3
  27. #define POS_MAX MAXIMUM_WAIT_OBJECTS
  28. #define ADJUST_ARRAY(a) ((a) + posBase)
  29. #define ADJUST_INDEX(i) ((i) - posBase)
  30. #define OFFSET_CLIENT(i,d) (((i) + MAX_CLIENT_COUNT + d) % MAX_CLIENT_COUNT)
  31. extern VOID StopWorkers (VOID);
  32. LPTRACE_SERVER g_server = NULL;
  33. HINSTANCE g_module;
  34. HANDLE g_loadevent = NULL;
  35. HMODULE g_moduleRef;
  36. HANDLE g_serverThread;
  37. ULONG g_traceCount; //attempt server thread creation ?
  38. ULONG g_traceTime; //when last attempt to create server thread.
  39. DWORD g_posBase, g_posLast;//not used by serverthread.
  40. // only to decide if new serverthread to be created
  41. HANDLE g_hWaitHandles[POS_MAX];
  42. HINSTANCE
  43. IncrementModuleReference (
  44. VOID
  45. );
  46. BOOL WINAPI DLLMAIN(HINSTANCE hInstDLL, DWORD dwReason, LPVOID lpvReserved) {
  47. BOOL bSuccess;
  48. HANDLE c_loadevent;
  49. switch (dwReason) {
  50. case DLL_PROCESS_ATTACH:
  51. DisableThreadLibraryCalls(hInstDLL);
  52. g_module = hInstDLL;
  53. // If a server threade managed to start before we got
  54. // DLL_PROCESS_ATTACH call (possible because of NT Loader
  55. // bug), we need to release it
  56. c_loadevent = (HANDLE)InterlockedExchangePointer (
  57. &g_loadevent,
  58. INVALID_HANDLE_VALUE);
  59. if (c_loadevent!=NULL) {
  60. bSuccess = SetEvent (c_loadevent);
  61. ASSERTMSG ("Could not signal waiting trace servers ", bSuccess);
  62. }
  63. else
  64. bSuccess = TRUE;
  65. break;
  66. case DLL_PROCESS_DETACH:
  67. if (g_server) {
  68. bSuccess = TraceShutdownServer(g_server);
  69. g_server = NULL;
  70. }
  71. else
  72. bSuccess = TRUE;
  73. StopWorkers ();
  74. break;
  75. default:
  76. bSuccess = TRUE;
  77. }
  78. return bSuccess;
  79. }
  80. HINSTANCE
  81. IncrementModuleReference (
  82. VOID
  83. ) {
  84. HMODULE hmodule;
  85. TCHAR szmodule[MAX_PATH+1];
  86. HANDLE l_loadevent;
  87. HANDLE c_loadevent;
  88. DWORD rc;
  89. // Create an event in case we need to wait for DLL_PROCESS_ATTACH
  90. l_loadevent = CreateEvent (NULL, TRUE, FALSE, NULL);
  91. ASSERTMSG ("Could not create load event ", l_loadevent!=NULL);
  92. if (l_loadevent!=NULL) {
  93. // Make our event global if either no-one else
  94. // has done this yet
  95. c_loadevent = (HANDLE)InterlockedCompareExchangePointer (
  96. (PVOID *)&g_loadevent,
  97. l_loadevent,
  98. NULL);
  99. if (c_loadevent==NULL) {
  100. rc = WaitForSingleObject (l_loadevent, INFINITE);
  101. // Let other waiting threads run as we going to close
  102. // our event right after this
  103. Sleep (0);
  104. }
  105. else if (c_loadevent==INVALID_HANDLE_VALUE) {
  106. // DLL_PROCESS_ATTACH has already been called
  107. rc = WAIT_OBJECT_0;
  108. }
  109. else {
  110. // Somebody else managed to start before us
  111. // -> wait on that event
  112. #if DBG
  113. DbgPrint ("RTUTILS: %lx - trace server thread waiting for load on existing event.\n",
  114. GetCurrentThreadId ());
  115. #endif
  116. rc = WaitForSingleObject (c_loadevent, INFINITE);
  117. // Just in case the handle has been closed before we
  118. // managed to start the wait (unlikely because
  119. // of Sleep call above, but just in case ...)
  120. if ((rc!=WAIT_OBJECT_0)
  121. && (GetLastError ()==ERROR_INVALID_HANDLE)) {
  122. #if DBG
  123. DbgPrint ("RTUTILS: %lx - trace server thread load event was destroyed during wait.\n",
  124. GetCurrentThreadId ());
  125. #endif
  126. rc = WAIT_OBJECT_0;
  127. }
  128. }
  129. ASSERTMSG ("Wait on load event failed ", rc==WAIT_OBJECT_0);
  130. CloseHandle (l_loadevent);
  131. if (rc==WAIT_OBJECT_0) {
  132. //
  133. // we do a LoadLibrary to increment the reference count
  134. // on RTUTILS.DLL, so that when we're unloaded by the application,
  135. // our address space doesn't disappear.
  136. // instead, our event will be signalled and then we cleanup
  137. // and call FreeLibraryAndExitThread to unload ourselves
  138. //
  139. rc = GetModuleFileName(g_module, szmodule, sizeof(szmodule)/sizeof (szmodule[0]));
  140. ASSERTMSG ("Could not get dll path ", rc>0);
  141. if (rc>0) {
  142. hmodule = LoadLibrary(szmodule);
  143. if (hmodule!=NULL)
  144. return hmodule;
  145. }
  146. }
  147. }
  148. return NULL;
  149. }
  150. //
  151. // sets up server struct in readiness for clients registering
  152. //
  153. LPTRACE_SERVER TraceCreateServer (
  154. LPTRACE_SERVER *lpserver
  155. ) {
  156. LPTRACE_SERVER l_lpserver, c_lpserver;
  157. DWORD rc;
  158. l_lpserver = (LPTRACE_SERVER)GlobalAlloc (GPTR, sizeof (TRACE_SERVER));
  159. if (l_lpserver!=NULL) {
  160. l_lpserver->TS_Flags = 0;
  161. l_lpserver->TS_Console = NULL;
  162. l_lpserver->TS_StopEvent = NULL;
  163. l_lpserver->TS_TableEvent = NULL;
  164. l_lpserver->TS_ClientCount = 0;
  165. l_lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT;
  166. ZeroMemory(l_lpserver->TS_FlagsCache, MAX_CLIENT_COUNT * sizeof(DWORD));
  167. ZeroMemory(
  168. l_lpserver->TS_ClientTable, MAX_CLIENT_COUNT * sizeof(LPTRACE_CLIENT)
  169. );
  170. try {
  171. TRACE_STARTUP_LOCKING(l_lpserver);
  172. rc = NO_ERROR;
  173. }
  174. except (EXCEPTION_EXECUTE_HANDLER) {
  175. rc = GetExceptionCode ();
  176. }
  177. ASSERTMSG ("Cound not initialize lock ", rc==NO_ERROR);
  178. if (rc==NO_ERROR) {
  179. c_lpserver = InterlockedCompareExchangePointer (
  180. (PVOID *)lpserver,
  181. l_lpserver,
  182. NULL
  183. );
  184. if (c_lpserver==NULL) {
  185. return l_lpserver;
  186. }
  187. else {
  188. TRACE_CLEANUP_LOCKING (l_lpserver);
  189. GlobalFree (l_lpserver);
  190. return c_lpserver;
  191. }
  192. }
  193. GlobalFree (l_lpserver);
  194. }
  195. #if DBG
  196. DbgPrint ("RTUTILS: %lx - trace server creation failed.\n",
  197. GetCurrentThreadId ());
  198. #endif
  199. return NULL;
  200. }
  201. //
  202. // cleans server struct and de-allocates memory used
  203. //
  204. BOOL
  205. TraceShutdownServer(
  206. LPTRACE_SERVER lpserver
  207. ) {
  208. if (lpserver->TS_StopEvent != NULL &&
  209. (lpserver->TS_Flags & TRACEFLAGS_SERVERTHREAD)) {
  210. //
  211. // server thread is active, let it do cleanup
  212. //
  213. SetEvent(lpserver->TS_StopEvent);
  214. }
  215. else {
  216. //
  217. // we'll do the cleanup ourselves
  218. //
  219. TraceCleanUpServer(lpserver);
  220. }
  221. return TRUE;
  222. }
  223. DWORD
  224. TraceCleanUpServer(
  225. LPTRACE_SERVER lpserver
  226. ) {
  227. LPTRACE_CLIENT lpclient, *lplpc, *lplpcstart, *lplpcend;
  228. // TRACE_ACQUIRE_WRITELOCK(lpserver);
  229. TRACE_CLEANUP_LOCKING(lpserver);
  230. //
  231. // delete client structures
  232. //
  233. lplpcstart = lpserver->TS_ClientTable;
  234. lplpcend = lplpcstart + MAX_CLIENT_COUNT;
  235. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  236. if (*lplpc != NULL) {
  237. TraceDeleteClient(lpserver, lplpc);
  238. }
  239. }
  240. lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT;
  241. lpserver->TS_ClientCount = 0;
  242. if (lpserver->TS_TableEvent != NULL) {
  243. CloseHandle(lpserver->TS_TableEvent);
  244. lpserver->TS_TableEvent = NULL;
  245. }
  246. if (lpserver->TS_StopEvent != NULL) {
  247. CloseHandle(lpserver->TS_StopEvent);
  248. lpserver->TS_StopEvent = NULL;
  249. }
  250. if (lpserver->TS_Console != NULL) {
  251. CloseHandle(lpserver->TS_Console);
  252. lpserver->TS_Console = NULL;
  253. FreeConsole();
  254. }
  255. lpserver->TS_Flags = 0;
  256. return TRUE;
  257. }
  258. //
  259. // assumes server is locked for writing
  260. //
  261. DWORD
  262. TraceCreateServerComplete(
  263. LPTRACE_SERVER lpserver
  264. ) {
  265. HKEY hkeyConfig;
  266. DWORD dwType, dwSize, dwValue;
  267. DWORD dwErr, dwThread, dwDisposition;
  268. do { // breakout loop
  269. //
  270. // create event signalled to stop server thread
  271. //
  272. lpserver->TS_StopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  273. if (lpserver->TS_StopEvent == NULL) {
  274. dwErr = GetLastError(); break;
  275. }
  276. //
  277. // create event signalled when client registers/deregisters
  278. //
  279. lpserver->TS_TableEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  280. if (lpserver->TS_TableEvent == NULL) {
  281. dwErr = GetLastError(); break;
  282. }
  283. //
  284. // open registry key containing server configuration
  285. //
  286. dwErr = RegCreateKeyEx(
  287. HKEY_LOCAL_MACHINE, REGKEY_TRACING, 0, NULL,
  288. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
  289. &hkeyConfig, &dwDisposition
  290. );
  291. if (dwErr != NO_ERROR) { break; }
  292. //
  293. // read the server configuration from the config key
  294. //
  295. dwSize = sizeof(DWORD);
  296. dwErr = RegQueryValueEx(
  297. hkeyConfig, REGVAL_ENABLECONSOLETRACING, NULL,
  298. &dwType, (PBYTE)&dwValue, &dwSize
  299. );
  300. if (dwErr != NO_ERROR) {
  301. dwType = REG_DWORD;
  302. dwSize = sizeof(DWORD);
  303. dwValue = DEF_ENABLECONSOLETRACING;
  304. RegSetValueEx(
  305. hkeyConfig, REGVAL_ENABLECONSOLETRACING, 0,
  306. dwType, (PBYTE)&dwValue, dwSize
  307. );
  308. }
  309. if (dwValue != 0) { lpserver->TS_Flags |= TRACEFLAGS_USECONSOLE; }
  310. RegCloseKey(hkeyConfig); hkeyConfig = 0;
  311. //
  312. // set up array for client change notifications.
  313. // only used if server thread is not created
  314. //
  315. SetWaitArray(lpserver);
  316. return NO_ERROR;
  317. } while(FALSE);
  318. //
  319. // something went wrong, so clean up
  320. //
  321. if (lpserver->TS_TableEvent != NULL) {
  322. CloseHandle(lpserver->TS_TableEvent);
  323. lpserver->TS_TableEvent = NULL;
  324. }
  325. if (lpserver->TS_StopEvent != NULL) {
  326. CloseHandle(lpserver->TS_StopEvent);
  327. lpserver->TS_StopEvent = NULL;
  328. }
  329. return dwErr;
  330. }
  331. //
  332. // creates server thread if required
  333. //
  334. DWORD
  335. TraceCreateServerThread (
  336. DWORD dwFlags,
  337. BOOL bCallerLocked, //does caller have write lock
  338. BOOL bNewRegister //new client registered. so check
  339. )
  340. {
  341. DWORD dwErr=NO_ERROR;
  342. DWORD dwCurrentTime = GetTickCount();
  343. BOOL bCreate, bLocked=bCallerLocked;
  344. LPTRACE_SERVER lpserver;
  345. DWORD dwThread=0;
  346. lpserver = GET_TRACE_SERVER();
  347. if (!lpserver)
  348. return INVALID_TRACEID;
  349. //
  350. // check if serverthread should be created
  351. //
  352. bCreate = FALSE;
  353. do {
  354. if ((dwFlags & TRACE_USE_FILE) || (dwFlags & TRACE_USE_CONSOLE)) {
  355. bCreate = TRUE;
  356. break;
  357. }
  358. if (g_traceTime > dwCurrentTime)
  359. g_traceTime = dwCurrentTime;
  360. if (!bNewRegister) {
  361. if (dwCurrentTime - g_traceTime < 30000)
  362. break;
  363. }
  364. if (!bLocked){
  365. TRACE_ACQUIRE_WRITELOCK(lpserver);
  366. bLocked = TRUE;
  367. }
  368. // check again under lock if server thread has been created
  369. if (g_serverThread) {
  370. bCreate = FALSE;
  371. break;
  372. }
  373. //
  374. // enter the wait, passing the adjusted handle count
  375. // and the adjusted array base
  376. //
  377. {
  378. DWORD dwRetval;
  379. if (!bNewRegister) {
  380. // g_posLast points to the next empty event entry
  381. dwRetval = WaitForMultipleObjects(
  382. g_posLast - g_posBase, g_hWaitHandles + g_posBase, FALSE, 0
  383. );
  384. if (dwRetval==WAIT_TIMEOUT)
  385. break;
  386. }
  387. }
  388. {
  389. LPTRACE_CLIENT *lplpc, *lplpcstart, *lplpcend;
  390. g_traceTime = dwCurrentTime;
  391. lplpcstart = lpserver->TS_ClientTable;
  392. lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT;
  393. g_posLast = POS_CLIENT_0;
  394. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  395. if (*lplpc == NULL)
  396. continue;
  397. if (!TRACE_CLIENT_IS_DISABLED(*lplpc))
  398. TraceEnableClient(lpserver, *lplpc, FALSE);
  399. if (TRACE_CLIENT_USES_CONSOLE(*lplpc)
  400. || TRACE_CLIENT_USES_FILE(*lplpc))
  401. {
  402. bCreate = TRUE;
  403. break;
  404. }
  405. if (TRACE_CLIENT_USES_REGISTRY(*lplpc))
  406. g_hWaitHandles[g_posLast++] = (*lplpc)->TC_ConfigEvent;
  407. }
  408. }
  409. } while (FALSE);
  410. if (!bCreate) {
  411. if (bLocked && !bCallerLocked)
  412. TRACE_RELEASE_WRITELOCK(lpserver);
  413. return dwErr;
  414. }
  415. if (!bLocked) {
  416. TRACE_ACQUIRE_WRITELOCK(lpserver);
  417. bLocked = TRUE;
  418. }
  419. do {
  420. // final check under lock to see if thread created
  421. if (g_serverThread)
  422. break;
  423. g_moduleRef = IncrementModuleReference ();
  424. if (g_moduleRef==NULL) {
  425. dwErr = ERROR_CAN_NOT_COMPLETE;
  426. break;
  427. }
  428. g_serverThread = CreateThread(
  429. NULL, 0, TraceServerThread, lpserver, 0, &dwThread
  430. );
  431. if (g_serverThread == NULL) {
  432. dwErr = GetLastError();
  433. FreeLibrary(g_moduleRef);
  434. break;
  435. }
  436. CloseHandle(g_serverThread);
  437. } while (FALSE);
  438. if (bLocked && !bCallerLocked)
  439. TRACE_RELEASE_WRITELOCK(lpserver);
  440. return dwErr;
  441. }
  442. //----------------------------------------------------------------------------
  443. // Function: TraceServerThread
  444. //
  445. // Parameters:
  446. // LPVOID lpvParam
  447. //
  448. //----------------------------------------------------------------------------
  449. DWORD
  450. TraceServerThread(
  451. LPVOID lpvParam
  452. ) {
  453. DWORD dwErr;
  454. DWORD posBase, posLast;
  455. LPTRACE_SERVER lpserver;
  456. DWORD aWaitIndices[POS_MAX];
  457. HANDLE hWaitHandles[POS_MAX];
  458. LPTRACE_CLIENT lpclient, *lplpc, *lplpcstart, *lplpcend;
  459. //
  460. // get the server who owns this thread
  461. //
  462. lpserver = (LPTRACE_SERVER)lpvParam;
  463. //
  464. // set the flag to indicate we're running
  465. //
  466. InterlockedExchange(
  467. &lpserver->TS_Flags, lpserver->TS_Flags | TRACEFLAGS_SERVERTHREAD
  468. );
  469. posBase = posLast = 0;
  470. lplpcstart = lpserver->TS_ClientTable;
  471. lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT;
  472. //
  473. // make sure the latest config is loaded
  474. //
  475. TRACE_ACQUIRE_WRITELOCK(lpserver);
  476. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  477. if (*lplpc != NULL && !TRACE_CLIENT_IS_DISABLED(*lplpc))
  478. TraceEnableClient(lpserver, *lplpc, FALSE);
  479. }
  480. TRACE_RELEASE_WRITELOCK(lpserver);
  481. while (TRUE) {
  482. //
  483. // to figure out which handles will be waited on
  484. // first lock the server for reading
  485. //
  486. TRACE_ACQUIRE_READLOCK(lpserver);
  487. //
  488. // if a thread is using the console, wait on console input handle
  489. // otherwise, the base of the array of handles waited on
  490. // is adjusted upward (by setting posBase to 1); then, when the
  491. // wait returns the index of the signalled handle, the index is
  492. // compared against the POS_ constants adjusted downward
  493. // (by subtracting posBase from them);
  494. // thus if posBase is 1, we pass &hWaitHandles[1] and if we get
  495. // back 2, we compared it to (POS_CLIENT_0 - 1)==2 and then we
  496. // access position (2 - (POS_CLIENT_0 - 1))==0 in the actual
  497. // client table
  498. //
  499. if (lpserver->TS_Console != NULL) {
  500. posBase = 0;
  501. hWaitHandles[POS_CONSOLE] = lpserver->TS_Console;
  502. }
  503. else {
  504. posBase = 1;
  505. hWaitHandles[POS_CONSOLE] = NULL;
  506. }
  507. hWaitHandles[POS_STOP] = lpserver->TS_StopEvent;
  508. hWaitHandles[POS_TABLE] = lpserver->TS_TableEvent;
  509. posLast = POS_CLIENT_0;
  510. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  511. if (*lplpc != NULL && TRACE_CLIENT_USES_REGISTRY(*lplpc)) {
  512. aWaitIndices[posLast] = (ULONG) (lplpc - lplpcstart);
  513. hWaitHandles[posLast++] = (*lplpc)->TC_ConfigEvent;
  514. }
  515. }
  516. TRACE_RELEASE_READLOCK(lpserver);
  517. //
  518. // enter the wait, passing the adjusted handle count
  519. // and the adjusted array base
  520. //
  521. dwErr = WaitForMultipleObjects(
  522. posLast - posBase, hWaitHandles + posBase, FALSE, INFINITE
  523. );
  524. dwErr += (DWORD)posBase;
  525. if (dwErr == (WAIT_OBJECT_0 + POS_CONSOLE)) {
  526. //
  527. // must be a key pressed in the console, so
  528. // process it
  529. //
  530. // lock server for writing
  531. //
  532. TRACE_ACQUIRE_WRITELOCK(lpserver);
  533. if (lpserver->TS_Console != NULL) {
  534. TraceProcessConsoleInput(lpserver);
  535. }
  536. TRACE_RELEASE_WRITELOCK(lpserver);
  537. }
  538. else
  539. if (dwErr == (WAIT_OBJECT_0 + POS_STOP)) {
  540. //
  541. // time to break out of the loop
  542. //
  543. break;
  544. }
  545. else
  546. if (dwErr == (WAIT_OBJECT_0 + POS_TABLE)) {
  547. DWORD dwOwner;
  548. // a client registered or deregistered;
  549. // we pick up the new reg config change event
  550. // the next time through the loop
  551. }
  552. else
  553. if (dwErr >= (WAIT_OBJECT_0 + POS_CLIENT_0) &&
  554. dwErr <= (WAIT_OBJECT_0 + posLast)) {
  555. //
  556. // config changed for a client, lock server for writing
  557. // and lock client for writing, and reload the configuration
  558. // from the registry; take care in case the client has
  559. // already deregistered
  560. //
  561. TRACE_ACQUIRE_WRITELOCK(lpserver);
  562. lplpc = lpserver->TS_ClientTable +
  563. aWaitIndices[dwErr - WAIT_OBJECT_0];
  564. if (*lplpc == NULL) {
  565. TRACE_RELEASE_WRITELOCK(lpserver);
  566. continue;
  567. }
  568. //
  569. // load the client's configuration, unless it's disabled
  570. //
  571. if (!TRACE_CLIENT_IS_DISABLED(*lplpc)) {
  572. TraceEnableClient(lpserver, *lplpc, FALSE);
  573. }
  574. TRACE_RELEASE_WRITELOCK(lpserver);
  575. }
  576. }
  577. //
  578. // we've received the stop signal, so do cleanup and quit
  579. //
  580. TraceCleanUpServer(lpserver);
  581. //
  582. // unload the library and exit; this call never returns
  583. //
  584. FreeLibraryAndExitThread(g_moduleRef, 0);
  585. return dwErr;
  586. }
  587. //----------------------------------------------------------------------------
  588. // Function: TraceProcessConsoleInput
  589. //
  590. // Parameters:
  591. // LPTRACE_SERVER *lpserver
  592. //
  593. // Invoked when user presses a key in the console
  594. // Keypresses handle are
  595. // spacebar toggle the enabled/disabled flag for the client
  596. // whose screen buffer is active
  597. // pause same as <spacebar>
  598. // ctrl-tab set the active screen buffer to that of
  599. // the next client in the table
  600. // ctrl-shift-tab set the active screen buffer to that of
  601. // the previous client in the table
  602. // up, down, left, right scrolls the console window as expected
  603. // pageup, pagedown scrolls the console window as expected
  604. // assumes the server is locked for writing
  605. //----------------------------------------------------------------------------
  606. DWORD
  607. TraceProcessConsoleInput(
  608. LPTRACE_SERVER lpserver
  609. ) {
  610. INT dir;
  611. BOOL bSuccess;
  612. HANDLE hStdin;
  613. DWORD dwCount;
  614. WORD wRepCount;
  615. INPUT_RECORD inputRec;
  616. PKEY_EVENT_RECORD pkeyRec;
  617. DWORD dwConsoleOwner, dwNewOwner;
  618. LPTRACE_CLIENT lpclient, lpowner;
  619. //
  620. // see who owns the console
  621. //
  622. dwConsoleOwner = lpserver->TS_ConsoleOwner;
  623. if (dwConsoleOwner == MAX_CLIENT_COUNT) {
  624. //
  625. // no-one owns the console, so just return
  626. //
  627. return 0;
  628. }
  629. lpclient = lpserver->TS_ClientTable[dwConsoleOwner];
  630. //
  631. // get the console input handle
  632. //
  633. hStdin = lpserver->TS_Console;
  634. if (hStdin == NULL) {
  635. //
  636. // no console, so quit
  637. //
  638. return 0;
  639. }
  640. //
  641. // read input record
  642. //
  643. bSuccess = ReadConsoleInput(hStdin, &inputRec, 1, &dwCount);
  644. if (!bSuccess || dwCount == 0) {
  645. return GetLastError();
  646. }
  647. //
  648. // return if its not a keyboard event
  649. //
  650. if (inputRec.EventType != KEY_EVENT) {
  651. return 0;
  652. }
  653. //
  654. // if its one we handle, handle it
  655. //
  656. pkeyRec = &inputRec.Event.KeyEvent;
  657. if (!pkeyRec->bKeyDown) {
  658. //
  659. // we process when the key is pressed, not released
  660. //
  661. return 0;
  662. }
  663. wRepCount = pkeyRec->wRepeatCount;
  664. switch(pkeyRec->wVirtualKeyCode) {
  665. //
  666. // space-bar and pause are handled identically
  667. //
  668. case VK_PAUSE:
  669. case VK_SPACE:
  670. if (lpclient == NULL) { break; }
  671. //
  672. // if space bar or pause pressed an even
  673. // number of times, do nothing
  674. //
  675. if ((wRepCount & 1) == 0) { break; }
  676. //
  677. // toggle the enabled flag for the client
  678. //
  679. if (TRACE_CLIENT_IS_DISABLED(lpclient)) {
  680. TraceEnableClient(lpserver, lpclient, FALSE);
  681. }
  682. else {
  683. TraceDisableClient(lpserver, lpclient);
  684. }
  685. TraceUpdateConsoleTitle(lpclient);
  686. break;
  687. //
  688. // arrow keys are handled here
  689. //
  690. case VK_LEFT:
  691. if (lpclient == NULL) { break; }
  692. TraceShiftConsoleWindow(lpclient, -wRepCount, 0, NULL);
  693. break;
  694. case VK_RIGHT:
  695. if (lpclient == NULL) { break; }
  696. TraceShiftConsoleWindow(lpclient, wRepCount, 0, NULL);
  697. break;
  698. case VK_UP:
  699. if (lpclient == NULL) { break; }
  700. TraceShiftConsoleWindow(lpclient, 0, -wRepCount, NULL);
  701. break;
  702. case VK_DOWN:
  703. if (lpclient == NULL) { break; }
  704. TraceShiftConsoleWindow(lpclient, 0, wRepCount, NULL);
  705. break;
  706. //
  707. // page-up and page-down are handled here
  708. //
  709. case VK_PRIOR:
  710. case VK_NEXT: {
  711. INT iHeight;
  712. CONSOLE_SCREEN_BUFFER_INFO csbi;
  713. if (lpclient == NULL) { break; }
  714. //
  715. // find the current height of the window
  716. //
  717. GetConsoleScreenBufferInfo(lpclient->TC_Console, &csbi);
  718. iHeight = csbi.srWindow.Bottom - csbi.srWindow.Top;
  719. if (pkeyRec->wVirtualKeyCode == VK_PRIOR) {
  720. TraceShiftConsoleWindow(
  721. lpclient, 0, -(wRepCount * iHeight), &csbi
  722. );
  723. }
  724. else {
  725. TraceShiftConsoleWindow(
  726. lpclient, 0, (wRepCount * iHeight), &csbi
  727. );
  728. }
  729. break;
  730. }
  731. case VK_TAB:
  732. if ((pkeyRec->dwControlKeyState & LEFT_CTRL_PRESSED) ||
  733. (pkeyRec->dwControlKeyState & RIGHT_CTRL_PRESSED)) {
  734. //
  735. // ok, we can handle it.
  736. //
  737. // see if we are to move to
  738. // the previous screen buffer or to the next screen buffer
  739. //
  740. if (pkeyRec->dwControlKeyState & SHIFT_PRESSED) {
  741. // moving to previous screen buffer
  742. //
  743. dir = -1;
  744. }
  745. else {
  746. // moving to next screen buffer
  747. //
  748. dir = 1;
  749. }
  750. //
  751. // call the function which changes the console owner
  752. //
  753. TraceUpdateConsoleOwner(lpserver, dir);
  754. }
  755. break;
  756. }
  757. return 0;
  758. }
  759. //
  760. // assumes client is locked for reading or writing
  761. //
  762. DWORD
  763. TraceShiftConsoleWindow(
  764. LPTRACE_CLIENT lpclient,
  765. INT iXShift,
  766. INT iYShift,
  767. PCONSOLE_SCREEN_BUFFER_INFO pcsbi
  768. ) {
  769. PCOORD pc;
  770. PSMALL_RECT pr;
  771. CONSOLE_SCREEN_BUFFER_INFO csbi;
  772. //
  773. // if caller did not pass in current console info,
  774. // get the info before going any further
  775. //
  776. if (pcsbi == NULL) {
  777. pcsbi = &csbi;
  778. GetConsoleScreenBufferInfo(lpclient->TC_Console, pcsbi);
  779. }
  780. //
  781. // shift the window from its current position
  782. //
  783. pc = &pcsbi->dwSize;
  784. pr = &pcsbi->srWindow;
  785. pr->Left += (USHORT)iXShift; pr->Right += (USHORT)iXShift;
  786. pr->Top += (USHORT)iYShift; pr->Bottom += (USHORT)iYShift;
  787. if (pr->Left < 0 || pr->Top < 0) { return 0; }
  788. if (pr->Right >= pc->X || pr->Bottom >= pc->Y) { return 0; }
  789. SetConsoleWindowInfo(lpclient->TC_Console, TRUE, pr);
  790. return 0;
  791. }
  792. //
  793. // searches for a new console owner in the specified direction
  794. // assumes server is locked for writing
  795. //
  796. DWORD
  797. TraceUpdateConsoleOwner(
  798. LPTRACE_SERVER lpserver,
  799. INT dir
  800. ) {
  801. INT i;
  802. DWORD dwOldOwner, dwNewOwner;
  803. LPTRACE_CLIENT lpNewOwner, lpOldOwner;
  804. //
  805. // if no-one owns the console, dwOldOwner is MAX_CLIENT_COUNT
  806. // in this case, the algorithm below ensures that the console
  807. // is assigned to someone else, if there is another console client
  808. //
  809. dwOldOwner = lpserver->TS_ConsoleOwner;
  810. if (dwOldOwner != MAX_CLIENT_COUNT) {
  811. lpOldOwner = lpserver->TS_ClientTable[dwOldOwner];
  812. }
  813. else {
  814. lpOldOwner = NULL;
  815. }
  816. //
  817. // find another owner; the macro OFFSET_CLIENT wraps
  818. // around both ends of the array, so we only need to take care
  819. // that the loop executes no more than MAX_CLIENT_COUNT times
  820. //
  821. for (i = 0, dwNewOwner = OFFSET_CLIENT(dwOldOwner, dir);
  822. i < MAX_CLIENT_COUNT && dwNewOwner != dwOldOwner;
  823. i++, dwNewOwner = OFFSET_CLIENT(dwNewOwner, dir)) {
  824. lpNewOwner = lpserver->TS_ClientTable[dwNewOwner];
  825. if (lpNewOwner != NULL) {
  826. if (TRACE_CLIENT_USES_CONSOLE(lpNewOwner)) {
  827. //
  828. // found a console client, so break out of the search
  829. //
  830. break;
  831. }
  832. }
  833. }
  834. if (lpNewOwner != NULL && TRACE_CLIENT_USES_CONSOLE(lpNewOwner)) {
  835. //
  836. // switch to the next buffer as follows:
  837. // call SetConsoleActiveScreenBuffer
  838. // update lpserver->dwConsoleOwner
  839. // update the console title since the new console owner
  840. // may be disabled
  841. //
  842. SetConsoleActiveScreenBuffer(lpNewOwner->TC_Console);
  843. lpserver->TS_ConsoleOwner = dwNewOwner;
  844. TraceUpdateConsoleTitle(lpNewOwner);
  845. }
  846. else
  847. if (lpOldOwner == NULL || !TRACE_CLIENT_USES_CONSOLE(lpOldOwner)) {
  848. //
  849. // no owner was found, and the current owner is gone
  850. // set the owner ID to MAX_CLIENT_COUNT, thereby
  851. // guaranteeing that the next console client
  852. // will become the console owner
  853. //
  854. lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT;
  855. }
  856. return 0;
  857. }
  858. // assumes server lock
  859. VOID
  860. SetWaitArray(
  861. LPTRACE_SERVER lpserver
  862. )
  863. {
  864. //
  865. // reset array for client change notifications.
  866. // only used if server thread is not created
  867. //
  868. {
  869. LPTRACE_CLIENT *lplpc, *lplpcstart, *lplpcend;
  870. g_posBase = POS_TABLE;
  871. g_hWaitHandles[POS_TABLE] = lpserver->TS_TableEvent;
  872. g_posLast = POS_CLIENT_0;
  873. lplpcstart = lpserver->TS_ClientTable;
  874. lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT;
  875. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  876. if (*lplpc != NULL && TRACE_CLIENT_USES_REGISTRY(*lplpc)) {
  877. g_hWaitHandles[g_posLast++] = (*lplpc)->TC_ConfigEvent;
  878. }
  879. }
  880. }
  881. }