From defe2f59c9ae73d2b9cb8fa0d299c2a8501d0aff Mon Sep 17 00:00:00 2001 From: Christian Nagel Date: Sat, 7 Jun 2025 17:58:35 +0200 Subject: [PATCH] Refactor WakeLock.PInvoke for compatibility with the ARM CPU #2745 Replaced `SimpleReasonString` field from `REASON_CONTEXT` with a union, REASON_CONTEXT_UNION. The ARM CPU is more strict with unions. Within unions, strings are not allowed, thus using a pointer to a string, with updated allocation within PowerCreateRequest. --- .../Running/WakeLock.PInvoke.cs | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/BenchmarkDotNet/Running/WakeLock.PInvoke.cs b/src/BenchmarkDotNet/Running/WakeLock.PInvoke.cs index 9da3fd1eff..be002d5d20 100644 --- a/src/BenchmarkDotNet/Running/WakeLock.PInvoke.cs +++ b/src/BenchmarkDotNet/Running/WakeLock.PInvoke.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using System.Runtime.InteropServices; namespace BenchmarkDotNet.Running; @@ -9,15 +10,27 @@ private static class PInvoke { public static SafePowerHandle PowerCreateRequest(string reason) { - REASON_CONTEXT context = new REASON_CONTEXT() + IntPtr reasonPtr = Marshal.StringToHGlobalAuto(reason); + try { - Version = POWER_REQUEST_CONTEXT_VERSION, - Flags = POWER_REQUEST_CONTEXT_FLAGS.POWER_REQUEST_CONTEXT_SIMPLE_STRING, - SimpleReasonString = reason - }; - SafePowerHandle safePowerHandle = PowerCreateRequest(context); - if (safePowerHandle.IsInvalid) { throw new Win32Exception(); } - return safePowerHandle; + 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_CONTEXT_UNION { SimpleReasonString = 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)] @@ -48,14 +61,34 @@ public static void PowerClearRequest(SafePowerHandle safePowerHandle, POWER_REQU [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)] public static extern bool CloseHandle(nint hObject); + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct REASON_CONTEXT { public uint Version; public POWER_REQUEST_CONTEXT_FLAGS Flags; - [MarshalAs(UnmanagedType.LPWStr)] - public string SimpleReasonString; + 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; + } + } } private const uint POWER_REQUEST_CONTEXT_VERSION = 0U;