InputManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / System / Windows / Input / InputManager.cs / 1 / InputManager.cs

                            using System.Collections; 
using System.Windows.Threading;
using System.Threading;
using System.Windows;
using System.Security; 
using System.Security.Permissions;
using MS.Win32; 
using MS.Internal; 
using MS.Internal.PresentationCore;                        // SecurityHelper
using System; 
using System.Diagnostics;

using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID; 

namespace System.Windows.Input 
{ 
    /// 
    ///     The InputManager class is responsible for coordinating all of the 
    ///     input system in Avalon.
    /// 
    public sealed class InputManager : DispatcherObject
    { 
        /// 
        ///     A routed event indicating that an input report arrived. 
        ///  
        internal static readonly RoutedEvent PreviewInputReportEvent = GlobalEventManager.RegisterRoutedEvent("PreviewInputReport", RoutingStrategy.Tunnel, typeof(InputReportEventHandler), typeof(InputManager));
 
        /// 
        ///     A routed event indicating that an input report arrived.
        /// 
        [FriendAccessAllowed] 
        internal static readonly RoutedEvent InputReportEvent = GlobalEventManager.RegisterRoutedEvent("InputReport", RoutingStrategy.Bubble, typeof(InputReportEventHandler), typeof(InputManager));
 
        ///  
        ///     Return the input manager associated with the current context.
        ///  
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        /// 
        ///  
        ///     Critical: This class is not ok to expose since SEE apps
        ///               should not have to deal with this directly and 
        ///               it exposes methods that can be use for input spoofing 
        ///
        ///  
        public static InputManager Current
        {
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            get
            { 
                return GetCurrentInputManagerImpl(); 
            }
        } 

        ///
        ///     Internal implementation of InputManager.Current.
        ///     Critical but not TAS - for internal's to use. 
        ///     Only exists for perf. The link demand check was causing perf in some XAF scenarios.
        /// 
        ///  
        ///     Critical: This class is not ok to expose since SEE apps
        ///               should not have to deal with this directly and 
        ///               it exposes methods that can be use for input spoofing
        ///
        /// 
        internal static InputManager UnsecureCurrent 
        {
            [SecurityCritical] 
            [FriendAccessAllowed] 
            get
            { 
                return GetCurrentInputManagerImpl();
            }
        }
 
        ///
        ///     Implementation of InputManager.Current 
        /// 
        /// 
        ///     Critical: This class is not ok to expose since SEE apps 
        ///               should not have to deal with this directly and
        ///               it exposes methods that can be use for input spoofing
        /// 
        [SecurityCritical] 
        private static InputManager GetCurrentInputManagerImpl()
        { 
            InputManager inputManager = null; 

            Dispatcher dispatcher = Dispatcher.CurrentDispatcher; 
            inputManager = dispatcher.InputManager as InputManager;

            if (inputManager == null)
            { 
                inputManager = new InputManager();
                dispatcher.InputManager = inputManager; 
            } 

            return inputManager; 
        }

        /// 
        ///     Critical: This code causes critical data (inputmanager to be instantiated) 
        ///     This class should not be exposed in the SEE as it can be used for input spoofing.
        ///  
        [SecurityCritical] 
        private InputManager()
        { 
            // STA Requirement
            //
            // Avalon doesn't necessarily require STA, but many components do.  Examples
            // include Cicero, OLE, COM, etc.  So we throw an exception here if the 
            // thread is not STA.
            if(Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) 
            { 
                throw new InvalidOperationException(SR.Get(SRID.RequiresSTA));
            } 

            _stagingArea = new Stack();

            _primaryKeyboardDevice = new Win32KeyboardDevice(this); 
            _primaryMouseDevice = new Win32MouseDevice(this);
            _primaryCommandDevice = new CommandDevice(this); 
 
            _stylusLogic = new StylusLogic(this);
 
            _continueProcessingStagingAreaCallback = new DispatcherOperationCallback(ContinueProcessingStagingArea);

            _hitTestInvalidatedAsyncOperation = null;
            _hitTestInvalidatedAsyncCallback = new DispatcherOperationCallback(HitTestInvalidatedAsyncCallback); 

            _layoutUpdatedCallback = new EventHandler(OnLayoutUpdated); //need to cache it, LM only keeps weak ref 
            ContextLayoutManager.From(Dispatcher).LayoutEvents.Add(_layoutUpdatedCallback); 

            // Timer used to synchronize the input devices periodically 
            _inputTimer = new DispatcherTimer(DispatcherPriority.Background);
            _inputTimer.Tick += new EventHandler(ValidateInputDevices);
            _inputTimer.Interval = TimeSpan.FromMilliseconds(125);
        } 

        ///  
        ///  
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        /// 
        ///     Critical: This event lets people subscribe to all events in the system
        ///     PublicOk: Method is link demanded.
        ///  
        public event PreProcessInputEventHandler PreProcessInput
        { 
            [SecurityCritical ] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            add 
            {
                _preProcessInput += value;
            }
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove 
            { 
                _preProcessInput -= value;
            } 
        }


        ///  
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        ///  
        /// 
        ///     Critical: This event lets people subscribe to all events in the system 
        ///     Not safe to expose.
        ///     PublicOk: Method is link demanded.
        /// 
        public event NotifyInputEventHandler PreNotifyInput 
        {
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            add
            { 
                _preNotifyInput += value;
            }
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            remove
            { 
                _preNotifyInput -= value; 
            }
 
        }
        /// 
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        ///  
        ///     Critical: This event lets people subscribe to all events in the system 
        ///     Not safe to expose.
        ///     PublicOk: Method is link demanded. 
        /// 
        public event NotifyInputEventHandler PostNotifyInput
        {
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            add 
            { 
                _postNotifyInput += value;
            } 
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove
            { 
                _postNotifyInput -= value;
 
            } 
        }
 
        /// 
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        /// 
        ///     Critical: This event lets people subscribe to all events in the system 
        ///     Not safe to expose. 
        ///     PublicOk: Method is link demanded.
        ///  
        public event ProcessInputEventHandler PostProcessInput
        {
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            add
            { 
                _postProcessInput += value; 
            }
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove
            {
                _postProcessInput -= value; 
            }
        } 
 
        /// 
        /// This event is raised by the HwndSource.CriticalTranslateAccelerator 
        /// on descendent HwndSource instances. The only subscriber to this event
        /// is KeyboardNavigation.
        /// 
        ///  
        ///     Critical: This event lets people subscribe to all input notifications
        ///  
        internal event KeyEventHandler TranslateAccelerator 
        {
            [FriendAccessAllowed] // Used by KeyboardNavigation.cs in Framework 
            [SecurityCritical]
            add
            {
                _translateAccelerator += value; 
            }
            [FriendAccessAllowed] // Used by KeyboardNavigation.cs in Framework 
            [SecurityCritical] 
            remove
            { 
                _translateAccelerator -= value;
            }
        }
 
        /// 
        /// Raises the TranslateAccelerator event 
        ///  
        /// 
        ///     Critical: Accesses critical _translateAccelerator. 
        /// 
        [SecurityCritical]
        internal void RaiseTranslateAccelerator(KeyEventArgs e)
        { 
            if (_translateAccelerator != null)
            { 
                _translateAccelerator(this, e); 
            }
        } 

        /// 
        ///     Registers an input provider with the input manager.
        ///  
        /// 
        ///     The input provider to register. 
        ///  
        /// 
        ///     This class will not be available in internet zone. 
        ///     Critical: This code acceses and stores critical data (InputProvider)
        ///     TreatAsSafe: This code demands UIPermission.
        /// 
        [SecurityCritical] 
        internal InputProviderSite RegisterInputProvider(IInputProvider inputProvider)
        { 
            SecurityHelper.DemandUnrestrictedUIPermission(); 
//             VerifyAccess();
 

            // Create a site for this provider, and keep track of it.
            InputProviderSite site = new InputProviderSite(this, inputProvider);
            _inputProviders[inputProvider] = site; 

            return site; 
        } 

        ///  
        ///     This class will not be available in internet zone.
        ///     Critical: This code acceses critical data in the form of InputProvider
        /// 
        [SecurityCritical] 
        internal void UnregisterInputProvider(IInputProvider inputProvider)
        { 
            _inputProviders.Remove(inputProvider); 
        }
 
        /// 
        ///     Returns a collection of input providers registered with the input manager.
        /// 
        ///  
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        ///  
        ///     This class will not be available in internet zone.
        ///     Critical: This code exposes InputProviders which are 
        ///               considered as critical data
        ///     PublicOK: This code has a demand on it
        /// 
        public ICollection InputProviders 
        {
            [SecurityCritical] 
            get 
            {
                SecurityHelper.DemandUnrestrictedUIPermission(); 
                return UnsecureInputProviders;
            }
        }
 

        ///  
        ///     Returns a collection of input providers registered with the input manager. 
        /// 
        ///  
        ///     Critical: This code exposes InputProviders which are considered as critical data
        ///     This overload exists for perf. improvements in the internet zone since this function is called
        ///     quite often
        ///  
        internal ICollection UnsecureInputProviders
        { 
            [SecurityCritical] 
            get
            { 
                return _inputProviders.Keys;
            }
        }
        ///  
        ///     Read-only access to the primary keyboard device.
        ///  
        public KeyboardDevice PrimaryKeyboardDevice 
        {
            // 
            get {return _primaryKeyboardDevice;}
        }

        ///  
        ///     Read-only access to the primary mouse device.
        ///  
        public MouseDevice PrimaryMouseDevice 
        {
            // 
            get {return _primaryMouseDevice;}
        }
        /// 
        ///     Critical, accesses critical member _stylusLogic 
        /// 
        internal StylusLogic StylusLogic 
        { 
            [SecurityCritical, FriendAccessAllowed]
            get { return _stylusLogic; } 
        }

        /// 
        ///     Read-only access to the primary keyboard device. 
        /// 
        internal CommandDevice PrimaryCommandDevice 
        { 
            get {return _primaryCommandDevice;}
        } 

        /// 
        ///     The InDragDrop property represents whether we are currently inside
        ///     a OLE DragDrop operation. 
        /// 
        internal bool InDragDrop 
        { 
            get { return _inDragDrop; }
            set { _inDragDrop = value; } 
        }

        /// 
        ///     The MostRecentInputDevice represents the last input device to 
        ///     report an "interesting" user action.  What exactly constitutes
        ///     such an action is up to each device to implement. 
        ///  
        public InputDevice MostRecentInputDevice
        { 
            get { return _mostRecentInputDevice; }
            internal set { _mostRecentInputDevice = value; }
        }
 
        /// 
        ///     An event that is raised whenever the result of a hit-test may 
        ///     have changed. 
        /// 
        public event EventHandler HitTestInvalidatedAsync; 

        internal void NotifyHitTestInvalidated()
        {
            // The HitTest result may have changed for someone somewhere. 
            // Raise the HitTestInvalidatedAsync event after the next layout.
            if(_hitTestInvalidatedAsyncOperation == null) 
            { 
                // It would be best to re-evaluate anything dependent on the hit-test results
                // immediately after layout & rendering are complete.  Unfortunately this can 
                // lead to an infinite loop.  Consider the following scenario:
                //
                // If the mouse is over an element, hide it.
                // 
                // This never resolves to a "correct" state.  When the mouse moves over the
                // element, the element is hidden, so the mouse is no longer over it, so the 
                // element is shown, but that means the mouse is over it again.  Repeat. 
                //
                // We push our re-evaluation to a priority lower than input processing so that 
                // the user can change the input device to avoid the infinite loops, or close
                // the app if nothing else works.
                //
                _hitTestInvalidatedAsyncOperation = Dispatcher.BeginInvoke(DispatcherPriority.Input, 
                                                                        _hitTestInvalidatedAsyncCallback,
                                                                        null); 
            } 
            else if (_hitTestInvalidatedAsyncOperation.Priority == DispatcherPriority.Inactive)
            { 
                // This means that we are currently waiting for the timer to expire so
                // that we can promote the current queue item to Input prority. Since
                // we are now being told that we need to re-hittest, we simply stop the
                // timer and promote the queue item right now instead of waiting for expiry. 

                ValidateInputDevices(this, EventArgs.Empty); 
            } 
        }
 

        ///
        ///     Critical - calls UnsecureCurrent
        ///     TreatAsSafe - notifying the input manager that hit test information needs to be recalced. 
        ///                   is considered safe ( and currently this code is transparent).
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static void SafeCurrentNotifyHitTestInvalidated()
        { 
            UnsecureCurrent.NotifyHitTestInvalidated();
        }

        private object HitTestInvalidatedAsyncCallback(object arg) 
        {
            _hitTestInvalidatedAsyncOperation = null; 
            if (HitTestInvalidatedAsync != null) 
            {
                HitTestInvalidatedAsync(this, EventArgs.Empty); 
            }

            return null;
        } 

        private void OnLayoutUpdated(object sender, EventArgs e) 
        { 
            NotifyHitTestInvalidated();
        } 

        /// 
        /// Start the timer that will kick off synchronize
        /// operation on all the input devices upon expiry 
        /// 
        internal void InvalidateInputDevices() 
        { 
            // If there is no pending ansyc hittest operation
 
            if (_hitTestInvalidatedAsyncOperation == null)
            {
                // Post an inactive item to the queue. When the timer expires
                // we will promote this queue item to Input priority. 

                _hitTestInvalidatedAsyncOperation = Dispatcher.BeginInvoke(DispatcherPriority.Inactive, 
                                                                        _hitTestInvalidatedAsyncCallback, 
                                                                        null);
 
                // Start the input timer

                _inputTimer.IsEnabled = true;
            } 
        }
 
        ///  
        /// Synchronize the  input devices
        ///  
        private void ValidateInputDevices(object sender, EventArgs e)
        {
            Debug.Assert(_hitTestInvalidatedAsyncOperation != null &&
                         _hitTestInvalidatedAsyncOperation.Priority == DispatcherPriority.Inactive && 
                         _inputTimer.IsEnabled == true);
 
            // Promote the pending DispatcherOperation to Input Priority 

            _hitTestInvalidatedAsyncOperation.Priority = DispatcherPriority.Input; 

            // Stop the input timer

            _inputTimer.IsEnabled = false; 
        }
 
        ///  
        ///     Synchronously processes the specified input.
        ///  
        /// 
        ///     The specified input is processed by all of the filters and
        ///     monitors, and is finally dispatched to the appropriate
        ///     element as an input event. 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        ///  
        ///     Whether or not any event generated as a consequence of this
        ///     event was handled. 
        /// 
        /// 
        ///     Critical: This code can cause input to be processed.
        ///     PublicOK: This code link demands. 
        /// 
        [SecurityCritical] 
        [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
        public bool ProcessInput(InputEventArgs input)
        { 
//             VerifyAccess();

            if(input == null)
            { 
                throw new ArgumentNullException("input");
            } 
 
            // Push a marker indicating the portion of the staging area
            // that needs to be processed. 
            PushMarker();

            // Push the input to be processed onto the staging area.
            PushInput(input, null); 

            // Post a work item to continue processing the staging area 
            // in case someone pushes a dispatcher frame in the middle 
            // of input processing.
            RequestContinueProcessingStagingArea(); 

            // Now drain the staging area up to the marker we pushed.
            bool handled = ProcessStagingArea();
            return handled; 
        }
 
        /// 
        /// Critical - accesses critical data ( _stagingArea)
        /// 
        [SecurityCritical]
        internal StagingAreaInputItem PushInput(StagingAreaInputItem inputItem)
        {
            _stagingArea.Push(inputItem); 
            return inputItem;
        } 
 
        ///
        /// Critical - accesses critical function ( PushInput) 
        ///
        [SecurityCritical]
        internal StagingAreaInputItem PushInput(InputEventArgs input, StagingAreaInputItem promote)
        { 
            StagingAreaInputItem item = new StagingAreaInputItem(false);
            item.Reset(input, promote); 
 
            return PushInput(item);
        } 

        ///
        /// Critical - calls a critical function ( PushInput).
        /// 
        [SecurityCritical]
        internal StagingAreaInputItem PushMarker() 
        { 
            StagingAreaInputItem item = new StagingAreaInputItem(true);
 
            return PushInput(item);
        }

        /// 
        /// Critical - accesses critical data _stagingArea.
        /// 
        [SecurityCritical] 
        internal StagingAreaInputItem PopInput()
        { 
            object input = null;

            if(_stagingArea.Count > 0)
            { 
                input = _stagingArea.Pop();
            } 
 
            return input as StagingAreaInputItem;
        } 


        ///
        /// Critical - accesses the _stagingArea critical data. 
        ///
        [SecurityCritical] 
        internal StagingAreaInputItem PeekInput() 
        {
            object input = null; 

            if(_stagingArea.Count > 0)
            {
                input = _stagingArea.Peek(); 
            }
 
            return input as StagingAreaInputItem; 
        }
 
        ///
        /// Critical - accesses critical data ( _stagingArea.Count) and calls a critical function - ProcessStagingArea
        ///
        [SecurityCritical ] 
        internal object ContinueProcessingStagingArea(object unused)
        { 
            _continueProcessingStagingArea = false; 

            // It is possible that we can be re-entered by a nested 
            // dispatcher frame.  Continue processing the staging
            // area if we need to.
            if(_stagingArea.Count > 0)
            { 
                // Before we actually start to drain the staging area, we need
                // to post a work item to process more input.  This enables us 
                // to process more input if we enter a nested pump. 
                RequestContinueProcessingStagingArea();
 
                // Now synchronously drain the staging area.
                ProcessStagingArea();
            }
 
            return null;
        } 
 
        ///
        /// Critical: accesses critical data ( PopInput()) and raises events with the user-initiated flag 
        ///
        [SecurityCritical]
        private bool ProcessStagingArea()
        { 
            bool handled = false;
 
            // For performance reasons, try to reuse the input event args. 
            // If we are reentrered, we have to start over with fresh event
            // args, so we clear the member variables before continuing. 
            // Also, we cannot simply make an single instance of the
            // PreProcessedInputEventArgs and cast it to NotifyInputEventArgs
            // or ProcessInputEventArgs because a malicious user could upcast
            // the object and call inappropriate methods. 
            NotifyInputEventArgs notifyInputEventArgs = (_notifyInputEventArgs != null) ? _notifyInputEventArgs : new NotifyInputEventArgs();
            ProcessInputEventArgs processInputEventArgs = (_processInputEventArgs != null) ? _processInputEventArgs : new ProcessInputEventArgs(); 
            PreProcessInputEventArgs preProcessInputEventArgs = (_preProcessInputEventArgs != null) ? _preProcessInputEventArgs : new PreProcessInputEventArgs(); 
            _notifyInputEventArgs = null;
            _processInputEventArgs = null; 
            _preProcessInputEventArgs = null;

            // Because we can be reentered, we can't just enumerate over the
            // staging area - that could throw an exception if the queue 
            // changes underneath us.  Instead, just loop until we find a
            // frame marker or until the staging area is empty. 
            StagingAreaInputItem item = null; 
            while((item = PopInput()) != null)
            { 
                // If we found a marker, we have reached the end of a
                // "section" of the staging area.  We just return from
                // the synchronous processing of the staging area.
                // If a dispatcher frame has been pushed by someone, this 
                // will not return to the original ProcessInput.  Instead
                // it will unwind to the dispatcher and since we have 
                // already pushed a work item to continue processing the 
                // input, it will simply call back into us to do more
                // processing.  At which point we will continue to drain 
                // the staging area.  This could cause strage behavior,
                // but it is deemed more acceptable than stalling input
                // processing.
                // 

 
 

                if(item.IsMarker) 
                {
                    break;
                }
 
                // Pre-Process the input.  This could modify the staging
                // area, and it could cancel the processing of this 
                // input event. 
                //
                // Because we use multi-cast delegates, we always have to 
                // create a new multi-cast delegate when we add or remove
                // a handler.  This means we can just call the current
                // multi-cast delegate instance, and it is safe to iterate
                // over, even if we get reentered. 
                if (_preProcessInput != null)
                { 
                    preProcessInputEventArgs.Reset(item, this); 

                    // Invoke the handlers in reverse order so that handlers that 
                    // users add are invoked before handlers in the system.
                    Delegate[] handlers = _preProcessInput.GetInvocationList();
                    for(int i = (handlers.Length - 1); i >= 0; i--)
                    { 
                        PreProcessInputEventHandler handler = (PreProcessInputEventHandler) handlers[i];
                        handler(this, preProcessInputEventArgs); 
                    } 
                }
 
                if(!preProcessInputEventArgs.Canceled)
                {
                    // Pre-Notify the input.
                    // 
                    // Because we use multi-cast delegates, we always have to
                    // create a new multi-cast delegate when we add or remove 
                    // a handler.  This means we can just call the current 
                    // multi-cast delegate instance, and it is safe to iterate
                    // over, even if we get reentered. 
                    if(_preNotifyInput != null)
                    {
                        notifyInputEventArgs.Reset(item, this);
 
                        // Invoke the handlers in reverse order so that handlers that
                        // users add are invoked before handlers in the system. 
                        Delegate[] handlers = _preNotifyInput.GetInvocationList(); 
                        for(int i = (handlers.Length - 1); i >= 0; i--)
                        { 
                            NotifyInputEventHandler handler = (NotifyInputEventHandler) handlers[i];
                            handler(this, notifyInputEventArgs);
                        }
                    } 

                    // Raise the input event being processed. 
                    InputEventArgs input = item.Input; 

                    // Some input events are explicitly associated with 
                    // an element.  Those that are not are associated with
                    // the target of the input device for this event.
                    DependencyObject eventSource = input.Source as DependencyObject;
                    if(eventSource == null || !InputElement.IsValid(eventSource as IInputElement)) 
                    {
                        if (input.Device != null) 
                        { 
                            eventSource = input.Device.Target as DependencyObject;
                        } 
                    }

                    if (eventSource != null)
                    { 
                        if(InputElement.IsUIElement(eventSource))
                        { 
                            UIElement e = (UIElement) eventSource; 

                            e.RaiseEvent(input, true ); // Call the "trusted" flavor of RaiseEvent. 
                        }
                        else if(InputElement.IsContentElement(eventSource))
                        {
                            ContentElement ce = (ContentElement) eventSource; 

                            ce.RaiseEvent(input, true );// Call the "trusted" flavor of RaiseEvent. 
                        } 
                        else if (InputElement.IsUIElement3D(eventSource))
                        { 
                            UIElement3D e3D = (UIElement3D) eventSource;

                            e3D.RaiseEvent(input, true); // Call the "trusted" flavor of RaiseEvent
                        } 
                    }
 
                    // Post-Notify the input. 
                    //
                    // Because we use multi-cast delegates, we always have to 
                    // create a new multi-cast delegate when we add or remove
                    // a handler.  This means we can just call the current
                    // multi-cast delegate instance, and it is safe to iterate
                    // over, even if we get reentered. 
                    if(_postNotifyInput != null)
                    { 
                        notifyInputEventArgs.Reset(item, this); 

                        // Invoke the handlers in reverse order so that handlers that 
                        // users add are invoked before handlers in the system.
                        Delegate[] handlers = _postNotifyInput.GetInvocationList();
                        for(int i = (handlers.Length - 1); i >= 0; i--)
                        { 
                            NotifyInputEventHandler handler = (NotifyInputEventHandler) handlers[i];
                            handler(this, notifyInputEventArgs); 
                        } 
                    }
 
                    // Post-Process the input.  This could modify the staging
                    // area.
                    //
                    // Because we use multi-cast delegates, we always have to 
                    // create a new multi-cast delegate when we add or remove
                    // a handler.  This means we can just call the current 
                    // multi-cast delegate instance, and it is safe to iterate 
                    // over, even if we get reentered.
                    if(_postProcessInput != null) 
                    {
                        processInputEventArgs.Reset(item, this);

                        // Invoke the handlers in reverse order so that handlers that 
                        // users add are invoked before handlers in the system.
                        Delegate[] handlers = _postProcessInput.GetInvocationList(); 
                        for(int i = (handlers.Length - 1); i >= 0; i--) 
                        {
                            ProcessInputEventHandler handler = (ProcessInputEventHandler) handlers[i]; 
                            processInputEventArgs.StagingItem.Input.MarkAsUserInitiated();
                            try
                            {
                                handler(this, processInputEventArgs); 
                            }
                            finally // we do this in a finally block in case of exceptions 
                            { 
                                processInputEventArgs.StagingItem.Input.ClearUserInitiated();
                            } 
                        }

                        // PreviewInputReport --> InputReport
                        if(item.Input.RoutedEvent == InputManager.PreviewInputReportEvent) 
                        {
                            if(!item.Input.Handled) 
                            { 
                                InputReportEventArgs previewInputReport = (InputReportEventArgs) item.Input;
 
                                InputReportEventArgs inputReport = new InputReportEventArgs(previewInputReport.Device, previewInputReport.Report);
                                inputReport.RoutedEvent=InputManager.InputReportEvent;
                                PushInput(inputReport, item);
                            } 
                        }
                    } 
 
                    if(input.Handled)
                    { 
                        handled = true;
                    }
                }
            } 

            // Store our input event args so that we can use them again, and 
            // avoid having to allocate more. 
            _notifyInputEventArgs = notifyInputEventArgs;
            _processInputEventArgs = processInputEventArgs; 
            _preProcessInputEventArgs = preProcessInputEventArgs;

            // Make sure to throw away the contents of the event args so
            // we don't keep refs around to things we don't mean to. 
            _notifyInputEventArgs.Reset(null, null);
            _processInputEventArgs.Reset(null, null); 
            _preProcessInputEventArgs.Reset(null, null); 

            return handled; 
        }

        private void RequestContinueProcessingStagingArea()
        { 
            if(!_continueProcessingStagingArea)
            { 
                Dispatcher.BeginInvoke(DispatcherPriority.Input, _continueProcessingStagingAreaCallback, null); 
                _continueProcessingStagingArea = true;
            } 
        }

        private DispatcherOperationCallback _continueProcessingStagingAreaCallback;
        private bool _continueProcessingStagingArea; 

        private NotifyInputEventArgs _notifyInputEventArgs; 
        private ProcessInputEventArgs _processInputEventArgs; 
        private PreProcessInputEventArgs _preProcessInputEventArgs;
 
        //these four events introduced for secutiy purposes
        /// 
        ///     Critical: This code can be used to spoof input considered critical
        ///  
        [SecurityCritical]
        private event PreProcessInputEventHandler _preProcessInput; 
        ///  
        ///     Critical: This code can be used to spoof input considered critical
        ///  
        [SecurityCritical]
        private event NotifyInputEventHandler _preNotifyInput;
        /// 
        ///     Critical: This code can be used to spoof input considered critical 
        /// 
        [SecurityCritical] 
        private event NotifyInputEventHandler _postNotifyInput; 
        /// 
        ///     Critical: This code can be used to spoof input considered critical 
        /// 
        [SecurityCritical]
        private event ProcessInputEventHandler _postProcessInput;
        ///  
        ///     Critical: This code can be used to spoof input considered critical
        ///  
        [SecurityCritical] 
        private event KeyEventHandler _translateAccelerator;
 
        /// 
        ///     This table holds critical data not ok to expose
        /// 
        [SecurityCritical] 
        private Hashtable _inputProviders = new Hashtable();
 
        private KeyboardDevice _primaryKeyboardDevice; 
        private MouseDevice    _primaryMouseDevice;
        private CommandDevice  _primaryCommandDevice; 

        /// 
        ///     Critical to prevent accidental spread to transparent code
        ///  
        [SecurityCritical]
        private StylusLogic     _stylusLogic; 
 
        private bool            _inDragDrop;
 
        private DispatcherOperationCallback _hitTestInvalidatedAsyncCallback;
        private DispatcherOperation _hitTestInvalidatedAsyncOperation;
        private EventHandler _layoutUpdatedCallback;
 
        /// 
        ///     Critical: This stack holds all the input events and can be used 
        ///     to spoof or force arbitrary input 
        /// 
        [SecurityCritical] 
        private Stack _stagingArea;

        private InputDevice _mostRecentInputDevice;
 
        // Timer used to synchronize the input devices periodically
        private DispatcherTimer _inputTimer; 
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
using System.Collections; 
using System.Windows.Threading;
using System.Threading;
using System.Windows;
using System.Security; 
using System.Security.Permissions;
using MS.Win32; 
using MS.Internal; 
using MS.Internal.PresentationCore;                        // SecurityHelper
using System; 
using System.Diagnostics;

using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID; 

namespace System.Windows.Input 
{ 
    /// 
    ///     The InputManager class is responsible for coordinating all of the 
    ///     input system in Avalon.
    /// 
    public sealed class InputManager : DispatcherObject
    { 
        /// 
        ///     A routed event indicating that an input report arrived. 
        ///  
        internal static readonly RoutedEvent PreviewInputReportEvent = GlobalEventManager.RegisterRoutedEvent("PreviewInputReport", RoutingStrategy.Tunnel, typeof(InputReportEventHandler), typeof(InputManager));
 
        /// 
        ///     A routed event indicating that an input report arrived.
        /// 
        [FriendAccessAllowed] 
        internal static readonly RoutedEvent InputReportEvent = GlobalEventManager.RegisterRoutedEvent("InputReport", RoutingStrategy.Bubble, typeof(InputReportEventHandler), typeof(InputManager));
 
        ///  
        ///     Return the input manager associated with the current context.
        ///  
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        /// 
        ///  
        ///     Critical: This class is not ok to expose since SEE apps
        ///               should not have to deal with this directly and 
        ///               it exposes methods that can be use for input spoofing 
        ///
        ///  
        public static InputManager Current
        {
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            get
            { 
                return GetCurrentInputManagerImpl(); 
            }
        } 

        ///
        ///     Internal implementation of InputManager.Current.
        ///     Critical but not TAS - for internal's to use. 
        ///     Only exists for perf. The link demand check was causing perf in some XAF scenarios.
        /// 
        ///  
        ///     Critical: This class is not ok to expose since SEE apps
        ///               should not have to deal with this directly and 
        ///               it exposes methods that can be use for input spoofing
        ///
        /// 
        internal static InputManager UnsecureCurrent 
        {
            [SecurityCritical] 
            [FriendAccessAllowed] 
            get
            { 
                return GetCurrentInputManagerImpl();
            }
        }
 
        ///
        ///     Implementation of InputManager.Current 
        /// 
        /// 
        ///     Critical: This class is not ok to expose since SEE apps 
        ///               should not have to deal with this directly and
        ///               it exposes methods that can be use for input spoofing
        /// 
        [SecurityCritical] 
        private static InputManager GetCurrentInputManagerImpl()
        { 
            InputManager inputManager = null; 

            Dispatcher dispatcher = Dispatcher.CurrentDispatcher; 
            inputManager = dispatcher.InputManager as InputManager;

            if (inputManager == null)
            { 
                inputManager = new InputManager();
                dispatcher.InputManager = inputManager; 
            } 

            return inputManager; 
        }

        /// 
        ///     Critical: This code causes critical data (inputmanager to be instantiated) 
        ///     This class should not be exposed in the SEE as it can be used for input spoofing.
        ///  
        [SecurityCritical] 
        private InputManager()
        { 
            // STA Requirement
            //
            // Avalon doesn't necessarily require STA, but many components do.  Examples
            // include Cicero, OLE, COM, etc.  So we throw an exception here if the 
            // thread is not STA.
            if(Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) 
            { 
                throw new InvalidOperationException(SR.Get(SRID.RequiresSTA));
            } 

            _stagingArea = new Stack();

            _primaryKeyboardDevice = new Win32KeyboardDevice(this); 
            _primaryMouseDevice = new Win32MouseDevice(this);
            _primaryCommandDevice = new CommandDevice(this); 
 
            _stylusLogic = new StylusLogic(this);
 
            _continueProcessingStagingAreaCallback = new DispatcherOperationCallback(ContinueProcessingStagingArea);

            _hitTestInvalidatedAsyncOperation = null;
            _hitTestInvalidatedAsyncCallback = new DispatcherOperationCallback(HitTestInvalidatedAsyncCallback); 

            _layoutUpdatedCallback = new EventHandler(OnLayoutUpdated); //need to cache it, LM only keeps weak ref 
            ContextLayoutManager.From(Dispatcher).LayoutEvents.Add(_layoutUpdatedCallback); 

            // Timer used to synchronize the input devices periodically 
            _inputTimer = new DispatcherTimer(DispatcherPriority.Background);
            _inputTimer.Tick += new EventHandler(ValidateInputDevices);
            _inputTimer.Interval = TimeSpan.FromMilliseconds(125);
        } 

        ///  
        ///  
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        /// 
        ///     Critical: This event lets people subscribe to all events in the system
        ///     PublicOk: Method is link demanded.
        ///  
        public event PreProcessInputEventHandler PreProcessInput
        { 
            [SecurityCritical ] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            add 
            {
                _preProcessInput += value;
            }
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove 
            { 
                _preProcessInput -= value;
            } 
        }


        ///  
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        ///  
        /// 
        ///     Critical: This event lets people subscribe to all events in the system 
        ///     Not safe to expose.
        ///     PublicOk: Method is link demanded.
        /// 
        public event NotifyInputEventHandler PreNotifyInput 
        {
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            add
            { 
                _preNotifyInput += value;
            }
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            remove
            { 
                _preNotifyInput -= value; 
            }
 
        }
        /// 
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API. 
        /// 
        ///  
        ///     Critical: This event lets people subscribe to all events in the system 
        ///     Not safe to expose.
        ///     PublicOk: Method is link demanded. 
        /// 
        public event NotifyInputEventHandler PostNotifyInput
        {
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            add 
            { 
                _postNotifyInput += value;
            } 
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove
            { 
                _postNotifyInput -= value;
 
            } 
        }
 
        /// 
        /// 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        /// 
        ///     Critical: This event lets people subscribe to all events in the system 
        ///     Not safe to expose. 
        ///     PublicOk: Method is link demanded.
        ///  
        public event ProcessInputEventHandler PostProcessInput
        {
            [SecurityCritical]
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
            add
            { 
                _postProcessInput += value; 
            }
            [SecurityCritical] 
            [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)]
            remove
            {
                _postProcessInput -= value; 
            }
        } 
 
        /// 
        /// This event is raised by the HwndSource.CriticalTranslateAccelerator 
        /// on descendent HwndSource instances. The only subscriber to this event
        /// is KeyboardNavigation.
        /// 
        ///  
        ///     Critical: This event lets people subscribe to all input notifications
        ///  
        internal event KeyEventHandler TranslateAccelerator 
        {
            [FriendAccessAllowed] // Used by KeyboardNavigation.cs in Framework 
            [SecurityCritical]
            add
            {
                _translateAccelerator += value; 
            }
            [FriendAccessAllowed] // Used by KeyboardNavigation.cs in Framework 
            [SecurityCritical] 
            remove
            { 
                _translateAccelerator -= value;
            }
        }
 
        /// 
        /// Raises the TranslateAccelerator event 
        ///  
        /// 
        ///     Critical: Accesses critical _translateAccelerator. 
        /// 
        [SecurityCritical]
        internal void RaiseTranslateAccelerator(KeyEventArgs e)
        { 
            if (_translateAccelerator != null)
            { 
                _translateAccelerator(this, e); 
            }
        } 

        /// 
        ///     Registers an input provider with the input manager.
        ///  
        /// 
        ///     The input provider to register. 
        ///  
        /// 
        ///     This class will not be available in internet zone. 
        ///     Critical: This code acceses and stores critical data (InputProvider)
        ///     TreatAsSafe: This code demands UIPermission.
        /// 
        [SecurityCritical] 
        internal InputProviderSite RegisterInputProvider(IInputProvider inputProvider)
        { 
            SecurityHelper.DemandUnrestrictedUIPermission(); 
//             VerifyAccess();
 

            // Create a site for this provider, and keep track of it.
            InputProviderSite site = new InputProviderSite(this, inputProvider);
            _inputProviders[inputProvider] = site; 

            return site; 
        } 

        ///  
        ///     This class will not be available in internet zone.
        ///     Critical: This code acceses critical data in the form of InputProvider
        /// 
        [SecurityCritical] 
        internal void UnregisterInputProvider(IInputProvider inputProvider)
        { 
            _inputProviders.Remove(inputProvider); 
        }
 
        /// 
        ///     Returns a collection of input providers registered with the input manager.
        /// 
        ///  
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        ///  
        ///     This class will not be available in internet zone.
        ///     Critical: This code exposes InputProviders which are 
        ///               considered as critical data
        ///     PublicOK: This code has a demand on it
        /// 
        public ICollection InputProviders 
        {
            [SecurityCritical] 
            get 
            {
                SecurityHelper.DemandUnrestrictedUIPermission(); 
                return UnsecureInputProviders;
            }
        }
 

        ///  
        ///     Returns a collection of input providers registered with the input manager. 
        /// 
        ///  
        ///     Critical: This code exposes InputProviders which are considered as critical data
        ///     This overload exists for perf. improvements in the internet zone since this function is called
        ///     quite often
        ///  
        internal ICollection UnsecureInputProviders
        { 
            [SecurityCritical] 
            get
            { 
                return _inputProviders.Keys;
            }
        }
        ///  
        ///     Read-only access to the primary keyboard device.
        ///  
        public KeyboardDevice PrimaryKeyboardDevice 
        {
            // 
            get {return _primaryKeyboardDevice;}
        }

        ///  
        ///     Read-only access to the primary mouse device.
        ///  
        public MouseDevice PrimaryMouseDevice 
        {
            // 
            get {return _primaryMouseDevice;}
        }
        /// 
        ///     Critical, accesses critical member _stylusLogic 
        /// 
        internal StylusLogic StylusLogic 
        { 
            [SecurityCritical, FriendAccessAllowed]
            get { return _stylusLogic; } 
        }

        /// 
        ///     Read-only access to the primary keyboard device. 
        /// 
        internal CommandDevice PrimaryCommandDevice 
        { 
            get {return _primaryCommandDevice;}
        } 

        /// 
        ///     The InDragDrop property represents whether we are currently inside
        ///     a OLE DragDrop operation. 
        /// 
        internal bool InDragDrop 
        { 
            get { return _inDragDrop; }
            set { _inDragDrop = value; } 
        }

        /// 
        ///     The MostRecentInputDevice represents the last input device to 
        ///     report an "interesting" user action.  What exactly constitutes
        ///     such an action is up to each device to implement. 
        ///  
        public InputDevice MostRecentInputDevice
        { 
            get { return _mostRecentInputDevice; }
            internal set { _mostRecentInputDevice = value; }
        }
 
        /// 
        ///     An event that is raised whenever the result of a hit-test may 
        ///     have changed. 
        /// 
        public event EventHandler HitTestInvalidatedAsync; 

        internal void NotifyHitTestInvalidated()
        {
            // The HitTest result may have changed for someone somewhere. 
            // Raise the HitTestInvalidatedAsync event after the next layout.
            if(_hitTestInvalidatedAsyncOperation == null) 
            { 
                // It would be best to re-evaluate anything dependent on the hit-test results
                // immediately after layout & rendering are complete.  Unfortunately this can 
                // lead to an infinite loop.  Consider the following scenario:
                //
                // If the mouse is over an element, hide it.
                // 
                // This never resolves to a "correct" state.  When the mouse moves over the
                // element, the element is hidden, so the mouse is no longer over it, so the 
                // element is shown, but that means the mouse is over it again.  Repeat. 
                //
                // We push our re-evaluation to a priority lower than input processing so that 
                // the user can change the input device to avoid the infinite loops, or close
                // the app if nothing else works.
                //
                _hitTestInvalidatedAsyncOperation = Dispatcher.BeginInvoke(DispatcherPriority.Input, 
                                                                        _hitTestInvalidatedAsyncCallback,
                                                                        null); 
            } 
            else if (_hitTestInvalidatedAsyncOperation.Priority == DispatcherPriority.Inactive)
            { 
                // This means that we are currently waiting for the timer to expire so
                // that we can promote the current queue item to Input prority. Since
                // we are now being told that we need to re-hittest, we simply stop the
                // timer and promote the queue item right now instead of waiting for expiry. 

                ValidateInputDevices(this, EventArgs.Empty); 
            } 
        }
 

        ///
        ///     Critical - calls UnsecureCurrent
        ///     TreatAsSafe - notifying the input manager that hit test information needs to be recalced. 
        ///                   is considered safe ( and currently this code is transparent).
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static void SafeCurrentNotifyHitTestInvalidated()
        { 
            UnsecureCurrent.NotifyHitTestInvalidated();
        }

        private object HitTestInvalidatedAsyncCallback(object arg) 
        {
            _hitTestInvalidatedAsyncOperation = null; 
            if (HitTestInvalidatedAsync != null) 
            {
                HitTestInvalidatedAsync(this, EventArgs.Empty); 
            }

            return null;
        } 

        private void OnLayoutUpdated(object sender, EventArgs e) 
        { 
            NotifyHitTestInvalidated();
        } 

        /// 
        /// Start the timer that will kick off synchronize
        /// operation on all the input devices upon expiry 
        /// 
        internal void InvalidateInputDevices() 
        { 
            // If there is no pending ansyc hittest operation
 
            if (_hitTestInvalidatedAsyncOperation == null)
            {
                // Post an inactive item to the queue. When the timer expires
                // we will promote this queue item to Input priority. 

                _hitTestInvalidatedAsyncOperation = Dispatcher.BeginInvoke(DispatcherPriority.Inactive, 
                                                                        _hitTestInvalidatedAsyncCallback, 
                                                                        null);
 
                // Start the input timer

                _inputTimer.IsEnabled = true;
            } 
        }
 
        ///  
        /// Synchronize the  input devices
        ///  
        private void ValidateInputDevices(object sender, EventArgs e)
        {
            Debug.Assert(_hitTestInvalidatedAsyncOperation != null &&
                         _hitTestInvalidatedAsyncOperation.Priority == DispatcherPriority.Inactive && 
                         _inputTimer.IsEnabled == true);
 
            // Promote the pending DispatcherOperation to Input Priority 

            _hitTestInvalidatedAsyncOperation.Priority = DispatcherPriority.Input; 

            // Stop the input timer

            _inputTimer.IsEnabled = false; 
        }
 
        ///  
        ///     Synchronously processes the specified input.
        ///  
        /// 
        ///     The specified input is processed by all of the filters and
        ///     monitors, and is finally dispatched to the appropriate
        ///     element as an input event. 
        ///     Callers must have UIPermission(PermissionState.Unrestricted) to call this API.
        ///  
        ///  
        ///     Whether or not any event generated as a consequence of this
        ///     event was handled. 
        /// 
        /// 
        ///     Critical: This code can cause input to be processed.
        ///     PublicOK: This code link demands. 
        /// 
        [SecurityCritical] 
        [UIPermissionAttribute(SecurityAction.LinkDemand,Unrestricted=true)] 
        public bool ProcessInput(InputEventArgs input)
        { 
//             VerifyAccess();

            if(input == null)
            { 
                throw new ArgumentNullException("input");
            } 
 
            // Push a marker indicating the portion of the staging area
            // that needs to be processed. 
            PushMarker();

            // Push the input to be processed onto the staging area.
            PushInput(input, null); 

            // Post a work item to continue processing the staging area 
            // in case someone pushes a dispatcher frame in the middle 
            // of input processing.
            RequestContinueProcessingStagingArea(); 

            // Now drain the staging area up to the marker we pushed.
            bool handled = ProcessStagingArea();
            return handled; 
        }
 
        /// 
        /// Critical - accesses critical data ( _stagingArea)
        /// 
        [SecurityCritical]
        internal StagingAreaInputItem PushInput(StagingAreaInputItem inputItem)
        {
            _stagingArea.Push(inputItem); 
            return inputItem;
        } 
 
        ///
        /// Critical - accesses critical function ( PushInput) 
        ///
        [SecurityCritical]
        internal StagingAreaInputItem PushInput(InputEventArgs input, StagingAreaInputItem promote)
        { 
            StagingAreaInputItem item = new StagingAreaInputItem(false);
            item.Reset(input, promote); 
 
            return PushInput(item);
        } 

        ///
        /// Critical - calls a critical function ( PushInput).
        /// 
        [SecurityCritical]
        internal StagingAreaInputItem PushMarker() 
        { 
            StagingAreaInputItem item = new StagingAreaInputItem(true);
 
            return PushInput(item);
        }

        /// 
        /// Critical - accesses critical data _stagingArea.
        /// 
        [SecurityCritical] 
        internal StagingAreaInputItem PopInput()
        { 
            object input = null;

            if(_stagingArea.Count > 0)
            { 
                input = _stagingArea.Pop();
            } 
 
            return input as StagingAreaInputItem;
        } 


        ///
        /// Critical - accesses the _stagingArea critical data. 
        ///
        [SecurityCritical] 
        internal StagingAreaInputItem PeekInput() 
        {
            object input = null; 

            if(_stagingArea.Count > 0)
            {
                input = _stagingArea.Peek(); 
            }
 
            return input as StagingAreaInputItem; 
        }
 
        ///
        /// Critical - accesses critical data ( _stagingArea.Count) and calls a critical function - ProcessStagingArea
        ///
        [SecurityCritical ] 
        internal object ContinueProcessingStagingArea(object unused)
        { 
            _continueProcessingStagingArea = false; 

            // It is possible that we can be re-entered by a nested 
            // dispatcher frame.  Continue processing the staging
            // area if we need to.
            if(_stagingArea.Count > 0)
            { 
                // Before we actually start to drain the staging area, we need
                // to post a work item to process more input.  This enables us 
                // to process more input if we enter a nested pump. 
                RequestContinueProcessingStagingArea();
 
                // Now synchronously drain the staging area.
                ProcessStagingArea();
            }
 
            return null;
        } 
 
        ///
        /// Critical: accesses critical data ( PopInput()) and raises events with the user-initiated flag 
        ///
        [SecurityCritical]
        private bool ProcessStagingArea()
        { 
            bool handled = false;
 
            // For performance reasons, try to reuse the input event args. 
            // If we are reentrered, we have to start over with fresh event
            // args, so we clear the member variables before continuing. 
            // Also, we cannot simply make an single instance of the
            // PreProcessedInputEventArgs and cast it to NotifyInputEventArgs
            // or ProcessInputEventArgs because a malicious user could upcast
            // the object and call inappropriate methods. 
            NotifyInputEventArgs notifyInputEventArgs = (_notifyInputEventArgs != null) ? _notifyInputEventArgs : new NotifyInputEventArgs();
            ProcessInputEventArgs processInputEventArgs = (_processInputEventArgs != null) ? _processInputEventArgs : new ProcessInputEventArgs(); 
            PreProcessInputEventArgs preProcessInputEventArgs = (_preProcessInputEventArgs != null) ? _preProcessInputEventArgs : new PreProcessInputEventArgs(); 
            _notifyInputEventArgs = null;
            _processInputEventArgs = null; 
            _preProcessInputEventArgs = null;

            // Because we can be reentered, we can't just enumerate over the
            // staging area - that could throw an exception if the queue 
            // changes underneath us.  Instead, just loop until we find a
            // frame marker or until the staging area is empty. 
            StagingAreaInputItem item = null; 
            while((item = PopInput()) != null)
            { 
                // If we found a marker, we have reached the end of a
                // "section" of the staging area.  We just return from
                // the synchronous processing of the staging area.
                // If a dispatcher frame has been pushed by someone, this 
                // will not return to the original ProcessInput.  Instead
                // it will unwind to the dispatcher and since we have 
                // already pushed a work item to continue processing the 
                // input, it will simply call back into us to do more
                // processing.  At which point we will continue to drain 
                // the staging area.  This could cause strage behavior,
                // but it is deemed more acceptable than stalling input
                // processing.
                // 

 
 

                if(item.IsMarker) 
                {
                    break;
                }
 
                // Pre-Process the input.  This could modify the staging
                // area, and it could cancel the processing of this 
                // input event. 
                //
                // Because we use multi-cast delegates, we always have to 
                // create a new multi-cast delegate when we add or remove
                // a handler.  This means we can just call the current
                // multi-cast delegate instance, and it is safe to iterate
                // over, even if we get reentered. 
                if (_preProcessInput != null)
                { 
                    preProcessInputEventArgs.Reset(item, this); 

                    // Invoke the handlers in reverse order so that handlers that 
                    // users add are invoked before handlers in the system.
                    Delegate[] handlers = _preProcessInput.GetInvocationList();
                    for(int i = (handlers.Length - 1); i >= 0; i--)
                    { 
                        PreProcessInputEventHandler handler = (PreProcessInputEventHandler) handlers[i];
                        handler(this, preProcessInputEventArgs); 
                    } 
                }
 
                if(!preProcessInputEventArgs.Canceled)
                {
                    // Pre-Notify the input.
                    // 
                    // Because we use multi-cast delegates, we always have to
                    // create a new multi-cast delegate when we add or remove 
                    // a handler.  This means we can just call the current 
                    // multi-cast delegate instance, and it is safe to iterate
                    // over, even if we get reentered. 
                    if(_preNotifyInput != null)
                    {
                        notifyInputEventArgs.Reset(item, this);
 
                        // Invoke the handlers in reverse order so that handlers that
                        // users add are invoked before handlers in the system. 
                        Delegate[] handlers = _preNotifyInput.GetInvocationList(); 
                        for(int i = (handlers.Length - 1); i >= 0; i--)
                        { 
                            NotifyInputEventHandler handler = (NotifyInputEventHandler) handlers[i];
                            handler(this, notifyInputEventArgs);
                        }
                    } 

                    // Raise the input event being processed. 
                    InputEventArgs input = item.Input; 

                    // Some input events are explicitly associated with 
                    // an element.  Those that are not are associated with
                    // the target of the input device for this event.
                    DependencyObject eventSource = input.Source as DependencyObject;
                    if(eventSource == null || !InputElement.IsValid(eventSource as IInputElement)) 
                    {
                        if (input.Device != null) 
                        { 
                            eventSource = input.Device.Target as DependencyObject;
                        } 
                    }

                    if (eventSource != null)
                    { 
                        if(InputElement.IsUIElement(eventSource))
                        { 
                            UIElement e = (UIElement) eventSource; 

                            e.RaiseEvent(input, true ); // Call the "trusted" flavor of RaiseEvent. 
                        }
                        else if(InputElement.IsContentElement(eventSource))
                        {
                            ContentElement ce = (ContentElement) eventSource; 

                            ce.RaiseEvent(input, true );// Call the "trusted" flavor of RaiseEvent. 
                        } 
                        else if (InputElement.IsUIElement3D(eventSource))
                        { 
                            UIElement3D e3D = (UIElement3D) eventSource;

                            e3D.RaiseEvent(input, true); // Call the "trusted" flavor of RaiseEvent
                        } 
                    }
 
                    // Post-Notify the input. 
                    //
                    // Because we use multi-cast delegates, we always have to 
                    // create a new multi-cast delegate when we add or remove
                    // a handler.  This means we can just call the current
                    // multi-cast delegate instance, and it is safe to iterate
                    // over, even if we get reentered. 
                    if(_postNotifyInput != null)
                    { 
                        notifyInputEventArgs.Reset(item, this); 

                        // Invoke the handlers in reverse order so that handlers that 
                        // users add are invoked before handlers in the system.
                        Delegate[] handlers = _postNotifyInput.GetInvocationList();
                        for(int i = (handlers.Length - 1); i >= 0; i--)
                        { 
                            NotifyInputEventHandler handler = (NotifyInputEventHandler) handlers[i];
                            handler(this, notifyInputEventArgs); 
                        } 
                    }
 
                    // Post-Process the input.  This could modify the staging
                    // area.
                    //
                    // Because we use multi-cast delegates, we always have to 
                    // create a new multi-cast delegate when we add or remove
                    // a handler.  This means we can just call the current 
                    // multi-cast delegate instance, and it is safe to iterate 
                    // over, even if we get reentered.
                    if(_postProcessInput != null) 
                    {
                        processInputEventArgs.Reset(item, this);

                        // Invoke the handlers in reverse order so that handlers that 
                        // users add are invoked before handlers in the system.
                        Delegate[] handlers = _postProcessInput.GetInvocationList(); 
                        for(int i = (handlers.Length - 1); i >= 0; i--) 
                        {
                            ProcessInputEventHandler handler = (ProcessInputEventHandler) handlers[i]; 
                            processInputEventArgs.StagingItem.Input.MarkAsUserInitiated();
                            try
                            {
                                handler(this, processInputEventArgs); 
                            }
                            finally // we do this in a finally block in case of exceptions 
                            { 
                                processInputEventArgs.StagingItem.Input.ClearUserInitiated();
                            } 
                        }

                        // PreviewInputReport --> InputReport
                        if(item.Input.RoutedEvent == InputManager.PreviewInputReportEvent) 
                        {
                            if(!item.Input.Handled) 
                            { 
                                InputReportEventArgs previewInputReport = (InputReportEventArgs) item.Input;
 
                                InputReportEventArgs inputReport = new InputReportEventArgs(previewInputReport.Device, previewInputReport.Report);
                                inputReport.RoutedEvent=InputManager.InputReportEvent;
                                PushInput(inputReport, item);
                            } 
                        }
                    } 
 
                    if(input.Handled)
                    { 
                        handled = true;
                    }
                }
            } 

            // Store our input event args so that we can use them again, and 
            // avoid having to allocate more. 
            _notifyInputEventArgs = notifyInputEventArgs;
            _processInputEventArgs = processInputEventArgs; 
            _preProcessInputEventArgs = preProcessInputEventArgs;

            // Make sure to throw away the contents of the event args so
            // we don't keep refs around to things we don't mean to. 
            _notifyInputEventArgs.Reset(null, null);
            _processInputEventArgs.Reset(null, null); 
            _preProcessInputEventArgs.Reset(null, null); 

            return handled; 
        }

        private void RequestContinueProcessingStagingArea()
        { 
            if(!_continueProcessingStagingArea)
            { 
                Dispatcher.BeginInvoke(DispatcherPriority.Input, _continueProcessingStagingAreaCallback, null); 
                _continueProcessingStagingArea = true;
            } 
        }

        private DispatcherOperationCallback _continueProcessingStagingAreaCallback;
        private bool _continueProcessingStagingArea; 

        private NotifyInputEventArgs _notifyInputEventArgs; 
        private ProcessInputEventArgs _processInputEventArgs; 
        private PreProcessInputEventArgs _preProcessInputEventArgs;
 
        //these four events introduced for secutiy purposes
        /// 
        ///     Critical: This code can be used to spoof input considered critical
        ///  
        [SecurityCritical]
        private event PreProcessInputEventHandler _preProcessInput; 
        ///  
        ///     Critical: This code can be used to spoof input considered critical
        ///  
        [SecurityCritical]
        private event NotifyInputEventHandler _preNotifyInput;
        /// 
        ///     Critical: This code can be used to spoof input considered critical 
        /// 
        [SecurityCritical] 
        private event NotifyInputEventHandler _postNotifyInput; 
        /// 
        ///     Critical: This code can be used to spoof input considered critical 
        /// 
        [SecurityCritical]
        private event ProcessInputEventHandler _postProcessInput;
        ///  
        ///     Critical: This code can be used to spoof input considered critical
        ///  
        [SecurityCritical] 
        private event KeyEventHandler _translateAccelerator;
 
        /// 
        ///     This table holds critical data not ok to expose
        /// 
        [SecurityCritical] 
        private Hashtable _inputProviders = new Hashtable();
 
        private KeyboardDevice _primaryKeyboardDevice; 
        private MouseDevice    _primaryMouseDevice;
        private CommandDevice  _primaryCommandDevice; 

        /// 
        ///     Critical to prevent accidental spread to transparent code
        ///  
        [SecurityCritical]
        private StylusLogic     _stylusLogic; 
 
        private bool            _inDragDrop;
 
        private DispatcherOperationCallback _hitTestInvalidatedAsyncCallback;
        private DispatcherOperation _hitTestInvalidatedAsyncOperation;
        private EventHandler _layoutUpdatedCallback;
 
        /// 
        ///     Critical: This stack holds all the input events and can be used 
        ///     to spoof or force arbitrary input 
        /// 
        [SecurityCritical] 
        private Stack _stagingArea;

        private InputDevice _mostRecentInputDevice;
 
        // Timer used to synchronize the input devices periodically
        private DispatcherTimer _inputTimer; 
    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK