Skip to content

Instantly share code, notes, and snippets.

@michel-pi
Created January 7, 2021 22:37
Show Gist options
  • Save michel-pi/eae4d9c16e2dce8737e2f7780b38bc31 to your computer and use it in GitHub Desktop.
Save michel-pi/eae4d9c16e2dce8737e2f7780b38bc31 to your computer and use it in GitHub Desktop.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
namespace ProcessMemoryTest.Native
{
[SuppressUnmanagedCodeSecurity]
public static unsafe class Kernel32
{
public const uint Infinite = uint.MaxValue;
public static readonly delegate* unmanaged[Stdcall]
<ProcessAccessFlags, int, uint, IntPtr>
OpenProcess;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, int>
CloseHandle;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, uint, WaitObjectResult>
WaitForSingleObject;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, ThreadCreationFlags, IntPtr, out uint, IntPtr>
CreateRemoteThreadEx;
static Kernel32()
{
var handle = NativeLibrary.Load("kernel32.dll");
OpenProcess =
(delegate* unmanaged[Stdcall] <ProcessAccessFlags, int, uint, IntPtr>)
NativeLibrary.GetExport(handle, nameof(OpenProcess));
CloseHandle =
(delegate* unmanaged[Stdcall] <IntPtr, int>)
NativeLibrary.GetExport(handle, nameof(CloseHandle));
WaitForSingleObject =
(delegate* unmanaged[Stdcall] <IntPtr, uint, WaitObjectResult>)
NativeLibrary.GetExport(handle, nameof(WaitForSingleObject));
CreateRemoteThreadEx =
(delegate* unmanaged[Stdcall] <IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, ThreadCreationFlags, IntPtr, out uint, IntPtr>)
NativeLibrary.GetExport(handle, nameof(CreateRemoteThreadEx));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static WaitObjectResult GetRealWaitObjectResult(WaitObjectResult value)
{
return value switch
{
< WaitObjectResult.Abandoned => WaitObjectResult.Success,
< WaitObjectResult.Timeout => WaitObjectResult.Abandoned,
>= WaitObjectResult.Failed => WaitObjectResult.Failed,
_ => WaitObjectResult.Timeout
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static WaitObjectResult GetRealWaitObjectResult(WaitObjectResult value, out int index)
{
switch (value)
{
case < WaitObjectResult.Abandoned:
index = (int)value;
return WaitObjectResult.Success;
case < WaitObjectResult.Timeout:
index = (int)(value - WaitObjectResult.Abandoned);
return WaitObjectResult.Abandoned;
case < WaitObjectResult.Failed:
index = (int)(value - WaitObjectResult.Timeout);
return WaitObjectResult.Timeout;
default:
index = 0;
return WaitObjectResult.Failed;
}
}
}
[SuppressUnmanagedCodeSecurity]
public static unsafe class NtDll
{
public static readonly delegate* unmanaged[Stdcall]
<uint, uint>
RtlNtStatusToDosError;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, uint>
NtClose;
public static readonly delegate* unmanaged[Stdcall]
<out IntPtr, ProcessAccessFlags, ref ObjectAttributes, ref ClientId, uint>
NtOpenProcess;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, IntPtr, void*, IntPtr, out IntPtr, uint>
NtReadVirtualMemory;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, IntPtr, void*, IntPtr, out IntPtr, uint>
NtWriteVirtualMemory;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, out IntPtr, IntPtr, ref IntPtr, AllocationType, MemoryProtectionFlags, uint>
NtAllocateVirtualMemory;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, ref IntPtr, ref IntPtr, FreeType, uint>
NtFreeVirtualMemory;
public static readonly delegate* unmanaged[Stdcall]
<IntPtr, ref IntPtr, ref IntPtr, MemoryProtectionFlags, out MemoryProtectionFlags, uint>
NtProtectVirtualMemory;
static NtDll()
{
var handle = NativeLibrary.Load("ntdll.dll");
RtlNtStatusToDosError =
(delegate* unmanaged[Stdcall] <uint, uint>)
NativeLibrary.GetExport(handle, nameof(RtlNtStatusToDosError));
NtClose =
(delegate* unmanaged[Stdcall] <IntPtr, uint>)
NativeLibrary.GetExport(handle, nameof(NtClose));
NtOpenProcess =
(delegate* unmanaged[Stdcall] <out IntPtr, ProcessAccessFlags, ref ObjectAttributes, ref ClientId, uint>)
NativeLibrary.GetExport(handle, nameof(NtOpenProcess));
NtReadVirtualMemory =
(delegate* unmanaged[Stdcall] <IntPtr, IntPtr, void*, IntPtr, out IntPtr, uint>)
NativeLibrary.GetExport(handle, nameof(NtReadVirtualMemory));
NtWriteVirtualMemory =
(delegate* unmanaged[Stdcall] <IntPtr, IntPtr, void*, IntPtr, out IntPtr, uint>)
NativeLibrary.GetExport(handle, nameof(NtWriteVirtualMemory));
NtAllocateVirtualMemory =
(delegate* unmanaged[Stdcall] <IntPtr, out IntPtr, IntPtr, ref IntPtr, AllocationType, MemoryProtectionFlags, uint>)
NativeLibrary.GetExport(handle, nameof(NtAllocateVirtualMemory));
NtFreeVirtualMemory =
(delegate* unmanaged[Stdcall] <IntPtr, ref IntPtr, ref IntPtr, FreeType, uint>)
NativeLibrary.GetExport(handle, nameof(NtFreeVirtualMemory));
NtProtectVirtualMemory =
(delegate* unmanaged[Stdcall] <IntPtr, ref IntPtr, ref IntPtr, MemoryProtectionFlags, out MemoryProtectionFlags, uint>)
NativeLibrary.GetExport(handle, nameof(NtProtectVirtualMemory));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool NtSuccess(uint value)
=> value <= 0x3FFFFFFFu || (value >= 0x40000000u && value <= 0x7FFFFFFFu);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool NtSuccessOnly(uint value)
=> value <= 0x3FFFFFFFu;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool NtInformation(uint value)
=> value >= 0x40000000u && value <= 0x7FFFFFFFu;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool NtWarning(uint value)
=> value >= 0x80000000u && value <= 0xBFFFFFFFu;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool NtError(uint value)
=> value >= 0xC0000000u;
}
[StructLayout(LayoutKind.Sequential)]
public struct ClientId
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[StructLayout(LayoutKind.Sequential)]
public struct ObjectAttributes
{
public int Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public int Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
/// <summary>
/// Defines the different types of memory allocations.
/// </summary>
[Flags]
public enum AllocationType : uint
{
/// <summary>
/// An invalid value.
/// </summary>
Invalid = 0,
/// <summary>
/// Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved memory pages. The
/// function also guarantees that when the caller later initially accesses the memory, the contents will be zero. Actual physical pages are not
/// allocated unless/until the virtual addresses are actually accessed. To reserve and commit pages in one step, call VirtualAllocEx with
/// MEM_COMMIT | MEM_RESERVE. Attempting to commit a specific address range by specifying MEM_COMMIT without MEM_RESERVE and a non-NULL
/// lpAddress fails unless the entire range has already been reserved. The resulting error code is ERROR_INVALID_ADDRESS. An attempt to commit
/// a page that is already committed does not cause the function to fail. This means that you can commit pages without first determining the
/// current commitment state of each page. If lpAddress specifies an address within an enclave, flAllocationType must be MEM_COMMIT.
/// </summary>
Commit = 0x1000,
/// <summary>
/// Reserves a range of the process's virtual address space without allocating any actual physical storage in memory or in the paging file on
/// disk. You commit reserved pages by calling VirtualAllocEx again with MEM_COMMIT. To reserve and commit pages in one step, call
/// VirtualAllocEx with MEM_COMMIT | MEM_RESERVE. Other memory allocation functions, such as malloc and LocalAlloc, cannot use reserved memory
/// until it has been released.
/// </summary>
Reserve = 0x2000,
/// <summary>
/// Indicates that data in the memory range specified by lpAddress and dwSize is no longer of interest. The pages should not be read from or
/// written to the paging file. However, the memory block will be used again later, so it should not be decommitted. This value cannot be used
/// with any other value. Using this value does not guarantee that the range operated on with MEM_RESET will contain zeros. If you want the
/// range to contain zeros, decommit the memory and then recommit it. When you use MEM_RESET, the VirtualAllocEx function ignores the value of
/// fProtect. However, you must still set fProtect to a valid protection value, such as PAGE_NOACCESS. VirtualAllocEx returns an error if you
/// use MEM_RESET and the range of memory is mapped to a file. A shared view is only acceptable if it is mapped to a paging file.
/// </summary>
Reset = 0x80000,
/// <summary>
/// Allocates memory at the highest possible address. This can be slower than regular allocations, especially when there are many allocations.
/// </summary>
TopDown = 0x100000,
/// <summary>
/// Causes the system to track pages that are written to in the allocated region. If you specify this value, you must also specify MEM_RESERVE.
/// To retrieve the addresses of the pages that have been written to since the region was allocated or the write-tracking state was reset, call
/// the GetWriteWatch function. To reset the write-tracking state, call GetWriteWatch or ResetWriteWatch. The write-tracking feature remains
/// enabled for the memory region until the region is freed.
/// </summary>
WriteWatch = 0x200000,
/// <summary>
/// Reserves an address range that can be used to map Address Windowing Extensions (AWE) pages. This value must be used with MEM_RESERVE and no
/// other values.
/// </summary>
Physical = 0x400000,
/// <summary>
/// MEM_RESET_UNDO should only be called on an address range to which MEM_RESET was successfully applied earlier. It indicates that the data in
/// the specified memory range specified by lpAddress and dwSize is of interest to the caller and attempts to reverse the effects of MEM_RESET.
/// If the function succeeds, that means all data in the specified address range is intact. If the function fails, at least some of the data in
/// the address range has been replaced with zeroes. This value cannot be used with any other value. If MEM_RESET_UNDO is called on an address
/// range which was not MEM_RESET earlier, the behavior is undefined. When you specify MEM_RESET, the VirtualAllocEx function ignores the value
/// of flProtect. However, you must still set flProtect to a valid protection value, such as PAGE_NOACCESS. Windows Server 2008 R2, Windows 7,
/// Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: The MEM_RESET_UNDO flag is not supported until Windows 8 and
/// Windows Server 2012.
/// </summary>
ResetUndo = 0x1000000,
/// <summary>
/// Allocates memory using large page support. The size and alignment must be a multiple of the large-page minimum. To obtain this value, use
/// the GetLargePageMinimum function. If you specify this value, you must also specify MEM_RESERVE and MEM_COMMIT.
/// </summary>
LargePages = 0x20000000
}
/// <summary>
/// Defines the different types of free operations.
/// </summary>
[Flags]
public enum FreeType : uint
{
/// <summary>
/// An invalid value.
/// </summary>
Invalid = 0,
/// <summary>
/// To coalesce two adjacent placeholders, specify MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS. When you coalesce placeholders, lpAddress and
/// dwSize must exactly match those of the placeholder.
/// </summary>
CoalescePlaceholders = 0x00000001,
/// <summary>
/// Frees an allocation back to a placeholder (after you've replaced a placeholder with a private allocation using VirtualAlloc2 or
/// Virtual2AllocFromApp). To split a placeholder into two placeholders, specify MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER.
/// </summary>
PreservePlaceholder = 0x00000002,
/// <summary>
/// Decommits the specified region of committed pages. After the operation, the pages are in the reserved state. The function does not fail if
/// you attempt to decommit an uncommitted page. This means that you can decommit a range of pages without first determining their current
/// commitment state. Do not use this value with MEM_RELEASE. The MEM_DECOMMIT value is not supported when the lpAddress parameter provides the
/// base address for an enclave.
/// </summary>
Decommit = 0x4000,
/// <summary>
/// Releases the specified region of pages, or placeholder (for a placeholder, the address space is released and available for other
/// allocations). After the operation, the pages are in the free state. If you specify this value, dwSize must be 0 (zero), and lpAddress must
/// point to the base address returned by the VirtualAllocEx function when the region is reserved. The function fails if either of these
/// conditions is not met. If any pages in the region are committed currently, the function first decommits, and then releases them. The
/// function does not fail if you attempt to release pages that are in different states, some reserved and some committed. This means that you
/// can release a range of pages without first determining the current commitment state. Do not use this value with MEM_DECOMMIT.
/// </summary>
Release = 0x8000
}
/// <summary>
/// Defines the memory protection constants.
/// </summary>
[Flags]
public enum MemoryProtectionFlags : uint
{
/// <summary>
/// An invalid value.
/// </summary>
Invalid = 0,
/// <summary>
/// Disables all access to the committed region of pages. An attempt to read from, write to, or execute the committed region results in an
/// access violation. This flag is not supported by the CreateFileMapping function.
/// </summary>
NoAccess = 0x01,
/// <summary>
/// Enables read-only access to the committed region of pages. An attempt to write to the committed region results in an access violation. If
/// Data Execution Prevention is enabled, an attempt to execute code in the committed region results in an access violation.
/// </summary>
ReadOnly = 0x02,
/// <summary>
/// Enables read-only or read/write access to the committed region of pages. If Data Execution Prevention is enabled, attempting to execute
/// code in the committed region results in an access violation.
/// </summary>
ReadWrite = 0x04,
/// <summary>
/// Enables read-only or copy-on-write access to a mapped view of a file mapping object. An attempt to write to a committed copy-on-write page
/// results in a private copy of the page being made for the process. The private page is marked as PAGE_READWRITE, and the change is written
/// to the new page. If Data Execution Prevention is enabled, attempting to execute code in the committed region results in an access
/// violation. This flag is not supported by the VirtualAlloc or VirtualAllocEx functions.
/// </summary>
WriteCopy = 0x08,
/// <summary>
/// Enables execute access to the committed region of pages. An attempt to write to the committed region results in an access violation. This
/// flag is not supported by the CreateFileMapping function.
/// </summary>
Execute = 0x10,
/// <summary>
/// Enables execute or read-only access to the committed region of pages. An attempt to write to the committed region results in an access
/// violation. Windows Server 2003 and Windows XP: This attribute is not supported by the CreateFileMapping function until Windows XP with SP2
/// and Windows Server 2003 with SP1.
/// </summary>
ExecuteRead = 0x20,
/// <summary>
/// Enables execute, read-only, or read/write access to the committed region of pages. Windows Server 2003 and Windows XP: This attribute is
/// not supported by the CreateFileMapping function until Windows XP with SP2 and Windows Server 2003 with SP1.
/// </summary>
ExecuteReadWrite = 0x40,
/// <summary>
/// Enables execute, read-only, or copy-on-write access to a mapped view of a file mapping object. An attempt to write to a committed
/// copy-on-write page results in a private copy of the page being made for the process. The private page is marked as PAGE_EXECUTE_READWRITE,
/// and the change is written to the new page. This flag is not supported by the VirtualAlloc or VirtualAllocEx functions. Windows Vista,
/// Windows Server 2003 and Windows XP: This attribute is not supported by the CreateFileMapping function until Windows Vista with SP1 and
/// Windows Server 2008.
/// </summary>
ExecuteWriteCopy = 0x80,
/// <summary>
/// Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a STATUS_GUARD_PAGE_VIOLATION
/// exception and turn off the guard page status. Guard pages thus act as a one-time access alarm. For more information, see Creating Guard
/// Pages. When an access attempt leads the system to turn off guard page status, the underlying page protection takes over. If a guard page
/// exception occurs during a system service, the service typically returns a failure status indicator. This value cannot be used with
/// PAGE_NOACCESS. This flag is not supported by the CreateFileMapping function.
/// </summary>
GuardModifierflag = 0x100,
/// <summary>
/// Sets all pages to be non-cachable. Applications should not use this attribute except when explicitly required for a device. Using the
/// interlocked functions with memory that is mapped with SEC_NOCACHE can result in an EXCEPTION_ILLEGAL_INSTRUCTION exception. The
/// PAGE_NOCACHE flag cannot be used with the PAGE_GUARD, PAGE_NOACCESS, or PAGE_WRITECOMBINE flags. The PAGE_NOCACHE flag can be used only
/// when allocating private memory with the VirtualAlloc, VirtualAllocEx, or VirtualAllocExNuma functions. To enable non-cached memory access
/// for shared memory, specify the SEC_NOCACHE flag when calling the CreateFileMapping function.
/// </summary>
NoCacheModifierflag = 0x200,
/// <summary>
/// Sets all pages to be write-combined. Applications should not use this attribute except when explicitly required for a device. Using the
/// interlocked functions with memory that is mapped as write-combined can result in an EXCEPTION_ILLEGAL_INSTRUCTION exception. The
/// PAGE_WRITECOMBINE flag cannot be specified with the PAGE_NOACCESS, PAGE_GUARD, and PAGE_NOCACHE flags. The PAGE_WRITECOMBINE flag can be
/// used only when allocating private memory with the VirtualAlloc, VirtualAllocEx, or VirtualAllocExNuma functions. To enable write-combined
/// memory access for shared memory, specify the SEC_WRITECOMBINE flag when calling the CreateFileMapping function. Windows Server 2003 and
/// Windows XP: This flag is not supported until Windows Server 2003 with SP1.
/// </summary>
WriteCombineModifierflag = 0x400
}
/// <summary>
/// Contains all NTSTATUS codes that could possibly happen when using this library. These may be incomplete.
/// </summary>
public enum NtStatus : uint
{
/* Complete list https://gist.github.com/michel-pi/361f1f8bdca51235cb97aba0256d47e9#file-ntstatushelper-cs-L197
* Only add those that actually could happen
*/
/// <summary>
/// STATUS_SUCCESS
/// </summary>
SUCCESS = 0x0,
/// <summary>
/// A datatype misalignment was detected in a load or store instruction.
/// </summary>
DATATYPE_MISALIGNMENT = 0x80000002u,
/// <summary>
/// Due to protection conflicts not all the requested bytes could be copied.
/// </summary>
PARTIAL_COPY = 0x8000000Du,
/// <summary>
/// The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
/// </summary>
ACCESS_VIOLATION = 0xC0000005u,
/// <summary>
/// An invalid HANDLE was specified.
/// </summary>
INVALID_HANDLE = 0xC0000008u,
/// <summary>
/// A process has requested access to an object, but has not been granted those access rights.
/// </summary>
ACCESS_DENIED = 0xC0000022u,
/// <summary>
/// There is a mismatch between the type of object required by the requested operation and the type of object that is specified in the request.
/// </summary>
OBJECT_TYPE_MISMATCH = 0xC0000024u,
/// <summary>
/// Insufficient system resources exist to complete the API.
/// </summary>
INSUFFICIENT_RESOURCES = 0xC000009Au,
/// <summary>
/// An attempt was made to access an exiting process.
/// </summary>
PROCESS_IS_TERMINATING = 0xC000010Au,
}
/// <summary>
/// Defines process security and access rights.
/// </summary>
[Flags]
public enum ProcessAccessFlags : uint
{
/// <summary>
/// An invalid value.
/// </summary>
Invalid = 0,
/// <summary>
/// Required to terminate a process using TerminateProcess.
/// </summary>
Terminate = 0x00000001,
/// <summary>
/// Required to create a thread.
/// </summary>
CreateThread = 0x00000002,
/// <summary>
/// Required to allocate memory in the address space of another process.
/// </summary>
Allocate = VirtualMemoryOperation,
/// <summary>
/// Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
/// </summary>
VirtualMemoryOperation = 0x00000008,
/// <summary>
/// Required to read memory in a process using ReadProcessMemory.
/// </summary>
Read = VirtualMemoryRead,
/// <summary>
/// Required to read memory in a process using ReadProcessMemory.
/// </summary>
VirtualMemoryRead = 0x00000010,
/// <summary>
/// Combine with VirtualMemoryOperation for Write access.
/// </summary>
VirtualMemoryWrite = 0x00000020,
/// <summary>
/// Required to write to memory in a process using WriteProcessMemory.
/// </summary>
Write = VirtualMemoryWrite | VirtualMemoryOperation,
/// <summary>
/// Combines read and write access of a process memory.
/// </summary>
ReadWrite = VirtualMemoryRead | VirtualMemoryWrite | VirtualMemoryOperation,
/// <summary>
/// Required to duplicate a handle using DuplicateHandle.
/// </summary>
DuplicateHandle = 0x00000040,
/// <summary>
/// Required to create a process.
/// </summary>
CreateProcess = 0x000000080,
/// <summary>
/// Required to set memory limits using SetProcessWorkingSetSize.
/// </summary>
SetQuota = 0x00000100,
/// <summary>
/// Required to set certain information about a process, such as its priority class (see SetPriorityClass).
/// </summary>
SetInformation = 0x00000200,
/// <summary>
/// Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken).
/// </summary>
QueryInformation = 0x00000400,
/// <summary>
/// Required to set memory limits using SetProcessWorkingSetSize.
/// </summary>
SuspendResume = 0x0800,
/// <summary>
/// Required to retrieve certain information about a process (see GetExitCodeProcess, GetPriorityClass, IsProcessInJob,
/// QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted
/// PROCESS_QUERY_LIMITED_INFORMATION.Windows Server 2003 and Windows XP: This access right is not supported.
/// </summary>
QueryLimitedInformation = 0x00001000,
/// <summary>
/// Required to query information from another process.
/// </summary>
Information = QueryInformation | QueryLimitedInformation,
/// <summary>
/// Required when using CreateRemoteThread(Ex) to execute code in a another process.
/// </summary>
Execute = VirtualMemoryRead | VirtualMemoryWrite | VirtualMemoryOperation | QueryInformation | QueryLimitedInformation | CreateThread,
/// <summary>
/// Required to wait for the process to terminate using the wait functions.
/// </summary>
Synchronize = 0x00100000,
/// <summary>
/// All possible access rights for a process object.Windows Server 2003 and Windows XP: The size of the PROCESS_ALL_ACCESS flag increased on
/// Windows Server 2008 and Windows Vista. If an application compiled for Windows Server 2008 and Windows Vista is run on Windows Server 2003
/// or Windows XP, the PROCESS_ALL_ACCESS flag is too large and the function specifying this flag fails with ERROR_ACCESS_DENIED. To avoid this
/// problem, specify the minimum set of access rights required for the operation. If PROCESS_ALL_ACCESS must be used, set _WIN32_WINNT to the
/// minimum operating system targeted by your application (for example, #define _WIN32_WINNT _WIN32_WINNT_WINXP). For more information, see
/// Using the Windows Headers.
/// </summary>
All = 0x001FFFFF
}
/// <summary>
/// Defines flags that control the creation of a remote thread.
/// </summary>
[Flags]
public enum ThreadCreationFlags : uint
{
/// <summary>
/// The thread runs immediately after creation.
/// </summary>
Immediately = 0,
/// <summary>
/// The thread is created in a suspended state and does not run until the ResumeThread function is called.
/// </summary>
Suspended = 0x4,
/// <summary>
/// The dwStackSize parameter specifies the initial reserve size of the stack. If this flag is not specified, dwStackSize specifies the commit
/// size.
/// </summary>
StackSizeParamIsAReservation = 0x10000
}
/// <summary>
/// Defines the events that cause a function like WaitForSingleObject to return.
/// </summary>
public enum WaitObjectResult : uint
{
/// <summary>
/// The state of the specified object is signaled.
/// </summary>
Success = 0x0,
/// <summary>
/// The specified object is a mutex object that was not released by the thread that owned the mutex object before the owning thread terminated.
/// Ownership of the mutex object is granted to the calling thread and the mutex state is set to nonsignaled. If the mutex was protecting
/// persistent state information, you should check it for consistency.
/// </summary>
Abandoned = 0x80,
/// <summary>
/// The time-out interval elapsed, and the object's state is nonsignaled.
/// </summary>
Timeout = 0x102,
/// <summary>
/// The function has failed. To get extended error information, call GetLastError.
/// </summary>
Failed = 0xFFFFFFFF
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment