Skip to content

Commit

Permalink
Merge in 'release/6.0' changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dotnet-bot committed Mar 8, 2022
2 parents ef034e7 + bf059d9 commit 1565e21
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ internal static partial class User32

public const int WAIT_TIMEOUT = 0x00000102;

public const int WM_DESTROY = 0x0002;
public const int WM_CLOSE = 0x0010;
public const int WM_QUERYENDSESSION = 0x0011;
public const int WM_QUIT = 0x0012;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ internal static partial class Interop
internal static partial class User32
{
[DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)]
public static extern bool PeekMessageW([In, Out] ref MSG msg, IntPtr hwnd, int msgMin, int msgMax, int remove);
public static extern int GetMessageW(ref MSG msg, IntPtr hwnd, int msgMin, int msgMax);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class User32
{
[DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)]
public static extern void PostQuitMessage(int exitCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

Commonly Used Types:
Microsoft.Win32.SystemEvents</PackageDescription>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<ServicingVersion>1</ServicingVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent);netcoreapp3.1-windows;netcoreapp3.1;netstandard2.0;net461</TargetFrameworks>
Expand Down Expand Up @@ -35,6 +37,8 @@ Microsoft.Win32.SystemEvents</PackageDescription>
Link="Common\Interop\Windows\User32\Interop.DispatchMessage.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.GetClassInfo.cs"
Link="Common\Interop\Windows\User32\Interop.GetClassInfo.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.GetMessage.cs"
Link="Common\Interop\Windows\User32\Interop.GetMessage.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.GetProcessWindowStation.cs"
Link="Common\Interop\Windows\User32\Interop.GetProcessWindowStation.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.GetUserObjectInformation.cs"
Expand All @@ -49,10 +53,10 @@ Microsoft.Win32.SystemEvents</PackageDescription>
Link="Common\Interop\Windows\User32\Interop.MSG.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.MsgWaitForMultipleObjectsEx.cs"
Link="Common\Interop\Windows\User32\Interop.MsgWaitForMultipleObjectsEx.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.PeekMessage.cs"
Link="Common\Interop\Windows\User32\Interop.PeekMessage.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.PostMessage.cs"
Link="Common\Interop\Windows\User32\Interop.PostMessage.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.PostQuitMessage.cs"
Link="Common\Interop\Windows\User32\Interop.PostQuitMessage.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.RegisterClass.cs"
Link="Common\Interop\Windows\User32\Interop.RegisterClass.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.RegisterWindowMessage.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public sealed class SystemEvents
// cross-thread marshaling
private static volatile Queue<Delegate>? s_threadCallbackList; // list of Delegates
private static volatile int s_threadCallbackMessage;
private static volatile ManualResetEvent? s_eventThreadTerminated;

// Per-instance data that is isolated to the window thread.
private volatile IntPtr _windowHandle;
Expand Down Expand Up @@ -1071,7 +1070,7 @@ private static void RemoveEventHandler(object key, Delegate? value)

private static void Shutdown()
{
if (s_systemEvents != null && s_systemEvents._windowHandle != IntPtr.Zero)
if (s_systemEvents != null)
{
lock (s_procLockObject)
{
Expand All @@ -1080,17 +1079,22 @@ private static void Shutdown()
// If we are using system events from another thread, request that it terminate
if (s_windowThread != null)
{
s_eventThreadTerminated = new ManualResetEvent(false);

#if DEBUG
int pid;
int thread = Interop.User32.GetWindowThreadProcessId(new HandleRef(s_systemEvents, s_systemEvents._windowHandle), out pid);
Debug.Assert(thread != Interop.Kernel32.GetCurrentThreadId(), "Don't call Shutdown on the system events thread");
#endif
Interop.User32.PostMessageW(new HandleRef(s_systemEvents, s_systemEvents._windowHandle), Interop.User32.WM_QUIT, IntPtr.Zero, IntPtr.Zero);

s_eventThreadTerminated.WaitOne();
s_windowThread.Join(); // avoids an AppDomainUnloaded exception on our background thread.
// The handle could be valid, Zero or invalid depending on the state of the thread
// that is processing the messages. We optimistically expect it to be valid to
// notify the thread to shutdown. The Zero or invalid values should be present
// only when the thread is already shutting down due to external factors.
if (s_systemEvents._windowHandle != IntPtr.Zero)
{
Interop.User32.PostMessageW(new HandleRef(s_systemEvents, s_systemEvents._windowHandle), Interop.User32.WM_QUIT, IntPtr.Zero, IntPtr.Zero);
}

s_windowThread.Join();
}
else
{
Expand Down Expand Up @@ -1218,6 +1222,11 @@ private IntPtr WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
OnTimerElapsed(wParam);
break;

case Interop.User32.WM_DESTROY:
Interop.User32.PostQuitMessage(0);
_windowHandle = IntPtr.Zero;
break;

default:
// If we received a thread execute message, then execute it.
if (msg == s_threadCallbackMessage && msg != 0)
Expand Down Expand Up @@ -1248,33 +1257,10 @@ private void WindowThreadProc()
{
Interop.User32.MSG msg = default(Interop.User32.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)
while (Interop.User32.GetMessageW(ref msg, _windowHandle, 0, 0) > 0)
{
int ret = Interop.User32.MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 100, Interop.User32.QS_ALLINPUT, Interop.User32.MWMO_INPUTAVAILABLE);

if (ret == Interop.User32.WAIT_TIMEOUT)
{
Thread.Sleep(1);
}
else
{
while (Interop.User32.PeekMessageW(ref msg, IntPtr.Zero, 0, 0, Interop.User32.PM_REMOVE))
{
if (msg.message == Interop.User32.WM_QUIT)
{
keepRunning = false;
break;
}

Interop.User32.TranslateMessage(ref msg);
Interop.User32.DispatchMessageW(ref msg);
}
}
Interop.User32.TranslateMessage(ref msg);
Interop.User32.DispatchMessageW(ref msg);
}
}

Expand All @@ -1293,10 +1279,6 @@ private void WindowThreadProc()
}

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

// A class that helps fire events on the right thread.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<Compile Include="SystemEvents.SessionSwitch.cs" />
<Compile Include="SystemEvents.PowerMode.cs" />
<Compile Include="SystemEvents.TimeChanged.cs" />
<Compile Include="ShutdownTest.cs" />
<Compile Include="SystemEventsTest.cs" />
<Compile Include="SystemEvents.DisplaySettings.cs" />
<Compile Include="SystemEvents.CreateTimer.cs" />
Expand Down
33 changes: 33 additions & 0 deletions src/libraries/Microsoft.Win32.SystemEvents/tests/ShutdownTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Threading;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;
using static Interop;

namespace Microsoft.Win32.SystemEventsTests
{
public abstract class ShutdownTest : SystemEventsTest
{
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoNorServerCore))]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public void ShutdownThroughRestartManager()
{
RemoteExecutor.Invoke(() =>
{
// Register any event to ensure that SystemEvents get initialized
SystemEvents.TimeChanged += (o, e) => { };
// Fake Restart Manager behavior by sending external WM_CLOSE message
SendMessage(Interop.User32.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
// Emulate calling the Shutdown event
var shutdownMethod = typeof(SystemEvents).GetMethod("Shutdown", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, null, new Type[0], null);
Assert.NotNull(shutdownMethod);
shutdownMethod.Invoke(null, null);
}).Dispose();
}
}
}

0 comments on commit 1565e21

Please sign in to comment.