예제 #1
0
        void UpdateEditState(bool moveOnly = false)
        {
            _syncContext.Post(delegate
            {
                // TODO: This is not right yet - the list box might have the focus...
                AutomationElement focused;
                try
                {
                    focused = AutomationElement.FocusedElement;
                }
                catch (ArgumentException aex)
                {
                    Debug.Print("!!! ERROR: Failed to get Focused Element: " + aex.ToString());
                    // Not sure why I get this - sometimes with startup screen
                    return;
                }
                if (_formulaBar != null && _formulaBar.Equals(focused))
                {
                    EditWindowBounds = (Rect)_formulaBar.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
                    IntPtr hwnd      = (IntPtr)(int)_formulaBar.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty);
                    var pt           = Win32Helper.GetClientCursorPos(hwnd);
                    CaretPosition    = new Point(pt.X, pt.Y);
                }
                else if (_inCellEdit != null && _inCellEdit.Equals(focused))
                {
                    EditWindowBounds = (Rect)_inCellEdit.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
                    IntPtr hwnd      = (IntPtr)(int)_inCellEdit.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty);
                    var pt           = Win32Helper.GetClientCursorPos(hwnd);
                    CaretPosition    = new Point(pt.X, pt.Y);
                }
                else
                {
                    // CurrentFormula = null;
                    CurrentPrefix = null;
                    Debug.Print("Don't have a focused text box to update.");
                }

                // As long as we have an InCellEdit, we are editing the formula...
                IsEditingFormula = (_inCellEdit != null);

                // TODO: Smarter notification...?
                StateChanged(this, new StateChangeEventArgs(moveOnly ? StateChangeTypeEnum.Move : StateChangeTypeEnum.Undefined));
            }, null);
        }
        // Runs on our automation thread
        void InstallEventHandlers()
        {
            Logger.WindowWatcher.Verbose(string.Format("PopupList Installing event handlers on thread {0}", Thread.CurrentThread.ManagedThreadId));
            try
            {
                // TODO: Clean up
                var hwndListView = Win32Helper.GetFirstChildWindow(_hwndPopupList);

                _selectionChangeHook = new WinEventHook(WinEventHook.WinEvent.EVENT_OBJECT_SELECTION, WinEventHook.WinEvent.EVENT_OBJECT_SELECTION, _syncContextAuto, _syncContextMain, hwndListView);
                _selectionChangeHook.WinEventReceived += _selectionChangeHook_WinEventReceived;
                Logger.WindowWatcher.Verbose("PopupList selection event handler added");
            }
            catch (Exception ex)
            {
                // Probably no longer visible
                Logger.WindowWatcher.Warn(string.Format("PopupList event handler error {0}", ex));
                _hwndPopupList = IntPtr.Zero;
                IsVisible      = false;
            }
        }
예제 #3
0
        readonly WinEventDelegate _handleWinEventDelegate; // Ensures delegate that we pass to SetWinEventHook is not GC'd

        public WinEventHook(WinEvent eventMin, WinEvent eventMax, SynchronizationContext syncContextAuto, IntPtr hWndFilterOrZero)
        {
            if (syncContextAuto == null)
            {
                throw new ArgumentNullException(nameof(syncContextAuto));
            }
            _syncContextAuto  = syncContextAuto;
            _hWndFilterOrZero = hWndFilterOrZero;
            var xllModuleHandle = Win32Helper.GetXllModuleHandle();
            var excelProcessId  = Win32Helper.GetExcelProcessId();

            _handleWinEventDelegate = HandleWinEvent;
            _hWinEventHook          = SetWinEventHook(eventMin, eventMax, xllModuleHandle, _handleWinEventDelegate, excelProcessId, 0, SetWinEventHookFlags.WINEVENT_INCONTEXT);
            if (_hWinEventHook == IntPtr.Zero)
            {
                Logger.WinEvents.Error("SetWinEventHook failed");
                // Is SetLastError used? - SetWinEventHook documentation does not indicate so
                throw new Win32Exception("SetWinEventHook failed");
            }
            Logger.WinEvents.Info($"SetWinEventHook success on thread {Thread.CurrentThread.ManagedThreadId}");
        }
예제 #4
0
        void UpdateSelectedItem()
        {
            if (_hwndPopupList == IntPtr.Zero)
            {
                Logger.WindowWatcher.Verbose($"PopupList UpdateSelectedItem ignored: PopupList is null");
                return;
            }

            if (!IsVisible)
            {
                if (_selectedItemIndex == -1 &&
                    SelectedItemText == string.Empty &&
                    SelectedItemBounds == Rect.Empty)
                {
                    // Don't change anything, or fire an updated event
                    return;
                }

                // Set to the way things should be when not visible, and fire an updated event
                _selectedItemIndex = -1;
                SelectedItemText   = string.Empty;
                SelectedItemBounds = Rect.Empty;
                ListBounds         = Rect.Empty;
            }
            else
            {
                string text;
                Rect   itemBounds;
                var    hwndListView = Win32Helper.GetFirstChildWindow(_hwndPopupList);
                ListBounds = Win32Helper.GetWindowBounds(_hwndPopupList);

                _selectedItemIndex = Win32Helper.GetListViewSelectedItemInfo(hwndListView, out text, out itemBounds);
                itemBounds.Offset(ListBounds.Left, ListBounds.Top);
                SelectedItemBounds = itemBounds;
                SelectedItemText   = text;
            }
            OnSelectedItemChanged();
        }
예제 #5
0
        // Switches to our Automation thread, updates current state and raises StateChanged event
        void UpdateEditState(bool moveOnly = false)
        {
            Logger.WindowWatcher.Verbose("> FormulaEdit UpdateEditState - Posted");
            _syncContextAuto.Post(moveOnlyObj =>
            {
                Logger.WindowWatcher.Verbose($"FormulaEdit UpdateEditState - Focus: {_formulaEditFocus}");
                //// TODO: This is not right yet - the list box might have the focus... ?
                //AutomationElement focused;
                //try
                //{
                //    focused = AutomationElement.FocusedElement;
                //}
                //catch (ArgumentException aex)
                //{
                //    Debug.Print($"!!! ERROR: Failed to get Focused Element: {aex}");
                //    // Not sure why I get this - sometimes with startup screen
                //    return;
                //}
                AutomationElement focusedEdit = null;
                bool prefixChanged            = false;
                if (_formulaEditFocus == FormulaEditFocus.FormulaBar)
                {
                    focusedEdit = _formulaBar;
                }
                else if (_formulaEditFocus == FormulaEditFocus.InCellEdit)
                {
                    focusedEdit = _inCellEdit;
                }
                else
                {
                    // Neither have the focus, so we don't update anything
                    Logger.WindowWatcher.Verbose("FormulaEdit UpdateEditState End formula editing");
                    CurrentPrefix = null;
                    if (IsEditingFormula)
                    {
                        UninstallLocationMonitor();
                    }
                    IsEditingFormula = false;
                    prefixChanged    = true;
                    // Debug.Print("Don't have a focused text box to update.");
                }

                if (focusedEdit != null)
                {
                    EditWindowBounds = (Rect)focusedEdit.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
                    IntPtr hwnd      = (IntPtr)(int)focusedEdit.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty);

                    var pt = Win32Helper.GetClientCursorPos(hwnd);

                    if (!IsEditingFormula)
                    {
                        InstallLocationMonitor(GetTopLevelWindow(focusedEdit));
                    }
                    IsEditingFormula = true;

                    var newPrefix = XlCall.GetFormulaEditPrefix();      // What thread do we have to use here ...?
                    if (CurrentPrefix != newPrefix)
                    {
                        CurrentPrefix = newPrefix;
                        prefixChanged = true;
                    }
                    Logger.WindowWatcher.Verbose($"FormulaEdit UpdateEditState Formula editing: CurrentPrefix {CurrentPrefix}, EditWindowBounds: {EditWindowBounds}");
                }

                // TODO: Smarter notification...?
                if ((bool)moveOnlyObj && !prefixChanged)
                {
                    StateChanged?.Invoke(this, new StateChangeEventArgs(StateChangeType.Move));
                }
                else
                {
                    OnStateChanged(new StateChangeEventArgs(StateChangeType.Multiple));
                }
            }, moveOnly);
        }
예제 #6
0
        void _windowLocationChangeHook_WinEventReceived(object sender, WinEventHook.WinEventArgs winEventArgs)
        {
#if DEBUG
            Logger.WinEvents.Verbose($"{winEventArgs.EventType} - Window {winEventArgs.WindowHandle:X} ({Win32Helper.GetClassName(winEventArgs.WindowHandle)} - Object/Child {winEventArgs.ObjectId} / {winEventArgs.ChildId} - Thread {winEventArgs.EventThreadId} at {winEventArgs.EventTimeMs}");
#endif
            LocationChanged?.Invoke(this, EventArgs.Empty);
        }
        // Runs on our Automation thread (via SyncContext passed into the constructor)
        // CONSIDER: Performance impact of logging (including GetClassName) here
        void OnWinEventReceived(object winEventArgsObj)
        {
            var winEventArgs = (WinEventArgs)winEventArgsObj;

#if DEBUG
            if (winEventArgs.ObjectId != WinEventObjectId.OBJID_CURSOR)
            {
                Logger.WinEvents.Verbose($"{winEventArgs.EventType} - Window {winEventArgs.WindowHandle:X} ({Win32Helper.GetClassName(winEventArgs.WindowHandle)} - Object/Child {winEventArgs.ObjectId} / {winEventArgs.ChildId} - Thread {winEventArgs.EventThreadId} at {winEventArgs.EventTimeMs}");
            }
#endif
            WinEventReceived?.Invoke(this, winEventArgs);
        }
예제 #8
0
        void WindowStateChange(IntPtr hWinEventHook, WinEventHook.WinEvent eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
        {
            // This runs on the main application thread.
            Debug.Print("### Thread receiving WindowStateChange: " + Thread.CurrentThread.ManagedThreadId);
            switch (Win32Helper.GetClassName(hWnd))
            {
            case _classMain:
                //if (eventType == WinEventHook.WinEvent.EVENT_OBJECT_FOCUS ||
                //    eventType == WinEventHook.WinEvent.EVENT_OBJECT_SHOW)
            {
                Debug.Print("MainWindow update: " + hWnd.ToString("X") + ", EventType: " + eventType);
                UpdateMainWindow(hWnd);
            }
                if (eventType == WinEventHook.WinEvent.EVENT_OBJECT_DESTROY)
                {
                }
                break;

            case _classPopupList:
                if (PopupListWindow == IntPtr.Zero &&
                    (eventType == WinEventHook.WinEvent.EVENT_OBJECT_CREATE ||
                     eventType == WinEventHook.WinEvent.EVENT_OBJECT_SHOW))
                {
                    PopupListWindow = hWnd;
                    PopupListWindowChanged(this, EventArgs.Empty);
                }
                else
                {
                    Debug.Assert(PopupListWindow == hWnd);
                }
                break;

            case _classInCellEdit:

                Debug.Print("InCell Window update: " + hWnd.ToString("X") + ", EventType: " + eventType + ", idChild: " + idChild);
                if (eventType == WinEventHook.WinEvent.EVENT_OBJECT_CREATE ||
                    eventType == WinEventHook.WinEvent.EVENT_OBJECT_SHOW)
                {
                    InCellEditWindow = hWnd;
                    InCellEditWindowChanged(this, EventArgs.Empty);
                }
                else if (eventType == WinEventHook.WinEvent.EVENT_OBJECT_HIDE)
                {
                    InCellEditWindow = IntPtr.Zero;
                    InCellEditWindowChanged(this, EventArgs.Empty);
                }
                else if (eventType == WinEventHook.WinEvent.EVENT_OBJECT_FOCUS)
                {
                    InCellEditFocused(this, EventArgs.Empty);
                }
                break;

            case _classFormulaBar:
                Debug.Print("FormulaBar Window update: " + hWnd.ToString("X") + ", EventType: " + eventType + ", idChild: " + idChild);
                if (eventType == WinEventHook.WinEvent.EVENT_OBJECT_CREATE ||
                    eventType == WinEventHook.WinEvent.EVENT_OBJECT_SHOW)
                {
                    FormulaBarWindow = hWnd;
                    FormulaBarWindowChanged(this, EventArgs.Empty);
                }
                else if (eventType == WinEventHook.WinEvent.EVENT_OBJECT_FOCUS)
                {
                    FormulaBarFocused(this, EventArgs.Empty);
                }
                break;

            default:
                //InCellEditWindowChanged(this, EventArgs.Empty);
                break;
            }
        }
예제 #9
0
        // This runs on the Automation thread, via SyncContextAuto passed in to WinEventHook when we created this WindowWatcher
        // CONSIDER: We would be able to run all the watcher updates from WinEvents, including Location and Selection changes,
        //           but since WinEvents have no hwnd filter, UIAutomation events might be more efficient.
        // CONSIDER: Performance optimisation would keep a list of window handles we know about, preventing the class name check every time
        void _windowStateChangeHook_WinEventReceived(object sender, WinEventHook.WinEventArgs e)
        {
            var className = Win32Helper.GetClassName(e.WindowHandle);

            if (e.EventType == WinEventHook.WinEvent.EVENT_OBJECT_FOCUS)
            {
                // Might raise change event for Unfocus
                if (!UpdateFocus(e.WindowHandle, className))
                {
                    // We already have the right focus
                    return;
                }
            }

            // Debug.Print("### Thread receiving WindowStateChange: " + Thread.CurrentThread.ManagedThreadId);
            switch (className)
            {
            //case _sheetWindowClass:
            //    if (e.EventType == WinEventHook.WinEvent.EVENT_OBJECT_SHOW)
            //    {
            //        // Maybe a new workbook is on top...
            //        // Note that there is also an EVENT_OBJECT_PARENTCHANGE (which we are not subscribing to at the moment
            //    }
            //    break;
            case _popupListClass:
                PopupListWindowChanged?.Invoke(this, new WindowChangedEventArgs(e.WindowHandle, e.EventType, e.ObjectId));
                break;

            case _inCellEditClass:
                InCellEditWindowChanged?.Invoke(this, new WindowChangedEventArgs(e.WindowHandle, e.EventType, e.ObjectId));
                break;

            case _formulaBarClass:
                FormulaBarWindowChanged?.Invoke(this, new WindowChangedEventArgs(e.WindowHandle, e.EventType, e.ObjectId));
                break;

            case _excelToolTipClass:
                ExcelToolTipWindowChanged?.Invoke(this, new WindowChangedEventArgs(e.WindowHandle, e.EventType, e.ObjectId));
                break;

            //case _nuiDialogClass:
            //    // Debug.Print($"SelectDataSource {_selectDataSourceClass} Window update: {e.WindowHandle:X}, EventType: {e.EventType}, idChild: {e.ChildId}");
            //    if (e.EventType == WinEventHook.WinEvent.EVENT_OBJECT_CREATE)
            //    {
            //        // Get the name of this window - maybe ours or some other NUIDialog
            //        var windowTitle = Win32Helper.GetText(e.WindowHandle);
            //        if (windowTitle.Equals(_selectDataSourceTitle, StringComparison.OrdinalIgnoreCase))
            //        {
            //            SelectDataSourceWindow = e.WindowHandle;
            //            SelectDataSourceWindowChanged?.Invoke(this,
            //                new WindowChangedEventArgs { Type = WindowChangedEventArgs.ChangeType.Create });
            //        }
            //    }
            //    else if (SelectDataSourceWindow == e.WindowHandle && e.EventType == WinEventHook.WinEvent.EVENT_OBJECT_SHOW)
            //    {
            //        IsSelectDataSourceWindowVisible = true;
            //        SelectDataSourceWindowChanged?.Invoke(this,
            //                new WindowChangedEventArgs { Type = WindowChangedEventArgs.ChangeType.Create });
            //    }
            //    else if (SelectDataSourceWindow == e.WindowHandle && e.EventType == WinEventHook.WinEvent.EVENT_OBJECT_HIDE)
            //    {
            //        IsSelectDataSourceWindowVisible = false;
            //        SelectDataSourceWindowChanged?.Invoke(this, new WindowChangedEventArgs { Type = WindowChangedEventArgs.ChangeType.Hide });
            //    }
            //    else if (SelectDataSourceWindow == e.WindowHandle && e.EventType == WinEventHook.WinEvent.EVENT_OBJECT_DESTROY)
            //    {
            //        IsSelectDataSourceWindowVisible = false;
            //        SelectDataSourceWindow = IntPtr.Zero;
            //        SelectDataSourceWindowChanged?.Invoke(this, new WindowChangedEventArgs { Type = WindowChangedEventArgs.ChangeType.Destroy });
            //    }
            //    break;
            default:
                //InCellEditWindowChanged(this, EventArgs.Empty);
                break;
            }
        }
예제 #10
0
        void UpdateEditStateImpl(bool moveOnly = false)
        {
            Logger.WindowWatcher.Verbose($"> FormulaEdit UpdateEditState - Thread {Thread.CurrentThread.ManagedThreadId}");
            Logger.WindowWatcher.Verbose($"FormulaEdit UpdateEditState - Focus: {_formulaEditFocus} Window: {(_formulaEditFocus == FormulaEditFocus.FormulaBar ? _hwndFormulaBar : _hwndInCellEdit)}");

            IntPtr hwnd = IntPtr.Zero;

            bool prefixChanged = false;

            if (_formulaEditFocus == FormulaEditFocus.FormulaBar)
            {
                hwnd = _hwndFormulaBar;
            }
            else if (_formulaEditFocus == FormulaEditFocus.InCellEdit)
            {
                hwnd = _hwndInCellEdit;
            }
            else
            {
                // Neither have the focus, so we don't update anything
                Logger.WindowWatcher.Verbose("FormulaEdit UpdateEditState End formula editing");

                CurrentPrefix = null;

                if (IsEditingFormula)
                {
                    UninstallLocationMonitor();
                }

                IsEditingFormula = false;
                prefixChanged    = true;

                // Debug.Print("#### FormulaEditWatcher - No Window " + Environment.StackTrace);
            }

            if (hwnd != IntPtr.Zero)
            {
                EditWindowBounds = Win32Helper.GetWindowBounds(hwnd);

                if (!IsEditingFormula)
                {
                    IntPtr hwndTopLevel = Win32Helper.GetRootAncestor(hwnd);
                    InstallLocationMonitor(hwndTopLevel);
                    IsEditingFormula = true;
                }

                var newPrefix = XlCall.GetFormulaEditPrefix();  // What thread do we have to use here ...?

                if (CurrentPrefix != newPrefix)
                {
                    CurrentPrefix = newPrefix;
                    prefixChanged = true;
                }

                Logger.WindowWatcher.Verbose($"FormulaEdit UpdateEditState Formula editing: CurrentPrefix {CurrentPrefix}, EditWindowBounds: {EditWindowBounds}");
            }

            // TODO: Smarter (or more direct) notification...?
            if (moveOnly && !prefixChanged)
            {
                StateChanged?.Invoke(this, new StateChangeEventArgs(StateChangeType.Move));
            }
            else
            {
                OnStateChanged(StateChangeType.Multiple);
            }
        }
        void _windowLocationChangeHook_WinEventReceived(object sender, WinEventHook.WinEventArgs winEventArgs)
        {
#if DEBUG
            //Logger.WinEvents.Verbose(string.Format("{winEventArgs.EventType} - Window {winEventArgs.WindowHandle:X} ({Win32Helper.GetClassName(winEventArgs.WindowHandle)} - Object/Child {winEventArgs.ObjectId} / {winEventArgs.ChildId} - Thread {winEventArgs.EventThreadId} at {winEventArgs.EventTimeMs}");
            Logger.WinEvents.Verbose(string.Format("{0} - Window {1:X} ({2} - Object/Child {3} / {4} - Thread {5} at {6}", winEventArgs.EventType, winEventArgs.WindowHandle, Win32Helper.GetClassName(winEventArgs.WindowHandle), winEventArgs.ObjectId, winEventArgs.ChildId, winEventArgs.EventThreadId, winEventArgs.EventTimeMs));
#endif
            LocationChanged.Invoke(this, EventArgs.Empty);
        }
예제 #12
0
        // Runs on our Automation thread (via SyncContext passed into the constructor)
        // CONSIDER: Performance impact of logging (including GetClassName) here
        void OnWinEventReceived(object winEventArgsObj)
        {
            var winEventArgs = (WinEventArgs)winEventArgsObj;

#if DEBUG
            if (winEventArgs.ObjectId != WinEventObjectId.OBJID_CURSOR)
            {
                //Logger.WinEvents.Verbose(string.Format("{winEventArgs.EventType} - Window {winEventArgs.WindowHandle:X} ({Win32Helper.GetClassName(winEventArgs.WindowHandle)} - Object/Child {winEventArgs.ObjectId} / {winEventArgs.ChildId} - Thread {winEventArgs.EventThreadId} at {winEventArgs.EventTimeMs}");
                Logger.WinEvents.Verbose(string.Format("{0} - Window {1:X} ({2} - Object/Child {3} / {4} - Thread {5} at {6}", winEventArgs.EventType, winEventArgs.WindowHandle, Win32Helper.GetClassName(winEventArgs.WindowHandle), winEventArgs.ObjectId, winEventArgs.ChildId, winEventArgs.EventThreadId, winEventArgs.EventTimeMs));
            }
#endif
            WinEventReceived.Invoke(this, winEventArgs);
        }
예제 #13
0
        // Runs on our Automation thread (via SyncContext passed into the constructor)
        // CONSIDER: Performance impact of logging (including GetClassName) here
        void OnWinEventReceived(object winEventArgsObj)
        {
            var winEventArgs = (WinEventArgs)winEventArgsObj;

            if (winEventArgs.ObjectId == WinEventObjectId.OBJID_CURSOR)
            {
                return;
            }
#if DEBUG
            Logger.WinEvents.Verbose($"{winEventArgs.EventType} - Window {winEventArgs.WindowHandle:X} {(winEventArgs.WindowHandle != IntPtr.Zero ? Win32Helper.GetClassName(winEventArgs.WindowHandle) : "")} - Object/Child {winEventArgs.ObjectId} / {winEventArgs.ChildId} - Thread {winEventArgs.EventThreadId} at {winEventArgs.EventTimeMs}");
#endif
            WinEventReceived?.Invoke(this, winEventArgs);
        }