|
|
//============================================================================
// 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 <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <rtutils.h>
#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 <spacebar>
// 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; } } } }
|