Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / UIAutomation / Win32Providers / MS / Internal / AutomationProxies / WinEventTracker.cs / 1 / WinEventTracker.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Handles WinEvent notifications. // // History: // 07/01/2003 : a-jeanp Created //--------------------------------------------------------------------------- // PRESHARP: In order to avoid generating warnings about unkown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 using System; using System.Collections; using System.Reflection; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Runtime.InteropServices; using System.Diagnostics; using MS.Win32; namespace MS.Internal.AutomationProxies { // Manage a list of notifications per WinEvents to send for a given set of hwnd. // All members are static // // A static arrays is maintained with the size of the total // number of known WinEvents id (around 50). // // Each entry in the array contains a list of hwnd that should receive a // notification for a given EventId. The list is dynamically built on each call // to AdviseEventAdded/AdviseEventRemoved. // // Notifications are processed: // Convert an EventID into an index for the above array. // Sequential traverse of the list of windows handles // to find a match. (for one WinEvent Id) // Call the delegate associated with the hwnd to create a raw element. // Call the automation code to queue a new notification for the client. // static class WinEventTracker { #region Internal Methods // ----------------------------------------------------- // // Internal Methods // // ----------------------------------------------------- // Update the Array of hwnd requesting notifications // Param name="hwnd" // Param name="raiseEvents" - function to call to create a raw element // Param name="aEvtIdProp" // Param name="cProps" - Number of valid props in the array static internal void AddToNotificationList (IntPtr hwnd, ProxyRaiseEvents raiseEvents, EvtIdProperty[] aEvtIdProp, int cProps) { GetCallbackQueue(); // Build the list of Event to Window List BuildEventsList (EventFlag.Add, hwnd, raiseEvents, aEvtIdProp, cProps); } // Get the QueueProcessor so MSAA events can use it as well static internal QueueProcessor GetCallbackQueue() { lock (_queueLock) { // Create the thread to process the notification if necessary if (_callbackQueue == null) { _callbackQueue = new QueueProcessor(); _callbackQueue.StartOnThread(); } } return _callbackQueue; } // Update the Array of hwnd requesting notifications calling the main routine // Param name="hwnd" // Param name="raiseEvents" - Callback, should be null for non system-wide events // Param name="aEvtIdProp" // Param name="cProps" - Number of valid props in the array static internal void RemoveToNotificationList (IntPtr hwnd, EvtIdProperty[] aEvtIdProp, ProxyRaiseEvents raiseEvents, int cProps) { // Remove the list of Event to Window List // NOTE: raiseEvents must be null in the case when event is not a system-wide event BuildEventsList (EventFlag.Remove, hwnd, raiseEvents, aEvtIdProp, cProps); } #endregion #region Internal Types // ------------------------------------------------------ // // Internal Types Declaration // // ----------------------------------------------------- // Callback into the Proxy code to create a raw element based on a WinEvent callback parameters internal delegate void ProxyRaiseEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild); // Association, WinEvent/Automation Property/RawBase class property internal struct EvtIdProperty { // Win32 Win Events Id internal int _evtId; // Automation Property or Automation Event internal object _idProp; // constructor internal EvtIdProperty (int evtId, object idProp) { _evtId = evtId; _idProp = idProp; } } // WinEvent Notification parameters. Used as data in a hash table, there is one of these // per call to SetWineventHook. Must be a class as a ref is send to an another thread via // a messenging scheme. // The array list _alHwnd catains a struct of type EventCreateParams. internal class EventHookParams { // List of hwnd that requested to receive notification event internal ArrayList _alHwnd; // Win32 Hook handle from SetWinEventHook. internal IntPtr _winEventHook; // Reference to the locked Procedure internal GCHandle _procHookHandle; // the procces that we are getting events for internal uint _process; // index in the array of winevents internal int _evtId; } // Function call to either the Start or Stop Listener internal delegate void StartStopDelegate (ref EventHookParams hp); #endregion #region Private Methods // ------------------------------------------------------ // // Private Methods // // ------------------------------------------------------ // Enables the notification of WinEvents. // This function must be called once for each WinEvent to track. private static void StartListening (ref EventHookParams hp) { NativeMethods.WinEventProcDef proc = new NativeMethods.WinEventProcDef (WinEventProc); uint processId = hp._process; // The console window is special. It does not raise its own WinEvents. The CSRSS raises the // WinEvents for console windows. When calling SetWinEventHook use the CSRSS process id instead of the // console windows process id, so that we receive the WinEvents for the console windows. if (IsConsoleProcess((int)processId)) { try { processId = CSRSSProcessId; } catch (Exception e) { if (Misc.IsCriticalException(e)) { throw; } processId = hp._process; } } hp._procHookHandle = GCHandle.Alloc (proc); hp._winEventHook = Misc.SetWinEventHook(hp._evtId, hp._evtId, IntPtr.Zero, proc, processId, 0, NativeMethods.WINEVENT_OUTOFCONTEXT); } // Disable the notification of WinEvents. // This function must be called once for each WinEvent to track. private static void StopListening (ref EventHookParams hp) { // remove the hook Misc.UnhookWinEvent(hp._winEventHook); hp._procHookHandle.Free (); } // Callback function for WinEvents // // Notifications are processed that way: // Convert an EventID into an index for the above array. // Sequential traverse of the list of windows handles for a give EventID // to find a match. // Call the delegate associated with the hwnd to create a raw element. // Call the automation code to queue a new notification for the client. private static void WinEventProc (int winEventHook, int eventId, IntPtr hwnd, int idObject, int idChild, int eventThread, uint eventTime) { if (hwnd == IntPtr.Zero) { // filter out non-hwnd events - eg. listening for hide/show also gets us mouse pointer (OBJID_CURSOR) // hide/show events (eg. when mouse is hidden as text is being entered) that have NULL hwnd... return; } try { int evt = Array.BinarySearch(_eventIdToIndex, eventId); if (evt < 0) return; // negative means this event is unknown so ignore it // All operations in the list of events and windows handle must be atomic lock (_ahp) { EventHookParams hookParams = null; // Don't use the Misc.GetWindowThreadProcessId() helper since that throws; some events we want even // though the hwnd is no longer valid (e.g. menu item events). uint processId; // Disabling the PreSharp error since GetWindowThreadProcessId doesn't use SetLastError(). #pragma warning suppress 6523 if (UnsafeNativeMethods.GetWindowThreadProcessId(hwnd, out processId) != 0) { // Find the EventHookParams. // _ahp is an array of Hashtables where each Hashtable corrasponds to one event. // Get the correct Hashtable using the index evt. Then lookup the EventHookParams // in the hash table, using the process id. hookParams = (EventHookParams)_ahp[evt][processId]; } // Sanity check if (hookParams != null && hookParams._alHwnd != null) { ArrayList eventCreateParams = hookParams._alHwnd; // Loop for all the registered hwnd listeners for this event for (int index = eventCreateParams.Count - 1; index >= 0; index--) { EventCreateParams ecp = (EventCreateParams)eventCreateParams[index]; // if hwnd of the event matches the registered hwnd -OR- this is a global event (all hwnds) // -AND- the hwnd is still valid have the proxies raise appropriate events. if (ecp._hwnd == hwnd || ecp._hwnd == IntPtr.Zero) { // If this event isn't on a menu element that has just been invoked, throw away the event if the hwnd is gone if (!((idObject == NativeMethods.OBJID_MENU || idObject == NativeMethods.OBJID_SYSMENU) && eventId == NativeMethods.EventObjectInvoke) && !UnsafeNativeMethods.IsWindow(hwnd)) { continue; } try { // Call the proxy code to create a raw element. null is valid as a result // The proxy must fill in the parameters for the AutomationPropertyChangedEventArgs ecp._raiseEventFromRawElement(hwnd, eventId, ecp._idProp, idObject, idChild); } catch (ElementNotAvailableException) { // the element has gone away from the time the event happened and now // So continue the loop and allow the other proxies a chance to raise the event. continue; } catch (Exception e) { // If we get here there is a problem in a proxy that needs fixing. Debug.Assert(false, "Exception raising event " + eventId + " for prop " + ecp._idProp + " on hwnd " + hwnd + "\n" + e.Message); if (Misc.IsCriticalException(e)) { throw; } // Do not break the loop for one mis-behaving proxy. continue; } } } } // handle global events. These are usually for things that do not yet exist like show events // where the hwnd is not there until it is shown. So we need to raise these event all the time. // Office command bars use this. hookParams = (EventHookParams)_ahp[evt][_globalEventKey]; if (hookParams != null && hookParams._alHwnd != null) { ArrayList eventCreateParams = hookParams._alHwnd; // Loop for all the registered hwnd listeners for this event for (int index = eventCreateParams.Count - 1; index >= 0; index--) { EventCreateParams ecp = (EventCreateParams)eventCreateParams[index]; // We have global event if ((ecp._hwnd == IntPtr.Zero)) { try { // Call the proxy code to create a raw element. null is valid as a result // The proxy must fill in the parameters for the AutomationPropertyChangedEventArgs ecp._raiseEventFromRawElement(hwnd, eventId, ecp._idProp, idObject, idChild); } catch (ElementNotAvailableException) { // the element has gone away from the time the event happened and now // So continue the loop and allow the other proxies a chance to raise the event. continue; } catch (Exception e) { // If we get here there is a problem in a proxy that needs fixing. Debug.Assert(false, "Exception raising event " + eventId + " for prop " + ecp._idProp + " on hwnd " + hwnd + "\n" + e.Message); if (Misc.IsCriticalException(e)) { throw; } // Do not break the loop for one mis-behaving proxy. continue; } } } } } } catch (Exception e) { if (Misc.IsCriticalException(e)) throw; // ignore non-critical errors from external code } } // Update the Array of hwnd requesting notifications // eFlag - Add or Remove // hwnd // raiseEvents - function to call to create a raw element // aEvtIdProp - Array of Tupples WinEvent and Automation properties // cProps - Number of valid props in the array private static void BuildEventsList (EventFlag eFlag, IntPtr hwnd, ProxyRaiseEvents raiseEvents, EvtIdProperty[] aEvtIdProp, int cProps) { // All operations in the list of events and windows handle must be atomic lock (_ahp) { for (int i = 0; i < cProps; i++) { EvtIdProperty evtIdProp = aEvtIdProp[i]; // Map a property into a WinEventHookProperty int evt = Array.BinarySearch (_eventIdToIndex, evtIdProp._evtId); // add the window to the list if (evt >= 0) { EventHookParams hookParams = null; uint processId; if (hwnd == IntPtr.Zero) { // if its a global event use this well known key to the hash processId = _globalEventKey; } else { if (Misc.GetWindowThreadProcessId(hwnd, out processId) == 0) { processId = _globalEventKey; } } // If never seens this EventId before. Create the notification object if (_ahp[evt] == null) { // create the hash table the key is the process id _ahp[evt] = new Hashtable(10); } // Find the EventHookParams. // _ahp is an array of Hashtables where each Hashtable corrasponds to one event. // Get the correct Hashtable using the index evt. Then lookup the EventHookParams // in the hash table, using the process id. hookParams = (EventHookParams)_ahp[evt][processId]; // If there is not an entry for the event for the specified process then create one. if (hookParams == null) { hookParams = new EventHookParams(); hookParams._process = processId; _ahp[evt].Add(processId, hookParams); } ArrayList eventCreateParams = hookParams._alHwnd; if (eFlag == EventFlag.Add) { if (eventCreateParams == null) { // empty array, create the hwnd arraylist hookParams._evtId = evtIdProp._evtId; eventCreateParams = hookParams._alHwnd = new ArrayList (16); } // Check if the event for that window already exist. // Discard it as no dups are allowed for (int index = eventCreateParams.Count - 1; index >= 0; index--) { EventCreateParams ecp = (EventCreateParams)eventCreateParams[index]; // Code below will discard duplicates: // Proxy cannot subscribe same hwnd to the same event more than once // However proxy can be globaly registered to be always notified of some event, in order to // do this proxy will send IntPtr.Zero as hwnd. Please notice that a given Proxy can be globaly registered // to some EVENT_XXX only once. This will be ensured via delegate comparison. if ( (hwnd == IntPtr.Zero || ecp._hwnd == hwnd) && ecp._idProp == evtIdProp._idProp && ecp._raiseEventFromRawElement == raiseEvents) { return; } } // Set the WinEventHook if first time around if (eventCreateParams.Count == 0) { _callbackQueue.PostSyncWorkItem (new QueueItem.WinEventItem (ref hookParams, _startDelegate)); } // add the event into the list // Called after the Post does not matter because of the lock eventCreateParams.Add (new EventCreateParams (hwnd, evtIdProp._idProp, raiseEvents)); } else { if ( eventCreateParams == null ) return; // Remove a notification // Go through the list of window to find the one for (int index = eventCreateParams.Count - 1; index >= 0; index--) { EventCreateParams ecp = (EventCreateParams)eventCreateParams[index]; // Detect if caller should be removed from notification list bool remove = false; if (raiseEvents == null) { // Not a global wide events remove = (ecp._hwnd == hwnd && ecp._idProp == evtIdProp._idProp); } else { // Global events Debug.Assert (hwnd == IntPtr.Zero, @"BuildEventsList: event is global but hwnd is not null"); remove = (ecp._hwnd == hwnd && ecp._raiseEventFromRawElement == raiseEvents); } if (remove) { eventCreateParams.RemoveAt (index); // if empty then stop listening for this event arg if (eventCreateParams.Count == 0) { _callbackQueue.PostSyncWorkItem (new QueueItem.WinEventItem (ref hookParams, _stopDelegate)); _ahp[evt].Remove(processId); } break; } } } } } } } private static bool IsConsoleProcess(int processId) { try { return Misc.ProxyGetClassName(Process.GetProcessById(processId).MainWindowHandle).Equals("ConsoleWindowClass"); } catch (Exception e) { if (Misc.IsCriticalException(e)) { throw; } return false; } } private static uint CSRSSProcessId { get { if (_CSRSSProcessId == 0) { Process[] localByName = Process.GetProcessesByName("csrss"); if (localByName[0] != null) { _CSRSSProcessId = (uint)localByName[0].Id; } } return _CSRSSProcessId; } } #endregion #region Private Fields // ----------------------------------------------------- // // Private Fields // // ------------------------------------------------------ // Flag private enum EventFlag { Add, Remove } // Maps WinEvents ID to indices in _ahp private static readonly int[] _eventIdToIndex = new int[] { NativeMethods.EventSystemSound, NativeMethods.EventSystemAlert, NativeMethods.EventSystemForeground, NativeMethods.EventSystemMenuStart, NativeMethods.EventSystemMenuEnd, NativeMethods.EventSystemMenuPopupStart, NativeMethods.EventSystemMenuPopupEnd, NativeMethods.EventSystemCaptureStart, NativeMethods.EventSystemCaptureEnd, NativeMethods.EventSystemMoveSizeStart, NativeMethods.EventSystemMoveSizeEnd, NativeMethods.EventSystemContextHelpStart, NativeMethods.EventSystemContextHelpEnd, NativeMethods.EventSystemDragDropStart, NativeMethods.EventSystemDragDropEnd, NativeMethods.EventSystemDialogStart, NativeMethods.EventSystemDialogEnd, NativeMethods.EventSystemScrollingStart, NativeMethods.EventSystemScrollingEnd, NativeMethods.EventSystemSwitchEnd, NativeMethods.EventSystemMinimizeStart, NativeMethods.EventSystemMinimizeEnd, NativeMethods.EventSystemPaint, NativeMethods.EventConsoleCaret, NativeMethods.EventConsoleUpdateRegion, NativeMethods.EventConsoleUpdateSimple, NativeMethods.EventConsoleUpdateScroll, NativeMethods.EventConsoleLayout, NativeMethods.EventConsoleStartApplication, NativeMethods.EventConsoleEndApplication, NativeMethods.EventObjectCreate, NativeMethods.EventObjectDestroy, NativeMethods.EventObjectShow, NativeMethods.EventObjectHide, NativeMethods.EventObjectReorder, NativeMethods.EventObjectFocus, NativeMethods.EventObjectSelection, NativeMethods.EventObjectSelectionAdd, NativeMethods.EventObjectSelectionRemove, NativeMethods.EventObjectSelectionWithin, NativeMethods.EventObjectStateChange, NativeMethods.EventObjectLocationChange, NativeMethods.EventObjectNameChange, NativeMethods.EventObjectDescriptionChange, NativeMethods.EventObjectValueChange, NativeMethods.EventObjectParentChange, NativeMethods.EventObjectHelpChange, NativeMethods.EventObjectDefactionChange, NativeMethods.EventObjectAcceleratorChange, NativeMethods.EventObjectInvoke, NativeMethods.EventObjectTextSelectionChanged }; // WinEventHooks must be processed in the same thread that created them. // Use a seperate thread to manage the hooks private static QueueProcessor _callbackQueue = null; private static object _queueLock = new object(); // static: Array of Hashtables, one per WinEvent Id. // Each Hashtable contains EventHookParams classes the key is the process id. // Each element in the array list is a struct EventCreateParams that contains // the hwnd and the other parameters needed to call the Proxy and then // the client notification private static Hashtable[] _ahp = new Hashtable[_eventIdToIndex.Length]; private static uint _globalEventKey = 0; // Parameters needed to send a notification to a client struct EventCreateParams { // hwnd requesting a notification internal IntPtr _hwnd; // Automation Property or Automation Event internal object _idProp; // delegate to call to Create a RawElementProvider from a hwnd internal ProxyRaiseEvents _raiseEventFromRawElement; internal EventCreateParams (IntPtr hwnd, object idProp, ProxyRaiseEvents raiseEvents) { _hwnd = hwnd; _idProp = idProp; _raiseEventFromRawElement = raiseEvents; } } // function to start and stop WinEvents private static StartStopDelegate _startDelegate = new StartStopDelegate (StartListening); private static StartStopDelegate _stopDelegate = new StartStopDelegate (StopListening); private static uint _CSRSSProcessId = 0; #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Handles WinEvent notifications. // // History: // 07/01/2003 : a-jeanp Created //--------------------------------------------------------------------------- // PRESHARP: In order to avoid generating warnings about unkown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 using System; using System.Collections; using System.Reflection; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Runtime.InteropServices; using System.Diagnostics; using MS.Win32; namespace MS.Internal.AutomationProxies { // Manage a list of notifications per WinEvents to send for a given set of hwnd. // All members are static // // A static arrays is maintained with the size of the total // number of known WinEvents id (around 50). // // Each entry in the array contains a list of hwnd that should receive a // notification for a given EventId. The list is dynamically built on each call // to AdviseEventAdded/AdviseEventRemoved. // // Notifications are processed: // Convert an EventID into an index for the above array. // Sequential traverse of the list of windows handles // to find a match. (for one WinEvent Id) // Call the delegate associated with the hwnd to create a raw element. // Call the automation code to queue a new notification for the client. // static class WinEventTracker { #region Internal Methods // ----------------------------------------------------- // // Internal Methods // // ----------------------------------------------------- // Update the Array of hwnd requesting notifications // Param name="hwnd" // Param name="raiseEvents" - function to call to create a raw element // Param name="aEvtIdProp" // Param name="cProps" - Number of valid props in the array static internal void AddToNotificationList (IntPtr hwnd, ProxyRaiseEvents raiseEvents, EvtIdProperty[] aEvtIdProp, int cProps) { GetCallbackQueue(); // Build the list of Event to Window List BuildEventsList (EventFlag.Add, hwnd, raiseEvents, aEvtIdProp, cProps); } // Get the QueueProcessor so MSAA events can use it as well static internal QueueProcessor GetCallbackQueue() { lock (_queueLock) { // Create the thread to process the notification if necessary if (_callbackQueue == null) { _callbackQueue = new QueueProcessor(); _callbackQueue.StartOnThread(); } } return _callbackQueue; } // Update the Array of hwnd requesting notifications calling the main routine // Param name="hwnd" // Param name="raiseEvents" - Callback, should be null for non system-wide events // Param name="aEvtIdProp" // Param name="cProps" - Number of valid props in the array static internal void RemoveToNotificationList (IntPtr hwnd, EvtIdProperty[] aEvtIdProp, ProxyRaiseEvents raiseEvents, int cProps) { // Remove the list of Event to Window List // NOTE: raiseEvents must be null in the case when event is not a system-wide event BuildEventsList (EventFlag.Remove, hwnd, raiseEvents, aEvtIdProp, cProps); } #endregion #region Internal Types // ------------------------------------------------------ // // Internal Types Declaration // // ----------------------------------------------------- // Callback into the Proxy code to create a raw element based on a WinEvent callback parameters internal delegate void ProxyRaiseEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild); // Association, WinEvent/Automation Property/RawBase class property internal struct EvtIdProperty { // Win32 Win Events Id internal int _evtId; // Automation Property or Automation Event internal object _idProp; // constructor internal EvtIdProperty (int evtId, object idProp) { _evtId = evtId; _idProp = idProp; } } // WinEvent Notification parameters. Used as data in a hash table, there is one of these // per call to SetWineventHook. Must be a class as a ref is send to an another thread via // a messenging scheme. // The array list _alHwnd catains a struct of type EventCreateParams. internal class EventHookParams { // List of hwnd that requested to receive notification event internal ArrayList _alHwnd; // Win32 Hook handle from SetWinEventHook. internal IntPtr _winEventHook; // Reference to the locked Procedure internal GCHandle _procHookHandle; // the procces that we are getting events for internal uint _process; // index in the array of winevents internal int _evtId; } // Function call to either the Start or Stop Listener internal delegate void StartStopDelegate (ref EventHookParams hp); #endregion #region Private Methods // ------------------------------------------------------ // // Private Methods // // ------------------------------------------------------ // Enables the notification of WinEvents. // This function must be called once for each WinEvent to track. private static void StartListening (ref EventHookParams hp) { NativeMethods.WinEventProcDef proc = new NativeMethods.WinEventProcDef (WinEventProc); uint processId = hp._process; // The console window is special. It does not raise its own WinEvents. The CSRSS raises the // WinEvents for console windows. When calling SetWinEventHook use the CSRSS process id instead of the // console windows process id, so that we receive the WinEvents for the console windows. if (IsConsoleProcess((int)processId)) { try { processId = CSRSSProcessId; } catch (Exception e) { if (Misc.IsCriticalException(e)) { throw; } processId = hp._process; } } hp._procHookHandle = GCHandle.Alloc (proc); hp._winEventHook = Misc.SetWinEventHook(hp._evtId, hp._evtId, IntPtr.Zero, proc, processId, 0, NativeMethods.WINEVENT_OUTOFCONTEXT); } // Disable the notification of WinEvents. // This function must be called once for each WinEvent to track. private static void StopListening (ref EventHookParams hp) { // remove the hook Misc.UnhookWinEvent(hp._winEventHook); hp._procHookHandle.Free (); } // Callback function for WinEvents // // Notifications are processed that way: // Convert an EventID into an index for the above array. // Sequential traverse of the list of windows handles for a give EventID // to find a match. // Call the delegate associated with the hwnd to create a raw element. // Call the automation code to queue a new notification for the client. private static void WinEventProc (int winEventHook, int eventId, IntPtr hwnd, int idObject, int idChild, int eventThread, uint eventTime) { if (hwnd == IntPtr.Zero) { // filter out non-hwnd events - eg. listening for hide/show also gets us mouse pointer (OBJID_CURSOR) // hide/show events (eg. when mouse is hidden as text is being entered) that have NULL hwnd... return; } try { int evt = Array.BinarySearch(_eventIdToIndex, eventId); if (evt < 0) return; // negative means this event is unknown so ignore it // All operations in the list of events and windows handle must be atomic lock (_ahp) { EventHookParams hookParams = null; // Don't use the Misc.GetWindowThreadProcessId() helper since that throws; some events we want even // though the hwnd is no longer valid (e.g. menu item events). uint processId; // Disabling the PreSharp error since GetWindowThreadProcessId doesn't use SetLastError(). #pragma warning suppress 6523 if (UnsafeNativeMethods.GetWindowThreadProcessId(hwnd, out processId) != 0) { // Find the EventHookParams. // _ahp is an array of Hashtables where each Hashtable corrasponds to one event. // Get the correct Hashtable using the index evt. Then lookup the EventHookParams // in the hash table, using the process id. hookParams = (EventHookParams)_ahp[evt][processId]; } // Sanity check if (hookParams != null && hookParams._alHwnd != null) { ArrayList eventCreateParams = hookParams._alHwnd; // Loop for all the registered hwnd listeners for this event for (int index = eventCreateParams.Count - 1; index >= 0; index--) { EventCreateParams ecp = (EventCreateParams)eventCreateParams[index]; // if hwnd of the event matches the registered hwnd -OR- this is a global event (all hwnds) // -AND- the hwnd is still valid have the proxies raise appropriate events. if (ecp._hwnd == hwnd || ecp._hwnd == IntPtr.Zero) { // If this event isn't on a menu element that has just been invoked, throw away the event if the hwnd is gone if (!((idObject == NativeMethods.OBJID_MENU || idObject == NativeMethods.OBJID_SYSMENU) && eventId == NativeMethods.EventObjectInvoke) && !UnsafeNativeMethods.IsWindow(hwnd)) { continue; } try { // Call the proxy code to create a raw element. null is valid as a result // The proxy must fill in the parameters for the AutomationPropertyChangedEventArgs ecp._raiseEventFromRawElement(hwnd, eventId, ecp._idProp, idObject, idChild); } catch (ElementNotAvailableException) { // the element has gone away from the time the event happened and now // So continue the loop and allow the other proxies a chance to raise the event. continue; } catch (Exception e) { // If we get here there is a problem in a proxy that needs fixing. Debug.Assert(false, "Exception raising event " + eventId + " for prop " + ecp._idProp + " on hwnd " + hwnd + "\n" + e.Message); if (Misc.IsCriticalException(e)) { throw; } // Do not break the loop for one mis-behaving proxy. continue; } } } } // handle global events. These are usually for things that do not yet exist like show events // where the hwnd is not there until it is shown. So we need to raise these event all the time. // Office command bars use this. hookParams = (EventHookParams)_ahp[evt][_globalEventKey]; if (hookParams != null && hookParams._alHwnd != null) { ArrayList eventCreateParams = hookParams._alHwnd; // Loop for all the registered hwnd listeners for this event for (int index = eventCreateParams.Count - 1; index >= 0; index--) { EventCreateParams ecp = (EventCreateParams)eventCreateParams[index]; // We have global event if ((ecp._hwnd == IntPtr.Zero)) { try { // Call the proxy code to create a raw element. null is valid as a result // The proxy must fill in the parameters for the AutomationPropertyChangedEventArgs ecp._raiseEventFromRawElement(hwnd, eventId, ecp._idProp, idObject, idChild); } catch (ElementNotAvailableException) { // the element has gone away from the time the event happened and now // So continue the loop and allow the other proxies a chance to raise the event. continue; } catch (Exception e) { // If we get here there is a problem in a proxy that needs fixing. Debug.Assert(false, "Exception raising event " + eventId + " for prop " + ecp._idProp + " on hwnd " + hwnd + "\n" + e.Message); if (Misc.IsCriticalException(e)) { throw; } // Do not break the loop for one mis-behaving proxy. continue; } } } } } } catch (Exception e) { if (Misc.IsCriticalException(e)) throw; // ignore non-critical errors from external code } } // Update the Array of hwnd requesting notifications // eFlag - Add or Remove // hwnd // raiseEvents - function to call to create a raw element // aEvtIdProp - Array of Tupples WinEvent and Automation properties // cProps - Number of valid props in the array private static void BuildEventsList (EventFlag eFlag, IntPtr hwnd, ProxyRaiseEvents raiseEvents, EvtIdProperty[] aEvtIdProp, int cProps) { // All operations in the list of events and windows handle must be atomic lock (_ahp) { for (int i = 0; i < cProps; i++) { EvtIdProperty evtIdProp = aEvtIdProp[i]; // Map a property into a WinEventHookProperty int evt = Array.BinarySearch (_eventIdToIndex, evtIdProp._evtId); // add the window to the list if (evt >= 0) { EventHookParams hookParams = null; uint processId; if (hwnd == IntPtr.Zero) { // if its a global event use this well known key to the hash processId = _globalEventKey; } else { if (Misc.GetWindowThreadProcessId(hwnd, out processId) == 0) { processId = _globalEventKey; } } // If never seens this EventId before. Create the notification object if (_ahp[evt] == null) { // create the hash table the key is the process id _ahp[evt] = new Hashtable(10); } // Find the EventHookParams. // _ahp is an array of Hashtables where each Hashtable corrasponds to one event. // Get the correct Hashtable using the index evt. Then lookup the EventHookParams // in the hash table, using the process id. hookParams = (EventHookParams)_ahp[evt][processId]; // If there is not an entry for the event for the specified process then create one. if (hookParams == null) { hookParams = new EventHookParams(); hookParams._process = processId; _ahp[evt].Add(processId, hookParams); } ArrayList eventCreateParams = hookParams._alHwnd; if (eFlag == EventFlag.Add) { if (eventCreateParams == null) { // empty array, create the hwnd arraylist hookParams._evtId = evtIdProp._evtId; eventCreateParams = hookParams._alHwnd = new ArrayList (16); } // Check if the event for that window already exist. // Discard it as no dups are allowed for (int index = eventCreateParams.Count - 1; index >= 0; index--) { EventCreateParams ecp = (EventCreateParams)eventCreateParams[index]; // Code below will discard duplicates: // Proxy cannot subscribe same hwnd to the same event more than once // However proxy can be globaly registered to be always notified of some event, in order to // do this proxy will send IntPtr.Zero as hwnd. Please notice that a given Proxy can be globaly registered // to some EVENT_XXX only once. This will be ensured via delegate comparison. if ( (hwnd == IntPtr.Zero || ecp._hwnd == hwnd) && ecp._idProp == evtIdProp._idProp && ecp._raiseEventFromRawElement == raiseEvents) { return; } } // Set the WinEventHook if first time around if (eventCreateParams.Count == 0) { _callbackQueue.PostSyncWorkItem (new QueueItem.WinEventItem (ref hookParams, _startDelegate)); } // add the event into the list // Called after the Post does not matter because of the lock eventCreateParams.Add (new EventCreateParams (hwnd, evtIdProp._idProp, raiseEvents)); } else { if ( eventCreateParams == null ) return; // Remove a notification // Go through the list of window to find the one for (int index = eventCreateParams.Count - 1; index >= 0; index--) { EventCreateParams ecp = (EventCreateParams)eventCreateParams[index]; // Detect if caller should be removed from notification list bool remove = false; if (raiseEvents == null) { // Not a global wide events remove = (ecp._hwnd == hwnd && ecp._idProp == evtIdProp._idProp); } else { // Global events Debug.Assert (hwnd == IntPtr.Zero, @"BuildEventsList: event is global but hwnd is not null"); remove = (ecp._hwnd == hwnd && ecp._raiseEventFromRawElement == raiseEvents); } if (remove) { eventCreateParams.RemoveAt (index); // if empty then stop listening for this event arg if (eventCreateParams.Count == 0) { _callbackQueue.PostSyncWorkItem (new QueueItem.WinEventItem (ref hookParams, _stopDelegate)); _ahp[evt].Remove(processId); } break; } } } } } } } private static bool IsConsoleProcess(int processId) { try { return Misc.ProxyGetClassName(Process.GetProcessById(processId).MainWindowHandle).Equals("ConsoleWindowClass"); } catch (Exception e) { if (Misc.IsCriticalException(e)) { throw; } return false; } } private static uint CSRSSProcessId { get { if (_CSRSSProcessId == 0) { Process[] localByName = Process.GetProcessesByName("csrss"); if (localByName[0] != null) { _CSRSSProcessId = (uint)localByName[0].Id; } } return _CSRSSProcessId; } } #endregion #region Private Fields // ----------------------------------------------------- // // Private Fields // // ------------------------------------------------------ // Flag private enum EventFlag { Add, Remove } // Maps WinEvents ID to indices in _ahp private static readonly int[] _eventIdToIndex = new int[] { NativeMethods.EventSystemSound, NativeMethods.EventSystemAlert, NativeMethods.EventSystemForeground, NativeMethods.EventSystemMenuStart, NativeMethods.EventSystemMenuEnd, NativeMethods.EventSystemMenuPopupStart, NativeMethods.EventSystemMenuPopupEnd, NativeMethods.EventSystemCaptureStart, NativeMethods.EventSystemCaptureEnd, NativeMethods.EventSystemMoveSizeStart, NativeMethods.EventSystemMoveSizeEnd, NativeMethods.EventSystemContextHelpStart, NativeMethods.EventSystemContextHelpEnd, NativeMethods.EventSystemDragDropStart, NativeMethods.EventSystemDragDropEnd, NativeMethods.EventSystemDialogStart, NativeMethods.EventSystemDialogEnd, NativeMethods.EventSystemScrollingStart, NativeMethods.EventSystemScrollingEnd, NativeMethods.EventSystemSwitchEnd, NativeMethods.EventSystemMinimizeStart, NativeMethods.EventSystemMinimizeEnd, NativeMethods.EventSystemPaint, NativeMethods.EventConsoleCaret, NativeMethods.EventConsoleUpdateRegion, NativeMethods.EventConsoleUpdateSimple, NativeMethods.EventConsoleUpdateScroll, NativeMethods.EventConsoleLayout, NativeMethods.EventConsoleStartApplication, NativeMethods.EventConsoleEndApplication, NativeMethods.EventObjectCreate, NativeMethods.EventObjectDestroy, NativeMethods.EventObjectShow, NativeMethods.EventObjectHide, NativeMethods.EventObjectReorder, NativeMethods.EventObjectFocus, NativeMethods.EventObjectSelection, NativeMethods.EventObjectSelectionAdd, NativeMethods.EventObjectSelectionRemove, NativeMethods.EventObjectSelectionWithin, NativeMethods.EventObjectStateChange, NativeMethods.EventObjectLocationChange, NativeMethods.EventObjectNameChange, NativeMethods.EventObjectDescriptionChange, NativeMethods.EventObjectValueChange, NativeMethods.EventObjectParentChange, NativeMethods.EventObjectHelpChange, NativeMethods.EventObjectDefactionChange, NativeMethods.EventObjectAcceleratorChange, NativeMethods.EventObjectInvoke, NativeMethods.EventObjectTextSelectionChanged }; // WinEventHooks must be processed in the same thread that created them. // Use a seperate thread to manage the hooks private static QueueProcessor _callbackQueue = null; private static object _queueLock = new object(); // static: Array of Hashtables, one per WinEvent Id. // Each Hashtable contains EventHookParams classes the key is the process id. // Each element in the array list is a struct EventCreateParams that contains // the hwnd and the other parameters needed to call the Proxy and then // the client notification private static Hashtable[] _ahp = new Hashtable[_eventIdToIndex.Length]; private static uint _globalEventKey = 0; // Parameters needed to send a notification to a client struct EventCreateParams { // hwnd requesting a notification internal IntPtr _hwnd; // Automation Property or Automation Event internal object _idProp; // delegate to call to Create a RawElementProvider from a hwnd internal ProxyRaiseEvents _raiseEventFromRawElement; internal EventCreateParams (IntPtr hwnd, object idProp, ProxyRaiseEvents raiseEvents) { _hwnd = hwnd; _idProp = idProp; _raiseEventFromRawElement = raiseEvents; } } // function to start and stop WinEvents private static StartStopDelegate _startDelegate = new StartStopDelegate (StartListening); private static StartStopDelegate _stopDelegate = new StartStopDelegate (StopListening); private static uint _CSRSSProcessId = 0; #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- OleDbCommand.cs
- GeometryConverter.cs
- DocumentOrderComparer.cs
- StringWriter.cs
- RuleSetDialog.Designer.cs
- IconHelper.cs
- XmlDocumentSurrogate.cs
- ModuleBuilderData.cs
- DomNameTable.cs
- CharacterString.cs
- MeasureItemEvent.cs
- PolicyVersionConverter.cs
- CommonObjectSecurity.cs
- GeneralTransform.cs
- ResourceIDHelper.cs
- BamlRecords.cs
- SystemFonts.cs
- ConstraintEnumerator.cs
- DispatchWrapper.cs
- BufferedOutputStream.cs
- GPRECT.cs
- CodeMemberProperty.cs
- XamlPointCollectionSerializer.cs
- EntityDataSourceWizardForm.cs
- ActiveXHelper.cs
- Padding.cs
- ReferenceService.cs
- messageonlyhwndwrapper.cs
- MyContact.cs
- StandardBindingElementCollection.cs
- XmlIncludeAttribute.cs
- Expression.cs
- KeyboardEventArgs.cs
- RuntimeConfig.cs
- coordinatorscratchpad.cs
- X509CertificateClaimSet.cs
- FocusChangedEventArgs.cs
- MethodAccessException.cs
- Package.cs
- HttpUnhandledOperationInvoker.cs
- ProtocolState.cs
- IndexedGlyphRun.cs
- Thread.cs
- MouseCaptureWithinProperty.cs
- ExcCanonicalXml.cs
- CompiledQueryCacheEntry.cs
- CallbackCorrelationInitializer.cs
- CacheMemory.cs
- DependencyPropertyChangedEventArgs.cs
- DataListItemCollection.cs
- UnmanagedMemoryStream.cs
- XmlRootAttribute.cs
- PerformanceCounterPermissionAttribute.cs
- QilStrConcat.cs
- SynthesizerStateChangedEventArgs.cs
- ProxyFragment.cs
- QueryCursorEventArgs.cs
- TransactionManager.cs
- CodeGenerator.cs
- WebPartsSection.cs
- SizeLimitedCache.cs
- Binding.cs
- MethodCallTranslator.cs
- OleDbError.cs
- DataGridRow.cs
- DataServiceRequestOfT.cs
- ScrollData.cs
- mda.cs
- LogSwitch.cs
- CustomValidator.cs
- BindingCollection.cs
- RelationshipNavigation.cs
- documentsequencetextcontainer.cs
- IndentedWriter.cs
- DynamicPropertyReader.cs
- ACE.cs
- XmlSchemaExporter.cs
- ReadOnlyHierarchicalDataSourceView.cs
- ZoneLinkButton.cs
- DecoratedNameAttribute.cs
- InfoCardTraceRecord.cs
- DoubleCollection.cs
- NamespaceList.cs
- DSACryptoServiceProvider.cs
- SafeNativeMethods.cs
- ZoneButton.cs
- HtmlContainerControl.cs
- AsyncPostBackErrorEventArgs.cs
- IgnoreFileBuildProvider.cs
- UnsafePeerToPeerMethods.cs
- CrossContextChannel.cs
- SystemResources.cs
- UserMapPath.cs
- Preprocessor.cs
- CompoundFileIOPermission.cs
- InternalEnumValidatorAttribute.cs
- UserInitiatedNavigationPermission.cs
- XmlArrayItemAttribute.cs
- IEnumerable.cs
- DropSource.cs