From f6ea9a8c465380cfbcf00c2a8640b2e4664d99d7 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 29 May 2021 13:50:35 -0400 Subject: [PATCH 1/2] Always spawn message loop thread for SystemEvents --- .../src/Microsoft/Win32/SystemEvents.cs | 40 +++++++------------ .../Microsoft.Win32.SystemEvents.Tests.csproj | 1 + .../SystemEvents.InvokeOnEventsThread.cs | 30 ++++++++++++-- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft/Win32/SystemEvents.cs b/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft/Win32/SystemEvents.cs index 374070553e433..077e5fa1ad841 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft/Win32/SystemEvents.cs +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft/Win32/SystemEvents.cs @@ -469,33 +469,23 @@ private static void EnsureSystemEvents(bool requireHandle) { if (s_systemEvents == null) { - // 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 systemEvents = new SystemEvents(); - systemEvents.Initialize(); + // Create a new pumping thread. We always create one even if the current thread + // is STA, as there are no guarantees this thread will pump nor still be alive + // for the desired duration. - // ensure this is initialized last as that will force concurrent threads calling - // this method to block until after we've initialized. - s_systemEvents = systemEvents; - } - else + s_eventWindowReady = new ManualResetEvent(false); + SystemEvents systemEvents = new SystemEvents(); + s_windowThread = new Thread(new ThreadStart(systemEvents.WindowThreadProc)) { - s_eventWindowReady = new ManualResetEvent(false); - SystemEvents systemEvents = new SystemEvents(); - s_windowThread = new Thread(new ThreadStart(systemEvents.WindowThreadProc)) - { - IsBackground = true, - Name = ".NET System Events" - }; - s_windowThread.Start(); - s_eventWindowReady.WaitOne(); - - // ensure this is initialized last as that will force concurrent threads calling - // this method to block until after we've initialized. - s_systemEvents = systemEvents; - } + IsBackground = true, + Name = ".NET System Events" + }; + s_windowThread.Start(); + s_eventWindowReady.WaitOne(); + + // ensure this is initialized last as that will force concurrent threads calling + // this method to block until after we've initialized. + s_systemEvents = systemEvents; if (requireHandle && s_systemEvents._windowHandle == IntPtr.Zero) { diff --git a/src/libraries/Microsoft.Win32.SystemEvents/tests/Microsoft.Win32.SystemEvents.Tests.csproj b/src/libraries/Microsoft.Win32.SystemEvents/tests/Microsoft.Win32.SystemEvents.Tests.csproj index c8efc5731d0d2..4ccc4b8d8845d 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/tests/Microsoft.Win32.SystemEvents.Tests.csproj +++ b/src/libraries/Microsoft.Win32.SystemEvents/tests/Microsoft.Win32.SystemEvents.Tests.csproj @@ -1,6 +1,7 @@ $(NetCoreAppCurrent)-windows;net461-windows + true // to ensure no one has registered for any events before + { + bool changing = false, changed = false; + + // Register for the events on an STA thread that then immediately exits + var thread = new Thread(() => + { + SystemEvents.DisplaySettingsChanging += (o, e) => changing = true; + SystemEvents.DisplaySettingsChanged += (o, e) => changed = true; + }); + thread.SetApartmentState(ApartmentState.STA); + thread.IsBackground = true; + thread.Start(); + thread.Join(); + + SendMessage(User32.WM_REFLECT + User32.WM_DISPLAYCHANGE, IntPtr.Zero, IntPtr.Zero); + + Assert.True(changing); + Assert.True(changed); + }); + } } } From 260b3273143c2c08520d135350acade61d568bcb Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 29 May 2021 19:34:33 -0400 Subject: [PATCH 2/2] Update src/libraries/Microsoft.Win32.SystemEvents/tests/SystemEvents.InvokeOnEventsThread.cs Co-authored-by: Jan Kotas --- .../tests/SystemEvents.InvokeOnEventsThread.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/Microsoft.Win32.SystemEvents/tests/SystemEvents.InvokeOnEventsThread.cs b/src/libraries/Microsoft.Win32.SystemEvents/tests/SystemEvents.InvokeOnEventsThread.cs index 1074224ebcf0a..b665f5c5d7f5a 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/tests/SystemEvents.InvokeOnEventsThread.cs +++ b/src/libraries/Microsoft.Win32.SystemEvents/tests/SystemEvents.InvokeOnEventsThread.cs @@ -67,7 +67,6 @@ public void RegisterFromSTAThreadThatGoesAway_MessageStillDelivered() SystemEvents.DisplaySettingsChanged += (o, e) => changed = true; }); thread.SetApartmentState(ApartmentState.STA); - thread.IsBackground = true; thread.Start(); thread.Join();