Skip to content
This repository has been archived by the owner on Jul 26, 2023. It is now read-only.

Kernel32 Threading API #463

Merged
merged 12 commits into from
Jul 3, 2020
Merged
7 changes: 7 additions & 0 deletions src/CodeGenerationAttributes/FriendlyFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ public enum FriendlyFlags

Optional = 0x8,

/// <summary>
/// Represents a native integer whose size is platform specific, i.e., 32-bits on 32-bit h/w and OS, and
/// 64-bits on 64-bit h/w and OS.
/// </summary>
/// <remarks>Intended for use on <see cref="IntPtr"/> or <see cref="UIntPtr"/></remarks>
NativeInt = 0x16,

Bidirectional = In | Out,
}
}
119 changes: 119 additions & 0 deletions src/Kernel32.Tests/storebanned/Kernel32Facts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

public partial class Kernel32Facts
{
private static unsafe Kernel32.THREAD_START_ROUTINE threadProc = new THREAD_START_ROUTINE(CreateThread_Test_ThreadMain);
AArnott marked this conversation as resolved.
Show resolved Hide resolved
private readonly Random random = new Random();

[Fact]
Expand Down Expand Up @@ -944,6 +945,124 @@ public void SetHandleInformation_DoesNotThrow()
HandleFlags.HANDLE_FLAG_NONE));
}

/// <summary>
/// Basic validation for <see cref="Kernel32.CreateThread(SECURITY_ATTRIBUTES*, UIntPtr, THREAD_START_ROUTINE, void*, CreateThreadFlags, uint*)"/>
///
/// Creates a thread by supplying <see cref="CreateThread_Test_ThreadMain(void*)"/> as its ThreadProc/<see cref="Kernel32.THREAD_START_ROUTINE"/>.
/// The ThreadProc updates a bool value (supplied by the thread that created it) from false -> true. This change
/// is observed by the calling thread as proof of successful thread-creation.
///
/// Also validates that the (native) Thread-ID for the newly created Thread is different than the (native) Thread-ID
/// of that of the calling thread.
/// </summary>
[Fact]
public unsafe void CreateThread_Test()
{
var result = false;
var dwNewThreadId = 0u;

using var hThread =
Kernel32.CreateThread(
null,
UIntPtr.Zero,
Kernel32Facts.threadProc,
&result,
Kernel32.CreateThreadFlags.None,
&dwNewThreadId);
Kernel32.WaitForSingleObject(hThread, -1);

Assert.True(result);
Assert.NotEqual((uint)Kernel32.GetCurrentThreadId(), dwNewThreadId);
}

/// <summary>
/// Basic validation for <see cref="CreateRemoteThread(IntPtr, SECURITY_ATTRIBUTES*, UIntPtr, THREAD_START_ROUTINE, void*, CreateThreadFlags, uint*)"/>
/// Note that this test DOES NOT create a true REMOTE thread in a foreign process; it just leverages this function to create a thread in the current (i.e, the test) procrss.
/// Nevertheless, this approach provides modest confidence that the P/Invoke definition is well-formed.
///
/// Creates a thread by supplying <see cref="CreateThread_Test_ThreadMain(void*)"/> as its ThreadProc/<see cref="Kernel32.THREAD_START_ROUTINE"/>.
/// The ThreadProc updates a bool value (supplied by the thread that created it) from false -> true. This change
/// is observed by the calling thread as proof of successful thread-creation.
///
/// Also validates that the (native) Thread-ID for the newly created Thread is different than the (native) Thread-ID
/// of that of the calling thread.
/// </summary>
[Fact]
public unsafe void CreateRemoteThread_PseudoTest()
{
var result = false;
var dwNewThreadId = 0u;

using var hProcess = Kernel32.GetCurrentProcess();
using var hThread =
Kernel32.CreateRemoteThread(
hProcess.DangerousGetHandle(),
null,
UIntPtr.Zero,
Kernel32Facts.threadProc,
&result,
Kernel32.CreateThreadFlags.None,
&dwNewThreadId);
Kernel32.WaitForSingleObject(hThread, -1);

Assert.True(result);
Assert.NotEqual((uint)Kernel32.GetCurrentThreadId(), dwNewThreadId);
}

/// <summary>
/// Basic validation for <see cref="CreateRemoteThreadEx(IntPtr, SECURITY_ATTRIBUTES*, UIntPtr, THREAD_START_ROUTINE, void*, CreateThreadFlags, PROC_THREAD_ATTRIBUTE_LIST*, uint*)"/>
/// Note that this test DOES NOT create a true REMOTE thread in a foreign process; it just leverages this function to create a thread in the current (i.e, the test) procrss.
/// Nevertheless, this approach provides modest confidence that the P/Invoke definition is well-formed.
///
/// Creates a thread by supplying <see cref="CreateThread_Test_ThreadMain(void*)"/> as its ThreadProc/<see cref="Kernel32.THREAD_START_ROUTINE"/>.
/// The ThreadProc updates a bool value (supplied by the thread that created it) from false -> true. This change
/// is observed by the calling thread as proof of successful thread-creation.
///
/// Also validates that the (native) Thread-ID for the newly created Thread is different than the (native) Thread-ID
/// of that of the calling thread.
/// </summary>
[Fact]
public unsafe void CreateRemoteThreadEx_PseudoTest()
{
var result = false;
var dwNewThreadId = 0u;

using var hProcess = Kernel32.GetCurrentProcess();
using var hThread =
Kernel32.CreateRemoteThreadEx(
hProcess.DangerousGetHandle(),
null,
UIntPtr.Zero,
Kernel32Facts.threadProc,
&result,
Kernel32.CreateThreadFlags.None,
null,
&dwNewThreadId);
Kernel32.WaitForSingleObject(hThread, -1);

Assert.True(result);
Assert.NotEqual((uint)Kernel32.GetCurrentThreadId(), dwNewThreadId);
}

/// <summary>
/// Helper for <see cref="CreateThread_Test"/>, <see cref="CreateRemoteThread_PseudoTest"/> and
/// <see cref="CreateRemoteThreadEx_PseudoTest"/> tests.
///
/// Updates the boolean data pasesed in to true.
/// </summary>
/// <param name="data">
/// Data passed by the test. Contains a pointer to a boolean value.
/// </param>
/// <returns>
/// Returns 1.
/// </returns>
/// <remarks>See <see cref=" Kernel32.THREAD_START_ROUTINE"/> for general documentation</remarks>
private static unsafe uint CreateThread_Test_ThreadMain(void* data)
{
*(bool*)data = true;
return 1;
}

private ArraySegment<byte> GetRandomSegment(int size)
{
var result = new ArraySegment<byte>(new byte[size]);
Expand Down
17 changes: 17 additions & 0 deletions src/Kernel32/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
PInvoke.Kernel32.CreatePseudoConsoleFlags
PInvoke.Kernel32.CreatePseudoConsoleFlags.None = 0 -> PInvoke.Kernel32.CreatePseudoConsoleFlags
PInvoke.Kernel32.CreatePseudoConsoleFlags.PSEUDOCONSOLE_INHERIT_CURSOR = 1 -> PInvoke.Kernel32.CreatePseudoConsoleFlags
PInvoke.Kernel32.CreateThreadFlags
PInvoke.Kernel32.CreateThreadFlags.CREATE_SUSPENDED = 4 -> PInvoke.Kernel32.CreateThreadFlags
PInvoke.Kernel32.CreateThreadFlags.None = 0 -> PInvoke.Kernel32.CreateThreadFlags
PInvoke.Kernel32.CreateThreadFlags.STACK_SIZE_PARAM_IS_A_RESERVATION = 65536 -> PInvoke.Kernel32.CreateThreadFlags
PInvoke.Kernel32.ErrorModes
PInvoke.Kernel32.ErrorModes.SEM_DEFAULT = 0 -> PInvoke.Kernel32.ErrorModes
PInvoke.Kernel32.ErrorModes.SEM_FAILCRITICALERRORS = 1 -> PInvoke.Kernel32.ErrorModes
Expand Down Expand Up @@ -81,6 +85,7 @@ PInvoke.Kernel32.STARTUPINFO.lpTitle_IntPtr.set -> void
PInvoke.Kernel32.SafePseudoConsoleHandle
PInvoke.Kernel32.SafePseudoConsoleHandle.SafePseudoConsoleHandle() -> void
PInvoke.Kernel32.SafePseudoConsoleHandle.SafePseudoConsoleHandle(System.IntPtr preexistingHandle, bool ownsHandle = true) -> void
PInvoke.Kernel32.THREAD_START_ROUTINE
PInvoke.Kernel32.VER_CONDITION
PInvoke.Kernel32.VER_CONDITION.VER_AND = 6 -> PInvoke.Kernel32.VER_CONDITION
PInvoke.Kernel32.VER_CONDITION.VER_EQUAL = 1 -> PInvoke.Kernel32.VER_CONDITION
Expand All @@ -102,6 +107,15 @@ override PInvoke.Kernel32.SafePseudoConsoleHandle.IsInvalid.get -> bool
override PInvoke.Kernel32.SafePseudoConsoleHandle.ReleaseHandle() -> bool
static PInvoke.Kernel32.CompareFileTime(PInvoke.Kernel32.FILETIME lpFileTime1, PInvoke.Kernel32.FILETIME lpFileTime2) -> int
static PInvoke.Kernel32.CompareFileTime(System.IntPtr lpFileTime1, System.IntPtr lpFileTime2) -> int
static PInvoke.Kernel32.CreateRemoteThread(System.IntPtr hProcess, PInvoke.Kernel32.SECURITY_ATTRIBUTES? lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, System.IntPtr lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, ref uint? lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.CreateRemoteThread(System.IntPtr hProcess, PInvoke.Kernel32.SECURITY_ATTRIBUTES? lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, void* lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, ref uint? lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.CreateRemoteThread(System.IntPtr hProcess, System.IntPtr lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, System.IntPtr lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, System.IntPtr lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.CreateRemoteThreadEx(System.IntPtr hProcess, PInvoke.Kernel32.SECURITY_ATTRIBUTES lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, System.IntPtr lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, System.IntPtr lpAttributeList, ref uint? lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.CreateRemoteThreadEx(System.IntPtr hProcess, PInvoke.Kernel32.SECURITY_ATTRIBUTES lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, void* lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, PInvoke.Kernel32.PROC_THREAD_ATTRIBUTE_LIST* lpAttributeList, ref uint? lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.CreateRemoteThreadEx(System.IntPtr hProcess, System.IntPtr lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, System.IntPtr lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, System.IntPtr lpAttributeList, System.IntPtr lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.CreateThread(PInvoke.Kernel32.SECURITY_ATTRIBUTES? lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, System.IntPtr lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, ref uint? lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.CreateThread(PInvoke.Kernel32.SECURITY_ATTRIBUTES? lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, void* lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, ref uint? lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.CreateThread(System.IntPtr lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, System.IntPtr lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, System.IntPtr lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static PInvoke.Kernel32.DosDateTimeToFileTime(ushort wFatDate, ushort wFatTime, System.IntPtr lpFileTime) -> bool
static PInvoke.Kernel32.DosDateTimeToFileTime(ushort wFatDate, ushort wFatTime, out PInvoke.Kernel32.FILETIME lpFileTime) -> bool
static PInvoke.Kernel32.FILETIME.explicit operator System.DateTime(PInvoke.Kernel32.FILETIME fileTime) -> System.DateTime
Expand All @@ -126,6 +140,9 @@ static PInvoke.Kernel32.VerifyVersionInfo(ref PInvoke.Kernel32.OSVERSIONINFOEX l
static PInvoke.Kernel32.WriteProcessMemory(System.IntPtr hProcess, System.IntPtr lpBaseAddress, System.IntPtr lpBuffer, System.UIntPtr nSize, out System.UIntPtr lpNumberOfBytesWritten) -> bool
static extern PInvoke.Kernel32.CompareFileTime(PInvoke.Kernel32.FILETIME* lpFileTime1, PInvoke.Kernel32.FILETIME* lpFileTime2) -> int
static extern PInvoke.Kernel32.CreatePseudoConsole(PInvoke.COORD size, PInvoke.Kernel32.SafeObjectHandle hInput, PInvoke.Kernel32.SafeObjectHandle hOutput, PInvoke.Kernel32.CreatePseudoConsoleFlags dwFlags, out PInvoke.Kernel32.SafePseudoConsoleHandle phPC) -> PInvoke.HResult
static extern PInvoke.Kernel32.CreateRemoteThread(System.IntPtr hProcess, PInvoke.Kernel32.SECURITY_ATTRIBUTES* lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, void* lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, uint* lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static extern PInvoke.Kernel32.CreateRemoteThreadEx(System.IntPtr hProcess, PInvoke.Kernel32.SECURITY_ATTRIBUTES* lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, void* lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, PInvoke.Kernel32.PROC_THREAD_ATTRIBUTE_LIST* lpAttributeList, uint* lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static extern PInvoke.Kernel32.CreateThread(PInvoke.Kernel32.SECURITY_ATTRIBUTES* lpThreadAttributes, System.UIntPtr dwStackSize, PInvoke.Kernel32.THREAD_START_ROUTINE lpStartAddress, void* lpParameter, PInvoke.Kernel32.CreateThreadFlags dwCreationFlags, uint* lpThreadId) -> PInvoke.Kernel32.SafeObjectHandle
static extern PInvoke.Kernel32.DosDateTimeToFileTime(ushort wFatDate, ushort wFatTime, PInvoke.Kernel32.FILETIME* lpFileTime) -> bool
static extern PInvoke.Kernel32.FileTimeToSystemTime(PInvoke.Kernel32.FILETIME* lpFileTime, PInvoke.Kernel32.SYSTEMTIME* lpSystemTime) -> bool
static extern PInvoke.Kernel32.GetHandleInformation(System.Runtime.InteropServices.SafeHandle hObject, PInvoke.Kernel32.HandleFlags* lpdwFlags) -> bool
Expand Down
2 changes: 1 addition & 1 deletion src/Kernel32/storebanned/Kernel32+CreateProcessFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public enum CreateProcessFlags
PROCESS_MODE_BACKGROUND_END = 0x00200000,
PROFILE_USER = 0x10000000,
PROFILE_KERNEL = 0x20000000,
PROFILE_SERVER = 0x40000000
PROFILE_SERVER = 0x40000000,
}
}
}
34 changes: 34 additions & 0 deletions src/Kernel32/storebanned/Kernel32+CreateThreadFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright © .NET Foundation and Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace PInvoke
{
using System;

/// <content>
/// Contains the <see cref="CreateThreadFlags"/> nested type.
/// </content>
public partial class Kernel32
{
/// <summary>
/// Flags that may be passed to the <see cref="CreateThread(SECURITY_ATTRIBUTES*, UIntPtr, THREAD_START_ROUTINE, void*, CreateThreadFlags, uint*)"/> function
/// </summary>
[Flags]
public enum CreateThreadFlags
{
None = 0x0,

/// <summary>
/// The primary thread of the new process is created in a suspended state, and does not run until the <see cref="ResumeThread"/> function is called.
/// </summary>
CREATE_SUSPENDED = 0x00000004,

/// <summary>
/// The dwStackSize parameter in <see cref="CreateThread(SECURITY_ATTRIBUTES*, UIntPtr, THREAD_START_ROUTINE, void*, CreateThreadFlags, uint*)"/>
/// specifies the initial reserve size of the stack. If this flag is not specified,
/// dwStackSize specifies the commit size.
/// </summary>
STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000,
}
}
}
Loading