//============================================================================ // Copyright (c) 1995, Microsoft Corporation // // File: init.c // // History: // Abolade Gbadegesin July-24-1995 Created // // Server routines for tracing dll. // All functions invoked by the server thread are code-page independent. //============================================================================ #include #include #include #include #include #include "trace.h" // waits on // lpserver->hConsole // lpserver->hStopEvent // lpserver->hTableEvent // lpclient->hConfigEvent for each client #define POS_CONSOLE 0 #define POS_STOP 1 #define POS_TABLE 2 #define POS_CLIENT_0 3 #define POS_MAX MAXIMUM_WAIT_OBJECTS #define ADJUST_ARRAY(a) ((a) + posBase) #define ADJUST_INDEX(i) ((i) - posBase) #define OFFSET_CLIENT(i,d) (((i) + MAX_CLIENT_COUNT + d) % MAX_CLIENT_COUNT) extern VOID StopWorkers (VOID); LPTRACE_SERVER g_server = NULL; HINSTANCE g_module; HANDLE g_loadevent = NULL; HMODULE g_moduleRef; HANDLE g_serverThread; ULONG g_traceCount; //attempt server thread creation ? ULONG g_traceTime; //when last attempt to create server thread. DWORD g_posBase, g_posLast;//not used by serverthread. // only to decide if new serverthread to be created HANDLE g_hWaitHandles[POS_MAX]; HINSTANCE IncrementModuleReference ( VOID ); BOOL WINAPI DLLMAIN(HINSTANCE hInstDLL, DWORD dwReason, LPVOID lpvReserved) { BOOL bSuccess; HANDLE c_loadevent; switch (dwReason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hInstDLL); g_module = hInstDLL; // If a server threade managed to start before we got // DLL_PROCESS_ATTACH call (possible because of NT Loader // bug), we need to release it c_loadevent = (HANDLE)InterlockedExchangePointer ( &g_loadevent, INVALID_HANDLE_VALUE); if (c_loadevent!=NULL) { bSuccess = SetEvent (c_loadevent); ASSERTMSG ("Could not signal waiting trace servers ", bSuccess); } else bSuccess = TRUE; break; case DLL_PROCESS_DETACH: if (g_server) { bSuccess = TraceShutdownServer(g_server); g_server = NULL; } else bSuccess = TRUE; StopWorkers (); break; default: bSuccess = TRUE; } return bSuccess; } HINSTANCE IncrementModuleReference ( VOID ) { HMODULE hmodule; TCHAR szmodule[MAX_PATH+1]; HANDLE l_loadevent; HANDLE c_loadevent; DWORD rc; // Create an event in case we need to wait for DLL_PROCESS_ATTACH l_loadevent = CreateEvent (NULL, TRUE, FALSE, NULL); ASSERTMSG ("Could not create load event ", l_loadevent!=NULL); if (l_loadevent!=NULL) { // Make our event global if either no-one else // has done this yet c_loadevent = (HANDLE)InterlockedCompareExchangePointer ( (PVOID *)&g_loadevent, l_loadevent, NULL); if (c_loadevent==NULL) { rc = WaitForSingleObject (l_loadevent, INFINITE); // Let other waiting threads run as we going to close // our event right after this Sleep (0); } else if (c_loadevent==INVALID_HANDLE_VALUE) { // DLL_PROCESS_ATTACH has already been called rc = WAIT_OBJECT_0; } else { // Somebody else managed to start before us // -> wait on that event #if DBG DbgPrint ("RTUTILS: %lx - trace server thread waiting for load on existing event.\n", GetCurrentThreadId ()); #endif rc = WaitForSingleObject (c_loadevent, INFINITE); // Just in case the handle has been closed before we // managed to start the wait (unlikely because // of Sleep call above, but just in case ...) if ((rc!=WAIT_OBJECT_0) && (GetLastError ()==ERROR_INVALID_HANDLE)) { #if DBG DbgPrint ("RTUTILS: %lx - trace server thread load event was destroyed during wait.\n", GetCurrentThreadId ()); #endif rc = WAIT_OBJECT_0; } } ASSERTMSG ("Wait on load event failed ", rc==WAIT_OBJECT_0); CloseHandle (l_loadevent); if (rc==WAIT_OBJECT_0) { // // we do a LoadLibrary to increment the reference count // on RTUTILS.DLL, so that when we're unloaded by the application, // our address space doesn't disappear. // instead, our event will be signalled and then we cleanup // and call FreeLibraryAndExitThread to unload ourselves // rc = GetModuleFileName(g_module, szmodule, sizeof(szmodule)/sizeof (szmodule[0])); ASSERTMSG ("Could not get dll path ", rc>0); if (rc>0) { hmodule = LoadLibrary(szmodule); if (hmodule!=NULL) return hmodule; } } } return NULL; } // // sets up server struct in readiness for clients registering // LPTRACE_SERVER TraceCreateServer ( LPTRACE_SERVER *lpserver ) { LPTRACE_SERVER l_lpserver, c_lpserver; DWORD rc; l_lpserver = (LPTRACE_SERVER)GlobalAlloc (GPTR, sizeof (TRACE_SERVER)); if (l_lpserver!=NULL) { l_lpserver->TS_Flags = 0; l_lpserver->TS_Console = NULL; l_lpserver->TS_StopEvent = NULL; l_lpserver->TS_TableEvent = NULL; l_lpserver->TS_ClientCount = 0; l_lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT; ZeroMemory(l_lpserver->TS_FlagsCache, MAX_CLIENT_COUNT * sizeof(DWORD)); ZeroMemory( l_lpserver->TS_ClientTable, MAX_CLIENT_COUNT * sizeof(LPTRACE_CLIENT) ); try { TRACE_STARTUP_LOCKING(l_lpserver); rc = NO_ERROR; } except (EXCEPTION_EXECUTE_HANDLER) { rc = GetExceptionCode (); } ASSERTMSG ("Cound not initialize lock ", rc==NO_ERROR); if (rc==NO_ERROR) { c_lpserver = InterlockedCompareExchangePointer ( (PVOID *)lpserver, l_lpserver, NULL ); if (c_lpserver==NULL) { return l_lpserver; } else { TRACE_CLEANUP_LOCKING (l_lpserver); GlobalFree (l_lpserver); return c_lpserver; } } GlobalFree (l_lpserver); } #if DBG DbgPrint ("RTUTILS: %lx - trace server creation failed.\n", GetCurrentThreadId ()); #endif return NULL; } // // cleans server struct and de-allocates memory used // BOOL TraceShutdownServer( LPTRACE_SERVER lpserver ) { if (lpserver->TS_StopEvent != NULL && (lpserver->TS_Flags & TRACEFLAGS_SERVERTHREAD)) { // // server thread is active, let it do cleanup // SetEvent(lpserver->TS_StopEvent); } else { // // we'll do the cleanup ourselves // TraceCleanUpServer(lpserver); } return TRUE; } DWORD TraceCleanUpServer( LPTRACE_SERVER lpserver ) { LPTRACE_CLIENT lpclient, *lplpc, *lplpcstart, *lplpcend; // TRACE_ACQUIRE_WRITELOCK(lpserver); TRACE_CLEANUP_LOCKING(lpserver); // // delete client structures // lplpcstart = lpserver->TS_ClientTable; lplpcend = lplpcstart + MAX_CLIENT_COUNT; for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) { if (*lplpc != NULL) { TraceDeleteClient(lpserver, lplpc); } } lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT; lpserver->TS_ClientCount = 0; if (lpserver->TS_TableEvent != NULL) { CloseHandle(lpserver->TS_TableEvent); lpserver->TS_TableEvent = NULL; } if (lpserver->TS_StopEvent != NULL) { CloseHandle(lpserver->TS_StopEvent); lpserver->TS_StopEvent = NULL; } if (lpserver->TS_Console != NULL) { CloseHandle(lpserver->TS_Console); lpserver->TS_Console = NULL; FreeConsole(); } lpserver->TS_Flags = 0; return TRUE; } // // assumes server is locked for writing // DWORD TraceCreateServerComplete( LPTRACE_SERVER lpserver ) { HKEY hkeyConfig; DWORD dwType, dwSize, dwValue; DWORD dwErr, dwThread, dwDisposition; do { // breakout loop // // create event signalled to stop server thread // lpserver->TS_StopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (lpserver->TS_StopEvent == NULL) { dwErr = GetLastError(); break; } // // create event signalled when client registers/deregisters // lpserver->TS_TableEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (lpserver->TS_TableEvent == NULL) { dwErr = GetLastError(); break; } // // open registry key containing server configuration // dwErr = RegCreateKeyEx( HKEY_LOCAL_MACHINE, REGKEY_TRACING, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyConfig, &dwDisposition ); if (dwErr != NO_ERROR) { break; } // // read the server configuration from the config key // dwSize = sizeof(DWORD); dwErr = RegQueryValueEx( hkeyConfig, REGVAL_ENABLECONSOLETRACING, NULL, &dwType, (PBYTE)&dwValue, &dwSize ); if (dwErr != NO_ERROR) { dwType = REG_DWORD; dwSize = sizeof(DWORD); dwValue = DEF_ENABLECONSOLETRACING; RegSetValueEx( hkeyConfig, REGVAL_ENABLECONSOLETRACING, 0, dwType, (PBYTE)&dwValue, dwSize ); } if (dwValue != 0) { lpserver->TS_Flags |= TRACEFLAGS_USECONSOLE; } RegCloseKey(hkeyConfig); hkeyConfig = 0; // // set up array for client change notifications. // only used if server thread is not created // SetWaitArray(lpserver); return NO_ERROR; } while(FALSE); // // something went wrong, so clean up // if (lpserver->TS_TableEvent != NULL) { CloseHandle(lpserver->TS_TableEvent); lpserver->TS_TableEvent = NULL; } if (lpserver->TS_StopEvent != NULL) { CloseHandle(lpserver->TS_StopEvent); lpserver->TS_StopEvent = NULL; } return dwErr; } // // creates server thread if required // DWORD TraceCreateServerThread ( DWORD dwFlags, BOOL bCallerLocked, //does caller have write lock BOOL bNewRegister //new client registered. so check ) { DWORD dwErr=NO_ERROR; DWORD dwCurrentTime = GetTickCount(); BOOL bCreate, bLocked=bCallerLocked; LPTRACE_SERVER lpserver; DWORD dwThread=0; lpserver = GET_TRACE_SERVER(); if (!lpserver) return INVALID_TRACEID; // // check if serverthread should be created // bCreate = FALSE; do { if ((dwFlags & TRACE_USE_FILE) || (dwFlags & TRACE_USE_CONSOLE)) { bCreate = TRUE; break; } if (g_traceTime > dwCurrentTime) g_traceTime = dwCurrentTime; if (!bNewRegister) { if (dwCurrentTime - g_traceTime < 30000) break; } if (!bLocked){ TRACE_ACQUIRE_WRITELOCK(lpserver); bLocked = TRUE; } // check again under lock if server thread has been created if (g_serverThread) { bCreate = FALSE; break; } // // enter the wait, passing the adjusted handle count // and the adjusted array base // { DWORD dwRetval; if (!bNewRegister) { // g_posLast points to the next empty event entry dwRetval = WaitForMultipleObjects( g_posLast - g_posBase, g_hWaitHandles + g_posBase, FALSE, 0 ); if (dwRetval==WAIT_TIMEOUT) break; } } { LPTRACE_CLIENT *lplpc, *lplpcstart, *lplpcend; g_traceTime = dwCurrentTime; lplpcstart = lpserver->TS_ClientTable; lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT; g_posLast = POS_CLIENT_0; for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) { if (*lplpc == NULL) continue; if (!TRACE_CLIENT_IS_DISABLED(*lplpc)) TraceEnableClient(lpserver, *lplpc, FALSE); if (TRACE_CLIENT_USES_CONSOLE(*lplpc) || TRACE_CLIENT_USES_FILE(*lplpc)) { bCreate = TRUE; break; } if (TRACE_CLIENT_USES_REGISTRY(*lplpc)) g_hWaitHandles[g_posLast++] = (*lplpc)->TC_ConfigEvent; } } } while (FALSE); if (!bCreate) { if (bLocked && !bCallerLocked) TRACE_RELEASE_WRITELOCK(lpserver); return dwErr; } if (!bLocked) { TRACE_ACQUIRE_WRITELOCK(lpserver); bLocked = TRUE; } do { // final check under lock to see if thread created if (g_serverThread) break; g_moduleRef = IncrementModuleReference (); if (g_moduleRef==NULL) { dwErr = ERROR_CAN_NOT_COMPLETE; break; } g_serverThread = CreateThread( NULL, 0, TraceServerThread, lpserver, 0, &dwThread ); if (g_serverThread == NULL) { dwErr = GetLastError(); FreeLibrary(g_moduleRef); break; } CloseHandle(g_serverThread); } while (FALSE); if (bLocked && !bCallerLocked) TRACE_RELEASE_WRITELOCK(lpserver); return dwErr; } //---------------------------------------------------------------------------- // Function: TraceServerThread // // Parameters: // LPVOID lpvParam // //---------------------------------------------------------------------------- DWORD TraceServerThread( LPVOID lpvParam ) { DWORD dwErr; DWORD posBase, posLast; LPTRACE_SERVER lpserver; DWORD aWaitIndices[POS_MAX]; HANDLE hWaitHandles[POS_MAX]; LPTRACE_CLIENT lpclient, *lplpc, *lplpcstart, *lplpcend; // // get the server who owns this thread // lpserver = (LPTRACE_SERVER)lpvParam; // // set the flag to indicate we're running // InterlockedExchange( &lpserver->TS_Flags, lpserver->TS_Flags | TRACEFLAGS_SERVERTHREAD ); posBase = posLast = 0; lplpcstart = lpserver->TS_ClientTable; lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT; // // make sure the latest config is loaded // TRACE_ACQUIRE_WRITELOCK(lpserver); for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) { if (*lplpc != NULL && !TRACE_CLIENT_IS_DISABLED(*lplpc)) TraceEnableClient(lpserver, *lplpc, FALSE); } TRACE_RELEASE_WRITELOCK(lpserver); while (TRUE) { // // to figure out which handles will be waited on // first lock the server for reading // TRACE_ACQUIRE_READLOCK(lpserver); // // if a thread is using the console, wait on console input handle // otherwise, the base of the array of handles waited on // is adjusted upward (by setting posBase to 1); then, when the // wait returns the index of the signalled handle, the index is // compared against the POS_ constants adjusted downward // (by subtracting posBase from them); // thus if posBase is 1, we pass &hWaitHandles[1] and if we get // back 2, we compared it to (POS_CLIENT_0 - 1)==2 and then we // access position (2 - (POS_CLIENT_0 - 1))==0 in the actual // client table // if (lpserver->TS_Console != NULL) { posBase = 0; hWaitHandles[POS_CONSOLE] = lpserver->TS_Console; } else { posBase = 1; hWaitHandles[POS_CONSOLE] = NULL; } hWaitHandles[POS_STOP] = lpserver->TS_StopEvent; hWaitHandles[POS_TABLE] = lpserver->TS_TableEvent; posLast = POS_CLIENT_0; for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) { if (*lplpc != NULL && TRACE_CLIENT_USES_REGISTRY(*lplpc)) { aWaitIndices[posLast] = (ULONG) (lplpc - lplpcstart); hWaitHandles[posLast++] = (*lplpc)->TC_ConfigEvent; } } TRACE_RELEASE_READLOCK(lpserver); // // enter the wait, passing the adjusted handle count // and the adjusted array base // dwErr = WaitForMultipleObjects( posLast - posBase, hWaitHandles + posBase, FALSE, INFINITE ); dwErr += (DWORD)posBase; if (dwErr == (WAIT_OBJECT_0 + POS_CONSOLE)) { // // must be a key pressed in the console, so // process it // // lock server for writing // TRACE_ACQUIRE_WRITELOCK(lpserver); if (lpserver->TS_Console != NULL) { TraceProcessConsoleInput(lpserver); } TRACE_RELEASE_WRITELOCK(lpserver); } else if (dwErr == (WAIT_OBJECT_0 + POS_STOP)) { // // time to break out of the loop // break; } else if (dwErr == (WAIT_OBJECT_0 + POS_TABLE)) { DWORD dwOwner; // a client registered or deregistered; // we pick up the new reg config change event // the next time through the loop } else if (dwErr >= (WAIT_OBJECT_0 + POS_CLIENT_0) && dwErr <= (WAIT_OBJECT_0 + posLast)) { // // config changed for a client, lock server for writing // and lock client for writing, and reload the configuration // from the registry; take care in case the client has // already deregistered // TRACE_ACQUIRE_WRITELOCK(lpserver); lplpc = lpserver->TS_ClientTable + aWaitIndices[dwErr - WAIT_OBJECT_0]; if (*lplpc == NULL) { TRACE_RELEASE_WRITELOCK(lpserver); continue; } // // load the client's configuration, unless it's disabled // if (!TRACE_CLIENT_IS_DISABLED(*lplpc)) { TraceEnableClient(lpserver, *lplpc, FALSE); } TRACE_RELEASE_WRITELOCK(lpserver); } } // // we've received the stop signal, so do cleanup and quit // TraceCleanUpServer(lpserver); // // unload the library and exit; this call never returns // FreeLibraryAndExitThread(g_moduleRef, 0); return dwErr; } //---------------------------------------------------------------------------- // Function: TraceProcessConsoleInput // // Parameters: // LPTRACE_SERVER *lpserver // // Invoked when user presses a key in the console // Keypresses handle are // spacebar toggle the enabled/disabled flag for the client // whose screen buffer is active // pause same as // ctrl-tab set the active screen buffer to that of // the next client in the table // ctrl-shift-tab set the active screen buffer to that of // the previous client in the table // up, down, left, right scrolls the console window as expected // pageup, pagedown scrolls the console window as expected // assumes the server is locked for writing //---------------------------------------------------------------------------- DWORD TraceProcessConsoleInput( LPTRACE_SERVER lpserver ) { INT dir; BOOL bSuccess; HANDLE hStdin; DWORD dwCount; WORD wRepCount; INPUT_RECORD inputRec; PKEY_EVENT_RECORD pkeyRec; DWORD dwConsoleOwner, dwNewOwner; LPTRACE_CLIENT lpclient, lpowner; // // see who owns the console // dwConsoleOwner = lpserver->TS_ConsoleOwner; if (dwConsoleOwner == MAX_CLIENT_COUNT) { // // no-one owns the console, so just return // return 0; } lpclient = lpserver->TS_ClientTable[dwConsoleOwner]; // // get the console input handle // hStdin = lpserver->TS_Console; if (hStdin == NULL) { // // no console, so quit // return 0; } // // read input record // bSuccess = ReadConsoleInput(hStdin, &inputRec, 1, &dwCount); if (!bSuccess || dwCount == 0) { return GetLastError(); } // // return if its not a keyboard event // if (inputRec.EventType != KEY_EVENT) { return 0; } // // if its one we handle, handle it // pkeyRec = &inputRec.Event.KeyEvent; if (!pkeyRec->bKeyDown) { // // we process when the key is pressed, not released // return 0; } wRepCount = pkeyRec->wRepeatCount; switch(pkeyRec->wVirtualKeyCode) { // // space-bar and pause are handled identically // case VK_PAUSE: case VK_SPACE: if (lpclient == NULL) { break; } // // if space bar or pause pressed an even // number of times, do nothing // if ((wRepCount & 1) == 0) { break; } // // toggle the enabled flag for the client // if (TRACE_CLIENT_IS_DISABLED(lpclient)) { TraceEnableClient(lpserver, lpclient, FALSE); } else { TraceDisableClient(lpserver, lpclient); } TraceUpdateConsoleTitle(lpclient); break; // // arrow keys are handled here // case VK_LEFT: if (lpclient == NULL) { break; } TraceShiftConsoleWindow(lpclient, -wRepCount, 0, NULL); break; case VK_RIGHT: if (lpclient == NULL) { break; } TraceShiftConsoleWindow(lpclient, wRepCount, 0, NULL); break; case VK_UP: if (lpclient == NULL) { break; } TraceShiftConsoleWindow(lpclient, 0, -wRepCount, NULL); break; case VK_DOWN: if (lpclient == NULL) { break; } TraceShiftConsoleWindow(lpclient, 0, wRepCount, NULL); break; // // page-up and page-down are handled here // case VK_PRIOR: case VK_NEXT: { INT iHeight; CONSOLE_SCREEN_BUFFER_INFO csbi; if (lpclient == NULL) { break; } // // find the current height of the window // GetConsoleScreenBufferInfo(lpclient->TC_Console, &csbi); iHeight = csbi.srWindow.Bottom - csbi.srWindow.Top; if (pkeyRec->wVirtualKeyCode == VK_PRIOR) { TraceShiftConsoleWindow( lpclient, 0, -(wRepCount * iHeight), &csbi ); } else { TraceShiftConsoleWindow( lpclient, 0, (wRepCount * iHeight), &csbi ); } break; } case VK_TAB: if ((pkeyRec->dwControlKeyState & LEFT_CTRL_PRESSED) || (pkeyRec->dwControlKeyState & RIGHT_CTRL_PRESSED)) { // // ok, we can handle it. // // see if we are to move to // the previous screen buffer or to the next screen buffer // if (pkeyRec->dwControlKeyState & SHIFT_PRESSED) { // moving to previous screen buffer // dir = -1; } else { // moving to next screen buffer // dir = 1; } // // call the function which changes the console owner // TraceUpdateConsoleOwner(lpserver, dir); } break; } return 0; } // // assumes client is locked for reading or writing // DWORD TraceShiftConsoleWindow( LPTRACE_CLIENT lpclient, INT iXShift, INT iYShift, PCONSOLE_SCREEN_BUFFER_INFO pcsbi ) { PCOORD pc; PSMALL_RECT pr; CONSOLE_SCREEN_BUFFER_INFO csbi; // // if caller did not pass in current console info, // get the info before going any further // if (pcsbi == NULL) { pcsbi = &csbi; GetConsoleScreenBufferInfo(lpclient->TC_Console, pcsbi); } // // shift the window from its current position // pc = &pcsbi->dwSize; pr = &pcsbi->srWindow; pr->Left += (USHORT)iXShift; pr->Right += (USHORT)iXShift; pr->Top += (USHORT)iYShift; pr->Bottom += (USHORT)iYShift; if (pr->Left < 0 || pr->Top < 0) { return 0; } if (pr->Right >= pc->X || pr->Bottom >= pc->Y) { return 0; } SetConsoleWindowInfo(lpclient->TC_Console, TRUE, pr); return 0; } // // searches for a new console owner in the specified direction // assumes server is locked for writing // DWORD TraceUpdateConsoleOwner( LPTRACE_SERVER lpserver, INT dir ) { INT i; DWORD dwOldOwner, dwNewOwner; LPTRACE_CLIENT lpNewOwner, lpOldOwner; // // if no-one owns the console, dwOldOwner is MAX_CLIENT_COUNT // in this case, the algorithm below ensures that the console // is assigned to someone else, if there is another console client // dwOldOwner = lpserver->TS_ConsoleOwner; if (dwOldOwner != MAX_CLIENT_COUNT) { lpOldOwner = lpserver->TS_ClientTable[dwOldOwner]; } else { lpOldOwner = NULL; } // // find another owner; the macro OFFSET_CLIENT wraps // around both ends of the array, so we only need to take care // that the loop executes no more than MAX_CLIENT_COUNT times // for (i = 0, dwNewOwner = OFFSET_CLIENT(dwOldOwner, dir); i < MAX_CLIENT_COUNT && dwNewOwner != dwOldOwner; i++, dwNewOwner = OFFSET_CLIENT(dwNewOwner, dir)) { lpNewOwner = lpserver->TS_ClientTable[dwNewOwner]; if (lpNewOwner != NULL) { if (TRACE_CLIENT_USES_CONSOLE(lpNewOwner)) { // // found a console client, so break out of the search // break; } } } if (lpNewOwner != NULL && TRACE_CLIENT_USES_CONSOLE(lpNewOwner)) { // // switch to the next buffer as follows: // call SetConsoleActiveScreenBuffer // update lpserver->dwConsoleOwner // update the console title since the new console owner // may be disabled // SetConsoleActiveScreenBuffer(lpNewOwner->TC_Console); lpserver->TS_ConsoleOwner = dwNewOwner; TraceUpdateConsoleTitle(lpNewOwner); } else if (lpOldOwner == NULL || !TRACE_CLIENT_USES_CONSOLE(lpOldOwner)) { // // no owner was found, and the current owner is gone // set the owner ID to MAX_CLIENT_COUNT, thereby // guaranteeing that the next console client // will become the console owner // lpserver->TS_ConsoleOwner = MAX_CLIENT_COUNT; } return 0; } // assumes server lock VOID SetWaitArray( LPTRACE_SERVER lpserver ) { // // reset array for client change notifications. // only used if server thread is not created // { LPTRACE_CLIENT *lplpc, *lplpcstart, *lplpcend; g_posBase = POS_TABLE; g_hWaitHandles[POS_TABLE] = lpserver->TS_TableEvent; g_posLast = POS_CLIENT_0; lplpcstart = lpserver->TS_ClientTable; lplpcend = lpserver->TS_ClientTable + MAX_CLIENT_COUNT; for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) { if (*lplpc != NULL && TRACE_CLIENT_USES_REGISTRY(*lplpc)) { g_hWaitHandles[g_posLast++] = (*lplpc)->TC_ConfigEvent; } } } }