< Summary - Results for net8.0, Release

Information
Class: LockCheck.Windows.NativeMethods.ENERGY_STATE_DURATION
Assembly: LockCheck
File(s): D:\a\LockCheck\LockCheck\src\LockCheck\Windows\NativeMethods.cs
Tag: 117_11660770947
Line coverage
0%
Covered lines: 0
Uncovered lines: 3
Coverable lines: 3
Total lines: 1332
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_LastChangeTime()100%210%
get_Duration()100%210%
get_IsInState()100%210%

File(s)

D:\a\LockCheck\LockCheck\src\LockCheck\Windows\NativeMethods.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.ComponentModel;
 4using System.IO;
 5using System.Runtime.InteropServices;
 6using System.Security.Principal;
 7using System.Text;
 8using System.Threading;
 9using Microsoft.Win32.SafeHandles;
 10
 11#pragma warning disable IDE1006 // Naming Styles - off here, because we want to use native names
 12
 13namespace LockCheck.Windows;
 14
 15internal static partial class NativeMethods
 16{
 17    private const string NtDll = "ntdll.dll";
 18    private const string RestartManagerDll = "rstrtmgr.dll";
 19    private const string AdvApi32Dll = "advapi32.dll";
 20    private const string KernelDll = "kernel32.dll";
 21
 22    internal const int ERROR_SEM_TIMEOUT = 121;
 23    internal const int ERROR_INSUFFICIENT_BUFFER = 122;
 24    internal const int ERROR_BAD_ARGUMENTS = 160;
 25    internal const int ERROR_MAX_SESSIONS_REACHED = 353;
 26    internal const int ERROR_WRITE_FAULT = 29;
 27    internal const int ERROR_OUTOFMEMORY = 14;
 28    internal const int ERROR_MORE_DATA = 234;
 29    internal const int ERROR_ACCESS_DENIED = 5;
 30    internal const int ERROR_INVALID_HANDLE = 6;
 31    internal const int ERROR_GEN_FAILURE = 31;
 32    internal const int ERROR_SHARING_VIOLATION = 32;
 33    internal const int ERROR_LOCK_VIOLATION = 33;
 34    internal const int ERROR_CANCELLED = 1223;
 35
 36    internal const uint STATUS_SUCCESS = 0;
 37    internal const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
 38
 39    [StructLayout(LayoutKind.Sequential, Pack = 0)]
 40    internal struct IO_STATUS_BLOCK
 41    {
 42        public uint Status;
 43        public IntPtr Information;
 44    }
 45
 46    [StructLayout(LayoutKind.Sequential)]
 47    internal struct FILE_PROCESS_IDS_USING_FILE_INFORMATION
 48    {
 49        public uint NumberOfProcessIdsInList;
 50        public IntPtr ProcessIdList;
 51    }
 52
 53    internal enum FILE_INFORMATION_CLASS
 54    {
 55        FileProcessIdsUsingFileInformation = 47
 56    }
 57
 58    internal enum PROCESS_INFORMATION_CLASS
 59    {
 60        ProcessBasicInformation = 0,
 61        ProcessWow64Information = 26,
 62        ProcessSequenceNumber = 92,
 63    }
 64
 65    internal enum SYSTEM_INFORMATION_CLASS
 66    {
 67        SystemProcessInformation = 5,
 68        SystemExtendedProcessInformation = 0x39,
 69        SystemFullProcessInformation = 0x94
 70    }
 71
 72#if NET
 73    [LibraryImport(NtDll)]
 74    internal static partial uint NtQueryInformationFile(SafeFileHandle fileHandle, ref IO_STATUS_BLOCK IoStatusBlock,
 75        IntPtr pInfoBlock, uint length, FILE_INFORMATION_CLASS fileInformation);
 76#else
 77    [DllImport(NtDll)]
 78    internal static extern uint NtQueryInformationFile(SafeFileHandle fileHandle, ref IO_STATUS_BLOCK IoStatusBlock,
 79        IntPtr pInfoBlock, uint length, FILE_INFORMATION_CLASS fileInformation);
 80#endif
 81
 82#if NET
 83    [LibraryImport(NtDll)]
 84    internal static partial uint NtQueryInformationProcess(SafeProcessHandle hProcess,
 85        PROCESS_INFORMATION_CLASS processInformationClass,
 86        ref PROCESS_BASIC_INFORMATION processInformation, int processInformationLength, IntPtr returnLength);
 87#else
 88    [DllImport(NtDll)]
 89    internal static extern uint NtQueryInformationProcess(SafeProcessHandle hProcess,
 90        PROCESS_INFORMATION_CLASS processInformationClass,
 91        ref PROCESS_BASIC_INFORMATION processInformation, int processInformationLength, IntPtr returnLength);
 92#endif
 93
 94#if NET
 95    [LibraryImport(NtDll)]
 96    internal static partial uint NtQueryInformationProcess(SafeProcessHandle hProcess,
 97        PROCESS_INFORMATION_CLASS processInformationClass,
 98        ref IntPtr processInformation, int processInformationLength, IntPtr returnLength);
 99#else
 100    [DllImport(NtDll)]
 101    internal static extern uint NtQueryInformationProcess(SafeProcessHandle hProcess,
 102        PROCESS_INFORMATION_CLASS processInformationClass,
 103        ref IntPtr processInformation, int processInformationLength, IntPtr returnLength);
 104#endif
 105
 106
 107#if NET
 108    [LibraryImport(NtDll)]
 109    internal static partial int NtWow64QueryInformationProcess64(SafeProcessHandle hProcess,
 110        PROCESS_INFORMATION_CLASS processInformationClass,
 111        ref PROCESS_BASIC_INFORMATION_WOW64 processInformation, int processInformationLength, IntPtr returnLength);
 112#else
 113    [DllImport(NtDll)]
 114    internal static extern int NtWow64QueryInformationProcess64(SafeProcessHandle hProcess,
 115        PROCESS_INFORMATION_CLASS processInformationClass,
 116        ref PROCESS_BASIC_INFORMATION_WOW64 processInformation, int processInformationLength, IntPtr returnLength);
 117#endif
 118
 119#if NET
 120    [LibraryImport(NtDll)]
 121    internal static partial int NtWow64QueryInformationProcess64(SafeProcessHandle hProcess,
 122        PROCESS_INFORMATION_CLASS processInformationClass,
 123        ref IntPtr processInformation, int processInformationLength, IntPtr returnLength);
 124#else
 125    [DllImport(NtDll)]
 126    internal static extern int NtWow64QueryInformationProcess64(SafeProcessHandle hProcess,
 127        PROCESS_INFORMATION_CLASS processInformationClass,
 128        ref IntPtr processInformation, int processInformationLength, IntPtr returnLength);
 129#endif
 130
 131#if NET
 132    [LibraryImport(NtDll, EntryPoint = "NtQueryInformationProcess")]
 133    internal static partial int NtQueryInformationProcessWow64(SafeProcessHandle hProcess, PROCESS_INFORMATION_CLASS pro
 134        ref IntPtr processInformation, int processInformationLength, IntPtr returnLength);
 135#else
 136    [DllImport(NtDll, EntryPoint = "NtQueryInformationProcess")]
 137    internal static extern int NtQueryInformationProcessWow64(SafeProcessHandle hProcess, PROCESS_INFORMATION_CLASS proc
 138        ref IntPtr processInformation, int processInformationLength, IntPtr returnLength);
 139#endif
 140
 141#if NET
 142    [LibraryImport(NtDll)]
 143    internal static unsafe partial uint NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS systemInformationClass, void* 
 144#else
 145    [DllImport(NtDll)]
 146    internal static extern int NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS systemInformationClass, IntPtr dataPtr,
 147#endif
 148
 149#if NET
 150    [LibraryImport(NtDll)]
 151    internal static partial int RtlNtStatusToDosError(uint status);
 152#else
 153    [DllImport(NtDll)]
 154    internal static extern int RtlNtStatusToDosError(uint status);
 155#endif
 156
 157
 158    [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)]
 159    internal static extern int RmRegisterResources(uint pSessionHandle,
 160        uint nFiles,
 161        string[] rgsFilenames,
 162        uint nApplications,
 163        [In] RM_UNIQUE_PROCESS[]? rgApplications,
 164        uint nServices,
 165        string[]? rgsServiceNames);
 166
 167    [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)]
 168    internal static extern int RmStartSession(out uint pSessionHandle,
 169        int dwSessionFlags, StringBuilder strSessionKey);
 170
 171    [DllImport(RestartManagerDll)]
 172    internal static extern int RmEndSession(uint pSessionHandle);
 173
 174    [DllImport(RestartManagerDll, CharSet = CharSet.Unicode)]
 175    internal static extern int RmGetList(uint dwSessionHandle,
 176        out uint pnProcInfoNeeded,
 177        ref uint pnProcInfo,
 178        [In, Out] RM_PROCESS_INFO[]? rgAffectedApps,
 179        ref uint lpdwRebootReasons);
 180
 181    [StructLayout(LayoutKind.Sequential)]
 182    internal struct FILETIME
 183    {
 184        public uint dwLowDateTime;
 185        public uint dwHighDateTime;
 186    }
 187
 188    [StructLayout(LayoutKind.Sequential)]
 189    internal struct RM_UNIQUE_PROCESS
 190    {
 191        public uint dwProcessId;
 192        public FILETIME ProcessStartTime;
 193    }
 194
 195    internal const int RM_INVALID_SESSION = -1;
 196    internal const int RM_INVALID_PROCESS = -1;
 197
 198    internal const int CCH_RM_MAX_APP_NAME = 255;
 199    internal const int CCH_RM_MAX_SVC_NAME = 63;
 200
 201    internal static readonly int RM_SESSION_KEY_LEN = Guid.Empty.ToByteArray().Length; // 16-byte
 202    internal static readonly int CCH_RM_SESSION_KEY = RM_SESSION_KEY_LEN * 2;
 203
 204    internal enum RM_APP_TYPE
 205    {
 206        RmUnknownApp = 0,
 207        RmMainWindow = 1,
 208        RmOtherWindow = 2,
 209        RmService = 3,
 210        RmExplorer = 4,
 211        RmConsole = 5,
 212        RmCritical = 1000
 213    }
 214
 215    internal enum RM_APP_STATUS
 216    {
 217        RmStatusUnknown = 0x0,
 218        RmStatusRunning = 0x1,
 219        RmStatusStopped = 0x2,
 220        RmStatusStoppedOther = 0x4,
 221        RmStatusRestarted = 0x8,
 222        RmStatusErrorOnStop = 0x10,
 223        RmStatusErrorOnRestart = 0x20,
 224        RmStatusShutdownMasked = 0x40,
 225        RmStatusRestartMasked = 0x80
 226    }
 227
 228    internal enum RM_REBOOT_REASON
 229    {
 230        RmRebootReasonNone = 0x0,
 231        RmRebootReasonPermissionDenied = 0x1,
 232        RmRebootReasonSessionMismatch = 0x2,
 233        RmRebootReasonCriticalProcess = 0x4,
 234        RmRebootReasonCriticalService = 0x8,
 235        RmRebootReasonDetectedSelf = 0x10
 236    }
 237
 238    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
 239    internal struct RM_PROCESS_INFO
 240    {
 241        public RM_UNIQUE_PROCESS Process;
 242        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
 243        public string strAppName;
 244        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
 245        public string strServiceShortName;
 246        public RM_APP_TYPE ApplicationType;
 247        public uint AppStatus;
 248        public uint TSSessionId;
 249        [MarshalAs(UnmanagedType.Bool)]
 250        public bool bRestartable;
 251
 252        public DateTime GetStartTime() => DateTime.FromFileTime((((long)Process.ProcessStartTime.dwHighDateTime) << 32) 
 253    }
 254
 255#if NET
 256    [LibraryImport(AdvApi32Dll, SetLastError = true)]
 257    [return: MarshalAs(UnmanagedType.Bool)]
 258    internal static partial bool OpenProcessToken(SafeProcessHandle processHandle, int desiredAccess, out SafeAccessToke
 259#else
 260    [DllImport(AdvApi32Dll, SetLastError = true)]
 261    internal static extern bool OpenProcessToken(SafeProcessHandle processHandle, int desiredAccess, out SafeAccessToken
 262#endif
 263
 264#if NET
 265    [LibraryImport(AdvApi32Dll, SetLastError = true)]
 266    [return: MarshalAs(UnmanagedType.Bool)]
 267    internal static partial bool GetTokenInformation(SafeAccessTokenHandle hToken, TOKEN_INFORMATION_CLASS tokenInfoClas
 268#else
 269    [DllImport(AdvApi32Dll, CharSet = CharSet.Auto, SetLastError = true)]
 270    internal static extern bool GetTokenInformation(SafeAccessTokenHandle hToken, TOKEN_INFORMATION_CLASS tokenInfoClass
 271#endif
 272
 273    internal const int PROCESS_TERMINATE = 0x0001;
 274    internal const int PROCESS_CREATE_THREAD = 0x0002;
 275    internal const int PROCESS_DUP_HANDLE = 0x0040;
 276    internal const int PROCESS_CREATE_PROCESS = 0x0080;
 277    internal const int PROCESS_SET_QUOTA = 0x0100;
 278    internal const int PROCESS_SET_INFORMATION = 0x0200;
 279    internal const int PROCESS_SUSPEND_RESUME = 0x0800;
 280    internal const int PROCESS_QUERY_INFORMATION = 0x400;
 281    internal const int PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
 282    internal const int PROCESS_VM_OPERATION = 0x08;
 283    internal const int PROCESS_VM_READ = 0x10;
 284    internal const int PROCESS_VM_WRITE = 0x20;
 285
 286#if NET
 287    [LibraryImport(KernelDll, SetLastError = true)]
 288    private static partial SafeProcessHandle OpenProcess(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInhe
 289#else
 290    [DllImport(KernelDll, SetLastError = true)]
 291    private static extern SafeProcessHandle OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
 292#endif
 293
 294    internal static SafeProcessHandle OpenProcessLimited(int pid) => OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, fals
 295    internal static SafeProcessHandle OpenProcessRead(int pid) => OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_REA
 296
 297    internal static bool IsCurrentProcessWow64Process { get; } = Environment.Is64BitOperatingSystem && !Environment.Is64
 298
 299#if NET
 300    [LibraryImport(KernelDll, SetLastError = true)]
 301    internal static partial int GetProcessId(SafeProcessHandle handle);
 302#else
 303    [DllImport(KernelDll, SetLastError = true)]
 304    internal static extern int GetProcessId(SafeProcessHandle handle);
 305#endif
 306
 307#if NET
 308    [LibraryImport(KernelDll, SetLastError = true)]
 309    [return: MarshalAs(UnmanagedType.Bool)]
 310    private static partial bool IsProcessCritical(SafeProcessHandle hProcess, [MarshalAs(UnmanagedType.Bool)] out bool c
 311#else
 312    [DllImport(KernelDll, SetLastError = true)]
 313    private static extern bool IsProcessCritical(SafeProcessHandle hProcess, out bool critical);
 314#endif
 315
 316    private static readonly string[] s_criticalNames =
 317    {
 318        // List taken from taskmgr.exe "strings"
 319        "%windir%\\explorer.exe",
 320        "%windir%\\system32\\ntoskrnl.exe",
 321        "%windir%\\system32\\winlogon.exe",
 322        "%windir%\\system32\\wininit.exe",
 323        "%windir%\\system32\\csrss.exe",
 324        "%windir%\\system32\\lsass.exe",
 325        "%windir%\\system32\\smss.exe",
 326        "%windir%\\system32\\services.exe",
 327        "%windir%\\system32\\taskeng.exe",
 328        "%windir%\\system32\\taskhost.exe",
 329        "%windir%\\system32\\dwm.exe",
 330        "%windir%\\system32\\conhost.exe",
 331        "%windir%\\system32\\svchost.exe",
 332        "%windir%\\system32\\sihost.exe",
 333        "%windir%\\system32\\backgroundTaskHost.exe",
 334        "%windir%\\system32\\backgroundTransferHost.exe",
 335        "%windir%\\system32\\WerFault.exe",
 336        "%programfiles%\\Windows Defender\\msmpeng.exe",
 337        "%programfiles%\\Windows Defender\\nissrv.exe",
 338    };
 339
 340    private static readonly Lazy<HashSet<string>> s_critical = new(() =>
 341    {
 342        var result = new HashSet<string>(s_criticalNames.Length, StringComparer.OrdinalIgnoreCase);
 343
 344        foreach (string name in s_criticalNames)
 345        {
 346            if (IsCurrentProcessWow64Process)
 347            {
 348                // 32 bit process on 64 bit OS. Make sure we use 64 bit directories.
 349                // Note: we don't have to replace "%windir%\system32" with "%windir%\sysnative"
 350                // because the full path we compare with is ultimately retrieved by the QueryFullProcessImageName() Win3
 351                // That in turn, seems to always return the "actual" path. So even when running as 32 bit app on a 64 bi
 352                // (i.e. WOW64), it will return the true path.
 353                string nativeName = name.Replace("%programfiles%", "%programw6432%");
 354                result.Add(Environment.ExpandEnvironmentVariables(nativeName));
 355            }
 356            else
 357            {
 358                result.Add(Environment.ExpandEnvironmentVariables(name));
 359            }
 360        }
 361
 362        return result;
 363    }, LazyThreadSafetyMode.ExecutionAndPublication);
 364
 365    // The following lazy initializes whether ProcessSequenceNumber is available or not.
 366    // Doing it the following way saves us a Lazy<> instance's overhead at the cost of
 367    // potentially doing the logic multiple times if multiple threads make it inside the
 368    // "if (.. == 0)".
 369    private static int s_supportsProcessSequenceNumber;
 370    internal static bool SupportsProcessSequenceNumber
 371    {
 372        get
 373        {
 374            if (s_supportsProcessSequenceNumber == 0)
 375            {
 376                // Not available when self is WOW64.
 377                // NtQuerySystemInformation() does not return the SYSTEM_PROCESS_INFORMATION_EXTENSION then it seems.
 378                // Also PROCESS_INFORMATION_CLASS.ProcessSequenceNumber is not available.
 379                if (!IsCurrentProcessWow64Process)
 380                {
 381                    // According to: https://learn.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-enable_tra
 382                    // "Supported on Windows 10, version 1507 and later. This is also supported on Windows 8.1 and Windo
 383                    // We ignore versions 8.1 and 7. Version 1507 is build 10240.
 384                    var ver = Environment.OSVersion.Version;
 385                    s_supportsProcessSequenceNumber = ver.Major > 10 || (ver.Major == 10 && ver.Build >= 10240) ? 1 : 2;
 386                }
 387                else
 388                {
 389                    s_supportsProcessSequenceNumber = 2;
 390                }
 391            }
 392
 393            return s_supportsProcessSequenceNumber == 1;
 394        }
 395    }
 396
 397
 398    internal static IEnumerable<string> GetKnownCriticalProcesses() => s_critical.Value;
 399
 400    internal static bool? IsProcessCritical(SafeProcessHandle hProcess, IHasErrorState? errorState = null)
 401    {
 402        if (hProcess.IsInvalid)
 403        {
 404            errorState?.SetError();
 405            return null;
 406        }
 407
 408        bool? result = IsProcessCriticalByHandle(hProcess, errorState);
 409        if (result != null)
 410        {
 411            return result;
 412        }
 413
 414        return IsProcessCriticalByImagePath(hProcess, errorState);
 415    }
 416
 417    // internal for unit test access
 418    internal static bool? IsProcessCriticalByHandle(SafeProcessHandle hProcess, IHasErrorState? errorState)
 419    {
 420        if (!IsProcessCritical(hProcess, out bool critical))
 421        {
 422            errorState?.SetError(errorCode: Marshal.GetLastWin32Error());
 423            return null;
 424        }
 425
 426        return critical;
 427    }
 428
 429    // internal for unit test access
 430    internal static bool? IsProcessCriticalByImagePath(SafeProcessHandle hProcess, IHasErrorState? errorState)
 431    {
 432        // Check hardcoded list
 433        string? imagePath = GetProcessImagePath(hProcess, throwOnError: false);
 434        if (imagePath == null)
 435        {
 436            errorState?.SetError(errorCode: Marshal.GetLastWin32Error());
 437            return null;
 438        }
 439
 440        return s_critical.Value.Contains(imagePath);
 441    }
 442
 443#if NET
 444    [LibraryImport(KernelDll, SetLastError = true, EntryPoint = "QueryFullProcessImageNameW")]
 445    [return: MarshalAs(UnmanagedType.Bool)]
 446    private static unsafe partial bool QueryFullProcessImageName(SafeProcessHandle hProcess, int dwFlags, char* lpExeNam
 447#else
 448    [DllImport(KernelDll, SetLastError = true, CharSet = CharSet.Unicode)]
 449    private static extern bool QueryFullProcessImageName(SafeProcessHandle hProcess, int dwFlags, StringBuilder lpExeNam
 450#endif
 451
 452    private class DisableWow64FsRedirectionScope : IDisposable
 453    {
 454        private IntPtr _oldValue = IntPtr.Zero;
 455        private bool _shouldDispose;
 456
 457        public DisableWow64FsRedirectionScope()
 458        {
 459            if (IsCurrentProcessWow64Process)
 460            {
 461                if (!Wow64DisableWow64FsRedirection(ref _oldValue))
 462                {
 463                    // Shouldn't happen, but since we haven't actually changed the thread's state,
 464                    // an exception is sufficient.
 465                    throw new Win32Exception(Marshal.GetLastWin32Error());
 466                }
 467
 468                _shouldDispose = true;
 469            }
 470        }
 471
 472        public void Dispose()
 473        {
 474            if (_shouldDispose)
 475            {
 476                if (!Wow64RevertWow64FsRedirection(_oldValue))
 477                {
 478                    // This is catastrophic; any FS related function could not return unexpected values.
 479                    // It shouldn't *really* happen either, these APIs really just set a TLS slot for the current thread
 480                    int code = Marshal.GetLastWin32Error();
 481                    Environment.FailFast($"Failed to restore WOW64 FS redirection: 0x{code:X8}");
 482                }
 483
 484                _shouldDispose = false;
 485            }
 486        }
 487    }
 488
 489    internal static unsafe string? GetProcessImagePath(SafeProcessHandle hProcess, bool throwOnError = false)
 490    {
 491        // It *seems* as if QueryFullProcessImageName() always returns the "true" path, so no redirections
 492        // applied (e.g. for 64 bit C:\Windows\System32\notepad.exe it really does return that path and
 493        // not C:\Windows\sysnative\notepad.exe). However, I couldn't find any affirmative documentation
 494        // on that. So disable FS redirection anyway.
 495        using var disableFsRedirect = new DisableWow64FsRedirectionScope();
 496        {
 497#if NET
 498            const int stackSize = 260; // Actual Windows MAX_PATH value. But paths can get larger (up to 32k).
 499            int bufferSize = stackSize;
 500            Span<char> buffer = stackalloc char[bufferSize];
 501
 502            while (true)
 503            {
 504                fixed (char* bufferPtr = buffer)
 505                {
 506                    bool ret = QueryFullProcessImageName(hProcess, 0, bufferPtr, ref bufferSize);
 507                    if (!ret)
 508                    {
 509                        int code = Marshal.GetLastWin32Error();
 510                        if (code != ERROR_INSUFFICIENT_BUFFER)
 511                        {
 512                            if (!throwOnError)
 513                            {
 514                                return null;
 515                            }
 516
 517                            throw new Win32Exception(code);
 518                        }
 519
 520                        // Buffer too small. Double size; from now on need heap alloc to conserve stack space.
 521                        bufferSize *= 2;
 522                        buffer = new char[bufferSize];
 523                    }
 524                    else
 525                    {
 526                        return buffer.Slice(0, bufferSize).Trim('\0').ToString();
 527                    }
 528                }
 529            }
 530#else
 531            var sb = new StringBuilder(4096);
 532            int size = sb.Capacity;
 533            if (QueryFullProcessImageName(hProcess, 0, sb, ref size))
 534            {
 535                return sb.ToString();
 536            }
 537
 538            if (throwOnError)
 539            {
 540                throw new Win32Exception(Marshal.GetLastWin32Error());
 541            }
 542
 543            return null;
 544#endif
 545        }
 546    }
 547
 548#if NET
 549    [LibraryImport(KernelDll, SetLastError = true)]
 550    [return: MarshalAs(UnmanagedType.Bool)]
 551    private static partial bool GetProcessTimes(SafeProcessHandle handle, out long creation, out long exit, out long ker
 552#else
 553    [DllImport(KernelDll, CharSet = CharSet.Auto, SetLastError = true)]
 554    private static extern bool GetProcessTimes(SafeProcessHandle handle, out long creation, out long exit, out long kern
 555#endif
 556
 557    internal static DateTime GetProcessStartTime(int processId)
 558    {
 559        using var handle = OpenProcessLimited(processId);
 560
 561        if (!handle.IsInvalid && GetProcessTimes(handle, out long creation, out _, out _, out _))
 562        {
 563            return DateTime.FromFileTime(creation);
 564        }
 565
 566        return DateTime.MinValue;
 567    }
 568
 569#if NET
 570    [LibraryImport(KernelDll, SetLastError = true)]
 571    [return: MarshalAs(UnmanagedType.Bool)]
 572    private static partial bool ProcessIdToSessionId(int dwProcessId, out int sessionId);
 573#else
 574    [DllImport(KernelDll, SetLastError = true)]
 575    private static extern bool ProcessIdToSessionId(int dwProcessId, out int sessionId);
 576#endif
 577
 578    internal static int GetProcessSessionId(int dwProcessId)
 579    {
 580        if (ProcessIdToSessionId(dwProcessId, out int sessionId))
 581        {
 582            return sessionId;
 583        }
 584
 585        return -1;
 586    }
 587
 588    internal static string? GetProcessOwner(SafeProcessHandle handle)
 589    {
 590        try
 591        {
 592            if (OpenProcessToken(handle, TOKEN_QUERY, out var token))
 593            {
 594                if (ProcessTokenToSid(token, out var sid))
 595                {
 596                    var x = new SecurityIdentifier(sid);
 597                    return x.Translate(typeof(NTAccount)).Value;
 598                }
 599            }
 600        }
 601        catch
 602        {
 603            // If the computer is domain joined, and the connection to the domain controller is "broken", you may get th
 604            //
 605            // System.ComponentModel.Win32Exception (1789): The trust relationship between this workstation and the prim
 606            //   at System.Security.Principal.SecurityIdentifier.TranslateToNTAccounts(IdentityReferenceCollection sourc
 607            //   at System.Security.Principal.SecurityIdentifier.Translate(IdentityReferenceCollection sourceSids, Type 
 608            //   at System.Security.Principal.SecurityIdentifier.Translate(Type targetType)
 609            //   at LockCheck.Windows.NativeMethods.GetProcessOwner(SafeProcessHandle handle)
 610        }
 611
 612        return null;
 613    }
 614
 615    internal static bool ProcessTokenToSid(SafeAccessTokenHandle token, out IntPtr sid)
 616    {
 617        sid = IntPtr.Zero;
 618#if NET
 619        using var mem = new ScopedNativeMemory(stackalloc byte[256]);
 620#else
 621        using var mem = new ScopedNativeMemory(256);
 622#endif
 623        int cb = mem.Size;
 624        var ret = GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenUser, (IntPtr)mem, cb, ref cb);
 625        if (ret)
 626        {
 627            var tokUser = Marshal.PtrToStructure<TOKEN_USER>((IntPtr)mem);
 628            sid = tokUser.User.Sid;
 629        }
 630        return ret;
 631    }
 632
 633    internal const int TOKEN_QUERY = 0x0008;
 634
 635    [StructLayout(LayoutKind.Sequential)]
 636    internal struct TOKEN_USER
 637    {
 638        public SID_AND_ATTRIBUTES User;
 639    }
 640
 641    [StructLayout(LayoutKind.Sequential)]
 642    internal struct SID_AND_ATTRIBUTES
 643    {
 644        public IntPtr Sid;
 645        public int Attributes;
 646    }
 647
 648    internal enum TOKEN_INFORMATION_CLASS
 649    {
 650        TokenUser = 1,
 651    }
 652
 653
 654#if NET
 655    [LibraryImport(KernelDll, SetLastError = true, StringMarshalling = StringMarshalling.Utf16, EntryPoint = "CreateFile
 656    private static partial SafeFileHandle CreateFile(
 657        string lpFileName,
 658        int dwDesiredAccess,
 659        FileShare dwShareMode,
 660        IntPtr lpSecurityAttributes,
 661        FileMode dwCreationDisposition,
 662        int dwFlagsAndAttributes,
 663        IntPtr hTemplateFile);
 664#else
 665    [DllImport(KernelDll, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
 666    private static extern SafeFileHandle CreateFile(
 667        string lpFileName,
 668        int dwDesiredAccess,
 669        FileShare dwShareMode,
 670        IntPtr lpSecurityAttributes,
 671        FileMode dwCreationDisposition,
 672        int dwFlagsAndAttributes,
 673        IntPtr hTemplateFile);
 674
 675#endif
 676
 677    internal static SafeFileHandle GetFileHandle(string name)
 678    {
 679        return CreateFile(name,
 680            0, // "FileAccess.Neither" Read nor Write
 681            FileShare.Read | FileShare.Write | FileShare.Delete,
 682            IntPtr.Zero,
 683            FileMode.Open,
 684            (int)FileAttributes.Normal,
 685            IntPtr.Zero);
 686    }
 687
 688    internal struct PebOffsets
 689    {
 690        public int ProcessParametersOffset;
 691        public int CommandLineOffset;
 692        public int CurrentDirectoryOffset;
 693        public int WindowTitleOffset;
 694        public int DesktopInfoOffset;
 695        public int ImagePathNameOffset;
 696        public int EnvironmentOffset;
 697        public int EnvironmentSizeOffset;
 698        public int SessionIdOffset;
 699
 700        public static PebOffsets Get(bool target64)
 701        {
 702            var result = new PebOffsets();
 703
 704            // Use "windbg.exe" (the 32bit and 64bit version respectively!)
 705            // and start an arbitrary (32bit and 64bit process). Then run
 706            // "dt ntdll!_PEB"
 707            // "dt ntdll!_RTL_USER_PROCESS_PARAMETERS"
 708            // __ PEB __
 709            result.SessionIdOffset = target64 ? 0x02c0 : 0x01d4;
 710            result.ProcessParametersOffset = target64 ? 0x20 : 0x10;
 711            // __ RTL_USER_PROCESS_PARAMTERS __
 712            result.CommandLineOffset = target64 ? 0x70 : 0x40;
 713            result.CurrentDirectoryOffset = target64 ? 0x38 : 0x24;
 714            result.WindowTitleOffset = target64 ? 0xb0 : 0x70;
 715            result.DesktopInfoOffset = target64 ? 0xc0 : 0x78;
 716            // Note: we could use QueryFullProcessImageName() for this,
 717            // but since we're already mocking around, we might as well
 718            // use the following.
 719            result.ImagePathNameOffset = target64 ? 0x60 : 0x38;
 720            result.EnvironmentOffset = target64 ? 0x80 : 0x48;
 721            result.EnvironmentSizeOffset = target64 ? 0x03f0 : 0x0290;
 722
 723            return result;
 724        }
 725    }
 726
 727    [StructLayout(LayoutKind.Sequential)]
 728    private struct KSYSTEM_TIME
 729    {
 730        public uint LowPart;
 731        public int High1Time;
 732        public int High2Time;
 733    }
 734
 735    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
 736    private unsafe readonly struct KUSER_SHARED_DATA
 737    {
 738        // The kernel maps KUSER_SHARED_DATA at this address into each process.
 739        // Regardless of the bitness of the process. Also, the structure has the
 740        // same field-width, regardless of the bitness of the process.
 741        internal const nint Address = 0x7ffe_0000;
 742
 743        // Only part of the KUSER_SHARED_DATA up to "BootId", which is really the only field we need.
 744        // More fields. See https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_d
 745
 746        public readonly uint TickCountLowDeprecated;
 747        public readonly uint TickCountMultiplier;
 748        public readonly KSYSTEM_TIME InterruptTime;
 749        public readonly KSYSTEM_TIME SystemTime;
 750        public readonly KSYSTEM_TIME TimeZoneBias;
 751        public readonly ushort ImageNumberLow;
 752        public readonly ushort ImageNumberHigh;
 753
 754        public readonly STRING_260 NtSystemRoot;
 755
 756        public readonly uint MaxStackTraceDepth;
 757        public readonly uint CryptoExponent;
 758        public readonly uint TimeZoneId;
 759        public readonly uint LargePageMinimum;
 760        public readonly uint AitSamplingValue;
 761        public readonly uint AppCompatFlag;
 762        public readonly ulong RNGSeedVersion;
 763        public readonly uint GlobalValidationRunlevel;
 764        public readonly int TimeZoneBiasStamp;
 765        public readonly uint NtBuildNumber;
 766        public readonly int NtProductType;
 767        public readonly byte ProductTypeIsValid;
 768
 769        public readonly byte Reserved0;
 770
 771        public readonly ushort NativeProcessorArchitecture;
 772        public readonly uint NtMajorVersion;
 773        public readonly uint NtMinorVersion;
 774
 775        public readonly BOOL_ARRAY_64 ProcessorFeatures;
 776
 777        public readonly uint Reserved1;
 778        public readonly uint Reserved3;
 779        public readonly uint TimeSlip;
 780        public readonly int AlternativeArchitecture;
 781        public readonly uint BootId;
 782
 783        // ...
 784
 785        // Helpers so we can make this structure readonly. Currently we don't read the members using these
 786        // types, but for completeness we account for them properly.
 787
 788        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
 789        public struct STRING_260
 790        {
 791            private fixed char _buffer[260];
 792
 793            public override string ToString()
 794            {
 795                fixed (char* s = _buffer)
 796                {
 797                    return new string(s);
 798                }
 799            }
 800        }
 801
 802        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
 803        public struct BOOL_ARRAY_64
 804        {
 805            private fixed byte _buffer[64];
 806
 807            public bool[] Values
 808            {
 809                get
 810                {
 811                    fixed (byte* s = _buffer)
 812                    {
 813                        bool[] res = new bool[64];
 814                        for (int i = 0; i < res.Length; i++)
 815                        {
 816                            res[i] = 1 == *(s + i);
 817                        }
 818                        return res;
 819                    }
 820                }
 821            }
 822        }
 823    }
 824
 825    internal static unsafe uint GetBootId()
 826    {
 827        // If we ever need other fields from KUSER_SHARED_DATA - please don't - we can
 828        // simple unmarshall the whole thing using the typical:
 829        //
 830        //     var sharedData = Marshal.PtrToStructure<KUSER_SHARED_DATA>(KUSER_SHARED_DATA.Address);
 831        //
 832        // However, currently we only need the BootId, thus the following is more efficient.
 833
 834        var ptr = IntPtr.Add(KUSER_SHARED_DATA.Address, (int)Marshal.OffsetOf<KUSER_SHARED_DATA>(nameof(KUSER_SHARED_DAT
 835        return (uint)Marshal.ReadInt32(ptr);
 836    }
 837
 838    internal static ulong GetProcessStartKey(ulong processSequenceNumber)
 839    {
 840        // Apparently, this is how the ETW ProcessStartKey is calculated.
 841        // Reference: disassembly of PsGetProcessStartKey()
 842        //
 843        //    PsGetProcessStartKey proc near
 844        //       mov     rax, 0FFFFF780000002C4h  // Load memory address of field "BootId" (offset 0x2C4 in KUSER_SHARED
 845        //       mov     eax, [rax]               // store BootId in eax
 846        //       shl     rax, 30h                 // BootId >> 48 (0x30)
 847        //       or      rax, [rcx+8F8h]          // SequenceNumber | rax
 848        //       retn
 849        //    PsGetProcessStartKey endp
 850        //
 851        // Other, random, "art" on the internet does it the same way.
 852
 853        return ((ulong)GetBootId() << 0x30) | processSequenceNumber;
 854    }
 855
 856    // native struct defined in ntexapi.h
 857    [StructLayout(LayoutKind.Sequential)]
 858    internal struct SYSTEM_PROCESS_INFORMATION
 859    {
 860        internal uint NextEntryOffset;
 861        internal uint NumberOfThreads;
 862        internal long WorkingSetPrivateSize;
 863        internal uint HardFaultCount;
 864        internal uint NumberOfThreadsHighWatermark;
 865        internal long CycleTime;
 866        internal long CreateTime;
 867        internal long UserTime;
 868        internal long KernelTime;
 869
 870        internal ushort NameLength;
 871        internal ushort MaximumNameLength;
 872        internal IntPtr NamePtr;
 873
 874        internal int BasePriority;
 875        internal IntPtr UniqueProcessId;
 876        internal IntPtr InheritedFromUniqueProcessId;
 877        internal uint HandleCount;
 878        internal uint SessionId;
 879
 880        // This member looks promising in that it could contain the same value that the WMI "UniqueProcessKey"
 881        // and thus also ETW "UniqueProcessKey". However, unofficial research has this to say:
 882        //
 883        // (see https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/process.htm):
 884        // "The UniqueProcessKey is undefined for SystemProcessInformation [bug requires SystemExtendedProcessInformatio
 885        // which in turn requires administration privileges] For the newer information classes it originally revealed th
 886        // page number of the process’s page directory base. Version 6.0 instead reveals the address of the EPROCESS
 887        // structure that represents the process as a kernel object. Whether the member was named UniqueProcessKey in
 888        // these versions is not known. Whatever it was named, what it contained may have been thought to disclose too
 889        // much: [>>] since version 6.1 the UniqueProcessKey is set identically to the UniqueProcessId. [<<]"
 890        //
 891        // FWIW, WMI still documents "UniqueProcessKey" as "The address of the process object in the kernel."
 892        // This could of course be a totally different "address" than the one cited above, however (WMI/ETW)
 893        // traces show values that look like this: UniqueProcessKey=0xFFFF8905CFFF1080. Which suspiciously looks
 894        // like a kernel address.
 895        //
 896        // Anyway, I leave this comment here, should I (again!) attempt to use this member ;-)
 897        // Still it would be nice if we could determine this value for the processes we find to be locking
 898        // stuff and present them together with their PID, etc.
 899        internal UIntPtr UniqueProcessKey;
 900
 901        internal UIntPtr PeakVirtualSize;
 902        internal UIntPtr VirtualSize;
 903        internal uint PageFaultCount;
 904        internal UIntPtr PeakWorkingSetSize;
 905        internal UIntPtr WorkingSetSize;
 906        internal UIntPtr QuotaPeakPagedPoolUsage;
 907        internal UIntPtr QuotaPagedPoolUsage;
 908        internal UIntPtr QuotaPeakNonPagedPoolUsage;
 909        internal UIntPtr QuotaNonPagedPoolUsage;
 910        internal UIntPtr PagefileUsage;
 911        internal UIntPtr PeakPagefileUsage;
 912        internal UIntPtr PrivatePageCount;
 913        internal long ReadOperationCount;
 914        internal long WriteOperationCount;
 915        internal long OtherOperationCount;
 916        internal long ReadTransferCount;
 917        internal long WriteTransferCount;
 918        internal long OtherTransferCount;
 919        internal IntPtr Threads;
 920    }
 921
 922    public static int GetExtensionOffset(this SYSTEM_PROCESS_INFORMATION si)
 923    {
 924        // This is only valid when PROCESS_INFORMATION_CLASS.ProcessInformation was used.
 925        // ProcessFullInformation (only as Admin) and ProcessExtendedInformation are different.
 926
 927        int threadStructSize = Marshal.SizeOf<SYSTEM_THREAD_INFORMATION>();
 928        return (int)(
 929        IntPtr.Add(Marshal.OffsetOf(typeof(SYSTEM_PROCESS_INFORMATION), nameof(SYSTEM_PROCESS_INFORMATION.Threads)),
 930        (int)(threadStructSize * si.NumberOfThreads)));
 931    }
 932
 933    [StructLayout(LayoutKind.Sequential)]
 934    internal struct CLIENT_ID
 935    {
 936        public IntPtr UniqueProcess; // HANDLE to the process
 937        public IntPtr UniqueThread;  // HANDLE to the thread
 938    }
 939
 940    [StructLayout(LayoutKind.Sequential)]
 941    internal struct SYSTEM_THREAD_INFORMATION
 942    {
 943        public ulong KernelTime;         // Total time in kernel mode
 944        public ulong UserTime;           // Total time in user mode
 945        public ulong CreateTime;         // Time thread was created
 946        public uint WaitTime;            // Time the thread has been in the wait state
 947        public IntPtr StartAddress;      // Pointer to the thread start address
 948        public CLIENT_ID ClientId;       // Identifies the thread
 949        public int Priority;             // Thread priority
 950        public int BasePriority;         // Base priority of the thread
 951        public uint ContextSwitchCount;  // Number of context switches
 952        public uint ThreadState;         // State of the thread
 953        public uint WaitReason;          // Reason the thread is in the wait state
 954    }
 955
 956    [StructLayout(LayoutKind.Sequential)]
 957    internal struct SYSTEM_EXTENDED_THREAD_INFORMATION
 958    {
 959        public SYSTEM_THREAD_INFORMATION ThreadInfo;
 960        public IntPtr StackBase;
 961        public IntPtr StackLimit;
 962        public IntPtr Win32StartAddress;
 963        public IntPtr TebBase;
 964        public UIntPtr Reserved2;
 965        public UIntPtr Reserved3;
 966        public UIntPtr Reserved4;
 967    }
 968
 969    [StructLayout(LayoutKind.Sequential)]
 970    internal struct PROCESS_DISK_COUNTERS
 971    {
 972        public ulong BytesRead;
 973        public ulong BytesWritten;
 974        public ulong ReadOperationCount;
 975        public ulong WriteOperationCount;
 976        public ulong FlushOperationCount;
 977    }
 978
 979
 980    [StructLayout(LayoutKind.Sequential)]
 981    internal struct ENERGY_STATE_DURATION
 982    {
 983        public ulong Value; // Single ulong member to hold the combined data
 984
 0985        public uint LastChangeTime => (uint)(Value & 0xFFFFFFFF); // LastChangeTime: occupies the first 4 bytes
 0986        public uint Duration => (uint)((Value >> 32) & 0x7FFFFFFF);  // Duration: 31 bits (bits 32-62)
 0987        public bool IsInState => (Value & 0x8000000000000000UL) != 0;  // IsInState: 1 bit (bit 63)
 988    }
 989
 990    [StructLayout(LayoutKind.Sequential)]
 991    internal unsafe struct PROCESS_ENERGY_VALUES
 992    {
 993        public fixed ulong Cycles[8]; // This represents array[4][2]
 994
 995        public ulong DiskEnergy;
 996        public ulong NetworkTailEnergy;
 997        public ulong MBBTailEnergy;
 998        public ulong NetworkTxRxBytes;
 999        public ulong MBBTxRxBytes;
 1000
 1001        // Array of ENERGY_STATE_DURATION structs with a fixed size of 3
 1002        public ENERGY_STATE_DURATION ForegroundDuration;
 1003        public ENERGY_STATE_DURATION DesktopVisibleDuration;
 1004        public ENERGY_STATE_DURATION PSMForegroundDuration;
 1005
 1006        public uint CompositionRendered;
 1007        public uint CompositionDirtyGenerated;
 1008        public uint CompositionDirtyPropagated;
 1009        public uint Reserved1;
 1010
 1011        public fixed ulong AttributedCycles[8]; // This represents array[4][2]
 1012        public fixed ulong WorkOnBehalfCycles[8]; // This represents array[4][2]
 1013
 1014        public static ulong GetElement(int row, int column, ulong[] value)
 1015        {
 1016            if (row < 0 || row >= 4)
 1017            {
 1018                throw new ArgumentOutOfRangeException(nameof(row), row, null);
 1019            }
 1020
 1021            if (column < 0 || column >= 2)
 1022            {
 1023                throw new ArgumentOutOfRangeException(nameof(column), column, null);
 1024            }
 1025
 1026            return value[(row * 2) + column];
 1027        }
 1028    }
 1029
 1030    [StructLayout(LayoutKind.Sequential)]
 1031    internal struct SYSTEM_PROCESS_INFORMATION_EXTENSION
 1032    {
 1033        public PROCESS_DISK_COUNTERS DiskCounters;
 1034        public ulong ContextSwitches;
 1035        public uint Flags;
 1036        public uint UserSidOffset;
 1037        public uint PackageFullNameOffset;
 1038        public PROCESS_ENERGY_VALUES EnergyValues;
 1039        public uint AppIdOffset;
 1040        public IntPtr SharedCommitCharge;
 1041        public uint JobObjectId;
 1042        public uint SpareUlong;
 1043        public ulong ProcessSequenceNumber;
 1044    }
 1045#pragma warning restore 169
 1046
 1047#if NET
 1048    [LibraryImport(KernelDll, SetLastError = true)]
 1049    internal static partial SafeProcessHandle GetCurrentProcess();
 1050
 1051    [LibraryImport(KernelDll, SetLastError = true)]
 1052    [return: MarshalAs(UnmanagedType.Bool)]
 1053    internal static partial bool IsWow64Process(SafeProcessHandle hProcess, [MarshalAs(UnmanagedType.Bool)] out bool wow
 1054
 1055    [LibraryImport(KernelDll, SetLastError = true)]
 1056    [return: MarshalAs(UnmanagedType.Bool)]
 1057    private static partial bool Wow64DisableWow64FsRedirection(ref IntPtr oldValue);
 1058
 1059    [LibraryImport(KernelDll, SetLastError = true)]
 1060    [return: MarshalAs(UnmanagedType.Bool)]
 1061    private static partial bool Wow64RevertWow64FsRedirection(IntPtr oldValue);
 1062
 1063    [LibraryImport(KernelDll, SetLastError = true)]
 1064    [return: MarshalAs(UnmanagedType.Bool)]
 1065    internal static partial bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, ref IntPtr lpBuffer
 1066
 1067    [LibraryImport(KernelDll, SetLastError = true)]
 1068    [return: MarshalAs(UnmanagedType.Bool)]
 1069    internal static partial bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, In
 1070
 1071
 1072    [LibraryImport(KernelDll, SetLastError = true)]
 1073    [return: MarshalAs(UnmanagedType.Bool)]
 1074    internal static partial bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING_
 1075
 1076    [LibraryImport(KernelDll, SetLastError = true)]
 1077    [return: MarshalAs(UnmanagedType.Bool)]
 1078    internal static partial bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING 
 1079
 1080    [LibraryImport(KernelDll, SetLastError = true)]
 1081    [return: MarshalAs(UnmanagedType.Bool)]
 1082    internal static partial bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, [MarshalAs(Unmanage
 1083
 1084    [LibraryImport(KernelDll, SetLastError = true)]
 1085    [return: MarshalAs(UnmanagedType.Bool)]
 1086    internal static partial bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, ref uint data, IntP
 1087#else
 1088    [DllImport(KernelDll, SetLastError = true)]
 1089    internal static extern SafeProcessHandle GetCurrentProcess();
 1090
 1091    [DllImport(KernelDll, SetLastError = true)]
 1092    [return: MarshalAs(UnmanagedType.Bool)]
 1093    internal static extern bool IsWow64Process(SafeProcessHandle hProcess, [MarshalAs(UnmanagedType.Bool)] out bool wow6
 1094
 1095    [DllImport(KernelDll, SetLastError = true)]
 1096    [return: MarshalAs(UnmanagedType.Bool)]
 1097    private static extern bool Wow64DisableWow64FsRedirection(ref IntPtr oldValue);
 1098
 1099    [DllImport(KernelDll, SetLastError = true)]
 1100    [return: MarshalAs(UnmanagedType.Bool)]
 1101    private static extern bool Wow64RevertWow64FsRedirection(IntPtr oldValue);
 1102
 1103    [DllImport(KernelDll, SetLastError = true)]
 1104    [return: MarshalAs(UnmanagedType.Bool)]
 1105    internal static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, ref IntPtr lpBuffer,
 1106
 1107    [DllImport(KernelDll, SetLastError = true)]
 1108    [return: MarshalAs(UnmanagedType.Bool)]
 1109    internal static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, Int
 1110
 1111
 1112    [DllImport(KernelDll, SetLastError = true)]
 1113    [return: MarshalAs(UnmanagedType.Bool)]
 1114    internal static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING_3
 1115
 1116    [DllImport(KernelDll, SetLastError = true)]
 1117    [return: MarshalAs(UnmanagedType.Bool)]
 1118    internal static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING l
 1119
 1120    [DllImport(KernelDll, SetLastError = true)]
 1121    [return: MarshalAs(UnmanagedType.Bool)]
 1122    internal static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, [MarshalAs(Unmanaged
 1123
 1124    [DllImport(KernelDll, SetLastError = true)]
 1125    [return: MarshalAs(UnmanagedType.Bool)]
 1126    internal static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, ref uint data, IntPt
 1127#endif
 1128
 1129    // for 32-bit process in a 64-bit OS only
 1130
 1131#if NET
 1132    [LibraryImport(NtDll)]
 1133    internal static partial int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, IntPtr data, 
 1134
 1135    [LibraryImport(NtDll)]
 1136    internal static partial int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, ref long lpBu
 1137
 1138    [LibraryImport(NtDll)]
 1139    internal static partial int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, ref UNICODE_S
 1140
 1141    [LibraryImport(NtDll)]
 1142    internal static partial int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, [MarshalAs(Un
 1143
 1144    [LibraryImport(NtDll)]
 1145    internal static partial int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, ref uint data
 1146#else
 1147    [DllImport(NtDll)]
 1148    internal static extern int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, IntPtr data, l
 1149
 1150    [DllImport(NtDll)]
 1151    internal static extern int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, ref long lpBuf
 1152
 1153    [DllImport(NtDll)]
 1154    internal static extern int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, ref UNICODE_ST
 1155
 1156    [DllImport(NtDll)]
 1157    internal static extern int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, [MarshalAs(Unm
 1158
 1159    [DllImport(NtDll)]
 1160    internal static extern int NtWow64ReadVirtualMemory64(SafeProcessHandle hProcess, long lpBaseAddress, ref uint data,
 1161#endif
 1162    [StructLayout(LayoutKind.Sequential)]
 1163    internal struct PROCESS_BASIC_INFORMATION
 1164    {
 1165        public IntPtr Reserved1;
 1166        public IntPtr PebBaseAddress;
 1167        public IntPtr Reserved2_0;
 1168        public IntPtr Reserved2_1;
 1169        public IntPtr UniqueProcessId;
 1170        public IntPtr InheritedFromUniqueProcessId;
 1171    }
 1172
 1173    [StructLayout(LayoutKind.Sequential)]
 1174    internal struct UNICODE_STRING
 1175    {
 1176        public short Length;
 1177        public short MaximumLength;
 1178        public IntPtr Buffer;
 1179
 1180        public readonly string GetEmptyBuffer() => new('\0', Length / 2);
 1181    }
 1182
 1183    // for 32-bit process in a 64-bit OS only
 1184    [StructLayout(LayoutKind.Sequential)]
 1185    internal struct PROCESS_BASIC_INFORMATION_WOW64
 1186    {
 1187        public long Reserved1;
 1188        public long PebBaseAddress;
 1189        public long Reserved2_0;
 1190        public long Reserved2_1;
 1191        public long UniqueProcessId;
 1192        public long InheritedFromUniqueProcessId;
 1193    }
 1194
 1195    // for 32-bit process in a 64-bit OS only
 1196    [StructLayout(LayoutKind.Sequential)]
 1197    internal struct UNICODE_STRING_WOW64
 1198    {
 1199        public short Length;
 1200        public short MaximumLength;
 1201        public long Buffer;
 1202
 1203        public readonly string GetEmptyBuffer() => new('\0', Length / 2);
 1204    }
 1205
 1206    [StructLayout(LayoutKind.Sequential)]
 1207    internal struct UNICODE_STRING_32
 1208    {
 1209        public short Length;
 1210        public short MaximumLength;
 1211        public int Buffer;
 1212
 1213        public readonly string GetEmptyBuffer() => new('\0', Length / 2);
 1214    }
 1215
 1216
 1217    internal const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
 1218    internal const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
 1219    internal const int FORMAT_MESSAGE_FROM_STRING = 0x00000400;
 1220    internal const int FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
 1221    internal const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
 1222    internal const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
 1223
 1224#if NET
 1225    internal static string GetMessage(int errorCode) => $"{Marshal.GetPInvokeErrorMessage(errorCode)} (0x{errorCode:X8})
 1226#else
 1227    internal static string GetMessage(int errorCode) => $"{new Win32Exception(errorCode).Message}  (0x{errorCode:X8})";
 1228#endif
 1229
 1230    internal unsafe ref struct ScopedNativeMemory
 1231    {
 1232#if NET
 1233        private readonly Span<byte> _initialBuffer;
 1234        private void* _buffer;
 1235#else
 1236        private IntPtr _buffer;
 1237#endif
 1238        private int _size;
 1239
 1240        public ScopedNativeMemory(int size)
 1241        {
 1242            HeapAllocate(size);
 1243        }
 1244
 1245#if NET
 1246        public ScopedNativeMemory(Span<byte> initialBuffer)
 1247        {
 1248            _initialBuffer = initialBuffer;
 1249            _size = initialBuffer.Length;
 1250        }
 1251#endif
 1252
 1253        private void HeapAllocate(int size)
 1254        {
 1255#if DEBUG
 1256            if (IsHeapAllocated)
 1257            {
 1258                throw new InvalidOperationException("Already allocated");
 1259            }
 1260#endif
 1261
 1262            _size = size;
 1263#if NET
 1264            _buffer = NativeMemory.Alloc((UIntPtr)size);
 1265#else
 1266            _buffer = Marshal.AllocHGlobal(size);
 1267#endif
 1268        }
 1269
 1270        public int Size => _size;
 1271
 1272        public static explicit operator IntPtr(ScopedNativeMemory memory)
 1273        {
 1274#if NET
 1275            return new IntPtr((void*)memory);
 1276#else
 1277            return memory._buffer;
 1278#endif
 1279        }
 1280
 1281        public static explicit operator void*(ScopedNativeMemory memory)
 1282        {
 1283#if NET
 1284            if (!memory.IsHeapAllocated)
 1285            {
 1286                fixed (void* ptr = memory._initialBuffer)
 1287                {
 1288                    return ptr;
 1289                }
 1290            }
 1291
 1292            return memory._buffer;
 1293#else
 1294            return (void*)memory._buffer;
 1295#endif
 1296        }
 1297
 1298        public void Resize(int size)
 1299        {
 1300            Free();
 1301            HeapAllocate(size);
 1302        }
 1303
 1304        // No need to actually implement IDisposable. The compiler will pattern match for this,
 1305        // allowing to use this type with "using".
 1306        public void Dispose()
 1307        {
 1308            Free();
 1309        }
 1310
 1311        public void Free()
 1312        {
 1313            if (IsHeapAllocated)
 1314            {
 1315#if NET
 1316                NativeMemory.Free(_buffer);
 1317                _buffer = null;
 1318#else
 1319                Marshal.FreeHGlobal(_buffer);
 1320                _buffer = IntPtr.Zero;
 1321#endif
 1322            }
 1323        }
 1324
 1325        private bool IsHeapAllocated =>
 1326#if NET
 1327            _buffer != null;
 1328#else
 1329            _buffer != IntPtr.Zero;
 1330#endif
 1331    }
 1332}