#include "precomp.h" // // UT.CPP // Utility Functions // #include #include #include #include #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 field to point to the new item // pTemp->next = PTRBASE_TO_OFFSET(pNew, pTemp); pNew->prev = PTRBASE_TO_OFFSET(pTemp, pNew); // // Set 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 field to point to the new item // pTemp->prev = PTRBASE_TO_OFFSET(pNew, pTemp); pNew->next = PTRBASE_TO_OFFSET(pTemp, pNew); // // Set 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); }