// Copyright (C) 1997-1999 by Microsoft Corporation. All rights reserved. // // ---------------------------------------------------------------------- // Additions/ Bug fixes 1999 Anil Kumar // // InitMSAA calls InitHelperThread, which creates (duh) a helper thread. // In a past version, the WinEventProc would process the WinEvents by // calling AccessibleObjectFromEvent on some events, then calling // AddEventInfoToStack for all events. // Problem is that the objects obtained in the main thread cannot be // used in the helper thread. So now the helper thread will get it's // own IAccessibleObjects when it processes the events, and all // IAccessible objects will be created, used, and released by the // helper thread. // #define STRICT #include #include #include #include #include "keys.h" // for ProcessWinEvent #include "list.h" // include list.h before helpthd.h, GINFO needs CList #include "HelpThd.h" // // global variables // GINFO gInfo; // // Local function prototypes // BOOL OnHelperThreadEvent (void); DWORD MsgWaitForSingleObject(HANDLE hEvent, DWORD dwTimeout); /************************************************************************* Function: Purpose: Inputs: Returns: History: *************************************************************************/ DWORD WINAPI HelperThreadProc(LPVOID lpParameter) { HRESULT hr; DWORD dwWakeup; // start COM on this thread // SteveDon: CoInitializeEx is supported on both Win95 and WinNT, according to // the SDK docs. Exported from ole32.dll, and defined in objbase.h. // The thing is, for it to be defined, _WIN32_WINNT must be #defined and // greater than 0x0400. But since CoInitialize(NULL) is equivalent to // CoInitializeEx (NULL,COINIT_APARTMENTTHREADED), we'll just do the // former so it works for sure on both 95 and NT // hr = CoInitialize (NULL); if (FAILED (hr)) { DBPRINTF (TEXT("CoInitialize on helper thread returned 0x%lX\r\n"),hr); return (hr); } // GetGUIThreadInfo (called from acc_getState) will fail if both threads // are not on the same desktop. SetThreadDesktop(GetThreadDesktop( g_tidMain )); // ROBSI: 10-10-99 MSG msg; while (TRUE) { DWORD dwObj = WAIT_FAILED; dwObj = MsgWaitForMultipleObjects(1, &gInfo.hHelperEvent, FALSE, INFINITE, QS_ALLINPUT ); // Proccess the WinEvent if ( dwObj == WAIT_OBJECT_0 ) { // OnHelperThreadEvent will return FALSE when it gets // the EndHelper event, which means we can terminate // the helper thread. if (!OnHelperThreadEvent()) { DBPRINTF( TEXT("Terminating HelperThreadProc") ); CoUninitialize(); return 0; } } // procces messages that are sent to us. The only message we expect is // WM_TIMER message while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return 0; } /************************************************************************* Function: Purpose: Inputs: Returns: History: *************************************************************************/ BOOL OnHelperThreadEvent (void) { Sleep(100); // was this added by Paul? Not sure why it is here... STACKABLE_EVENT_INFO sei; while(RemoveInfoFromStack(&sei)) { switch(sei.m_Action) { case STACKABLE_EVENT_INFO::EndHelper: return (FALSE); case STACKABLE_EVENT_INFO::NewEvent: __try { ProcessWinEvent(sei.event, sei.hwndMsg, sei.idObject, sei.idChild, sei.idThread, sei.dwmsEventTime); } __except(EXCEPTION_EXECUTE_HANDLER) { DBPRINTF(TEXT("ProcessWinEvent Exception event=0x%x, hwndMsg=0x%x, idObject=0x%x, idChild=0x%x, idThread=0x%x, dwmsEventTime=0x%x\r\n"), sei.event, sei.hwndMsg, sei.idObject, sei.idChild, sei.idThread, sei.dwmsEventTime); } break; default: break; } // end switch sei.m_Action } // end while RemoveInfoFromStack return (TRUE); } // end OnHelperThreadEvent /************************************************************************* Function: Purpose: Inputs: Returns: History: *************************************************************************/ void AddEventInfoToStack(DWORD event,HWND hwndMsg, LONG idObject, LONG idChild, DWORD idThread, DWORD dwmsEventTime) { STACKABLE_EVENT_INFO sei; sei.m_Action = STACKABLE_EVENT_INFO::NewEvent; sei.event = event; sei.hwndMsg = hwndMsg; sei.idObject = idObject; sei.idChild = idChild; sei.idThread = idThread; sei.dwmsEventTime = dwmsEventTime; EnterCriticalSection(&gInfo.HelperCritSect); gInfo.EventInfoList.Add(&sei,sizeof(sei)); LeaveCriticalSection(&gInfo.HelperCritSect); SetEvent(gInfo.hHelperEvent); } /************************************************************************* Function: Purpose: Inputs: Returns: History: *************************************************************************/ BOOL RemoveInfoFromStack(STACKABLE_EVENT_INFO *pEventInfo) { BOOL bReturn = TRUE; EnterCriticalSection(&gInfo.HelperCritSect); bReturn = !(gInfo.EventInfoList.IsEmpty()); if (bReturn) gInfo.EventInfoList.RemoveHead(pEventInfo); LeaveCriticalSection(&gInfo.HelperCritSect); return bReturn; } /************************************************************************* Function: Purpose: Inputs: Returns: History: *************************************************************************/ void InitHelperThread() { DWORD dwThreadId; g_tidMain = GetCurrentThreadId(); // ROBSI: 10-10-99 InitializeCriticalSection(&gInfo.HelperCritSect); gInfo.hHelperEvent = CreateEvent(NULL, FALSE, FALSE, NULL); gInfo.hHelperThread = CreateThread(NULL, 0, HelperThreadProc, NULL, 0, &dwThreadId); } /************************************************************************* Function: Purpose: Inputs: Returns: History: *************************************************************************/ void UnInitHelperThread() { STACKABLE_EVENT_INFO sei; EnterCriticalSection(&gInfo.HelperCritSect); // Force only one event in the queue gInfo.EventInfoList.RemoveAll(); sei.m_Action = STACKABLE_EVENT_INFO::EndHelper; gInfo.EventInfoList.Add(&sei,sizeof(sei)); LeaveCriticalSection(&gInfo.HelperCritSect); SetEvent(gInfo.hHelperEvent); // Wait for the thread to die // note the last sei will be freed by deconstructor // Donot wait for eternity here!! Do not care really during Exit!! WaitForSingleObject(gInfo.hHelperThread, 3000); }