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();
}
}
}
}