admin
base
com
developer
drivers
ds
enduser
inetcore
inetsrv
loc
mergedcomponents
multimedia
net
printscan
public
published
sdktools
shell
termsrv
admtools
apisub
cdmodem
clcreator
common
dload
drivers
httpproxy
icaapi
inc
license
perfts
publish
rdfilter
regapi
remdsk
client
common
msngr
rds
as
as16
cpi32
res
asmaster.cpp
awc.cpp
ba.cpp
bcd.cpp
ch.cpp
cm.cpp
cmg.cpp
control.cpp
cpc.cpp
cpi32dll.cpp
dcs.cpp
fh.cpp
gdc.cpp
globals.cpp
globals.h
host.cpp
im.cpp
makefile
mgc.cpp
nmas.def
nmas.rc
oa.cpp
od.cpp
od2.cpp
oe.cpp
oe2.cpp
pm.cpp
precomp.h
rbc.cpp
s20.cpp
sbc.cpp
sc.cpp
sch.cpp
sdg.cpp
sdp.cpp
shm.cpp
sources
ssi.cpp
swl.cpp
up.cpp
usr.cpp
ut.cpp
view.cpp
cpi32.9x
cpi32.nt
dd
h
thk
ascom.inc
common.inc
dirs
cert
dev
extern
h
nmutil
samples
t120
dirs
ntx.reg
project.mk
setup9x.bat
setupnt4.cmd
setupw2k.cmd
server
unittest
dirs
reskit
sessdir
setup
sld
syslib
tsappcmp
tscert
tsuserex
tsutil
wince
winsta
wmi
wtsapi
dirs
project.mk
tools
windows
dirs
makefil0
1907 lines
47 KiB
C++
1907 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);
|
|
}
|