windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/cpi32/ut.cpp
2020-09-26 16:20:57 +08:00

1980 lines
47 KiB
C++

#include "precomp.h"
//
// UT.CPP
// Utility Functions
//
#include <limits.h>
#include <process.h>
#include <mmsystem.h>
#include <confreg.h>
#define MLZ_FILE_ZONE ZONE_UT
//
//
// UT_InitTask(...)
//
//
BOOL UT_InitTask
(
UT_TASK task,
PUT_CLIENT * pputTask
)
{
BOOL fInit = FALSE;
BOOL locked = FALSE;
PUT_CLIENT putTask = NULL;
DebugEntry(UT_InitTask);
UT_Lock(UTLOCK_UT);
//
// Initialise handle to NULL
//
*pputTask = NULL;
ASSERT(task >= UTTASK_FIRST);
ASSERT(task < UTTASK_MAX);
//
// The UT_TASK is an index into the tasks array.
//
putTask = &(g_autTasks[task]);
if (putTask->dwThreadId)
{
ERROR_OUT(("Task %d already exists", task));
putTask = NULL;
DC_QUIT;
}
ZeroMemory(putTask, sizeof(UT_CLIENT));
//
// Call routine to set up the process id information in the task CB.
//
putTask->dwThreadId = GetCurrentThreadId();
//
// Create the window
//
putTask->utHwnd = CreateWindow(MAKEINTATOM(g_utWndClass),
NULL, // name
0, // style
1, // x
1, // y
200, // width
100, // height
NULL, // parent
NULL, // menu
g_asInstance,
NULL); // create struct
if (!putTask->utHwnd)
{
ERROR_OUT(("Failed to create UT msg window"));
DC_QUIT;
}
//
// Now store the UT handle in the user data associated with the
// window. We will use this to get the UT handle when we are in
// the event procedure.
//
SetWindowLongPtr(putTask->utHwnd, GWLP_USERDATA, (LPARAM)putTask);
fInit = TRUE;
DC_EXIT_POINT:
//
// Callers will call UT_TermTask() on error, which will bump down
// the shared memory count. So we have no clean up on error here.
//
*pputTask = putTask;
//
// Release access to task stuff
//
UT_Unlock(UTLOCK_UT);
DebugExitBOOL(UT_InitTask, fInit);
return(fInit);
}
//
// UT_TermTask(...)
//
void UT_TermTask(PUT_CLIENT * pputTask)
{
DebugEntry(UT_TermTask);
//
// Check that the putTask is valid
//
if (!*pputTask)
{
WARNING_OUT(("UT_TermTask: null task"));
DC_QUIT;
}
UTTaskEnd(*pputTask);
*pputTask = NULL;
DC_EXIT_POINT:
DebugExitVOID(UT_TermTask);
}
//
//
// UTTaskEnd(...)
//
//
void UTTaskEnd(PUT_CLIENT putTask)
{
int i;
PUTEXIT_PROC_INFO pExit;
PUTEVENT_INFO pEventInfo;
DebugEntry(UTTaskEnd);
UT_Lock(UTLOCK_UT);
if (!putTask->dwThreadId)
{
// Nothing to do
DC_QUIT;
}
ValidateUTClient(putTask);
//
// Call any registered exit procedures. Since we guarantee to call
// exit procs in the reverse order to the order they were registered,
// we start at the end of the array and call each proc in turn back to
// the first one registered:
//
TRACE_OUT(("Calling exit procedures..."));
for (i = UTEXIT_PROCS_MAX-1 ; i >= 0; i--)
{
pExit = &(putTask->exitProcs[i]);
if (pExit->exitProc != NULL)
{
pExit->exitProc(pExit->exitData);
//
// If any exit proc still exists in slot i, then this proc has
// failed to deregister itself. This is not mandatory but is
// expected.
//
if (pExit->exitProc != NULL)
{
TRACE_OUT(("Exit proc 0x%08x failed to deregister itself when called",
pExit->exitProc));
}
}
}
//
// Free delayed events
//
pEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putTask->delayedEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
while (pEventInfo != NULL)
{
COM_BasedListRemove(&(pEventInfo->chain));
delete pEventInfo;
pEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putTask->delayedEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
}
//
// Free pending events
//
pEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putTask->pendingEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
while (pEventInfo != NULL)
{
COM_BasedListRemove(&(pEventInfo->chain));
delete pEventInfo;
pEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putTask->pendingEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
}
//
// If we created a window to post UT events to for this task, then
// destroy the window. This will also kill all the timers which are
// pending for this window.
//
if (putTask->utHwnd != NULL)
{
DestroyWindow(putTask->utHwnd);
putTask->utHwnd = NULL;
}
//
// Clear out the thread ID
//
putTask->dwThreadId = 0;
DC_EXIT_POINT:
UT_Unlock(UTLOCK_UT);
DebugExitVOID(UTTaskEnd);
}
//
//
// UT_RegisterEvent(...)
//
//
void WINAPI UT_RegisterEvent
(
PUT_CLIENT putTask,
UTEVENT_PROC eventProc,
LPVOID eventData,
UT_PRIORITY priority
)
{
int i;
PUTEVENT_PROC_INFO pEventProcData;
DebugEntry(UT_RegisterEvent);
ValidateUTClient(putTask);
//
// Check that the priority is valid
//
ASSERT(priority <= UT_PRIORITY_MAX);
//
// Check that we have room for this event handler
//
pEventProcData = putTask->eventHandlers;
ASSERT(pEventProcData[UTEVENT_HANDLERS_MAX-1].eventProc == NULL);
//
// Find the place to insert this event handler
//
TRACE_OUT(("Looking for pos for event proc at priority %d", priority));
for (i = 0; i < UTEVENT_HANDLERS_MAX; i++)
{
if (pEventProcData[i].eventProc == NULL)
{
TRACE_OUT(("Found NULL slot at position %d", i));
break;
}
if (pEventProcData[i].priority <= priority)
{
TRACE_OUT(("Found event proc of priority %d at pos %d",
pEventProcData[i].priority, i));
break;
}
}
//
// Shift all lower and equal priority event handlers down a slot
//
UT_MoveMemory(&pEventProcData[i+1], &pEventProcData[i],
sizeof(UTEVENT_PROC_INFO) * (UTEVENT_HANDLERS_MAX - 1 - i));
pEventProcData[i].eventProc = eventProc;
pEventProcData[i].eventData = eventData;
pEventProcData[i].priority = priority;
DebugExitVOID(UT_RegisterEvent);
}
//
//
// UT_DeregisterEvent(...)
//
//
void UT_DeregisterEvent
(
PUT_CLIENT putTask,
UTEVENT_PROC eventProc,
LPVOID eventData
)
{
int i;
BOOL found = FALSE;
DebugEntry(UT_DeregisterEvent);
ValidateUTClient(putTask);
//
// Find the Event handler
//
for (i = 0; i < UTEVENT_HANDLERS_MAX; i++)
{
if ( (putTask->eventHandlers[i].eventProc == eventProc) &&
(putTask->eventHandlers[i].eventData == eventData) )
{
//
// Found handler - shuffle down stack on top of it
//
TRACE_OUT(("Deregistering event proc 0x%08x from position %d",
eventProc, i));
found = TRUE;
//
// Slide all the other event procs up one
//
UT_MoveMemory(&putTask->eventHandlers[i],
&putTask->eventHandlers[i+1],
sizeof(UTEVENT_PROC_INFO) * (UTEVENT_HANDLERS_MAX - 1 - i));
putTask->eventHandlers[UTEVENT_HANDLERS_MAX-1].eventProc = NULL;
break;
}
}
//
// Check that we found the event handler
//
ASSERT(found);
DebugExitVOID(UT_DeregisterEvent);
}
//
//
// UT_PostEvent(...)
//
//
void UT_PostEvent
(
PUT_CLIENT putFrom,
PUT_CLIENT putTo,
UINT delay,
UINT eventNo,
UINT_PTR param1,
UINT_PTR param2
)
{
DebugEntry(UT_PostEvent);
//
// Get exclusive access to the UTM while we move event pool entries --
// we are changing fields in a task, so we need to protect it.
//
UT_Lock(UTLOCK_UT);
if (!putTo || (putTo->utHwnd == NULL))
{
TRACE_OUT(("NULL destination task %x in UT_PostEvent", putTo));
DC_QUIT;
}
ValidateUTClient(putFrom);
ValidateUTClient(putTo);
if (delay != 0)
{
//
// A delay was specified...
//
UTPostDelayedEvt(putFrom, putTo, delay, eventNo, param1, param2);
}
else
{
//
// No delay specified - post the event now
//
UTPostImmediateEvt(putFrom, putTo, eventNo, param1, param2);
}
DC_EXIT_POINT:
UT_Unlock(UTLOCK_UT);
DebugExitVOID(UT_PostEvent);
}
//
// UTPostImmediateEvt(...)
//
void UTPostImmediateEvt
(
PUT_CLIENT putFrom,
PUT_CLIENT putTo,
UINT event,
UINT_PTR param1,
UINT_PTR param2
)
{
PUTEVENT_INFO pEventInfo;
BOOL destQueueEmpty;
DebugEntry(UTPostImmediateEvt);
TRACE_OUT(("Posting event %d (%#.4hx, %#.8lx) from 0x%08x to 0x%08x",
event,
param1,
param2,
putFrom, putTo));
//
// Allocate an event.
//
pEventInfo = new UTEVENT_INFO;
if (!pEventInfo)
{
WARNING_OUT(("UTPostImmediateEvent failed; out of memory"));
DC_QUIT;
}
ZeroMemory(pEventInfo, sizeof(*pEventInfo));
SET_STAMP(pEventInfo, UTEVENT);
//
// Determine whether the target queue is empty
//
destQueueEmpty = COM_BasedListIsEmpty(&(putTo->pendingEvents));
//
// Copy the event into the memory
//
pEventInfo->putTo = putTo;
pEventInfo->popTime = 0;
pEventInfo->event = event;
pEventInfo->param1 = param1;
pEventInfo->param2 = param2;
//
// Add to the end of the target queue
//
COM_BasedListInsertBefore(&(putTo->pendingEvents), &(pEventInfo->chain));
//
// If the target queue was empty, or the destination task is currently
// waiting for an event (in UT_WaitEvent()), we have to post a trigger
// event to get it to check its event queue.
//
if (destQueueEmpty)
{
UTTriggerEvt(putFrom, putTo);
}
DC_EXIT_POINT:
DebugExitVOID(UTPostImmediateEvt);
}
//
//
// UTPostDelayedEvt(...)
//
//
void UTPostDelayedEvt
(
PUT_CLIENT putFrom,
PUT_CLIENT putTo,
UINT delay,
UINT event,
UINT_PTR param1,
UINT_PTR param2
)
{
PUTEVENT_INFO pDelayedEventInfo;
PUTEVENT_INFO pTempEventInfo;
BOOL firstDelayed = TRUE;
DebugEntry(UTPostDelayedEvt);
TRACE_OUT(("Posting delayed event %d (%#.4hx, %#.8lx) " \
"from 0x%08x to 0x%08x, delay %u ms",
event,
param1,
param2, putFrom, putTo, delay));
//
// Get an entry from the event pool of the destination
//
pDelayedEventInfo = new UTEVENT_INFO;
if (!pDelayedEventInfo)
{
ERROR_OUT(("UTPostDelayedEvt failed; out of memory"));
DC_QUIT;
}
ZeroMemory(pDelayedEventInfo, sizeof(*pDelayedEventInfo));
SET_STAMP(pDelayedEventInfo, UTEVENT);
//
// Copy the event into the memory
//
pDelayedEventInfo->putTo = putTo;
pDelayedEventInfo->popTime = GetTickCount() + delay;
pDelayedEventInfo->event = event;
pDelayedEventInfo->param1 = param1;
pDelayedEventInfo->param2 = param2;
TRACE_OUT(("This event set to pop at %x",
pDelayedEventInfo->popTime));
//
// Insert the delayed event into the delayed queue at the sender. The
// list is ordered by the time the event needs to be scheduled.
//
pTempEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putFrom->delayedEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
while (pTempEventInfo != NULL)
{
ValidateEventInfo(pTempEventInfo);
TRACE_OUT(("Check if before %d popTime %x",
pTempEventInfo->event, pTempEventInfo->popTime));
if (pTempEventInfo->popTime > pDelayedEventInfo->popTime)
{
//
// we have found the first event in the list which pops after
// this event so insert before it.
//
break;
}
pTempEventInfo = (PUTEVENT_INFO)COM_BasedListNext(&(putFrom->delayedEvents),
pTempEventInfo, FIELD_OFFSET(UTEVENT_INFO, chain));
//
// Flag that we are not the first delayed event so we know not to
// (re)start a timer.
//
firstDelayed = FALSE;
}
if (pTempEventInfo == NULL)
{
//
// After all in queue so add to end
//
COM_BasedListInsertBefore(&(putFrom->delayedEvents),
&(pDelayedEventInfo->chain));
}
else
{
//
// Delayed event pops before pTempEventInfo so insert before.
//
COM_BasedListInsertBefore(&(pTempEventInfo->chain),
&(pDelayedEventInfo->chain));
}
//
// If we have inserted the delayed event at the front of the queue then
// restart the timer with the time this event is set to pop.
//
if (firstDelayed)
{
UTStartDelayedEventTimer(putFrom, pDelayedEventInfo->popTime);
}
DC_EXIT_POINT:
DebugExitVOID(UTPostDelayedEvt);
}
//
//
// UTCheckDelayedEvents(...)
//
//
void UTCheckDelayedEvents
(
PUT_CLIENT putTask
)
{
PUT_CLIENT putTo;
UINT timeNow;
PUTEVENT_INFO pEventInfo;
DebugEntry(UTCheckDelayedEvents);
//
// Get exclusive access to the UTM while we move event pool entries
// (these are in shared memory)
//
UT_Lock(UTLOCK_UT);
ValidateUTClient(putTask);
//
// Get time now to check against popTime.
//
timeNow = GetTickCount();
TRACE_OUT(("time now is %x", timeNow));
//
// Move through the queue of delayed events to see if any have popped.
// If so send them immediately. When we get to the first one that
// hasn't popped restart a timer to schedule it.
//
pEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putTask->delayedEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
while (pEventInfo != NULL)
{
ValidateEventInfo(pEventInfo);
//
// Got an event so check to see if it has popped
//
TRACE_OUT(("Event popTime is %x", pEventInfo->popTime));
if (timeNow >= pEventInfo->popTime)
{
TRACE_OUT(("Event popped so post now"));
//
// Event has popped so remove from delayed queue and post as an
// immediate event.
//
COM_BasedListRemove(&(pEventInfo->chain));
//
// The check on the destination handle should be less strict
// than that on the source (we shouldn't assert). This is
// because the caller may be pre-empted before this check is
// done, and the destination may shut down in this time.
//
ValidateUTClient(pEventInfo->putTo);
UTPostImmediateEvt(putTask, pEventInfo->putTo,
pEventInfo->event,
pEventInfo->param1,
pEventInfo->param2);
//
// Free the event
//
delete pEventInfo;
//
// Last one popped so move on to next to see if that has popped
// too.
//
pEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putTask->delayedEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
}
else
{
//
// got to an event which hasn't popped yet. Start timer to pop
// for this one. The OS specific code in UTStartDelayedEventTimer checks
// to see if the new timer is required (not already running)
// and will stop and restart if already running but has the
// incorrect timeout.
//
TRACE_OUT(("Event not popped so restart timer and leave"));
UTStartDelayedEventTimer(putTask, pEventInfo->popTime);
break;
}
}
UT_Unlock(UTLOCK_UT);
DebugExitVOID(UTCheckDelayedEvents);
}
//
// UTProcessEvent(...)
//
void UTProcessEvent
(
PUT_CLIENT putTask,
UINT event,
UINT_PTR param1,
UINT_PTR param2
)
{
int i;
PUTEVENT_PROC_INFO pEventHandler;
DebugEntry(UTProcessEvent);
ValidateUTClient(putTask);
//
// Call all registered event handlers until somebody returns TRUE, that
// the event has been processed.
//
for (i = 0; i < UTEVENT_HANDLERS_MAX ; i++)
{
pEventHandler = &(putTask->eventHandlers[i]);
if (pEventHandler->eventProc == NULL)
{
//
// Nothing's here.
//
break;
}
//
// Call the registered event handler
//
TRACE_OUT(("Call event proc 0x%08x priority %d from position %d",
pEventHandler->eventProc,
pEventHandler->priority,
i));
if ((pEventHandler->eventProc)(pEventHandler->eventData, event,
param1, param2))
{
//
// Event handler processed event
//
break;
}
}
DebugExitVOID(UTProcessEvent);
}
//
//
//
// EXIT PROCS
//
// Our strategy for registering/deregistering/calling exit procs is as
// follows:
//
// - we register procs in the first free slot in the array hung off the
// task data
//
// - we deregister procs by shuffling down other procs after it in the
// array
//
// - we call procs starting at the last entry in the array and working
// backwards.
//
// The above ensures that
//
// - if a proc deregisters itself before task termination, no gaps are
// left in the array
//
// - if a proc deregisters itself during task termination, all
// remaining procs are called in the correct order
//
// - if a proc doesn't deregister itself during task termination, it is
// left in the array but does not affect future processing as the task
// end loop will call the previous one anyway.
//
//
//
//
//
// UT_RegisterExit(...)
//
//
void UT_RegisterExit
(
PUT_CLIENT putTask,
UTEXIT_PROC exitProc,
LPVOID exitData
)
{
int i;
PUTEXIT_PROC_INFO pExitProcs;
DebugEntry(UT_RegisterExit);
ValidateUTClient(putTask);
pExitProcs = putTask->exitProcs;
ASSERT(pExitProcs[UTEXIT_PROCS_MAX-1].exitProc == NULL);
//
// Now we look for the first free slot in the array, since we guarantee
// to call exit procs in the order they were registered in:
//
for (i = 0; i < UTEXIT_PROCS_MAX; i++)
{
if (pExitProcs[i].exitProc == NULL)
{
TRACE_OUT(("Storing exit proc 0x%08x data 0x%08x at position %d",
exitProc, exitData, i));
pExitProcs[i].exitProc = exitProc;
pExitProcs[i].exitData = exitData;
break;
}
}
ASSERT(i < UTEXIT_PROCS_MAX);
DebugExitVOID(UT_RegisterExit);
}
//
//
// UT_DeregisterExit(...)
//
//
void UT_DeregisterExit
(
PUT_CLIENT putTask,
UTEXIT_PROC exitProc,
LPVOID exitData
)
{
int i;
BOOL found = FALSE;
PUTEXIT_PROC_INFO pExitProcs;
DebugEntry(UT_DeregisterExit);
ValidateUTClient(putTask);
pExitProcs = putTask->exitProcs;
//
// Find this exit proc
//
for (i = 0 ; i < UTEXIT_PROCS_MAX; i++)
{
if ((pExitProcs[i].exitProc == exitProc) &&
(pExitProcs[i].exitData == exitData))
{
//
// Found exit proc. Shuffle list down.
//
TRACE_OUT(("Deregistering exit proc 0x%08x from position %d",
exitProc, i));
found = TRUE;
UT_MoveMemory(&pExitProcs[i],
&pExitProcs[i+1],
sizeof(UTEXIT_PROC_INFO) * (UTEXIT_PROCS_MAX - 1 - i));
pExitProcs[UTEXIT_PROCS_MAX-1].exitProc = NULL;
break;
}
}
//
// Check that we found the exit procs
//
ASSERT(found);
DebugExitVOID(UT_DeregisterExit);
}
//
// UTTriggerEvt()
//
void UTTriggerEvt
(
PUT_CLIENT putFrom,
PUT_CLIENT putTo
)
{
DebugEntry(UTTriggerEvt);
ValidateUTClient(putFrom);
ValidateUTClient(putTo);
if (putTo->utHwnd)
{
if (!PostMessage(putTo->utHwnd, WM_UTTRIGGER_MSG, 0, 0))
{
//
// Failed to send event
//
WARNING_OUT(("Failed to post trigger message from %x to %x",
putFrom, putTo));
}
}
DebugExitVOID(UTTriggerEvt);
}
//
//
// UTStartDelayedEventTimer(...)
//
//
void UTStartDelayedEventTimer(PUT_CLIENT putTask, UINT popTime)
{
UINT currentTickCount;
UINT delay = 1;
DebugEntry(UTStartDelayedEventTimer);
//
// Work out the delay from the current time to popTime (popTime is
// given in terms of the system tick count). Be careful in the case
// where we have already passed popTime...
//
currentTickCount = GetTickCount();
if (popTime > currentTickCount)
{
delay = popTime - currentTickCount;
}
//
// Set the timer going. Note that if the timer has already been
// started, this call will reset it using the new delay.
//
if (!SetTimer(putTask->utHwnd, UT_DELAYED_TIMER_ID, delay, NULL))
{
ERROR_OUT(("Could not create timer for delayed event"));
}
DebugExitVOID(UTStartDelayedEventTimer);
}
//
// UT_HandleProcessStart()
//
BOOL UT_HandleProcessStart(HINSTANCE hInstance)
{
BOOL rc = FALSE;
int lock;
WNDCLASS windowClass;
DebugEntry(UT_HandleProcessStart);
//
// Save our dll handle.
//
g_asInstance = hInstance;
//
// Init our critical sections
//
for (lock = UTLOCK_FIRST; lock < UTLOCK_MAX; lock++)
{
InitializeCriticalSection(&g_utLocks[lock]);
}
//
// Register the UT window class
//
windowClass.style = 0;
windowClass.lpfnWndProc = UT_WndProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = g_asInstance;
windowClass.hIcon = NULL;
windowClass.hCursor = NULL;
windowClass.hbrBackground = NULL;
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = UT_WINDOW_CLASS;
g_utWndClass = RegisterClass(&windowClass);
if (!g_utWndClass)
{
ERROR_OUT(("Failed to register class"));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
DebugExitBOOL(UT_HandleProcessStart, rc);
return(rc);
}
//
// UT_HandleProcessEnd()
//
void UT_HandleProcessEnd(void)
{
int lock;
PUT_CLIENT putTask;
int task;
DebugEntry(UT_HandleProcessEnd);
TRACE_OUT(("Process is ending"));
//
// Loop through all the registered UT tasks looking for those on this
// process. Start at the end, and work up to the front.
//
putTask = &(g_autTasks[UTTASK_MAX - 1]);
for (task = UTTASK_MAX - 1; task >= UTTASK_FIRST; task--, putTask--)
{
//
// Is this entry in the UTM in use ?
//
if (putTask->dwThreadId)
{
//
// Clean up after this UT task
//
TRACE_OUT(("Task %x ending without calling UT_TermTask", putTask));
//
// On ProcessEnd, the windows are no longer valid. If it took
// too long to shutdown, we might not have received a thread
// detach notification. In which case we wouldn't have cleaned
// up the thread objects.
//
if (putTask->dwThreadId != GetCurrentThreadId())
{
putTask->utHwnd = NULL;
}
UTTaskEnd(putTask);
}
}
if (g_utWndClass)
{
UnregisterClass(MAKEINTATOM(g_utWndClass), g_asInstance);
g_utWndClass = 0;
}
//
// Clean up the critical sections. Do this last to first, in inverse
// order that they are created.
//
for (lock = UTLOCK_MAX-1; lock >= UTLOCK_FIRST; lock--)
{
DeleteCriticalSection(&g_utLocks[lock]);
}
DebugExitVOID(UT_HandleProcessEnd);
}
//
// UT_HandleThreadEnd()
//
void UT_HandleThreadEnd(void)
{
PUT_CLIENT putTask;
DWORD dwThreadId;
int task;
DebugEntry(UT_HandleThreadEnd);
UT_Lock(UTLOCK_UT);
//
// Get the current thread ID
//
dwThreadId = GetCurrentThreadId();
//
// Loop through all the registered UT tasks looking for one on this
// process and thread. Note that there should only be one entry in the
// UTM for each thread, so we can break out of the loop if we get a
// match.
//
putTask = &(g_autTasks[UTTASK_MAX - 1]);
for (task = UTTASK_MAX - 1; task >= UTTASK_FIRST; task--, putTask--)
{
//
// Is there a task here that matches the current thread?
// Tasks not present have 0 for the thread ID, which won't match
//
if (putTask->dwThreadId == dwThreadId)
{
//
// Clean up after this UT task
//
WARNING_OUT(("Task %x ending without calling UT_TermTask", putTask));
UTTaskEnd(putTask);
}
}
UT_Unlock(UTLOCK_UT);
DebugExitVOID(UT_HandleThreadEnd);
}
//
//
// UT_WndProc(...)
//
//
LRESULT CALLBACK UT_WndProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT retVal = 0;
PUT_CLIENT putTask;
DebugEntry(UT_WndProc);
//
// This isn't a UT message, so we should handle it
//
switch (message)
{
case WM_TIMER:
//
// WM_TIMER is used for delayed events...
//
TRACE_OUT(("Timer Id is 0x%08x", wParam));
if (wParam == UT_DELAYED_TIMER_ID) // defined as 0x10101010
{
//
// Get our UT handle from the window data
//
putTask = (PUT_CLIENT)GetWindowLongPtr(hwnd, GWLP_USERDATA);
ValidateUTClient(putTask);
//
// Stop the timer before it ticks again !
//
KillTimer(putTask->utHwnd, UT_DELAYED_TIMER_ID);
//
// Process the delayed event
//
UTCheckDelayedEvents(putTask);
}
break;
case WM_UTTRIGGER_MSG:
putTask = (PUT_CLIENT)GetWindowLongPtr(hwnd, GWLP_USERDATA);
ValidateUTClient(putTask);
//
// Distribute pending events
//
UTCheckEvents(putTask);
break;
default:
//
// Call on to the default handler
//
retVal = DefWindowProc(hwnd, message, wParam, lParam);
break;
}
DebugExitDWORD(UT_WndProc, retVal);
return(retVal);
}
//
//
// UTCheckEvents()
// This delivers any normal pending events
//
//
void UTCheckEvents
(
PUT_CLIENT putTask
)
{
PUTEVENT_INFO pEventInfo;
BOOL eventsOnQueue = TRUE;
int eventsProcessed = 0;
UINT event;
UINT_PTR param1, param2;
DebugEntry(UTCheckEvents);
UT_Lock(UTLOCK_UT);
//
// This while-loop picks any events off our queue and calls the
// handers. We only process a certain number, to be a well behaved
// task. Many event handlers in turn post other events...
//
while (eventsOnQueue && (eventsProcessed < MAX_EVENTS_TO_PROCESS))
{
//
// Are there any events waiting on the queue?
//
pEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putTask->pendingEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
if (pEventInfo != NULL)
{
ValidateEventInfo(pEventInfo);
TRACE_OUT(("Event(s) pending - returning first one in queue"));
//
// Return event from queue
//
event = pEventInfo->event;
param1 = pEventInfo->param1;
param2 = pEventInfo->param2;
//
// Remove event from queue
//
COM_BasedListRemove(&(pEventInfo->chain));
//
// Free the event
//
delete pEventInfo;
}
else
{
//
// No events on the queue - this can happen if we
// process the event queue between the trigger event
// being sent, amd the trigger event being received.
//
TRACE_OUT(("Got event trigger but no events on queue!"));
DC_QUIT;
}
//
// Check now if there are still events on the queue.
//
// NOTE:
// We set up eventsOnQueue now, rather than after the call
// to ProcessEvent - this means that if processing the last
// event on the queue (say, event A) causes event B to be
// posted back to ourselves, we will not process B until
// later, when the event arrives for it. This may seem
// like an unnecessary delay but it is vital to prevent
// yield nesting.
//
pEventInfo = (PUTEVENT_INFO)COM_BasedListFirst(&(putTask->pendingEvents),
FIELD_OFFSET(UTEVENT_INFO, chain));
if (pEventInfo == NULL)
{
eventsOnQueue = FALSE;
}
//
// Unlock access to shared memory -- we're about to yield
//
UT_Unlock(UTLOCK_UT);
UTProcessEvent(putTask, event, param1, param2);
UT_Lock(UTLOCK_UT);
if (!putTask->dwThreadId)
{
//
// The task was terminated by the event. bail out.
//
WARNING_OUT(("Task %x terminated in event handler", putTask));
DC_QUIT;
}
//
// Increment the number of events we've processed in this
// loop.
//
eventsProcessed++;
}
//
// There is an upper limit to the number of events we try to
// process in one loop. If we've reached this limit, post a
// trigger event to ensure that we process the remaining events
// later, then quit.
//
if (eventsProcessed >= MAX_EVENTS_TO_PROCESS)
{
TRACE_OUT(("Another trigger event required"));
UTTriggerEvt(putTask, putTask);
}
DC_EXIT_POINT:
UT_Unlock(UTLOCK_UT);
DebugExitVOID(UTUtilitiesWndProc);
}
//
// UT_MallocRefCount()
//
// This allocates a ref-count block, one that doesn't go away until
// the ref-count reaches zero.
//
void * UT_MallocRefCount
(
UINT cbSizeMem,
BOOL fZeroMem
)
{
PUTREFCOUNTHEADER pHeader;
void * pMemory = NULL;
DebugEntry(UT_MallocRefCount);
//
// Allocate a block the client's size + our header's size
//
pHeader = (PUTREFCOUNTHEADER)new BYTE[sizeof(UTREFCOUNTHEADER) + cbSizeMem];
if (!pHeader)
{
ERROR_OUT(("UT_MallocRefCount failed; out of memory"));
DC_QUIT;
}
if (fZeroMem)
{
ZeroMemory(pHeader, sizeof(UTREFCOUNTHEADER) + cbSizeMem);
}
SET_STAMP(pHeader, UTREFCOUNTHEADER);
pHeader->refCount = 1;
pMemory = (pHeader + 1);
DC_EXIT_POINT:
DebugExitPTR(UT_MallocRefCount, pMemory);
return(pMemory);
}
//
// UT_BumpUpRefCount()
//
void UT_BumpUpRefCount
(
void * pMemory
)
{
PUTREFCOUNTHEADER pHeader;
DebugEntry(UT_BumpUpRefCount);
ASSERT(pMemory);
pHeader = (PUTREFCOUNTHEADER)((LPBYTE)pMemory - sizeof(UTREFCOUNTHEADER));
ASSERT(!IsBadWritePtr(pHeader, sizeof(UTREFCOUNTHEADER)));
ASSERT(pHeader->stamp.idStamp[0] == 'A');
ASSERT(pHeader->stamp.idStamp[1] == 'S');
ASSERT(pHeader->refCount);
pHeader->refCount++;
TRACE_OUT(("Bumped up ref-counted memory block 0x%08x to %d", pHeader, pHeader->refCount));
DebugExitVOID(UT_BumpUpRefCount);
}
//
// UT_FreeRefCount()
//
void UT_FreeRefCount
(
void ** ppMemory,
BOOL fNullOnlyWhenFreed
)
{
void * pMemory;
PUTREFCOUNTHEADER pHeader;
DebugEntry(UT_FreeRefCount);
ASSERT(ppMemory);
pMemory = *ppMemory;
ASSERT(pMemory);
pHeader = (PUTREFCOUNTHEADER)((LPBYTE)pMemory - sizeof(UTREFCOUNTHEADER));
ASSERT(!IsBadWritePtr(pHeader, sizeof(UTREFCOUNTHEADER)));
ASSERT(pHeader->stamp.idStamp[0] == 'A');
ASSERT(pHeader->stamp.idStamp[1] == 'S');
ASSERT(pHeader->refCount);
if (--(pHeader->refCount) == 0)
{
TRACE_OUT(("Freeing ref-counted memory block 0x%08x", pHeader));
delete[] pHeader;
*ppMemory = NULL;
}
else
{
TRACE_OUT(("Bumped down ref-counted memory block 0x%08x to %d", pHeader, pHeader->refCount));
if (!fNullOnlyWhenFreed)
*ppMemory = NULL;
}
DebugExitVOID(UT_FreeRefCount);
}
//
// UT_MoveMemory - Copy source buffer to destination buffer
//
// Purpose:
// UT_MoveMemory() copies a source memory buffer to a destination memory buffer.
// This routine recognize overlapping buffers to avoid propogation.
// For cases where propogation is not a problem, memcpy() can be used.
//
// Entry:
// void *dst = pointer to destination buffer
// const void *src = pointer to source buffer
// size_t count = number of bytes to copy
//
// Exit:
// Returns a pointer to the destination buffer
//
//Exceptions:
//
void * UT_MoveMemory (
void * dst,
const void * src,
size_t count
)
{
void * ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + count)) {
//
// Non-Overlapping Buffers
// copy from lower addresses to higher addresses
//
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else {
//
// Overlapping Buffers
// copy from higher addresses to lower addresses
//
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return(ret);
}
//
// COM_BasedListInsertBefore(...)
//
// See ut.h for description.
//
void COM_BasedListInsertBefore(PBASEDLIST pExisting, PBASEDLIST pNew)
{
PBASEDLIST pTemp;
DebugEntry(COM_BasedListInsertBefore);
//
// Check for bad parameters.
//
ASSERT((pNew != NULL));
ASSERT((pExisting != NULL));
//
// Find the item before pExisting:
//
pTemp = COM_BasedPrevListField(pExisting);
ASSERT((pTemp != NULL));
TRACE_OUT(("Inserting item at 0x%08x into list between 0x%08x and 0x%08x",
pNew, pTemp, pExisting));
//
// Set its <next> field to point to the new item
//
pTemp->next = PTRBASE_TO_OFFSET(pNew, pTemp);
pNew->prev = PTRBASE_TO_OFFSET(pTemp, pNew);
//
// Set <prev> field of pExisting to point to new item:
//
pExisting->prev = PTRBASE_TO_OFFSET(pNew, pExisting);
pNew->next = PTRBASE_TO_OFFSET(pExisting, pNew);
DebugExitVOID(COM_BasedListInsertBefore);
} // COM_BasedListInsertBefore
//
// COM_BasedListInsertAfter(...)
//
// See ut.h for description.
//
void COM_BasedListInsertAfter(PBASEDLIST pExisting,
PBASEDLIST pNew)
{
PBASEDLIST pTemp;
DebugEntry(COM_BasedListInsertAfter);
//
// Check for bad parameters.
//
ASSERT((pNew != NULL));
ASSERT((pExisting != NULL));
//
// Find the item after pExisting:
//
pTemp = COM_BasedNextListField(pExisting);
ASSERT((pTemp != NULL));
TRACE_OUT(("Inserting item at 0x%08x into list between 0x%08x and 0x%08x",
pNew, pExisting, pTemp));
//
// Set its <prev> field to point to the new item
//
pTemp->prev = PTRBASE_TO_OFFSET(pNew, pTemp);
pNew->next = PTRBASE_TO_OFFSET(pTemp, pNew);
//
// Set <next> field of pExisting to point to new item:
//
pExisting->next = PTRBASE_TO_OFFSET(pNew, pExisting);
pNew->prev = PTRBASE_TO_OFFSET(pExisting, pNew);
DebugExitVOID(COM_BasedListInsertAfter);
} // COM_BasedListInsertAfter
//
// COM_BasedListRemove(...)
//
// See ut.h for description.
//
void COM_BasedListRemove(PBASEDLIST pListItem)
{
PBASEDLIST pNext = NULL;
PBASEDLIST pPrev = NULL;
DebugEntry(COM_BasedListRemove);
//
// Check for bad parameters.
//
ASSERT((pListItem != NULL));
pPrev = COM_BasedPrevListField(pListItem);
pNext = COM_BasedNextListField(pListItem);
ASSERT((pPrev != NULL));
ASSERT((pNext != NULL));
TRACE_OUT(("Removing item 0x%08x from list", pListItem));
pPrev->next = PTRBASE_TO_OFFSET(pNext, pPrev);
pNext->prev = PTRBASE_TO_OFFSET(pPrev, pNext);
DebugExitVOID(COM_BasedListRemove);
}
void FAR * COM_BasedListNext ( PBASEDLIST pHead, void FAR * pEntry, UINT nOffset )
{
PBASEDLIST p;
ASSERT(pHead != NULL);
ASSERT(pEntry != NULL);
p = COM_BasedNextListField(COM_BasedStructToField(pEntry, nOffset));
return ((p == pHead) ? NULL : COM_BasedFieldToStruct(p, nOffset));
}
void FAR * COM_BasedListPrev ( PBASEDLIST pHead, void FAR * pEntry, UINT nOffset )
{
PBASEDLIST p;
ASSERT(pHead != NULL);
ASSERT(pEntry != NULL);
p = COM_BasedPrevListField(COM_BasedStructToField(pEntry, nOffset));
return ((p == pHead) ? NULL : COM_BasedFieldToStruct(p, nOffset));
}
void FAR * COM_BasedListFirst ( PBASEDLIST pHead, UINT nOffset )
{
return (COM_BasedListIsEmpty(pHead) ?
NULL :
COM_BasedFieldToStruct(COM_BasedNextListField(pHead), nOffset));
}
void FAR * COM_BasedListLast ( PBASEDLIST pHead, UINT nOffset )
{
return (COM_BasedListIsEmpty(pHead) ?
NULL :
COM_BasedFieldToStruct(COM_BasedPrevListField(pHead), nOffset));
}
void COM_BasedListFind ( LIST_FIND_TYPE eType,
PBASEDLIST pHead,
void FAR * FAR* ppEntry,
UINT nOffset,
int nOffsetKey,
DWORD_PTR Key,
int cbKeySize )
{
void *p = *ppEntry;
DWORD val;
switch (eType)
{
case LIST_FIND_FROM_FIRST:
p = COM_BasedListFirst(pHead, nOffset);
break;
case LIST_FIND_FROM_NEXT:
p = COM_BasedListNext(pHead, p, nOffset);
break;
default:
ASSERT(FALSE);
}
// make sure the key size is no more than a dword
ASSERT(cbKeySize <= sizeof(DWORD_PTR));
while (p != NULL)
{
val = 0;
CopyMemory(&val, (void *) ((DWORD_PTR) p + nOffsetKey), cbKeySize);
if (val == Key)
{
break;
}
p = COM_BasedListNext(pHead, p, nOffset);
}
*ppEntry = p;
}
//
// COM_SimpleListAppend()
//
// For simple lists, such as hwnd list, app name list, proc id list
//
PSIMPLE_LIST COM_SimpleListAppend ( PBASEDLIST pHead, void FAR * pData )
{
PSIMPLE_LIST p = new SIMPLE_LIST;
if (p != NULL)
{
ZeroMemory(p, sizeof(*p));
p->pData = pData;
COM_BasedListInsertBefore(pHead, &(p->chain));
}
return p;
}
void FAR * COM_SimpleListRemoveHead ( PBASEDLIST pHead )
{
void *pData = NULL;
PBASEDLIST pdclist;
PSIMPLE_LIST p;
if (! COM_BasedListIsEmpty(pHead))
{
// get the first entry in the list
pdclist = COM_BasedNextListField(pHead);
p = (PSIMPLE_LIST) COM_BasedFieldToStruct(pdclist,
offsetof(SIMPLE_LIST, chain));
pData = p->pData;
// remove the first entry in the list
COM_BasedListRemove(pdclist);
delete p;
}
return pData;
}
//
// COM_ReadProfInt(...)
//
// See ut.h for description.
//
void COM_ReadProfInt
(
LPSTR pSection,
LPSTR pEntry,
int defaultValue,
int * pValue
)
{
int localValue;
DebugEntry(COM_ReadProfInt);
//
// Check for NULL parameters
//
ASSERT(pSection != NULL);
ASSERT(pEntry != NULL);
//
// First try to read the value from the current user section.
// Then try to read the value from the global local machine section.
//
if (COMReadEntry(HKEY_CURRENT_USER, pSection, pEntry, (LPSTR)&localValue,
sizeof(int), REG_DWORD) ||
COMReadEntry(HKEY_LOCAL_MACHINE, pSection, pEntry, (LPSTR)&localValue,
sizeof(int), REG_DWORD))
{
*pValue = localValue;
}
else
{
*pValue = defaultValue;
}
DebugExitVOID(COM_ReadProfInt);
}
//
// FUNCTION: COMReadEntry(...)
//
// DESCRIPTION:
// ============
// Read an entry from the given section of the registry. Allow type
// REG_BINARY (4 bytes) if REG_DWORD was requested.
//
//
// PARAMETERS:
// ===========
// topLevelKey : one of:
// - HKEY_CURRENT_USER
// - HKEY_LOCAL_MACHINE
// pSection : the section name to read from. The DC_REG_PREFIX
// string is prepended to give the full name.
// pEntry : the entry name to read.
// pBuffer : a buffer to read the entry to.
// bufferSize : the size of the buffer.
// expectedDataType : the type of data stored in the entry.
//
// RETURNS:
// ========
// Nothing.
//
//
BOOL COMReadEntry(HKEY topLevelKey,
LPSTR pSection,
LPSTR pEntry,
LPSTR pBuffer,
int bufferSize,
ULONG expectedDataType)
{
LONG sysrc;
HKEY key;
ULONG dataType;
ULONG dataSize;
char subKey[COM_MAX_SUBKEY];
BOOL keyOpen = FALSE;
BOOL rc = FALSE;
DebugEntry(COMReadEntry);
//
// Get a subkey for the value.
//
wsprintf(subKey, "%s%s", DC_REG_PREFIX, pSection);
//
// Try to open the key. If the entry does not exist, RegOpenKeyEx will
// fail.
//
sysrc = RegOpenKeyEx(topLevelKey,
subKey,
0, // reserved
KEY_ALL_ACCESS,
&key);
if (sysrc != ERROR_SUCCESS)
{
//
// Don't trace an error here since the subkey may not exist...
//
TRACE_OUT(("Failed to open key %s, rc = %d", subKey, sysrc));
DC_QUIT;
}
keyOpen = TRUE;
//
// We successfully opened the key so now try to read the value. Again
// it may not exist.
//
dataSize = bufferSize;
sysrc = RegQueryValueEx(key,
pEntry,
0, // reserved
&dataType,
(LPBYTE)pBuffer,
&dataSize);
if (sysrc != ERROR_SUCCESS)
{
TRACE_OUT(("Failed to read value of [%s] %s, rc = %d",
pSection,
pEntry,
sysrc));
DC_QUIT;
}
//
// Check that the type is correct. Special case: allow REG_BINARY
// instead of REG_DWORD, as long as the length is 32 bits.
//
if ((dataType != expectedDataType) &&
((dataType != REG_BINARY) ||
(expectedDataType != REG_DWORD) ||
(dataSize != 4)))
{
WARNING_OUT(("Read value from [%s] %s, but type is %d - expected %d",
pSection,
pEntry,
dataType,
expectedDataType));
DC_QUIT;
}
rc = TRUE;
DC_EXIT_POINT:
//
// Close the key (if required).
//
if (keyOpen)
{
sysrc = RegCloseKey(key);
if (sysrc != ERROR_SUCCESS)
{
ERROR_OUT(("Failed to close key, rc = %d", sysrc));
}
}
DebugExitBOOL(COMReadEntry, rc);
return(rc);
}
//
// COM_GetSiteName()
//
void COM_GetSiteName(LPSTR siteName, UINT siteNameLen)
{
LRESULT rc;
HKEY hkeyUserDetails;
DWORD cbData;
TCHAR szNameBuffer[MAX_PATH];
DebugEntry(COM_GetSiteName);
//
// Get this site address from the registry
//
rc = RegOpenKey(HKEY_CURRENT_USER,
ISAPI_KEY TEXT("\\") REGKEY_USERDETAILS,
&hkeyUserDetails);
if (rc == ERROR_SUCCESS)
{
//
// We read the data into our own local buffer, rather than directly
// into the passed buffer, because the passed buffer is normally 48
// bytes long, but if the registry has been mis-setup it may be
// longer than this.
//
// Unfortunately Windows stubbornly returns an error when the
// buffer is smaller than is required, and has no way of returning
// the truncated string.
//
// To avoid this we get the value into a good size buffer, then
// copy just the bit we want.
//
cbData = sizeof(szNameBuffer);
rc = RegQueryValueEx(hkeyUserDetails,
REGVAL_ULS_NAME,
NULL,
NULL,
(LPBYTE)szNameBuffer,
&cbData);
RegCloseKey(hkeyUserDetails);
}
if (rc == ERROR_SUCCESS)
{
//
// Copy from our local buffer into the passed buffer.
// Ensure there is a NUL terminator at the end.
//
lstrcpyn(siteName, szNameBuffer, siteNameLen);
}
else
{
//
// Failing to read the site name is not an error.
// Use the computer name instead.
//
DWORD dwComputerNameLength = MAX_PATH;
GetComputerName(siteName, &dwComputerNameLength);
}
TRACE_OUT(("Site name is <%s>", siteName));
DebugExitVOID(COM_GetSiteName);
}