SystemEvents.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / CompMod / Microsoft / Win32 / SystemEvents.cs / 1 / SystemEvents.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/* 
 */ 
namespace Microsoft.Win32 {
    using System; 
    using System.Diagnostics;
    using System.Security;
    using System.Security.Permissions;
    using System.Collections; 
    using System.Collections.Generic;
    using System.ComponentModel; 
    using System.Diagnostics.CodeAnalysis; 
    using System.Reflection;
    using System.Runtime.ConstrainedExecution; 
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
    using System.Text;
    using System.Threading; 

    ///  
    ///     
    ///       Provides a
    ///       set of global system events to callers. This 
    ///       class cannot be inherited.
    /// 
    [HostProtectionAttribute(MayLeakOnAbort = true)]
    [ 
    // Disabling partial trust scenarios
    PermissionSet(SecurityAction.LinkDemand, Name="FullTrust") 
    ] 
    [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")]
    public sealed class SystemEvents { 
        // Almost all of our data is static.  We keep a single instance of
        // SystemEvents around so we can bind delegates to it.
        // Non-static methods in this class will only be called through
        // one of the delegates. 
        //
        private static readonly object      eventLockObject = new object(); 
        private static readonly object      procLockObject = new object(); 
        private static SystemEvents         systemEvents;
        private static Thread               windowThread; 
        private static ManualResetEvent     eventWindowReady;
        private static Random               randomTimerId = new Random();
        private static bool                 startupRecreates;
        private static bool                 registeredSessionNotification = false; 
        private static int                  domainQualifier;
        private static NativeMethods.WNDCLASS staticwndclass; 
        [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] 
        private static IntPtr               defWindowProc;
 
        static string className = null;

        // cross-thread marshaling
        private static Queue            threadCallbackList; // list of Delegates 
        private static int threadCallbackMessage = 0;
        private static ManualResetEvent eventThreadTerminated; 
 
        //Decide whether to marshal or use Everett-style non-marshaled calls
        private static bool checkedThreadAffinity = false; 
        private static bool useEverettThreadAffinity = false;
        private const string everettThreadAffinityValue = "EnableSystemEventsThreadAffinityCompatibility";

        // Per-instance data that is isolated to the window thread. 
        //
        private IntPtr                  windowHandle; 
        private NativeMethods.WndProc   windowProc; 
        private NativeMethods.ConHndlr  consoleHandler;
 
        // The set of events we respond to.
        //
        private static readonly object       OnUserPreferenceChangingEvent   = new object();
        private static readonly object       OnUserPreferenceChangedEvent    = new object(); 
        private static readonly object       OnSessionEndingEvent            = new object();
        private static readonly object       OnSessionEndedEvent             = new object(); 
        private static readonly object       OnPowerModeChangedEvent         = new object(); 
        private static readonly object       OnLowMemoryEvent                = new object();
        private static readonly object       OnDisplaySettingsChangingEvent  = new object(); 
        private static readonly object       OnDisplaySettingsChangedEvent   = new object();
        private static readonly object       OnInstalledFontsChangedEvent    = new object();
        private static readonly object       OnTimeChangedEvent              = new object();
        private static readonly object       OnTimerElapsedEvent             = new object(); 
        private static readonly object       OnPaletteChangedEvent           = new object();
        private static readonly object       OnEventsThreadShutdownEvent     = new object(); 
        private static readonly object       OnSessionSwitchEvent            = new object(); 

 
        // Our list of handler information.  This is a lookup of the above keys and objects that
        // match a delegate with a SyncronizationContext so we can fire on the proper thread.
        //
        private static Dictionary>  _handlers; 

 
        ///  
        ///     This class is static, there is no need to ever create it.
        ///  
        private SystemEvents() {
        }

 
        // stole from SystemInformation... if we get SystemInformation moved
        // to somewhere that we can use it... rip this! 
        // 
        [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
        private static IntPtr processWinStation = IntPtr.Zero; 
        private static bool isUserInteractive = false;
        private static bool UserInteractive {
            get {
                if (Environment.OSVersion.Platform == System.PlatformID.Win32NT) { 
                    IntPtr hwinsta = IntPtr.Zero;
 
                    hwinsta = UnsafeNativeMethods.GetProcessWindowStation(); 
                    if (hwinsta != IntPtr.Zero && processWinStation != hwinsta) {
                        isUserInteractive = true; 

                        int lengthNeeded = 0;
                        NativeMethods.USEROBJECTFLAGS flags = new NativeMethods.USEROBJECTFLAGS();
 
                        if (UnsafeNativeMethods.GetUserObjectInformation(new HandleRef(null, hwinsta), NativeMethods.UOI_FLAGS, flags, Marshal.SizeOf(flags), ref lengthNeeded)) {
                            if ((flags.dwFlags & NativeMethods.WSF_VISIBLE) == 0) { 
                                isUserInteractive = false; 
                            }
                        } 
                        processWinStation = hwinsta;
                    }
                }
                else { 
                    isUserInteractive = true;
                } 
                return isUserInteractive; 
            }
        } 


        /// 
        ///    Occurs when the display settings are changing. 
        /// 
        public static event EventHandler DisplaySettingsChanging { 
            add { 
                AddEventHandler(OnDisplaySettingsChangingEvent, value);
            } 
            remove {
                RemoveEventHandler(OnDisplaySettingsChangingEvent, value);
            }
        } 

 
        ///  
        ///    Occurs when the user changes the display settings.
        ///  
        public static event EventHandler DisplaySettingsChanged {
            add {
                AddEventHandler(OnDisplaySettingsChangedEvent, value);
            } 
            remove {
                RemoveEventHandler(OnDisplaySettingsChangedEvent, value); 
            } 
        }
 

        /// 
        ///    Occurs before the thread that listens for system events is terminated.
        ///           Delegates will be invoked on the events thread. 
        /// 
        public static event EventHandler EventsThreadShutdown { 
            // Really only here for GDI+ initialization and shut down 
            add {
                AddEventHandler(OnEventsThreadShutdownEvent, value); 
            }
            remove {
                RemoveEventHandler(OnEventsThreadShutdownEvent, value);
            } 
        }
 
 
        /// 
        ///    Occurs when the user adds fonts to or removes fonts from the system. 
        /// 
        public static event EventHandler InstalledFontsChanged {
            add {
                AddEventHandler(OnInstalledFontsChangedEvent, value); 
            }
            remove { 
                RemoveEventHandler(OnInstalledFontsChangedEvent, value); 
            }
        } 


        /// 
        ///    Occurs when the system is running out of available RAM. 
        /// 
        [Obsolete("This event has been deprecated. http://go.microsoft.com/fwlink/?linkid=14202")] 
        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] 
        public static event EventHandler LowMemory
        { 
            add {
                EnsureSystemEvents(true, true);
                AddEventHandler(OnLowMemoryEvent, value);
            } 
            remove {
                RemoveEventHandler(OnLowMemoryEvent, value); 
            } 
        }
 

        /// 
        ///    Occurs when the user switches to an application that uses a different
        ///       palette. 
        /// 
        public static event EventHandler PaletteChanged { 
            add { 
                AddEventHandler(OnPaletteChangedEvent, value);
            } 
            remove {
                RemoveEventHandler(OnPaletteChangedEvent, value);
            }
        } 

 
        ///  
        ///    Occurs when the user suspends or resumes the system.
        ///  
        public static event PowerModeChangedEventHandler PowerModeChanged {
            add {
                EnsureSystemEvents(true, true);
                AddEventHandler(OnPowerModeChangedEvent, value); 
            }
            remove { 
                RemoveEventHandler(OnPowerModeChangedEvent, value); 
            }
        } 


        /// 
        ///    Occurs when the user is logging off or shutting down the system. 
        /// 
        public static event SessionEndedEventHandler SessionEnded { 
            add { 
                EnsureSystemEvents(true, false);
                AddEventHandler(OnSessionEndedEvent, value); 
            }
            remove {
                RemoveEventHandler(OnSessionEndedEvent, value);
            } 
        }
 
 
        /// 
        ///    Occurs when the user is trying to log off or shutdown the system. 
        /// 
        public static event SessionEndingEventHandler SessionEnding {
            add {
                EnsureSystemEvents(true, false); 
                AddEventHandler(OnSessionEndingEvent, value);
            } 
            remove { 
                RemoveEventHandler(OnSessionEndingEvent, value);
            } 
        }

        /// 
        ///    Occurs when a user session switches. 
        /// 
        public static event SessionSwitchEventHandler SessionSwitch { 
            add { 
                EnsureSystemEvents(true, true);
                EnsureRegisteredSessionNotification(); 
                AddEventHandler(OnSessionSwitchEvent, value);
            }
            remove {
                RemoveEventHandler(OnSessionSwitchEvent, value); 
            }
        } 
 

        ///  
        ///    Occurs when the user changes the time on the system clock.
        /// 
        public static event EventHandler TimeChanged {
            add { 
                EnsureSystemEvents(true, false);
                AddEventHandler(OnTimeChangedEvent, value); 
            } 
            remove {
                RemoveEventHandler(OnTimeChangedEvent, value); 
            }
        }

 
        /// 
        ///    Occurs when a windows timer interval has expired. 
        ///  
        public static event TimerElapsedEventHandler TimerElapsed {
            add { 
                EnsureSystemEvents(true, false);
                AddEventHandler(OnTimerElapsedEvent, value);
            }
            remove { 
                RemoveEventHandler(OnTimerElapsedEvent, value);
            } 
        } 

 
        /// 
        ///    Occurs when a user preference has changed.
        /// 
        public static event UserPreferenceChangedEventHandler UserPreferenceChanged { 
            add {
                AddEventHandler(OnUserPreferenceChangedEvent, value); 
            } 
            remove {
                RemoveEventHandler(OnUserPreferenceChangedEvent, value); 
            }
        }

        ///  
        ///    Occurs when a user preference is changing.
        ///  
        public static event UserPreferenceChangingEventHandler UserPreferenceChanging { 
            add {
                AddEventHandler(OnUserPreferenceChangingEvent, value); 
            }
            remove {
                RemoveEventHandler(OnUserPreferenceChangingEvent, value);
            } 
        }
 
        [SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity")] 
        private static void AddEventHandler(object key, Delegate value) {
            lock (eventLockObject) { 

               if (_handlers == null) {
                    _handlers = new Dictionary>();
                    EnsureSystemEvents(false, false); 
               }
 
               List invokeItems; 

               if (!_handlers.TryGetValue(key, out invokeItems)) { 

                    invokeItems = new List();
                    _handlers[key] = invokeItems;
               } 
               else {
                    invokeItems = _handlers[key]; 
               } 

               invokeItems.Add(new SystemEventInvokeInfo(value)); 
            }
        }

        ///  
        ///      Console handler we add in case we are a console application or a service.
        ///      Without this we will not get end session events. 
        ///  
        private int ConsoleHandlerProc(int signalType) {
 
            switch (signalType) {
                case NativeMethods.CTRL_LOGOFF_EVENT:
                    OnSessionEnded((IntPtr) 1, (IntPtr) NativeMethods.ENDSESSION_LOGOFF);
                    break; 

                case NativeMethods.CTRL_SHUTDOWN_EVENT: 
                    OnSessionEnded((IntPtr) 1, (IntPtr) 0); 
                    break;
            } 

            return 0;
        }
 
        private NativeMethods.WNDCLASS WndClass {
            get { 
                if (staticwndclass == null) { 
                    const string classNameFormat = ".NET-BroadcastEventWindow.{0}.{1}.{2}";
 
                    IntPtr hInstance = UnsafeNativeMethods.GetModuleHandle(null);

                    className = string.Format(System.Globalization.CultureInfo.InvariantCulture,
                        classNameFormat, 
                        ThisAssembly.Version,
                        Convert.ToString(AppDomain.CurrentDomain.GetHashCode(), 16), 
                        domainQualifier); 

                    staticwndclass = new NativeMethods.WNDCLASS(); 
                    staticwndclass.hbrBackground = (IntPtr)(NativeMethods.COLOR_WINDOW + 1);
                    staticwndclass.style = 0;

                    windowProc = new NativeMethods.WndProc(this.WindowProc); 
                    staticwndclass.lpszClassName = className;
                    staticwndclass.lpfnWndProc = windowProc; 
                    staticwndclass.hInstance = hInstance; 
                }
                return staticwndclass; 
            }
        }

        private IntPtr DefWndProc { 
            get {
                if (defWindowProc == IntPtr.Zero) { 
                    string defproc = (Marshal.SystemDefaultCharSize == 1 ? "DefWindowProcA" : "DefWindowProcW"); 

                    defWindowProc = UnsafeNativeMethods.GetProcAddress(new HandleRef(this, UnsafeNativeMethods.GetModuleHandle("user32.dll")), defproc); 
                }
                return defWindowProc;
            }
        } 

        private void BumpQualifier() { 
            staticwndclass = null; 
            domainQualifier++;
        } 

        /// 
        /// 
        ///      Goes through the work to register and create a window. 
        /// 
        private IntPtr CreateBroadcastWindow() { 
 
            // Register the window class.
            // 
            NativeMethods.WNDCLASS_I wndclassi = new NativeMethods.WNDCLASS_I();
            IntPtr hInstance = UnsafeNativeMethods.GetModuleHandle(null);

            if (!UnsafeNativeMethods.GetClassInfo(new HandleRef(this, hInstance), WndClass.lpszClassName, wndclassi)) { 

                if (UnsafeNativeMethods.RegisterClass(WndClass) == 0) { 
                    windowProc = null; 
                    Debug.Fail("Unable to register broadcast window class");
                    return IntPtr.Zero; 
                }
            }
            else {
                //lets double check the wndproc returned by getclassinfo for defwndproc. 
                if (wndclassi.lpfnWndProc == DefWndProc) {
 
                    //if we are in there, it means className belongs to an unloaded appdomain. 
                    short atom = 0;
 
                    //try to unregister it.
                    if (0 != UnsafeNativeMethods.UnregisterClass(WndClass.lpszClassName, new HandleRef(null, UnsafeNativeMethods.GetModuleHandle(null)))) {
                        atom = UnsafeNativeMethods.RegisterClass(WndClass);
                    } 

                    if (atom == 0) { 
                        do { 
                            BumpQualifier();
                            atom = UnsafeNativeMethods.RegisterClass(WndClass); 
                        } while (atom == 0 && Marshal.GetLastWin32Error() == NativeMethods.ERROR_CLASS_ALREADY_EXISTS);
                    }
                }
            } 

            // And create an instance of the window. 
            // 
            IntPtr hwnd = UnsafeNativeMethods.CreateWindowEx(
                                                            0, 
                                                            WndClass.lpszClassName,
                                                            WndClass.lpszClassName,
                                                            NativeMethods.WS_POPUP,
                                                            0, 0, 0, 0, NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, 
                                                            new HandleRef(this, hInstance), null);
            return hwnd; 
        } 

        ///  
        /// 
        ///    Creates a new window timer asociated with the
        ///       system events window.
        ///  
        public static IntPtr CreateTimer(int interval) {
            if (interval <= 0) { 
                throw new ArgumentException(SR.GetString(SR.InvalidLowBoundArgument, "interval", interval.ToString(System.Threading.Thread.CurrentThread.CurrentCulture), "0")); 
            }
 
            EnsureSystemEvents(true, true);
            IntPtr timerId = UnsafeNativeMethods.SendMessage(new HandleRef(systemEvents, systemEvents.windowHandle),
                                                             NativeMethods.WM_CREATETIMER, (IntPtr)interval, IntPtr.Zero);
 
            if (timerId == IntPtr.Zero) {
                throw new ExternalException(SR.GetString(SR.ErrorCreateTimer)); 
            } 
            return timerId;
        } 

        private void Dispose() {
            if (windowHandle != IntPtr.Zero) {
 
                if (registeredSessionNotification) {
                    UnsafeNativeMethods.WTSUnRegisterSessionNotification(new HandleRef(systemEvents, systemEvents.windowHandle)); 
                } 

                IntPtr handle = windowHandle; 
                windowHandle = IntPtr.Zero;

                HandleRef href = new HandleRef(this, handle);
 
                //we check IsWindow because Application may have rudely destroyed our broadcast window.
                //if this were true, we want to unregister the class. 
                if (UnsafeNativeMethods.IsWindow(href) && DefWndProc != IntPtr.Zero) { 
                    UnsafeNativeMethods.SetWindowLong(href, NativeMethods.GWL_WNDPROC, new HandleRef(this, DefWndProc));
 
                    //set our sentinel value that we will look for upon initialization to indicate
                    //the window class belongs to an unloaded appdomain and therefore should not be used.
                    UnsafeNativeMethods.SetClassLong(href, NativeMethods.GCL_WNDPROC, DefWndProc);
                } 

                // If DestroyWindow failed, it is because we're being 
                // shutdown from another thread.  In this case, locate the 
                // DefWindowProc call in User32, sling the window back to it,
                // and post a nice fat WM_CLOSE 
                //
                if (UnsafeNativeMethods.IsWindow(href) && !UnsafeNativeMethods.DestroyWindow(href)) {
                    UnsafeNativeMethods.PostMessage(href, NativeMethods.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
                } 
                else {
                    IntPtr hInstance = UnsafeNativeMethods.GetModuleHandle(null); 
                    UnsafeNativeMethods.UnregisterClass(className, new HandleRef(this, hInstance)); 
                }
            } 

            if (consoleHandler != null) {
                UnsafeNativeMethods.SetConsoleCtrlHandler(consoleHandler, 0);
                consoleHandler = null; 
            }
        } 
 
        /// 
        ///  Creates the static resources needed by 
        ///  system events.
        /// 
        private static void EnsureSystemEvents(bool requireHandle, bool throwOnRefusal) {
 
            // The secondary check here is to detect asp.net.  Asp.net uses multiple
            // app domains to field requests and we do not want to gobble up an 
            // additional thread per domain.  So under this scenario SystemEvents 
            // becomes a nop.
            // 
            if (systemEvents == null) {
                lock (procLockObject) {
                    if (systemEvents == null) {
                        if (Thread.GetDomain().GetData(".appDomain") != null) { 
                            if (throwOnRefusal) {
                                throw new InvalidOperationException(SR.GetString(SR.ErrorSystemEventsNotSupported)); 
                            } 
                            return;
                        } 

                        // If we are creating system events on a thread declared as STA, then
                        // just share the thread.
                        // 
                        if (!UserInteractive || Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) {
                            systemEvents = new SystemEvents(); 
                            systemEvents.Initialize(); 
                        }
                        else { 
                            eventWindowReady = new ManualResetEvent(false);
                            systemEvents = new SystemEvents();
                            windowThread = new Thread(new ThreadStart(systemEvents.WindowThreadProc));
                            windowThread.IsBackground = true; 
                            windowThread.Name = ".NET SystemEvents";
                            windowThread.Start(); 
                            eventWindowReady.WaitOne(); 
                        }
 
                        if (requireHandle && systemEvents.windowHandle == IntPtr.Zero) {
                            // In theory, it's not the end of the world that
                            // we don't get system events.  Unfortunately, the main reason windowHandle == 0
                            // is CreateWindowEx failed for mysterious reasons, and when that happens, 
                            // subsequent (and more important) CreateWindowEx calls also fail.
                            // See ASURT #44424 for a rather lengthy discussion of this. 
                            throw new ExternalException(SR.GetString(SR.ErrorCreateSystemEvents)); 
                        }
 
                        startupRecreates = false;
                    }
                }
            } 
        }
 
        private static void EnsureRegisteredSessionNotification() { 
            if (!registeredSessionNotification) {
                IntPtr retval = SafeNativeMethods.LoadLibrary(ExternDll.Wtsapi32); 

                if (retval != IntPtr.Zero) {
                    UnsafeNativeMethods.WTSRegisterSessionNotification(new HandleRef(systemEvents, systemEvents.windowHandle), NativeMethods.NOTIFY_FOR_THIS_SESSION);
                    registeredSessionNotification = true; 
                    SafeNativeMethods.FreeLibrary(new HandleRef(null, retval));
                } 
            } 
        }
 
        [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")]
        private UserPreferenceCategory GetUserPreferenceCategory(int msg, IntPtr wParam, IntPtr lParam) {

            UserPreferenceCategory pref = UserPreferenceCategory.General; 

            if (msg == NativeMethods.WM_SETTINGCHANGE) { 
 
                if (lParam != IntPtr.Zero && Marshal.PtrToStringAuto(lParam).Equals("Policy")) {
                    pref = UserPreferenceCategory.Policy; 
                }
                else if (lParam != IntPtr.Zero && Marshal.PtrToStringAuto(lParam).Equals("intl")) {
                    pref = UserPreferenceCategory.Locale;
                } 
                else {
                    switch ((int) wParam) { 
                        case NativeMethods.SPI_SETACCESSTIMEOUT: 
                        case NativeMethods.SPI_SETFILTERKEYS:
                        case NativeMethods.SPI_SETHIGHCONTRAST: 
                        case NativeMethods.SPI_SETMOUSEKEYS:
                        case NativeMethods.SPI_SETSCREENREADER:
                        case NativeMethods.SPI_SETSERIALKEYS:
                        case NativeMethods.SPI_SETSHOWSOUNDS: 
                        case NativeMethods.SPI_SETSOUNDSENTRY:
                        case NativeMethods.SPI_SETSTICKYKEYS: 
                        case NativeMethods.SPI_SETTOGGLEKEYS: 
                            pref = UserPreferenceCategory.Accessibility;
                            break; 

                        case NativeMethods.SPI_SETDESKWALLPAPER:
                        case NativeMethods.SPI_SETFONTSMOOTHING:
                        case NativeMethods.SPI_SETCURSORS: 
                        case NativeMethods.SPI_SETDESKPATTERN:
                        case NativeMethods.SPI_SETGRIDGRANULARITY: 
                        case NativeMethods.SPI_SETWORKAREA: 
                            pref = UserPreferenceCategory.Desktop;
                            break; 

                        case NativeMethods.SPI_ICONHORIZONTALSPACING:
                        case NativeMethods.SPI_ICONVERTICALSPACING:
                        case NativeMethods.SPI_SETICONMETRICS: 
                        case NativeMethods.SPI_SETICONS:
                        case NativeMethods.SPI_SETICONTITLELOGFONT: 
                        case NativeMethods.SPI_SETICONTITLEWRAP: 
                            pref = UserPreferenceCategory.Icon;
                            break; 

                        case NativeMethods.SPI_SETDOUBLECLICKTIME:
                        case NativeMethods.SPI_SETDOUBLECLKHEIGHT:
                        case NativeMethods.SPI_SETDOUBLECLKWIDTH: 
                        case NativeMethods.SPI_SETMOUSE:
                        case NativeMethods.SPI_SETMOUSEBUTTONSWAP: 
                        case NativeMethods.SPI_SETMOUSEHOVERHEIGHT: 
                        case NativeMethods.SPI_SETMOUSEHOVERTIME:
                        case NativeMethods.SPI_SETMOUSESPEED: 
                        case NativeMethods.SPI_SETMOUSETRAILS:
                        case NativeMethods.SPI_SETSNAPTODEFBUTTON:
                        case NativeMethods.SPI_SETWHEELSCROLLLINES:
                        case NativeMethods.SPI_SETCURSORSHADOW: 
                        case NativeMethods.SPI_SETHOTTRACKING:
                        case NativeMethods.SPI_SETTOOLTIPANIMATION: 
                        case NativeMethods.SPI_SETTOOLTIPFADE: 
                            pref = UserPreferenceCategory.Mouse;
                            break; 

                        case NativeMethods.SPI_SETKEYBOARDDELAY:
                        case NativeMethods.SPI_SETKEYBOARDPREF:
                        case NativeMethods.SPI_SETKEYBOARDSPEED: 
                        case NativeMethods.SPI_SETLANGTOGGLE:
                            pref = UserPreferenceCategory.Keyboard; 
                            break; 

                        case NativeMethods.SPI_SETMENUDROPALIGNMENT: 
                        case NativeMethods.SPI_SETMENUFADE:
                        case NativeMethods.SPI_SETMENUSHOWDELAY:
                        case NativeMethods.SPI_SETMENUANIMATION:
                        case NativeMethods.SPI_SETSELECTIONFADE: 
                            pref = UserPreferenceCategory.Menu;
                            break; 
 
                        case NativeMethods.SPI_SETLOWPOWERACTIVE:
                        case NativeMethods.SPI_SETLOWPOWERTIMEOUT: 
                        case NativeMethods.SPI_SETPOWEROFFACTIVE:
                        case NativeMethods.SPI_SETPOWEROFFTIMEOUT:
                            pref = UserPreferenceCategory.Power;
                            break; 

                        case NativeMethods.SPI_SETSCREENSAVEACTIVE: 
                        case NativeMethods.SPI_SETSCREENSAVERRUNNING: 
                        case NativeMethods.SPI_SETSCREENSAVETIMEOUT:
                            pref = UserPreferenceCategory.Screensaver; 
                            break;

                        case NativeMethods.SPI_SETKEYBOARDCUES:
                        case NativeMethods.SPI_SETCOMBOBOXANIMATION: 
                        case NativeMethods.SPI_SETLISTBOXSMOOTHSCROLLING:
                        case NativeMethods.SPI_SETGRADIENTCAPTIONS: 
                        case NativeMethods.SPI_SETUIEFFECTS: 
                        case NativeMethods.SPI_SETACTIVEWINDOWTRACKING:
                        case NativeMethods.SPI_SETACTIVEWNDTRKZORDER: 
                        case NativeMethods.SPI_SETACTIVEWNDTRKTIMEOUT:
                        case NativeMethods.SPI_SETANIMATION:
                        case NativeMethods.SPI_SETBORDER:
                        case NativeMethods.SPI_SETCARETWIDTH: 
                        case NativeMethods.SPI_SETDRAGFULLWINDOWS:
                        case NativeMethods.SPI_SETDRAGHEIGHT: 
                        case NativeMethods.SPI_SETDRAGWIDTH: 
                        case NativeMethods.SPI_SETFOREGROUNDFLASHCOUNT:
                        case NativeMethods.SPI_SETFOREGROUNDLOCKTIMEOUT: 
                        case NativeMethods.SPI_SETMINIMIZEDMETRICS:
                        case NativeMethods.SPI_SETNONCLIENTMETRICS:
                        case NativeMethods.SPI_SETSHOWIMEUI:
                            pref = UserPreferenceCategory.Window; 
                            break;
                    } 
                } 
            }
            else if (msg == NativeMethods.WM_SYSCOLORCHANGE) { 
                pref = UserPreferenceCategory.Color;
            }
            else {
                Debug.Fail("Unrecognized message passed to UserPreferenceCategory"); 
            }
 
            return pref; 
        }
 
        private void Initialize() {
            consoleHandler = new NativeMethods.ConHndlr(this.ConsoleHandlerProc);
            if (!UnsafeNativeMethods.SetConsoleCtrlHandler(consoleHandler, 1)) {
                Debug.Fail("Failed to install console handler."); 
                consoleHandler = null;
            } 
 
            windowHandle = CreateBroadcastWindow();
            Debug.Assert(windowHandle != IntPtr.Zero, "CreateBroadcastWindow failed"); 

            AppDomain.CurrentDomain.ProcessExit += new EventHandler(SystemEvents.Shutdown);
            AppDomain.CurrentDomain.DomainUnload += new EventHandler(SystemEvents.Shutdown);
        } 

        ///  
        ///     Called on the control's owning thread to perform the actual callback. 
        ///     This empties this control's callback queue, propagating any excpetions
        ///     back as needed. 
        /// 
        [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void InvokeMarshaledCallbacks() { 
            Debug.Assert(threadCallbackList != null, "Invoking marshaled callbacks before there are any");
 
            Delegate current = null; 
            lock (threadCallbackList) {
                if (threadCallbackList.Count > 0) { 
                    current = (Delegate)threadCallbackList.Dequeue();
                }
            }
 
            // Now invoke on all the queued items.
            // 
            while (current != null) { 
                try {
                    // Optimize a common case of using EventHandler. This allows us to invoke 
                    // early bound, which is a bit more efficient.
                    //
                    EventHandler c = current as EventHandler;
                    if (c != null) { 
                        c(null, EventArgs.Empty);
                    } 
                    else { 
                        current.DynamicInvoke(new object[0]);
                    } 
                }
                catch (Exception t) {
                    Debug.Fail("SystemEvents marshaled callback failed:" + t);
                } 
                lock (threadCallbackList) {
                    if (threadCallbackList.Count > 0) { 
                        current = (Delegate)threadCallbackList.Dequeue(); 
                    }
                    else { 
                        current = null;
                    }
                }
            } 
        }
 
        ///  
        ///     Executes the given delegate on the thread that listens for system events.  Similar to Control.Invoke().
        ///  
        public static void InvokeOnEventsThread(Delegate method) {
            // This method is really only here for GDI+ initialization/shutdown
            EnsureSystemEvents(true, true);
 
#if DEBUG
            int pid; 
            int thread = SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(systemEvents, systemEvents.windowHandle), out pid); 
            Debug.Assert(windowThread == null || thread != SafeNativeMethods.GetCurrentThreadId(), "Don't call MarshaledInvoke on the system events thread");
#endif 

            if (threadCallbackList == null) {
                lock (eventLockObject) {
                    if (threadCallbackList == null) { 
                        threadCallbackList = new Queue();
                        threadCallbackMessage = SafeNativeMethods.RegisterWindowMessage("SystemEventsThreadCallbackMessage"); 
                    } 
                }
            } 

            Debug.Assert(threadCallbackMessage != 0, "threadCallbackList initialized but threadCallbackMessage not?");

            lock (threadCallbackList) { 
                threadCallbackList.Enqueue(method);
            } 
 
            UnsafeNativeMethods.PostMessage(new HandleRef(systemEvents, systemEvents.windowHandle), threadCallbackMessage, IntPtr.Zero, IntPtr.Zero);
        } 

        /// 
        /// 
        ///    Kills the timer specified by the given id. 
        /// 
        public static void KillTimer(IntPtr timerId) { 
            EnsureSystemEvents(true, true); 
            if (systemEvents.windowHandle != IntPtr.Zero) {
                int res = (int) UnsafeNativeMethods.SendMessage(new HandleRef(systemEvents, systemEvents.windowHandle), 
                                                                NativeMethods.WM_KILLTIMER, timerId, IntPtr.Zero);

                if (res == 0)
                    throw new ExternalException(SR.GetString(SR.ErrorKillTimer)); 
            }
        } 
 
        /// 
        ///      Callback that handles the create timer 
        ///      user message.
        /// 
        private IntPtr OnCreateTimer(IntPtr wParam) {
            IntPtr timerId = (IntPtr) randomTimerId.Next(); 
            IntPtr res = UnsafeNativeMethods.SetTimer(new HandleRef(this, windowHandle), new HandleRef(this, timerId), (int) wParam, NativeMethods.NullHandleRef);
            return(res == IntPtr.Zero ? IntPtr.Zero: timerId); 
        } 

        ///  
        ///      Handler that raises the DisplaySettings changing event
        /// 
        private void OnDisplaySettingsChanging() {
            RaiseEvent(OnDisplaySettingsChangingEvent, this, EventArgs.Empty); 
        }
 
        ///  
        ///      Handler that raises the DisplaySettings changed event
        ///  
        private void OnDisplaySettingsChanged() {
            RaiseEvent(OnDisplaySettingsChangedEvent, this, EventArgs.Empty);
        }
 
        /// 
        ///      Handler for any event that fires a standard EventHandler delegate. 
        ///  
        private void OnGenericEvent(object eventKey) {
            RaiseEvent(eventKey, this, EventArgs.Empty); 
        }

        private void OnShutdown(object eventKey) {
            RaiseEvent(false, eventKey, this, EventArgs.Empty); 
        }
 
        ///  
        ///      Callback that handles the KillTimer
        ///      user message. 
        /// 
        private bool OnKillTimer(IntPtr wParam) {
            bool res = UnsafeNativeMethods.KillTimer(new HandleRef(this, windowHandle), new HandleRef(this, wParam));
            return res; 
        }
 
        ///  
        ///      Handler for WM_POWERBROADCAST.
        ///  
        private void OnPowerModeChanged(IntPtr wParam) {

            PowerModes mode;
 
            switch ((int)wParam)
            { 
                case NativeMethods.PBT_APMSUSPEND: 
                case NativeMethods.PBT_APMSTANDBY:
                    mode = PowerModes.Suspend; 
                    break;

                case NativeMethods.PBT_APMRESUMECRITICAL:
                case NativeMethods.PBT_APMRESUMESUSPEND: 
                case NativeMethods.PBT_APMRESUMESTANDBY:
                    mode = PowerModes.Resume; 
                    break; 

                case NativeMethods.PBT_APMBATTERYLOW: 
                case NativeMethods.PBT_APMPOWERSTATUSCHANGE:
                case NativeMethods.PBT_APMOEMEVENT:
                    mode = PowerModes.StatusChange;
                    break; 

                default: 
                    return; 
            }
 
            RaiseEvent(OnPowerModeChangedEvent ,this, new PowerModeChangedEventArgs (mode));

        }
 
        /// 
        ///      Handler for WM_ENDSESSION. 
        ///  
        private void OnSessionEnded(IntPtr wParam, IntPtr lParam) {
 
            // wParam will be nonzero if the session is actually ending.  If
            // it was canceled then we do not want to raise the event.
            //
            if (wParam != (IntPtr) 0) { 

                SessionEndReasons reason = SessionEndReasons.SystemShutdown; 
 
                if ((((int) lParam) & NativeMethods.ENDSESSION_LOGOFF) != 0) {
                    reason = SessionEndReasons.Logoff; 
                }

                SessionEndedEventArgs endEvt = new SessionEndedEventArgs(reason);
 
                RaiseEvent(OnSessionEndedEvent, this, endEvt);
            } 
        } 

        ///  
        ///      Handler for WM_QUERYENDSESSION.
        /// 
        private int OnSessionEnding(IntPtr lParam) {
            int endOk = 1; 

            SessionEndReasons reason = SessionEndReasons.SystemShutdown; 
 
            //Casting to (int) is bad if we're 64-bit; casting to (long) is ok whether we're 64- or 32-bit.
            if ((((long)lParam) & NativeMethods.ENDSESSION_LOGOFF) != 0) { 
                reason = SessionEndReasons.Logoff;
            }

            SessionEndingEventArgs endEvt = new SessionEndingEventArgs(reason); 

            RaiseEvent(OnSessionEndingEvent, this, endEvt); 
            endOk = (endEvt.Cancel ? 0 : 1); 

            return endOk; 
        }

        private void OnSessionSwitch(int wParam) {
            SessionSwitchEventArgs switchEventArgs = new SessionSwitchEventArgs((SessionSwitchReason)wParam); 

            RaiseEvent (OnSessionSwitchEvent, this, switchEventArgs); 
        } 

        ///  
        ///      Handler for WM_THEMECHANGED
        ///      Whidbey note: Before Whidbey, we used to fire UserPreferenceChanged with category
        ///      set to Window. In Whidbey, we support visual styles and need a new category Theme
        ///      since Window is too general. We fire UserPreferenceChanged with this category, but 
        ///      for backward compat, we also fire it with category set to Window.
        ///  
        private void OnThemeChanged() { 
            //we need to fire a changing event handler for Themes.
            //note that it needs to be documented that accessing theme information during the changing event is forbidden. 
            RaiseEvent(OnUserPreferenceChangingEvent, this, new UserPreferenceChangingEventArgs(UserPreferenceCategory.VisualStyle));

            UserPreferenceCategory pref = UserPreferenceCategory.Window;
 
            RaiseEvent (OnUserPreferenceChangedEvent, this, new UserPreferenceChangedEventArgs (pref));
 
            pref = UserPreferenceCategory.VisualStyle; 

            RaiseEvent(OnUserPreferenceChangedEvent, this, new UserPreferenceChangedEventArgs (pref)); 
        }

        /// 
        ///      Handler for WM_SETTINGCHANGE and WM_SYSCOLORCHANGE. 
        /// 
        private void OnUserPreferenceChanged(int msg, IntPtr wParam, IntPtr lParam) { 
            UserPreferenceCategory pref = GetUserPreferenceCategory(msg, wParam, lParam); 

            RaiseEvent(OnUserPreferenceChangedEvent, this, new UserPreferenceChangedEventArgs (pref)); 
        }

        private void OnUserPreferenceChanging(int msg, IntPtr wParam, IntPtr lParam) {
 
            UserPreferenceCategory pref = GetUserPreferenceCategory(msg, wParam, lParam);
 
            RaiseEvent(OnUserPreferenceChangingEvent, this, new UserPreferenceChangingEventArgs(pref)); 
        }
 
        /// 
        ///      Handler for WM_TIMER.
        /// 
        private void OnTimerElapsed(IntPtr wParam) { 
            RaiseEvent(OnTimerElapsedEvent, this, new TimerElapsedEventArgs (wParam));
        } 
 
        #region EverettThreadAffinity
        //VSWhidbey 470990: we need a backdoor to allow applications to enable the old, broken 
        //behavior for SystemEvents, where we fire them on whichever thread they end up on.
        //It's unlikely that someone's depending on this behavior, but we want to avoid a QFE if
        //they are.  Unfortunately, all of CommonAppDataRegistry's friends are on
        //System.Windows.Forms.Application, and we can't take a dependency to windows forms from here. 
        internal static bool UseEverettThreadAffinity {
            get { 
                if (!checkedThreadAffinity) { 
                    //No point in locking if we don't have to...
                    lock (eventLockObject) { 
                        //...but now that we have the lock, make sure nobody else just beat us here.
                        if (!checkedThreadAffinity) {
                            checkedThreadAffinity = true;
                            string template = @"Software\{0}\{1}\{2}"; 
                            try {
                                //We need access to be able to read from the registry here.  We're not creating a 
                                //registry key, nor are we returning information from the registry to the user. 
                                new RegistryPermission(PermissionState.Unrestricted).Assert();
                                RegistryKey key = Registry.LocalMachine.OpenSubKey(string.Format(System.Globalization.CultureInfo.CurrentCulture, 
                                    template, CompanyNameInternal, ProductNameInternal, ProductVersionInternal));
                                if (key != null) {
                                    object value = key.GetValue(everettThreadAffinityValue);
                                    if (value != null && (int)value != 0) { 
                                        useEverettThreadAffinity = true;
                                    } 
                                } 
                            }
                            catch (SecurityException) { 
                                // Can't read the key: use default value (false)
                            }
                            catch (InvalidCastException) {
                                // Key is of wrong type: use default value (false) 
                            }
                        } 
                    } 
                }
                return useEverettThreadAffinity; 
            }
        }

        private static string CompanyNameInternal { 
            //No point in caching the value: we're only using it once.
            get { 
                string companyName = null; 
                // custom attribute
                // 
                Assembly entryAssembly = Assembly.GetEntryAssembly();
                if (entryAssembly != null) {
                    object[] attrs = entryAssembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
                    if (attrs != null && attrs.Length > 0) { 
                        companyName = ((AssemblyCompanyAttribute)attrs[0]).Company;
                    } 
                } 

                // win32 version 
                //
                if (companyName == null || companyName.Length == 0) {
                    companyName = GetAppFileVersionInfo().CompanyName;
                    if (companyName != null) { 
                        companyName = companyName.Trim();
                    } 
                } 

                // fake it with a namespace 
                // won't work with MC++ see GetAppMainType.
                if (companyName == null || companyName.Length == 0) {
                    Type t = GetAppMainType();
 
                    if (t != null) {
                        string ns = t.Namespace; 
 
                        if (!string.IsNullOrEmpty(ns)) {
                            int firstDot = ns.IndexOf(".", StringComparison.Ordinal); 
                            if (firstDot != -1) {
                                companyName = ns.Substring(0, firstDot);
                            }
                            else { 
                                companyName = ns;
                            } 
                        } 
                        else {
                            // last ditch... no namespace, use product name... 
                            //
                            companyName = ProductNameInternal;
                        }
                    } 
                }
                return companyName; 
            } 
        }
 
        private static string ProductNameInternal {
            get {
                string productName = null;
                // custom attribute 
                //
                Assembly entryAssembly = Assembly.GetEntryAssembly(); 
                if (entryAssembly != null) { 
                    object[] attrs = entryAssembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false);
                    if (attrs != null && attrs.Length > 0) { 
                        productName = ((AssemblyProductAttribute)attrs[0]).Product;
                    }
                }
 
                // win32 version info
                // 
                if (productName == null || productName.Length == 0) { 
                    productName = GetAppFileVersionInfo().ProductName;
                    if (productName != null) { 
                        productName = productName.Trim();
                    }
                }
 
                // fake it with namespace
                // won't work with MC++ see GetAppMainType. 
                if (productName == null || productName.Length == 0) { 
                    Type t = GetAppMainType();
 
                    if (t != null) {
                        string ns = t.Namespace;

                        if (!string.IsNullOrEmpty(ns)) { 
                            int lastDot = ns.LastIndexOf(".", StringComparison.Ordinal);
                            if (lastDot != -1 && lastDot < ns.Length - 1) { 
                                productName = ns.Substring(lastDot + 1); 
                            }
                            else { 
                                productName = ns;
                            }
                        }
                        else { 
                            // last ditch... use the main type
                            // 
                            productName = t.Name; 
                        }
                    } 
                }
                return productName;
            }
        } 

        private static string ProductVersionInternal { 
            get { 
                string productVersion = null;
                // custom attribute 
                //
                Assembly entryAssembly = Assembly.GetEntryAssembly();
                if (entryAssembly != null) {
                    object[] attrs = entryAssembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); 
                    if (attrs != null && attrs.Length > 0) {
                        productVersion = ((AssemblyInformationalVersionAttribute)attrs[0]).InformationalVersion; 
                    } 
                }
 
                // win32 version info
                //
                if (productVersion == null || productVersion.Length == 0) {
                    productVersion = GetAppFileVersionInfo().ProductVersion; 
                    if (productVersion != null) {
                        productVersion = productVersion.Trim(); 
                    } 
                }
 
                // fake it
                //
                if (productVersion == null || productVersion.Length == 0) {
                    productVersion = "1.0.0.0"; 
                }
                return productVersion; 
            } 
        }
 
        private static object appFileVersion;
        private static FileVersionInfo GetAppFileVersionInfo() {
            if (appFileVersion == null) {
                Type t = GetAppMainType(); 
                if (t != null) {
                    // SECREVIEW : This Assert is ok, getting the module's version is a safe operation, 
                    //             the result is provided by the system. 
                    //
                    FileIOPermission fiop = new FileIOPermission(PermissionState.None); 
                    fiop.AllFiles = FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read;
                    fiop.Assert();

                    try { 
                        appFileVersion = FileVersionInfo.GetVersionInfo(t.Module.FullyQualifiedName);
                    } 
                    finally { 
                        CodeAccessPermission.RevertAssert();
                    } 
                }
                else {
                    appFileVersion = FileVersionInfo.GetVersionInfo(ExecutablePath);
                } 
            }
 
            return (FileVersionInfo)appFileVersion; 
        }
 
        /// 
        /// 
        ///     Retrieves the Type that contains the "Main" method.
        ///  
        private static Type mainType;
        private static Type GetAppMainType() { 
            if (mainType == null) { 
                Assembly exe = Assembly.GetEntryAssembly();
 
                // Get Main type...This doesn't work in MC++ because Main is a global function and not
                // a class static method (it doesn't belong to a Type).
                if (exe != null) {
                    mainType = exe.EntryPoint.ReflectedType; 
                }
            } 
 
            return mainType;
        } 

        private static string executablePath = null;
        private static string ExecutablePath {
            [SuppressMessage("Microsoft.Security", "CA2103:ReviewImperativeSecurity")] 
            get {
                if (executablePath == null) { 
                    Assembly asm = Assembly.GetEntryAssembly(); 
                    if (asm == null) {
                        StringBuilder sb = new StringBuilder(NativeMethods.MAX_PATH); 
                        UnsafeNativeMethods.GetModuleFileName(NativeMethods.NullHandleRef, sb, sb.Capacity);

                        executablePath = IntSecurity.UnsafeGetFullPath(sb.ToString());
                    } 
                    else {
                        String ecb = asm.EscapedCodeBase; 
                        Uri codeBase = new Uri(ecb); 
                        if (codeBase.Scheme == "file") {
                            executablePath = NativeMethods.GetLocalPath(ecb); 
                        }
                        else {
                            executablePath = codeBase.ToString();
                        } 
                    }
                } 
                Uri exeUri = new Uri(executablePath); 
                if (exeUri.Scheme == "file") {
                    new FileIOPermission(FileIOPermissionAccess.PathDiscovery, executablePath).Demand(); 
                }
                return executablePath;
            }
        } 
        #endregion
 
        private static void RaiseEvent(object key, params object[] args) { 
            RaiseEvent(true, key, args);
        } 

        [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private static void RaiseEvent(bool checkFinalization, object key, params object[] args) { 
            //If the AppDomain's unloading, we shouldn't fire SystemEvents other than Shutdown.
            if (checkFinalization && AppDomain.CurrentDomain.IsFinalizingForUnload()) { 
                return; 
            }
 
            SystemEventInvokeInfo[] invokeItemArray = null;

            lock (eventLockObject) {
 
                if (_handlers != null && _handlers.ContainsKey(key)) {
                    List invokeItems = _handlers[key]; 
 
                    // clone the list so we don't have this type locked and cause
                    // a deadlock if someone tries to modify handlers during an invoke. 
                    //
                    if (invokeItems != null) {
                        invokeItemArray = invokeItems.ToArray();
                    } 
                }
 
            } 

            if (invokeItemArray != null) { 
                for (int i = 0; i < invokeItemArray.Length; i++) {
                    try
                    {
                        SystemEventInvokeInfo info = invokeItemArray[i]; 
                        info.Invoke(checkFinalization, args);
                        invokeItemArray[i] = null; // clear it if it's valid 
                    } 
                    catch (Exception)
                    { 
                        //Eat exceptions (Everett compat)
                    }
                }
 
                // clean out any that are dead.
                // 
                lock (eventLockObject) { 
                    List invokeItems = null;
 
                    for (int i = 0; i < invokeItemArray.Length; i++) {
                        SystemEventInvokeInfo info = invokeItemArray[i];
                        if (info != null) {
                            if (invokeItems == null) { 
                                if (!_handlers.TryGetValue(key, out invokeItems)) {
                                    // weird.  just to be safe. 
                                    // 
                                    return;
                                } 
                            }

                            invokeItems.Remove(info);
                        } 
                    }
                } 
 
            }
        } 

        [SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity")]
        private static void RemoveEventHandler(object key, Delegate value) {
            lock (eventLockObject) { 

                if (_handlers != null && _handlers.ContainsKey(key)) { 
                    List invokeItems = (List)_handlers[key]; 

                    invokeItems.Remove(new SystemEventInvokeInfo(value)); 
                }
            }
        }
 
        /// 
        ///     This method is invoked via reflection from windows forms.  Why?  Because when the runtime is hosted in IE, 
        ///     IE doesn't tell it when to shut down.  The first notification the runtime gets is 
        ///     DLL_PROCESS_DETACH, at which point it is too late for us to run any managed code.  But,
        ///     if we don't destroy our system events window the HWND will fault if it 
        ///     receives a message after the runtime shuts down.  So it is imparative that
        ///     we destroy the window, but it is also necessary to recreate the window on demand.
        ///     That's hard to do, because we originally created it in response to an event
        ///     wire-up, but that event is still bound so technically we should still have the 
        ///     window around.  To work around this crashing fiasco, we have special code
        ///     in the ActiveXImpl class within Control.  This code checks to see if it is running 
        ///     inside of IE, and if so, it will invoke these methods via private reflection. 
        ///     It will invoke Shutdown when the last active X control is destroyed, and then
        ///     call Startup with the first activeX control is recreated. 
        /// 
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        private static void Startup() {
            if (startupRecreates) { 
                EnsureSystemEvents(false, false);
            } 
        } 

        ///  
        ///     This method is invoked via reflection from windows forms.  Why?  Because when the runtime is hosted in IE,
        ///     IE doesn't tell it when to shut down.  The first notification the runtime gets is
        ///     DLL_PROCESS_DETACH, at which point it is too late for us to run any managed code.  But,
        ///     if we don't destroy our system events window the HWND will fault if it 
        ///     receives a message after the runtime shuts down.  So it is imparative that
        ///     we destroy the window, but it is also necessary to recreate the window on demand. 
        ///     That's hard to do, because we originally created it in response to an event 
        ///     wire-up, but that event is still bound so technically we should still have the
        ///     window around.  To work around this crashing fiasco, we have special code 
        ///     in the ActiveXImpl class within Control.  This code checks to see if it is running
        ///     inside of IE, and if so, it will invoke these methods via private reflection.
        ///     It will invoke Shutdown when the last active X control is destroyed, and then
        ///     call Startup with the first activeX control is recreated. 
        /// 
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 
        private static void Shutdown() { 
            if (systemEvents != null && systemEvents.windowHandle != IntPtr.Zero) {
                lock(procLockObject) { 
                    if (systemEvents != null) {
                        startupRecreates = true;

                        // If we are using system events from another thread, request that it terminate 
                        //
                        if (windowThread != null) { 
                            eventThreadTerminated = new ManualResetEvent(false); 

#if DEBUG 
                            int pid;
                            int thread = SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(systemEvents, systemEvents.windowHandle), out pid);
                            Debug.Assert(thread != SafeNativeMethods.GetCurrentThreadId(), "Don't call Shutdown on the system events thread");
#endif 
                            UnsafeNativeMethods.PostMessage(new HandleRef(systemEvents, systemEvents.windowHandle), NativeMethods.WM_QUIT, IntPtr.Zero, IntPtr.Zero);
 
                            eventThreadTerminated.WaitOne(); 
                            windowThread.Join(); //avoids an AppDomainUnloaded exception on our background thread.
                        } 
                        else {
                            systemEvents.Dispose();
                            systemEvents = null;
                        } 
                    }
                } 
            } 
        }
 
        [PrePrepareMethod]
        private static void Shutdown(object sender, EventArgs e) {
            Shutdown();
        } 

        ///  
        ///      A standard Win32 window proc for our broadcast window. 
        /// 
        [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")] 
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private IntPtr WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam) {

            switch (msg) { 
                case NativeMethods.WM_SETTINGCHANGE:
                    string newString; 
                    IntPtr newStringPtr = lParam; 
                    if (lParam != IntPtr.Zero) {
                        newString = Marshal.PtrToStringAuto(lParam); 
                        if (newString != null) {
                            newStringPtr = Marshal.StringToHGlobalAuto(newString);
                        }
                    } 
                    UnsafeNativeMethods.PostMessage(new HandleRef(this, windowHandle), NativeMethods.WM_REFLECT + msg, wParam, newStringPtr);
                    break; 
                case NativeMethods.WM_WTSSESSION_CHANGE: 
                    OnSessionSwitch((int)wParam);
                    break; 
                case NativeMethods.WM_SYSCOLORCHANGE:
                case NativeMethods.WM_COMPACTING:
                case NativeMethods.WM_DISPLAYCHANGE:
                case NativeMethods.WM_FONTCHANGE: 
                case NativeMethods.WM_PALETTECHANGED:
                case NativeMethods.WM_TIMECHANGE: 
                case NativeMethods.WM_TIMER: 
                case NativeMethods.WM_THEMECHANGED:
                    UnsafeNativeMethods.PostMessage(new HandleRef(this, windowHandle), NativeMethods.WM_REFLECT + msg, wParam, lParam); 
                    break;

                case NativeMethods.WM_CREATETIMER:
                    return OnCreateTimer(wParam); 

                case NativeMethods.WM_KILLTIMER: 
                    return (IntPtr)(OnKillTimer(wParam) ? 1 : 0); 

                case NativeMethods.WM_REFLECT + NativeMethods.WM_SETTINGCHANGE: 
                    try {
                        OnUserPreferenceChanging(msg - NativeMethods.WM_REFLECT, wParam, lParam);
                        OnUserPreferenceChanged(msg - NativeMethods.WM_REFLECT, wParam, lParam);
                    } 
                    finally {
                        try { 
                            if (lParam != IntPtr.Zero) { 
                                Marshal.FreeHGlobal(lParam);
                            } 
                        }
                        catch (Exception e) {
                            Debug.Assert(false, "Exception occurred while freeing memory: " + e.ToString());
                        } 
                    }
                    break; 
 
                case NativeMethods.WM_REFLECT + NativeMethods.WM_SYSCOLORCHANGE:
                    OnUserPreferenceChanging(msg - NativeMethods.WM_REFLECT, wParam, lParam); 
                    OnUserPreferenceChanged(msg - NativeMethods.WM_REFLECT, wParam, lParam);
                    break;

                case NativeMethods.WM_REFLECT + NativeMethods.WM_THEMECHANGED: 
                    OnThemeChanged();
                    break; 
 
                case NativeMethods.WM_QUERYENDSESSION:
                    return(IntPtr) OnSessionEnding(lParam); 

                case NativeMethods.WM_ENDSESSION:
                    OnSessionEnded(wParam, lParam);
                    break; 

                case NativeMethods.WM_POWERBROADCAST: 
                    OnPowerModeChanged(wParam); 
                    break;
 
                    // WM_HIBERNATE on WinCE
                case NativeMethods.WM_REFLECT + NativeMethods.WM_COMPACTING:
                    OnGenericEvent(OnLowMemoryEvent);
                    break; 

                case NativeMethods.WM_REFLECT + NativeMethods.WM_DISPLAYCHANGE: 
                    OnDisplaySettingsChanging(); 
                    OnDisplaySettingsChanged();
                    break; 

                case NativeMethods.WM_REFLECT + NativeMethods.WM_FONTCHANGE:
                    OnGenericEvent(OnInstalledFontsChangedEvent);
                    break; 

                case NativeMethods.WM_REFLECT + NativeMethods.WM_PALETTECHANGED: 
                    OnGenericEvent(OnPaletteChangedEvent); 
                    break;
 
                case NativeMethods.WM_REFLECT + NativeMethods.WM_TIMECHANGE:
                    OnGenericEvent(OnTimeChangedEvent);
                    break;
 
                case NativeMethods.WM_REFLECT + NativeMethods.WM_TIMER:
                    OnTimerElapsed(wParam); 
                    break; 

                default: 
                    // If we received a thread execute message, then execute it.
                    //
                    if (msg == threadCallbackMessage && msg != 0) {
                        InvokeMarshaledCallbacks(); 
                        return IntPtr.Zero;
                    } 
 		    break; 
            }
 
            return UnsafeNativeMethods.DefWindowProc(hWnd, msg, wParam, lParam);
        }

        ///  
        ///      This is the method that runs our window thread.  This method
        ///      creates a window and spins up a message loop.  The window 
        ///      is made visible with a size of 0, 0, so that it will trap 
        ///      global broadcast messages.
        ///  
        [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void WindowThreadProc() {
            try { 
                Initialize();
                eventWindowReady.Set(); 
 
                if (windowHandle != IntPtr.Zero) {
                    NativeMethods.MSG msg = new NativeMethods.MSG(); 

                    bool keepRunning = true;

                    // Blocking on a GetMessage() call prevents the EE from being able to unwind 
                    // this thread properly (e.g. during AppDomainUnload). So, we use PeekMessage()
                    // and sleep so we always block in managed code instead. 
                    // 
                    while (keepRunning) {
                        int ret = UnsafeNativeMethods.MsgWaitForMultipleObjects(0, 0, false, 100, NativeMethods.QS_ALLINPUT); 

                        if (ret == NativeMethods.WAIT_TIMEOUT) {
                            Thread.Sleep(1);
                        } 
                        else {
                            while (UnsafeNativeMethods.PeekMessage(ref msg, NativeMethods.NullHandleRef, 0, 0, NativeMethods.PM_REMOVE)) 
                            { 
                                if (msg.message == NativeMethods.WM_QUIT) {
                                    keepRunning = false; 
                                    break;
                                }

                                UnsafeNativeMethods.TranslateMessage(ref msg); 
                                UnsafeNativeMethods.DispatchMessage(ref msg);
                            } 
                        } 
                    }
                } 

                OnShutdown(OnEventsThreadShutdownEvent);
            }
            catch (Exception e) { 
                // In case something very very wrong happend during the creation action.
                // This will unblock the calling thread. 
                // 
                eventWindowReady.Set();
 
                if (!((e is ThreadInterruptedException) || (e is ThreadAbortException))) {
                    Debug.Fail("Unexpected thread exception in system events window thread proc", e.ToString());
                }
            } 

            Dispose(); 
            if (eventThreadTerminated != null) { 
                eventThreadTerminated.Set();
            } 
        }

        // A class that helps fire events on the right thread.
        // 
        private class SystemEventInvokeInfo {
 
            private SynchronizationContext _syncContext; // the context that we'll use to fire against. 
            private Delegate    _delegate;     // the delegate we'll fire.  This is a weak ref so we don't hold object in memory.
            public SystemEventInvokeInfo(Delegate d) { 

                _delegate = d;
                _syncContext = AsyncOperationManager.SynchronizationContext;
            } 

            // fire the given event with the given params. 
            // 
            public void Invoke(bool checkFinalization, params object[] args) {
                try { 
                    // If we didn't get call back, or if we're using Everett threading, invoke directly.
                    //
                    if (_syncContext == null || SystemEvents.UseEverettThreadAffinity) {
                        InvokeCallback(args); 
                    }
                    else { 
                        // otherwise tell the context to do it for us. 
                        //
                        _syncContext.Send(new SendOrPostCallback(InvokeCallback), args); 
                    }
                }
                catch (InvalidAsynchronousStateException) {
                    //if the synch context is invalid -- do the invoke directly for app compat. 
                    //If the app's shutting down, don't fire the event (unless it's shutdown).
                    if (!checkFinalization || !AppDomain.CurrentDomain.IsFinalizingForUnload()) { 
                        InvokeCallback(args); 
                    }
                } 
            }

            // our delegate method that the SyncContext will call on.
            // 
            private void InvokeCallback(object arg) {
 
                _delegate.DynamicInvoke((object[])arg); 
            }
 
            public override bool Equals(object other) {
                SystemEventInvokeInfo otherInvoke = other as SystemEventInvokeInfo;

                if (otherInvoke == null) { 
                    return false;
                } 
                return otherInvoke._delegate.Equals(_delegate); 
            }
 
            public override int GetHashCode() {
                return base.GetHashCode();
            }
        } 
    }
} 
 


                        

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