windows-nt/Source/XPSP1/NT/net/rras/common/rtutils/server.c
2020-09-26 16:20:57 +08:00

1148 lines
29 KiB
C

//============================================================================
// 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;
}
}
}
}