示例#1
0
        //------------------------------------------------------
        //
        //  Patterns Implementation
        //
        //------------------------------------------------------

        #region ProxyHwnd Methods

        // ------------------------------------------------------
        //
        // Internal Methods
        //
        // ------------------------------------------------------

        // Advises proxy that an event has been added.
        // Maps the Automation Events into WinEvents and add those to the list of WinEvents notification hooks
        internal virtual void AdviseEventAdded(AutomationEvent eventId, AutomationProperty [] aidProps)
        {
            // No RawElementBase creation callback, exit from here
            if (_createOnEvent == null)
            {
                return;
            }

            int cEvents = 0;

            WinEventTracker.EvtIdProperty [] aEvents;

            // Gets an Array of WinEvents to trap on a per window handle basis
            if (eventId == AutomationElement.AutomationPropertyChangedEvent)
            {
                aEvents = PropertyToWinEvent(aidProps, out cEvents);
            }
            else
            {
                aEvents = EventToWinEvent(eventId, out cEvents);
            }

            // If we have WinEvents to trap, add those to the list of WinEvent
            // notification list
            if (cEvents > 0)
            {
                WinEventTracker.AddToNotificationList(_hwnd, _createOnEvent, aEvents, cEvents);
            }
        }
        // a client has stopped listening for an event on object(s) in the specified window.
        internal void AdviseEventRemoved(IntPtr hwnd, AutomationEvent eventId, AutomationProperty[] properties)
        {
            // note: we don't need to worry about removing entries from our tables when windows are destroyed
            //(EVENT_OBJECT_DESTROY with OBJID_WINDOW) because UIA already watches for this event and calls AdviseEventRemoved.

            // use a lock to update our tables in one atomic operation.
            lock (this)
            {
                // we should have a 2-nd level table for this hwnd...
                if (_hwndTable.ContainsKey(hwnd))
                {
                    Hashtable eventTable = (Hashtable)(_hwndTable[hwnd]);

                    // for the single event or each of possibly multiple properties decrement the reference count.
                    foreach (AutomationIdentifier key in EventKeys(eventId, properties))
                    {
                        // we should have an entry in the 2-nd level table for this event or property...
                        if (eventTable.ContainsKey(key))
                        {
                            // decrement the reference count
                            int refcount = (int)eventTable[key] - 1;
                            Debug.Assert(refcount >= 0);

                            if (refcount > 0)
                            {
                                eventTable[key] = refcount;
                            }
                            else
                            {
                                // if the refcount has gone to zero then remove this entry from the 2-nd level table.
                                eventTable.Remove(key);

                                // if this window doesn't have any more advises then remove it's entry from the top-level table
                                if (eventTable.Count == 0)
                                {
                                    _hwndTable.Remove(hwnd);

                                    // if there are no more advises then we can stop listening for WinEvents
                                    if (_hwndTable.Count == 0)
                                    {
                                        //Debug.WriteLine("Stop listening for WinEvents.", "NativeMsaaProxy");
                                        WinEventTracker.GetCallbackQueue().PostSyncWorkItem(new QueueItem.MSAAWinEventItem(StopListening));
                                    }
                                }
                            }
                        }
                        else
                        {
                            Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "ERROR: AdviseEventRemoved called for {0} and event/property {1} without matching AdviseEventAdded.", hwnd, key), "NativeMsaaProxy");
                        }
                    }
                }
                else
                {
                    Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "ERROR: AdviseEventRemoved called for {0} without matching AdviseEventAdded.", hwnd), "NativeMsaaProxy");
                }
            }
        }
 internal override void AdviseEventRemoved(
     AutomationEvent eventId, AutomationProperty[] aidProps)
 {
     if (eventId == AutomationElementIdentifiers.AutomationPropertyChangedEvent &&
         aidProps.Length > 0 &&
         aidProps[0] == ScrollPatternIdentifiers.HorizontalScrollPercentProperty)
     {
         IntPtr upDownHwnd = GetUpDownHwnd();
         if (upDownHwnd != IntPtr.Zero)
         {
             WinEventTracker.RemoveToNotificationList(
                 upDownHwnd, _upDownEvents, null, 1);
         }
     }
     base.AdviseEventRemoved(eventId, aidProps);
 }
示例#4
0
        internal override void AdviseEventRemoved(AutomationEvent eventId, AutomationProperty[] aidProps)
        {
            base.AdviseEventRemoved(eventId, aidProps);

            // For now, ToolTips only raise ToolTip-specific events when they close
            if (eventId != AutomationElement.ToolTipClosedEvent)
            {
                return;
            }

            if (_listenerCount > 0)
            {
                // decrement the event counter
                --_listenerCount;
                WinEventTracker.RemoveToNotificationList(IntPtr.Zero, _toolTipEventIds, new WinEventTracker.ProxyRaiseEvents(OnToolTipEvents), _toolTipEventIds.Length);
            }
        }
        // a client is listening for an event on object(s) in the specified window.
        internal void AdviseEventAdded(IntPtr hwnd, AutomationEvent eventId, AutomationProperty[] properties)
        {
            // When you start listening for events you listen for all events and then
            // when you get the event you filter by any hwnd that got advised.  But you have enough
            // information when you get advised to listen only for in process where that hwnd resides.
            // The SetWinEventHook api takes a process id, you could get the process id from the hwnd
            // and limit the number of event you have to process.

            // we have a two-level table structure. the top-level table maps an hwnd to another table.
            // the second-level table maps an event (or a property in the case of property-changed-event)
            // in that hwnd to a reference count of the number of clients listening for that event.

            // use a lock to update our tables in one atomic operation.
            lock (this)
            {
                // if we aren't listening for WinEvents then start listening.
                if (_hwndTable.Count == 0)
                {
                    //Debug.WriteLine("Starting listening for WinEvents.", "NativeMsaaProxy");
                    // Posting an item to the queue to start listening for WinEvents. This makes sure it is done on the proper thread
                    // Notably the same thread WinEventTracker uses, which guarantees the thread that SetWinEventHook is called on is
                    // actively pumping messages, which is required for SetWinEventHook to function properly.
                    WinEventTracker.GetCallbackQueue().PostSyncWorkItem(new QueueItem.MSAAWinEventItem(StartListening));
                }

                // if we already have a 2-nd level table for this hwnd then simply update the 2nd-level table.
                // otherwise we need to create a 2-nd level table.
                Hashtable eventTable;
                if (_hwndTable.ContainsKey(hwnd))
                {
                    eventTable = (Hashtable)(_hwndTable[hwnd]);
                }
                else
                {
                    eventTable       = new Hashtable();
                    _hwndTable[hwnd] = eventTable;
                }

                // for the single event or each of possibly multiple properties increment the reference counter.
                foreach (AutomationIdentifier key in EventKeys(eventId, properties))
                {
                    eventTable[key] = eventTable.ContainsKey(key) ? (int)eventTable[key] + 1 : 1;
                }
            }
        }
        internal override void AdviseEventAdded(
            AutomationEvent eventId, AutomationProperty[] aidProps)
        {
            if (eventId == AutomationElementIdentifiers.AutomationPropertyChangedEvent &&
                aidProps.Length > 0 &&
                aidProps[0] == ScrollPatternIdentifiers.HorizontalScrollPercentProperty)
            {
                IntPtr upDownHwnd = GetUpDownHwnd();
                if (upDownHwnd != IntPtr.Zero)
                {
                    // Register for UpDown ValueChange WinEvents, which will be
                    // translated to scrolling events for the tab control.
                    WinEventTracker.AddToNotificationList(
                        upDownHwnd,
                        new WinEventTracker.ProxyRaiseEvents(UpDownControlRaiseEvents),
                        _upDownEvents, 1);
                }
            }

            base.AdviseEventAdded(eventId, aidProps);
        }
示例#7
0
        internal override void AdviseEventAdded(AutomationEvent eventId, AutomationProperty[] aidProps)
        {
            base.AdviseEventAdded(eventId, aidProps);

            // If the framework is advising for ToolTipOpenedEvent then go ahead and raise this event now
            // since the WinEvent we would listen to (usually EVENT_OBJECT_SHOW) has already occurrred
            // (it is why Advise is being called).  No other action is necessary because when this ToolTip
            // goes away, AdviseEventRemoved is going to be called.  In other words, this proxy gets
            // created when the ToolTip opens and thrown away when it closes so no need to keep state
            // that we want to listen for more SHOWS or CREATES - there's always one for any one instance.
            if (eventId == AutomationElement.ToolTipOpenedEvent)
            {
                AutomationEventArgs e = new AutomationEventArgs(AutomationElement.ToolTipOpenedEvent);
                AutomationInteropProvider.RaiseAutomationEvent(AutomationElement.ToolTipOpenedEvent, this, e);
            }
            else if (eventId == AutomationElement.ToolTipClosedEvent)
            {
                // subscribe to ToolTip specific events, keeping track of how many times the event has been added
                WinEventTracker.AddToNotificationList(IntPtr.Zero, new WinEventTracker.ProxyRaiseEvents(OnToolTipEvents), _toolTipEventIds, _toolTipEventIds.Length);
                _listenerCount++;
            }
        }
示例#8
0
        internal override void AdviseEventRemoved(AutomationEvent eventId, AutomationProperty [] aidProps)
        {
            // remove combo-related events
            base.AdviseEventRemoved(eventId, aidProps);

            NativeMethods.COMBOBOXINFO cbInfo = new NativeMethods.COMBOBOXINFO(NativeMethods.comboboxInfoSize);

            if (GetComboInfo(_hwnd, ref cbInfo))
            {
                // remove edit and list specific events that got remapped into combo's events
                if (eventId == AutomationElement.AutomationPropertyChangedEvent)
                {
                    // ComboBoxEx32 controls with the style CBS_DROPDOWNLIST are still editable.
                    if (cbInfo.hwndItem != IntPtr.Zero && IsEditableCombo())
                    {
                        // un-subscribe from edit-specific notifications
                        // ValueAsString, ValueAsObject, IsReadOnly
                        // create array containing events from which user wants to unsubscribe
                        WinEventTracker.EvtIdProperty [] editPortionEvents;
                        int counter;

                        CreateEditPortionEvents(out editPortionEvents, out counter, aidProps);
                        if (counter > 0)
                        {
                            WinEventTracker.RemoveToNotificationList(cbInfo.hwndItem, editPortionEvents, null, counter);
                        }
                    }
                }

                // Need to also remove the advise from the list portions of the combobox.
                if (cbInfo.hwndList != IntPtr.Zero)
                {
                    WindowsListBox listbox = new WindowsListBox(cbInfo.hwndList, this, 0, true);
                    listbox.AdviseEventRemoved(eventId, aidProps);
                }
            }
        }
示例#9
0
        // override the default implementation so we can handle the WinEvents that are send to the edit
        // portion of ComboBox (Combo proxy will hide edit portion from the user, but will take care of all
        // the features/functionality that Edit presents) and some(show, hide) events that are send to the List portion of combo
        // In both cases this events will be presented to the user as Combo's LE events
        internal override void AdviseEventAdded(AutomationEvent eventId, AutomationProperty [] aidProps)
        {
            // call the base class implementation first to add combo-specific things and register combo specific callback
            base.AdviseEventAdded(eventId, aidProps);

            NativeMethods.COMBOBOXINFO cbInfo = new NativeMethods.COMBOBOXINFO(NativeMethods.comboboxInfoSize);

            if (GetComboInfo(_hwnd, ref cbInfo))
            {
                if (eventId == AutomationElement.AutomationPropertyChangedEvent)
                {
                    // ComboBoxEx32 controls with the style CBS_DROPDOWNLIST are still editable.
                    if (cbInfo.hwndItem != IntPtr.Zero && IsEditableCombo())
                    {
                        // subscribe to edit-specific notifications, that would be presented as combo le event
                        // ValueAsString, ValueAsObject, IsReadOnly
                        // create array containing events that user is interested in
                        WinEventTracker.EvtIdProperty [] editPortionEvents;
                        int counter;

                        CreateEditPortionEvents(out editPortionEvents, out counter, aidProps);
                        if (counter > 0)
                        {
                            WinEventTracker.AddToNotificationList(cbInfo.hwndItem, new WinEventTracker.ProxyRaiseEvents(EditPortionEvents), editPortionEvents, counter);
                        }
                    }
                }

                // Need to also advise the list portions of the combobox so that it can raise events.
                if (cbInfo.hwndList != IntPtr.Zero)
                {
                    WindowsListBox listbox = new WindowsListBox(cbInfo.hwndList, this, 0, true);
                    listbox.AdviseEventAdded(eventId, aidProps);
                }
            }
        }
示例#10
0
        private static void RaiseEventsOnClient(IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        {
            ProxySimple el = null;

            WindowsListBox wlb = (WindowsListBox)WindowsListBox.Create(hwnd, 0);

            // Upon creation, a single selection Listbox can have no selection to start with
            // however once an item has been selection, the selection cannot be removed.
            // This WinEvent can only be received once, on the first selection.
            // Once the notification is received the notification handler is removed to not get it a
            // second time.
            if ((eventId == NativeMethods.EventObjectSelection || eventId == NativeMethods.EventObjectSelectionAdd) && (idProp as AutomationProperty) == SelectionPattern.IsSelectionRequiredProperty)
            {
                // This array must be kept in sync with the array in PropertyToWinEvent
                WinEventTracker.EvtIdProperty[] aEvtIdProperties = new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty(NativeMethods.EventObjectSelection, SelectionPattern.IsSelectionRequiredProperty) };

                WinEventTracker.RemoveToNotificationList(hwnd, aEvtIdProperties, null, aEvtIdProperties.Length);
                el = wlb;
            }
            else if (eventId == NativeMethods.EventObjectSelection || eventId == NativeMethods.EventObjectSelectionRemove || eventId == NativeMethods.EventObjectSelectionAdd)
            {
                bool isMultipleSelection = wlb.IsMultipleSelection();

                // User should send SelectionAdd for a Multiselect listbox but it sends instead
                // Selection. The code below fixes the
                if (eventId == NativeMethods.EventObjectSelection && isMultipleSelection && wlb.HasOtherSelections(idChild - 1))
                {
                    eventId = NativeMethods.EventObjectSelectionAdd;
                }

                // The spec says a ElementSelectionEvent should be fired when action causes only one
                // selection.
                if ((eventId == NativeMethods.EventObjectSelectionRemove || eventId == NativeMethods.EventObjectSelectionAdd) &&
                    isMultipleSelection && wlb.GetSelectionCount() == 1)
                {
                    // The net result of the user action is that there is only one item selected in the
                    // listbox, so change the event to an EventObjectSelected.
                    idProp  = SelectionItemPattern.ElementSelectedEvent;
                    eventId = NativeMethods.EventObjectSelection;

                    // Now need to find what item is selected.
                    int selection = wlb.GetOtherSelection(idChild - 1);
                    if (selection != NativeMethods.LB_ERR)
                    {
                        idChild = selection;
                    }
                }

                el = wlb.CreateListboxItem(idChild - 1);
            }
            else
            {
                el = wlb;
            }

            // Special case for logical element change for listbox item
            if ((idProp as AutomationEvent) == AutomationElement.StructureChangedEvent &&
                (eventId == NativeMethods.EventObjectDestroy || eventId == NativeMethods.EventObjectCreate))
            {
                // Since children are referenced by position in the tree, addition and removal
                // of items leads to different results when asking properties for the same element
                // On removal, item + 1 is now item!
                // Use Children Invalidated to let the client knows that all the cached
                AutomationInteropProvider.RaiseStructureChangedEvent(wlb, new StructureChangedEventArgs(StructureChangeType.ChildrenInvalidated, wlb.MakeRuntimeId()));
                return;
            }

            if (el != null)
            {
                el.DispatchEvents(eventId, idProp, idObject, idChild);
            }
        }
            // ------------------------------------------------------
            //
            // Constructors
            //
            // ------------------------------------------------------

            #region Constructors

            internal WinEventItem (ref WinEventTracker.EventHookParams hp, WinEventTracker.StartStopDelegate ssd)
            {
                _hp = hp;
                _ssd = ssd;
            }
示例#12
0
        // Return an array that contains combo's edit portion specific events
        // These events will be remapped as combo box events
        private static void CreateEditPortionEvents (out WinEventTracker.EvtIdProperty [] editPortionEvents, out int counter, AutomationProperty [] aidProps)
        {
            // count how many events to pass back for the edit part of combo
            int c = 0;
            foreach ( AutomationProperty p in aidProps )
            {
                if ( p == ValuePattern.ValueProperty || p == ValuePattern.IsReadOnlyProperty )
                {
                    c++;
                }
            }

            if (c == 0)
            {
                editPortionEvents = null;
                counter = 0;
                return;
            }

            // allocate array with the number of events from above
            editPortionEvents = new WinEventTracker.EvtIdProperty[c];

            c = 0;
            foreach ( AutomationProperty p in aidProps )
            {
                if ( p == ValuePattern.ValueProperty || p == ValuePattern.IsReadOnlyProperty )
                {
                    editPortionEvents[c]._idProp = p;
                    editPortionEvents[c]._evtId = (p == ValuePattern.ValueProperty) ? NativeMethods.EventObjectValueChange : NativeMethods.EventObjectStateChange;
                    c++;
                }
            }

            counter = c;
        }