diff --git a/src/BenchmarkDotNet/BenchmarkDotNet.csproj b/src/BenchmarkDotNet/BenchmarkDotNet.csproj
index 751b75a5da..660e222453 100644
--- a/src/BenchmarkDotNet/BenchmarkDotNet.csproj
+++ b/src/BenchmarkDotNet/BenchmarkDotNet.csproj
@@ -48,9 +48,17 @@
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
true
+
+ System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute
diff --git a/src/BenchmarkDotNet/Detectors/OsDetector.cs b/src/BenchmarkDotNet/Detectors/OsDetector.cs
index 1ccd86d26b..2f360d01f4 100644
--- a/src/BenchmarkDotNet/Detectors/OsDetector.cs
+++ b/src/BenchmarkDotNet/Detectors/OsDetector.cs
@@ -127,7 +127,6 @@ internal static bool IsWindows() =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
#endif
-
[SupportedOSPlatformGuard("linux")]
internal static bool IsLinux() =>
#if NET6_0_OR_GREATER
@@ -170,4 +169,12 @@ internal static bool IsTvOS() =>
#else
RuntimeInformation.IsOSPlatform(OSPlatform.Create("TVOS"));
#endif
+
+ [SupportedOSPlatformGuard("windows6.1")]
+ internal static bool IsWindows7OrLater() =>
+#if NET6_0_OR_GREATER
+ OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(6, 1);
+#else
+ RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version >= new Version(6, 1);
+#endif
}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Environments/PhysicalMemoryInfo.cs b/src/BenchmarkDotNet/Environments/PhysicalMemoryInfo.cs
index 56c8aeee2e..114836c438 100644
--- a/src/BenchmarkDotNet/Environments/PhysicalMemoryInfo.cs
+++ b/src/BenchmarkDotNet/Environments/PhysicalMemoryInfo.cs
@@ -1,6 +1,10 @@
+using BenchmarkDotNet.Detectors;
using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
using System.Text.RegularExpressions;
+using Windows.Win32;
+using Windows.Win32.System.SystemInformation;
namespace BenchmarkDotNet.Environments
{
@@ -35,13 +39,13 @@ public static class SystemMemory
{
try
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (OsDetector.IsWindows7OrLater())
return GetWindowsMemory();
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ if (OsDetector.IsLinux())
return GetLinuxMemory();
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ if (OsDetector.IsMacOS())
return GetMacMemory();
}
catch (Exception)
@@ -52,36 +56,13 @@ public static class SystemMemory
return null;
}
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
- private class MEMORYSTATUSEX
- {
- public uint dwLength;
- public uint dwMemoryLoad;
- public ulong ullTotalPhys;
- public ulong ullAvailPhys;
- public ulong ullTotalPageFile;
- public ulong ullAvailPageFile;
- public ulong ullTotalVirtual;
- public ulong ullAvailVirtual;
- public ulong ullAvailExtendedVirtual;
-
- public MEMORYSTATUSEX()
- {
- dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
- }
- }
-
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer);
-
+ [SupportedOSPlatform("windows5.1.2600")]
private static PhysicalMemoryInfo? GetWindowsMemory()
{
- var memStatus = new MEMORYSTATUSEX();
- if (GlobalMemoryStatusEx(memStatus))
- {
+ var memStatus = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf() };
+ if (PInvoke.GlobalMemoryStatusEx(ref memStatus))
return new PhysicalMemoryInfo((long)memStatus.ullTotalPhys, (long)memStatus.ullAvailPhys);
- }
+
return null;
}
diff --git a/src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs b/src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs
index 62ca6d6004..f25a5579b7 100644
--- a/src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs
+++ b/src/BenchmarkDotNet/Helpers/PowerManagementHelper.cs
@@ -1,64 +1,64 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
-using System.Text;
+using System.Runtime.Versioning;
+using Windows.Win32;
+using Windows.Win32.Foundation;
namespace BenchmarkDotNet.Helpers
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
+ [SupportedOSPlatform("windows6.0.6000")]
internal class PowerManagementHelper
{
- private const uint ErrorMoreData = 234;
- private const uint SuccessCode = 0;
-
- internal static Guid? CurrentPlan
+ internal static unsafe Guid? CurrentPlan
{
get
{
- IntPtr activeGuidPtr = IntPtr.Zero;
- uint res = PowerGetActiveScheme(IntPtr.Zero, ref activeGuidPtr);
- if (res != SuccessCode)
+ WIN32_ERROR res = PInvoke.PowerGetActiveScheme(null, out var activePolicyGuidPtr);
+ if (res != WIN32_ERROR.NO_ERROR)
return null;
- return Marshal.PtrToStructure(activeGuidPtr);
+ var activePolicyGuid = *activePolicyGuidPtr;
+
+ PInvoke.LocalFree((HLOCAL)activePolicyGuidPtr);
+
+ return activePolicyGuid;
}
}
- internal static string CurrentPlanFriendlyName
+ internal unsafe static string CurrentPlanFriendlyName
{
get
{
- uint buffSize = 0;
- StringBuilder buffer = new StringBuilder();
- IntPtr activeGuidPtr = IntPtr.Zero;
- uint res = PowerGetActiveScheme(IntPtr.Zero, ref activeGuidPtr);
- if (res != SuccessCode)
+ WIN32_ERROR res = PInvoke.PowerGetActiveScheme(null, out var activeGuidPtr);
+ if (res != WIN32_ERROR.NO_ERROR)
return "";
- res = PowerReadFriendlyName(IntPtr.Zero, activeGuidPtr, IntPtr.Zero, IntPtr.Zero, buffer, ref buffSize);
- if (res == ErrorMoreData)
+
+ Guid activeSchemaGuid = *activeGuidPtr;
+ PInvoke.LocalFree((HLOCAL)activeGuidPtr);
+
+ Span buffer = stackalloc byte[260];
+ uint bufferSize = (uint)buffer.Length;
+
+ res = PInvoke.PowerReadFriendlyName(null, activeSchemaGuid, null, null, buffer, ref bufferSize);
+ if (res == WIN32_ERROR.ERROR_MORE_DATA)
{
- buffer.Capacity = (int)buffSize;
- res = PowerReadFriendlyName(IntPtr.Zero, activeGuidPtr,
- IntPtr.Zero, IntPtr.Zero, buffer, ref buffSize);
+ buffer = new byte[(int)bufferSize];
+ res = PInvoke.PowerReadFriendlyName(null, activeSchemaGuid, null, null, buffer, ref bufferSize);
+ bufferSize = (uint)buffer.Length;
}
- if (res != SuccessCode)
+
+ if (res != WIN32_ERROR.NO_ERROR)
return "";
- return buffer.ToString();
+ ReadOnlySpan chars = MemoryMarshal.Cast(buffer.Slice(0, (int)bufferSize));
+ return chars.TrimEnd('\0').ToString(); // Trim null terminator of PWSTR.
}
}
internal static bool Set(Guid newPolicy)
{
- return PowerSetActiveScheme(IntPtr.Zero, ref newPolicy) == 0;
+ return PInvoke.PowerSetActiveScheme(null, newPolicy) == WIN32_ERROR.NO_ERROR;
}
-
- [DllImport("powrprof.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
- private static extern uint PowerReadFriendlyName(IntPtr RootPowerKey, IntPtr SchemeGuid, IntPtr SubGroupOfPowerSettingGuid, IntPtr PowerSettingGuid, StringBuilder Buffer, ref uint BufferSize);
-
- [DllImport("powrprof.dll", ExactSpelling = true)]
- private static extern int PowerSetActiveScheme(IntPtr ReservedZero, ref Guid policyGuid);
-
- [DllImport("powrprof.dll", ExactSpelling = true)]
- private static extern uint PowerGetActiveScheme(IntPtr UserRootPowerKey, ref IntPtr ActivePolicyGuid);
}
}
diff --git a/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs b/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs
index 5585dfbd49..953cf2d0fa 100644
--- a/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs
+++ b/src/BenchmarkDotNet/Helpers/TaskbarProgress.cs
@@ -1,5 +1,7 @@
using BenchmarkDotNet.Detectors;
using System.Runtime.InteropServices;
+using Windows.Win32.System.Console;
+using Windows.Win32;
namespace BenchmarkDotNet.Helpers
{
@@ -15,9 +17,7 @@ internal enum TaskbarProgressState
internal class TaskbarProgress : DisposeAtProcessTermination
{
- private static readonly bool OsVersionIsSupported = OsDetector.IsWindows()
- // Must be windows 7 or greater
- && Environment.OSVersion.Version >= new Version(6, 1);
+ private static readonly bool OsVersionIsSupported = OsDetector.IsWindows7OrLater();
private Com? com;
private Terminal? terminal;
@@ -97,9 +97,6 @@ private interface ITaskbarList3
[ComImport]
private class TaskbarInstance { }
- [DllImport("kernel32.dll")]
- private static extern IntPtr GetConsoleWindow();
-
private readonly ITaskbarList3 taskbarInstance;
private readonly IntPtr consoleWindowHandle;
@@ -113,7 +110,7 @@ private Com(IntPtr handle)
{
try
{
- IntPtr handle = GetConsoleWindow();
+ IntPtr handle = PInvoke.GetConsoleWindow();
if (handle == IntPtr.Zero)
{
return null;
@@ -142,49 +139,21 @@ internal void SetValue(uint progressValue)
private sealed class Terminal
{
- [Flags]
- private enum ConsoleModes : uint
- {
- ENABLE_PROCESSED_INPUT = 0x0001,
- ENABLE_LINE_INPUT = 0x0002,
- ENABLE_ECHO_INPUT = 0x0004,
- ENABLE_WINDOW_INPUT = 0x0008,
- ENABLE_MOUSE_INPUT = 0x0010,
- ENABLE_INSERT_MODE = 0x0020,
- ENABLE_QUICK_EDIT_MODE = 0x0040,
- ENABLE_EXTENDED_FLAGS = 0x0080,
- ENABLE_AUTO_POSITION = 0x0100,
-
- ENABLE_PROCESSED_OUTPUT = 0x0001,
- ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002,
- ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004,
- DISABLE_NEWLINE_AUTO_RETURN = 0x0008,
- ENABLE_LVB_GRID_WORLDWIDE = 0x0010
- }
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleModes lpMode);
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern bool SetConsoleMode(IntPtr hConsoleHandle, ConsoleModes dwMode);
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern IntPtr GetStdHandle(int nStdHandle);
- private const int STD_OUTPUT_HANDLE = -11;
-
- private readonly IntPtr consoleHandle;
+ private readonly SafeHandle consoleHandle;
private uint currentProgress;
- private Terminal(IntPtr handle)
+ private Terminal(SafeHandle handle)
=> consoleHandle = handle;
internal static Terminal? MaybeCreateInstanceAndSetInitialState(TaskbarProgressState initialTaskbarState)
{
- IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);
- if (handle == IntPtr.Zero)
+ var handle = PInvoke.GetStdHandle_SafeHandle(STD_HANDLE.STD_OUTPUT_HANDLE);
+ if (handle.IsInvalid)
{
return null;
}
- if (!GetConsoleMode(handle, out ConsoleModes previousConsoleMode)
- || !SetConsoleMode(handle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT))
+ if (!PInvoke.GetConsoleMode(handle, out CONSOLE_MODE previousConsoleMode)
+ || !PInvoke.SetConsoleMode(handle, CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING | CONSOLE_MODE.ENABLE_PROCESSED_OUTPUT))
{
// If we failed to set virtual terminal processing mode, it is likely due to an older Windows version that does not support it,
// or legacy console. In either case the TaskbarProgressCom will take care of the progress. See https://stackoverflow.com/a/44574463/5703407.
@@ -193,16 +162,16 @@ private Terminal(IntPtr handle)
}
var terminal = new Terminal(handle);
terminal.WriteStateSequence(initialTaskbarState);
- SetConsoleMode(handle, previousConsoleMode);
+ PInvoke.SetConsoleMode(handle, previousConsoleMode);
return terminal;
}
internal void SetState(TaskbarProgressState taskbarState)
{
- GetConsoleMode(consoleHandle, out ConsoleModes previousConsoleMode);
- SetConsoleMode(consoleHandle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT);
+ PInvoke.GetConsoleMode(consoleHandle, out CONSOLE_MODE previousConsoleMode);
+ PInvoke.SetConsoleMode(consoleHandle, CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING | CONSOLE_MODE.ENABLE_PROCESSED_OUTPUT);
WriteStateSequence(taskbarState);
- SetConsoleMode(consoleHandle, previousConsoleMode);
+ PInvoke.SetConsoleMode(consoleHandle, previousConsoleMode);
}
private void WriteStateSequence(TaskbarProgressState taskbarState)
@@ -238,10 +207,10 @@ internal void SetValue(uint progressValue)
{
currentProgress = progressValue;
// Write progress sequence to console for Windows Terminal (https://github.com/microsoft/terminal/discussions/14268).
- GetConsoleMode(consoleHandle, out ConsoleModes previousConsoleMode);
- SetConsoleMode(consoleHandle, ConsoleModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING | ConsoleModes.ENABLE_PROCESSED_OUTPUT);
+ PInvoke.GetConsoleMode(consoleHandle, out CONSOLE_MODE previousConsoleMode);
+ PInvoke.SetConsoleMode(consoleHandle, CONSOLE_MODE.ENABLE_VIRTUAL_TERMINAL_PROCESSING | CONSOLE_MODE.ENABLE_PROCESSED_OUTPUT);
WriteProgressSequence(progressValue);
- SetConsoleMode(consoleHandle, previousConsoleMode);
+ PInvoke.SetConsoleMode(consoleHandle, previousConsoleMode);
}
private static void WriteProgressSequence(uint progressValue)
diff --git a/src/BenchmarkDotNet/NativeMethods.json b/src/BenchmarkDotNet/NativeMethods.json
new file mode 100644
index 0000000000..e0369e5437
--- /dev/null
+++ b/src/BenchmarkDotNet/NativeMethods.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://aka.ms/CsWin32.schema.json",
+ "allowMarshaling": false,
+ "multiTargetingFriendlyAPIs": true
+}
diff --git a/src/BenchmarkDotNet/NativeMethods.txt b/src/BenchmarkDotNet/NativeMethods.txt
new file mode 100644
index 0000000000..2b98f025cd
--- /dev/null
+++ b/src/BenchmarkDotNet/NativeMethods.txt
@@ -0,0 +1,14 @@
+PowerGetActiveScheme
+PowerSetActiveScheme
+PowerReadFriendlyName
+GetConsoleWindow
+GetConsoleMode
+GetStdHandle
+SetConsoleMode
+CloseHandle
+PowerCreateRequest
+PowerClearRequest
+PowerSetRequest
+PowerGetActiveScheme
+LocalFree
+GlobalMemoryStatusEx
diff --git a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs
index 8e61198c3e..b3b0fb32f5 100644
--- a/src/BenchmarkDotNet/Running/PowerManagementApplier.cs
+++ b/src/BenchmarkDotNet/Running/PowerManagementApplier.cs
@@ -46,26 +46,32 @@ internal void ApplyPerformancePlan(Guid id)
private void ApplyUserPowerPlan()
{
- if (powerPlanChanged && OsDetector.IsWindows())
+ if (!powerPlanChanged)
+ return;
+
+ if (!OsDetector.IsWindows7OrLater())
+ return;
+
+ try
{
- try
- {
- if (userCurrentPowerPlan != null && PowerManagementHelper.Set(userCurrentPowerPlan.Value))
- {
- powerPlanChanged = false;
- var powerPlanFriendlyName = PowerManagementHelper.CurrentPlanFriendlyName;
- logger.WriteLineInfo($"Successfully reverted power plan (GUID: {userCurrentPowerPlan.Value} FriendlyName: {powerPlanFriendlyName})");
- }
- }
- catch (Exception ex)
+ if (userCurrentPowerPlan != null && PowerManagementHelper.Set(userCurrentPowerPlan.Value))
{
- logger.WriteLineError($"Cannot revert power plan (error message: {ex.Message})");
+ powerPlanChanged = false;
+ var powerPlanFriendlyName = PowerManagementHelper.CurrentPlanFriendlyName;
+ logger.WriteLineInfo($"Successfully reverted power plan (GUID: {userCurrentPowerPlan.Value} FriendlyName: {powerPlanFriendlyName})");
}
}
+ catch (Exception ex)
+ {
+ logger.WriteLineError($"Cannot revert power plan (error message: {ex.Message})");
+ }
}
private void ApplyPlanByGuid(Guid guid)
{
+ if (!OsDetector.IsWindows7OrLater())
+ return;
+
try
{
if (isInitialized == false)
diff --git a/src/BenchmarkDotNet/Running/WakeLock.PInvoke.cs b/src/BenchmarkDotNet/Running/WakeLock.PInvoke.cs
index 4edbc43720..c041ba175f 100644
--- a/src/BenchmarkDotNet/Running/WakeLock.PInvoke.cs
+++ b/src/BenchmarkDotNet/Running/WakeLock.PInvoke.cs
@@ -1,109 +1,61 @@
+using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using Windows.Win32;
+using Windows.Win32.System.Power;
+using Windows.Win32.System.Threading;
namespace BenchmarkDotNet.Running;
internal partial class WakeLock
{
- private static class PInvoke
+ [SupportedOSPlatform("windows6.1")]
+ public static SafeFileHandle PowerCreateRequest(string reason)
{
- public static SafePowerHandle PowerCreateRequest(string reason)
+ IntPtr reasonPtr = Marshal.StringToHGlobalAuto(reason);
+ try
{
- IntPtr reasonPtr = Marshal.StringToHGlobalAuto(reason);
- try
+ const uint POWER_REQUEST_CONTEXT_VERSION = 0U;
+ var context = new REASON_CONTEXT()
{
- REASON_CONTEXT context = new REASON_CONTEXT()
+ Version = POWER_REQUEST_CONTEXT_VERSION,
+ Flags = POWER_REQUEST_CONTEXT_FLAGS.POWER_REQUEST_CONTEXT_SIMPLE_STRING,
+ Reason = new REASON_CONTEXT._Reason_e__Union
{
- Version = POWER_REQUEST_CONTEXT_VERSION,
- Flags = POWER_REQUEST_CONTEXT_FLAGS.POWER_REQUEST_CONTEXT_SIMPLE_STRING,
- Reason = new REASON_CONTEXT.REASON_CONTEXT_UNION { SimpleReasonString = reasonPtr }
+ SimpleReasonString = new Windows.Win32.Foundation.PWSTR(reasonPtr)
+ },
+ };
- };
- SafePowerHandle safePowerHandle = PowerCreateRequest(context);
- if (safePowerHandle.IsInvalid) { throw new Win32Exception(); }
- return safePowerHandle;
- }
- finally
- {
- if (reasonPtr != IntPtr.Zero)
- {
- Marshal.FreeHGlobal(reasonPtr);
- }
- }
- }
-
- [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
- private static extern SafePowerHandle PowerCreateRequest(REASON_CONTEXT Context);
+ var safePowerHandle = PInvoke.PowerCreateRequest(context);
- public static void PowerSetRequest(SafePowerHandle safePowerHandle, POWER_REQUEST_TYPE requestType)
- {
- if (!InvokePowerSetRequest(safePowerHandle, requestType))
- {
+ if (safePowerHandle.IsInvalid)
throw new Win32Exception();
- }
- }
-
- [DllImport("kernel32.dll", EntryPoint = "PowerSetRequest", ExactSpelling = true, SetLastError = true)]
- private static extern bool InvokePowerSetRequest(SafePowerHandle PowerRequest, POWER_REQUEST_TYPE RequestType);
- public static void PowerClearRequest(SafePowerHandle safePowerHandle, POWER_REQUEST_TYPE requestType)
- {
- if (!InvokePowerClearRequest(safePowerHandle, requestType))
- {
- throw new Win32Exception();
- }
+ return safePowerHandle;
}
-
- [DllImport("kernel32.dll", EntryPoint = "PowerClearRequest", ExactSpelling = true, SetLastError = true)]
- private static extern bool InvokePowerClearRequest(SafePowerHandle PowerRequest, POWER_REQUEST_TYPE RequestType);
-
- [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
- public static extern bool CloseHandle(nint hObject);
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- private struct REASON_CONTEXT
+ finally
{
- public uint Version;
-
- public POWER_REQUEST_CONTEXT_FLAGS Flags;
-
- public REASON_CONTEXT_UNION Reason;
-
- [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
- public struct REASON_CONTEXT_UNION
- {
- [FieldOffset(0)]
- public nint SimpleReasonString;
-
- // The DETAILED structure is not (yet) used, but needed for ARM CPUs, otherwise PowerCreateRequest fails, see #2745
- [FieldOffset(0)]
- public DETAILED Detailed;
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- public struct DETAILED
- {
- public nint LocalizedReasonModule;
- public uint LocalizedReasonId;
- public uint ReasonStringCount;
- public nint ReasonStrings;
- }
- }
+ if (reasonPtr != IntPtr.Zero)
+ Marshal.FreeHGlobal(reasonPtr);
}
+ }
- private const uint POWER_REQUEST_CONTEXT_VERSION = 0U;
+ [SupportedOSPlatform("windows6.1")]
+ private static void PowerSetRequest(SafeFileHandle safePowerHandle, POWER_REQUEST_TYPE requestType)
+ {
+ if (PInvoke.PowerSetRequest(safePowerHandle, requestType))
+ return;
- private enum POWER_REQUEST_CONTEXT_FLAGS : uint
- {
- POWER_REQUEST_CONTEXT_DETAILED_STRING = 2U,
- POWER_REQUEST_CONTEXT_SIMPLE_STRING = 1U,
- }
+ throw new Win32Exception();
+ }
- public enum POWER_REQUEST_TYPE
- {
- PowerRequestDisplayRequired = 0,
- PowerRequestSystemRequired = 1,
- PowerRequestAwayModeRequired = 2,
- PowerRequestExecutionRequired = 3,
- }
+ [SupportedOSPlatform("windows6.1")]
+ private static void PowerClearRequest(SafeFileHandle safePowerHandle, POWER_REQUEST_TYPE requestType)
+ {
+ if (PInvoke.PowerClearRequest(safePowerHandle, requestType))
+ return;
+
+ throw new Win32Exception();
}
-}
\ No newline at end of file
+}
diff --git a/src/BenchmarkDotNet/Running/WakeLock.SafePowerHandle.cs b/src/BenchmarkDotNet/Running/WakeLock.SafePowerHandle.cs
deleted file mode 100644
index eaa1f3b2b2..0000000000
--- a/src/BenchmarkDotNet/Running/WakeLock.SafePowerHandle.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Microsoft.Win32.SafeHandles;
-
-namespace BenchmarkDotNet.Running;
-
-internal partial class WakeLock
-{
- private sealed class SafePowerHandle : SafeHandleZeroOrMinusOneIsInvalid
- {
- private SafePowerHandle() : base(true) { }
-
- protected override bool ReleaseHandle() => PInvoke.CloseHandle(handle);
- }
-}
\ No newline at end of file
diff --git a/src/BenchmarkDotNet/Running/WakeLock.cs b/src/BenchmarkDotNet/Running/WakeLock.cs
index 507958957b..5982172cad 100644
--- a/src/BenchmarkDotNet/Running/WakeLock.cs
+++ b/src/BenchmarkDotNet/Running/WakeLock.cs
@@ -2,26 +2,35 @@
using BenchmarkDotNet.Detectors;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Loggers;
+using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
+using System.Runtime.Versioning;
+using Windows.Win32.System.Power;
namespace BenchmarkDotNet.Running;
internal partial class WakeLock
{
public static WakeLockType GetWakeLockType(BenchmarkRunInfo[] benchmarkRunInfos) =>
- benchmarkRunInfos.Length == 0 ? WakeLockType.None : benchmarkRunInfos.Select(static i => i.Config.WakeLock).Max();
+ benchmarkRunInfos.Length == 0 ? WakeLockType.None : benchmarkRunInfos.Max(static i => i.Config.WakeLock);
+
+ public static IDisposable? Request(WakeLockType wakeLockType, string reason, ILogger logger)
+ {
+ if (wakeLockType == WakeLockType.None)
+ return null;
- private static readonly bool OsVersionIsSupported =
// Must be windows 7 or greater
- OsDetector.IsWindows() && Environment.OSVersion.Version >= new Version(6, 1);
+ if (!OsDetector.IsWindows7OrLater())
+ return null;
- public static IDisposable? Request(WakeLockType wakeLockType, string reason, ILogger logger) =>
- wakeLockType == WakeLockType.None || !OsVersionIsSupported ? null : new WakeLockSentinel(wakeLockType, reason, logger);
+ return new WakeLockSentinel(wakeLockType, reason, logger);
+ }
+ [SupportedOSPlatform("windows6.1")]
private class WakeLockSentinel : DisposeAtProcessTermination
{
private readonly WakeLockType wakeLockType;
- private readonly SafePowerHandle? safePowerHandle;
+ private readonly SafeFileHandle? safePowerHandle;
private readonly ILogger logger;
public WakeLockSentinel(WakeLockType wakeLockType, string reason, ILogger logger)
@@ -30,11 +39,11 @@ public WakeLockSentinel(WakeLockType wakeLockType, string reason, ILogger logger
this.logger = logger;
try
{
- safePowerHandle = PInvoke.PowerCreateRequest(reason);
- PInvoke.PowerSetRequest(safePowerHandle, PInvoke.POWER_REQUEST_TYPE.PowerRequestSystemRequired);
+ safePowerHandle = PowerCreateRequest(reason);
+ PowerSetRequest(safePowerHandle, POWER_REQUEST_TYPE.PowerRequestSystemRequired);
if (wakeLockType == WakeLockType.Display)
{
- PInvoke.PowerSetRequest(safePowerHandle, PInvoke.POWER_REQUEST_TYPE.PowerRequestDisplayRequired);
+ PowerSetRequest(safePowerHandle, POWER_REQUEST_TYPE.PowerRequestDisplayRequired);
}
}
catch (Win32Exception ex)
@@ -51,9 +60,9 @@ protected override void Dispose(bool exiting)
{
if (wakeLockType == WakeLockType.Display)
{
- PInvoke.PowerClearRequest(safePowerHandle, PInvoke.POWER_REQUEST_TYPE.PowerRequestDisplayRequired);
+ PowerClearRequest(safePowerHandle, POWER_REQUEST_TYPE.PowerRequestDisplayRequired);
}
- PInvoke.PowerClearRequest(safePowerHandle, PInvoke.POWER_REQUEST_TYPE.PowerRequestSystemRequired);
+ PowerClearRequest(safePowerHandle, POWER_REQUEST_TYPE.PowerRequestSystemRequired);
}
catch (Win32Exception ex)
{
diff --git a/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs b/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs
index 0ed58915d5..bc9094407e 100644
--- a/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs
+++ b/tests/BenchmarkDotNet.IntegrationTests/PowerManagementApplierTests.cs
@@ -1,3 +1,4 @@
+using BenchmarkDotNet.Detectors;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Running;
@@ -16,6 +17,9 @@ public PowerManagementApplierTests(ITestOutputHelper output) : base(output) { }
[FactEnvSpecific("Setting high-performance plan is suitable only on Windows", EnvRequirement.WindowsOnly)]
public void TestSettingAndRevertingBackGuid()
{
+ if (!OsDetector.IsWindows7OrLater())
+ return;
+
var userPlan = PowerManagementHelper.CurrentPlan;
using var powerManagementApplier = new PowerManagementApplier(new OutputLogger(Output));
@@ -33,6 +37,9 @@ public void TestSettingAndRevertingBackGuid()
[FactEnvSpecific("Setting high-performance plan is suitable only on Windows", EnvRequirement.WindowsOnly)]
public void TestPowerPlanShouldNotChange()
{
+ if (!OsDetector.IsWindows7OrLater())
+ return;
+
var userPlan = PowerManagementHelper.CurrentPlan;
using var powerManagementApplier = new PowerManagementApplier(new OutputLogger(Output));