WinEventWrap.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / UIAutomation / UIAutomationClient / MS / Internal / Automation / WinEventWrap.cs / 1 / WinEventWrap.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: Lightweight class to wrap Win32 WinEvents. 
//
// History: 
//  06/17/2003 : BrendanM Ported to WCP
//
//---------------------------------------------------------------------------
 
// PRESHARP: In order to avoid generating warnings about unkown message numbers and unknown pragmas.
#pragma warning disable 1634, 1691 
 
using System;
using System.Security; 
using System.Collections;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Diagnostics; 
using MS.Win32;
 
namespace MS.Internal.Automation 
{
    // Lightweight class to wrap Win32 WinEvents.  Users of this class would 
    // inherit from WinEventWrap do the following:
    //   1. Call the base constructor with a params array of event identifiers
    //   2. Override WinEventProc to provide an implementation.
    internal class WinEventWrap 
    {
        //----------------------------------------------------- 
        // 
        //  Constructors
        // 
        //-----------------------------------------------------

        #region Constructors
 
        // ctor that takes an array of events
        internal WinEventWrap(int [] eventIds) 
        { 
            Debug.Assert(eventIds != null && eventIds.Length > 0, "eventIds is invalid");
            _eventIds = (int [])eventIds.Clone(); 
            _hHooks = new IntPtr[_eventIds.Length];
            Init();
        }
 
        ~WinEventWrap()
        { 
            Clear(); 
        }
 
        #endregion Constructors


        //------------------------------------------------------ 
        //
        //  Internal Methods 
        // 
        //-----------------------------------------------------
 
        #region Internal Methods

        internal virtual void WinEventProc(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        { 
            // override this to provide an implementation
        } 
 
        internal void Clear()
        { 
            StopListening();
            lock(this)
            {
                _clientCallbacks.Clear (); 
            }
            if (_gchThis.IsAllocated) 
            { 
                _gchThis.Free();
            } 
        }

        internal void AddCallback(object clientCallback)
        { 
            lock (this)
            { 
                _clientCallbacks.Add(clientCallback); 
            }
        } 

        internal bool RemoveCallback(object clientCallback)
        {
            if (clientCallback == null) 
                return true;    // temp until cleanup of WinEvent code is complete
 
            bool listIsEmpty = true; 
            lock (this)
            { 
                if (_clientCallbacks.Count == 0)
                    return true;

                // remove a specific callback 
                _clientCallbacks.Remove(clientCallback);
                listIsEmpty = (_clientCallbacks.Count == 0); 
            } 

            return listIsEmpty; 
        }

        // install WinEvent hook and start getting the callback.
        ///  
        /// Critical - as this calls SetWinEventHook which has a SUC on it and also calls
        ///            the setter for _hHook. 
        /// Safe - as this does not allow an arbitrary method to be set up as a hook and 
        ///        also doesn't allow an aribtrary value to be set on _hHook.Value.
        ///  
        internal void StartListening()
        {
            _fBusy = true;
 
            int i = 0;
            foreach (int eventId in _eventIds) 
            { 
                // There is no indication in the Windows SDK documentation that SetWinEventHook()
                // will set an error to be retrieved with GetLastError, so set the pragma to ignore 
                // the PERSHARP warning.
#pragma warning suppress 6523
                _hHooks[i] = UnsafeNativeMethods.SetWinEventHook(eventId, eventId, IntPtr.Zero, _winEventProc, 0, 0, _fFlags);
                if (_hHooks[i] == IntPtr.Zero) 
                {
                    StopListening(); 
                    throw new Win32Exception(); 
                }
                i++; 
            }
            _fBusy = false;
        }
 
        internal void StopListening()
        { 
            // ASSUMPTION: Before StopListening is called, all callback delegates have been removed 
            // so that any events received while hooks are being removed become noops (since there's
            // no handlers for them to call). 
            _fBusy = true;

            for (int i=0;i<_hHooks.Length;i++)
            { 
                if (_hHooks[i] != IntPtr.Zero)
                { 
                    // There is no indication in the Windows SDK documentation that UnhookWinEvent() 
                    // will set an error to be retrieved with GetLastError, so set the pragma to ignore
                    // the PERSHARP warning. 
#pragma warning suppress 6523
                    UnsafeNativeMethods.UnhookWinEvent(_hHooks[i]);
                    _hHooks[i] = IntPtr.Zero;
                } 
            }
            if (_qEvents != null) 
            { 
                _qEvents.Clear();
            } 
            _fBusy = false;
        }

        // Handlers may make a call to another process so don't want to lock around code that protects _clientCallbacks. 
        // Instead, grab the callbacks w/in a lock then call them outside of the lock.  This technique has potential for
        // error if (for instance) handler A could remove both itself and handler B but for now don't need to worry 
        // about handlers getting out of [....] with the ones in the master array because the callbacks are defined w/in 
        // this code and the handler for new UI doesn't remove itself (or anyone else).
        internal object[] GetHandlers() 
        {
            lock (this)
            {
                object[] handlers = new object[_clientCallbacks.Count]; 
                for (int i = 0; i < _clientCallbacks.Count; i++)
                    handlers[i] = _clientCallbacks[i]; 
 
                return handlers;
            } 
        }

        #endregion Internal Methods
 

        //------------------------------------------------------ 
        // 
        //  Private Methods
        // 
        //------------------------------------------------------

        #region Private Methods
 
        // queue winevents so that the get processed in the order we receive them. If we just
        // dispatch them as we get them, we'll end up getting some _while_ we are processing others, 
        // and end up completing those events out of order, making the event order appear backwards. 
        // This code checks whether we are currently processing an event, and if so, queues it so that
        // we process it when we're done with the current event. 
        private void WinEventReentrancyFilter(int winEventHook, int eventId, IntPtr hwnd, int idObject, int idChild, int eventThread, uint eventTime)
        {
            if ( _fBusy )
            { 
                _qEvents.Enqueue(new WinEvent(eventId, hwnd, idObject, idChild, eventTime));
            } 
            else 
            {
                _fBusy = true; 
                try
                {
                    PreWinEventProc(eventId, hwnd, idObject, idChild, eventTime); // deliver this event
                } 
                catch( Exception e )
                { 
                    if( Misc.IsCriticalException( e ) ) 
                        throw;
 
                    // ignore exceptions for now since we've no way to let clients add exception handlers
                }

                while (_qEvents.Count > 0) 
                {
                    WinEvent e = (WinEvent)_qEvents.Dequeue(); // process queued events 
                    try 
                    {
                        PreWinEventProc(e._eventId, e._hwnd, e._idObject, e._idChild, e._eventTime); 
                    }
                    catch( Exception ex )
                    {
                        if( Misc.IsCriticalException( ex ) ) 
                            throw;
 
                        // ignore exceptions for now since we've no way to let clients add exception handlers 
                    }
                } 
                _fBusy = false;
            }
        }
 
        private void PreWinEventProc(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        { 
            // Ignore events from the UIA->MSAA bridge: these are recognizable as having 
            // >0 idObject, and the target HWND having a UIA impl.
            if (idObject > 0) 
            {
                if (UiaCoreApi.UiaHasServerSideProvider(hwnd))
                {
                    // Bridge event - ignore it. 
                    return;
                } 
            } 

            // 0 is used as a marker value elsewhere, so bump up to 1 
            if(eventTime == 0)
            {
                eventTime = 1;
            } 
            WinEventProc(eventId, hwnd, idObject, idChild, eventTime);
        } 
 

        private void Init() 
        {
            // Keep the garbage collector from moving things around
            _winEventProc = new NativeMethods.WinEventProcDef(WinEventReentrancyFilter);
            _gchThis = GCHandle.Alloc(_winEventProc); 

            _clientCallbacks = new ArrayList(2); 
            _qEvents = new Queue(16, (float)2.0); // (initial cap, growth factor) 
            _fFlags = NativeMethods.WINEVENT_OUTOFCONTEXT;
        } 

        #endregion Private Methods

 
        //-----------------------------------------------------
        // 
        //  Private Fields 
        //
        //------------------------------------------------------ 

        #region Private Fields

        private class WinEvent 
        {
            internal WinEvent(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime) 
            { 
                _eventId = eventId;
                _hwnd = hwnd; 
                _idObject = idObject;
                _idChild = idChild;
                _eventTime = eventTime;
            } 
            public int _eventId;
            public IntPtr _hwnd; 
            public int _idObject; 
            public int _idChild;
            public uint _eventTime; 
        }

        private Queue _qEvents;                // Queue of events waiting to be processed
        private int [] _eventIds;              // the WinEvent(s) this instance is handling 
        private IntPtr [] _hHooks;             // the returned handles(s) from SetWinEventHook
        private bool _fBusy;                   // Flag indicating if we're busy processing 
        private int _fFlags;                   // SetWinEventHook flags 
        private GCHandle _gchThis;             // GCHandle to keep GCs from moving this callback
        private NativeMethods.WinEventProcDef _winEventProc; // the callback handed to USER for WinEvents 
        protected ArrayList _clientCallbacks;  // the client callback interface objects

        #endregion Private Fields
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: Lightweight class to wrap Win32 WinEvents. 
//
// History: 
//  06/17/2003 : BrendanM Ported to WCP
//
//---------------------------------------------------------------------------
 
// PRESHARP: In order to avoid generating warnings about unkown message numbers and unknown pragmas.
#pragma warning disable 1634, 1691 
 
using System;
using System.Security; 
using System.Collections;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Diagnostics; 
using MS.Win32;
 
namespace MS.Internal.Automation 
{
    // Lightweight class to wrap Win32 WinEvents.  Users of this class would 
    // inherit from WinEventWrap do the following:
    //   1. Call the base constructor with a params array of event identifiers
    //   2. Override WinEventProc to provide an implementation.
    internal class WinEventWrap 
    {
        //----------------------------------------------------- 
        // 
        //  Constructors
        // 
        //-----------------------------------------------------

        #region Constructors
 
        // ctor that takes an array of events
        internal WinEventWrap(int [] eventIds) 
        { 
            Debug.Assert(eventIds != null && eventIds.Length > 0, "eventIds is invalid");
            _eventIds = (int [])eventIds.Clone(); 
            _hHooks = new IntPtr[_eventIds.Length];
            Init();
        }
 
        ~WinEventWrap()
        { 
            Clear(); 
        }
 
        #endregion Constructors


        //------------------------------------------------------ 
        //
        //  Internal Methods 
        // 
        //-----------------------------------------------------
 
        #region Internal Methods

        internal virtual void WinEventProc(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        { 
            // override this to provide an implementation
        } 
 
        internal void Clear()
        { 
            StopListening();
            lock(this)
            {
                _clientCallbacks.Clear (); 
            }
            if (_gchThis.IsAllocated) 
            { 
                _gchThis.Free();
            } 
        }

        internal void AddCallback(object clientCallback)
        { 
            lock (this)
            { 
                _clientCallbacks.Add(clientCallback); 
            }
        } 

        internal bool RemoveCallback(object clientCallback)
        {
            if (clientCallback == null) 
                return true;    // temp until cleanup of WinEvent code is complete
 
            bool listIsEmpty = true; 
            lock (this)
            { 
                if (_clientCallbacks.Count == 0)
                    return true;

                // remove a specific callback 
                _clientCallbacks.Remove(clientCallback);
                listIsEmpty = (_clientCallbacks.Count == 0); 
            } 

            return listIsEmpty; 
        }

        // install WinEvent hook and start getting the callback.
        ///  
        /// Critical - as this calls SetWinEventHook which has a SUC on it and also calls
        ///            the setter for _hHook. 
        /// Safe - as this does not allow an arbitrary method to be set up as a hook and 
        ///        also doesn't allow an aribtrary value to be set on _hHook.Value.
        ///  
        internal void StartListening()
        {
            _fBusy = true;
 
            int i = 0;
            foreach (int eventId in _eventIds) 
            { 
                // There is no indication in the Windows SDK documentation that SetWinEventHook()
                // will set an error to be retrieved with GetLastError, so set the pragma to ignore 
                // the PERSHARP warning.
#pragma warning suppress 6523
                _hHooks[i] = UnsafeNativeMethods.SetWinEventHook(eventId, eventId, IntPtr.Zero, _winEventProc, 0, 0, _fFlags);
                if (_hHooks[i] == IntPtr.Zero) 
                {
                    StopListening(); 
                    throw new Win32Exception(); 
                }
                i++; 
            }
            _fBusy = false;
        }
 
        internal void StopListening()
        { 
            // ASSUMPTION: Before StopListening is called, all callback delegates have been removed 
            // so that any events received while hooks are being removed become noops (since there's
            // no handlers for them to call). 
            _fBusy = true;

            for (int i=0;i<_hHooks.Length;i++)
            { 
                if (_hHooks[i] != IntPtr.Zero)
                { 
                    // There is no indication in the Windows SDK documentation that UnhookWinEvent() 
                    // will set an error to be retrieved with GetLastError, so set the pragma to ignore
                    // the PERSHARP warning. 
#pragma warning suppress 6523
                    UnsafeNativeMethods.UnhookWinEvent(_hHooks[i]);
                    _hHooks[i] = IntPtr.Zero;
                } 
            }
            if (_qEvents != null) 
            { 
                _qEvents.Clear();
            } 
            _fBusy = false;
        }

        // Handlers may make a call to another process so don't want to lock around code that protects _clientCallbacks. 
        // Instead, grab the callbacks w/in a lock then call them outside of the lock.  This technique has potential for
        // error if (for instance) handler A could remove both itself and handler B but for now don't need to worry 
        // about handlers getting out of [....] with the ones in the master array because the callbacks are defined w/in 
        // this code and the handler for new UI doesn't remove itself (or anyone else).
        internal object[] GetHandlers() 
        {
            lock (this)
            {
                object[] handlers = new object[_clientCallbacks.Count]; 
                for (int i = 0; i < _clientCallbacks.Count; i++)
                    handlers[i] = _clientCallbacks[i]; 
 
                return handlers;
            } 
        }

        #endregion Internal Methods
 

        //------------------------------------------------------ 
        // 
        //  Private Methods
        // 
        //------------------------------------------------------

        #region Private Methods
 
        // queue winevents so that the get processed in the order we receive them. If we just
        // dispatch them as we get them, we'll end up getting some _while_ we are processing others, 
        // and end up completing those events out of order, making the event order appear backwards. 
        // This code checks whether we are currently processing an event, and if so, queues it so that
        // we process it when we're done with the current event. 
        private void WinEventReentrancyFilter(int winEventHook, int eventId, IntPtr hwnd, int idObject, int idChild, int eventThread, uint eventTime)
        {
            if ( _fBusy )
            { 
                _qEvents.Enqueue(new WinEvent(eventId, hwnd, idObject, idChild, eventTime));
            } 
            else 
            {
                _fBusy = true; 
                try
                {
                    PreWinEventProc(eventId, hwnd, idObject, idChild, eventTime); // deliver this event
                } 
                catch( Exception e )
                { 
                    if( Misc.IsCriticalException( e ) ) 
                        throw;
 
                    // ignore exceptions for now since we've no way to let clients add exception handlers
                }

                while (_qEvents.Count > 0) 
                {
                    WinEvent e = (WinEvent)_qEvents.Dequeue(); // process queued events 
                    try 
                    {
                        PreWinEventProc(e._eventId, e._hwnd, e._idObject, e._idChild, e._eventTime); 
                    }
                    catch( Exception ex )
                    {
                        if( Misc.IsCriticalException( ex ) ) 
                            throw;
 
                        // ignore exceptions for now since we've no way to let clients add exception handlers 
                    }
                } 
                _fBusy = false;
            }
        }
 
        private void PreWinEventProc(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime)
        { 
            // Ignore events from the UIA->MSAA bridge: these are recognizable as having 
            // >0 idObject, and the target HWND having a UIA impl.
            if (idObject > 0) 
            {
                if (UiaCoreApi.UiaHasServerSideProvider(hwnd))
                {
                    // Bridge event - ignore it. 
                    return;
                } 
            } 

            // 0 is used as a marker value elsewhere, so bump up to 1 
            if(eventTime == 0)
            {
                eventTime = 1;
            } 
            WinEventProc(eventId, hwnd, idObject, idChild, eventTime);
        } 
 

        private void Init() 
        {
            // Keep the garbage collector from moving things around
            _winEventProc = new NativeMethods.WinEventProcDef(WinEventReentrancyFilter);
            _gchThis = GCHandle.Alloc(_winEventProc); 

            _clientCallbacks = new ArrayList(2); 
            _qEvents = new Queue(16, (float)2.0); // (initial cap, growth factor) 
            _fFlags = NativeMethods.WINEVENT_OUTOFCONTEXT;
        } 

        #endregion Private Methods

 
        //-----------------------------------------------------
        // 
        //  Private Fields 
        //
        //------------------------------------------------------ 

        #region Private Fields

        private class WinEvent 
        {
            internal WinEvent(int eventId, IntPtr hwnd, int idObject, int idChild, uint eventTime) 
            { 
                _eventId = eventId;
                _hwnd = hwnd; 
                _idObject = idObject;
                _idChild = idChild;
                _eventTime = eventTime;
            } 
            public int _eventId;
            public IntPtr _hwnd; 
            public int _idObject; 
            public int _idChild;
            public uint _eventTime; 
        }

        private Queue _qEvents;                // Queue of events waiting to be processed
        private int [] _eventIds;              // the WinEvent(s) this instance is handling 
        private IntPtr [] _hHooks;             // the returned handles(s) from SetWinEventHook
        private bool _fBusy;                   // Flag indicating if we're busy processing 
        private int _fFlags;                   // SetWinEventHook flags 
        private GCHandle _gchThis;             // GCHandle to keep GCs from moving this callback
        private NativeMethods.WinEventProcDef _winEventProc; // the callback handed to USER for WinEvents 
        protected ArrayList _clientCallbacks;  // the client callback interface objects

        #endregion Private Fields
    } 
}

// 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