From 44d9c2a3b255c0c05b7785a1fea27744b5c2540e Mon Sep 17 00:00:00 2001 From: David Hall Date: Tue, 4 Apr 2023 08:00:22 -0600 Subject: [PATCH] Minor tweaks to PR #386 --- PInvoke/NtDll/Winternl.cs | 320 +++++++++++++++---------------- UnitTests/PInvoke/NtDll/WinternlTests.cs | 208 ++++++++++---------- 2 files changed, 264 insertions(+), 264 deletions(-) diff --git a/PInvoke/NtDll/Winternl.cs b/PInvoke/NtDll/Winternl.cs index a5666191..766d8ecf 100644 --- a/PInvoke/NtDll/Winternl.cs +++ b/PInvoke/NtDll/Winternl.cs @@ -300,169 +300,169 @@ namespace Vanara.PInvoke [In] HPROCESS ParentProcess, [In] PROCESS_CREATE_FLAGS Flags, [In, Optional] IntPtr SectionHandle, [In, Optional] IntPtr DebugPort, [In, Optional] IntPtr ExceptionPort, uint JobMemberLevel); - /// Set the debug object handle in the TEB. This function is UNDOCUMENTED. - /// Debug object handle. Retrieve from NtQueryInformationProcess - /// - /// The function returns an NTSTATUS success or error code. - /// - /// The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the DDK, and are - /// described in the DDK documentation under Kernel-Mode Driver Architecture / Design Guide / Driver Programming Techniques / - /// Logging Errors. - /// - /// - [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] - public static extern NTStatus DbgUiSetThreadDebugObject(IntPtr DebugObjectHandle); + /// Set the debug object handle in the TEB. This function is UNDOCUMENTED. + /// Debug object handle. Retrieve from NtQueryInformationProcess + /// + /// The function returns an NTSTATUS success or error code. + /// + /// The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the DDK, and are + /// described in the DDK documentation under Kernel-Mode Driver Architecture / Design Guide / Driver Programming Techniques / + /// Logging Errors. + /// + /// + [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] + public static extern NTStatus DbgUiSetThreadDebugObject(IntPtr DebugObjectHandle); - /// Call the kernel to remove the debug object. This function is UNDOCUMENTED. + /// Call the kernel to remove the debug object. This function is UNDOCUMENTED. /// The process handle. - /// Debug object handle. Retrieve from NtQueryInformationProcess - /// - /// The function returns an NTSTATUS success or error code. - /// - /// The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the DDK, and are - /// described in the DDK documentation under Kernel-Mode Driver Architecture / Design Guide / Driver Programming Techniques / - /// Logging Errors. - /// - /// - [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] - public static extern NTStatus NtRemoveProcessDebug(HPROCESS ProcessHandle, IntPtr DebugObjectHandle); + /// Debug object handle. Retrieve from NtQueryInformationProcess + /// + /// The function returns an NTSTATUS success or error code. + /// + /// The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the DDK, and are + /// described in the DDK documentation under Kernel-Mode Driver Architecture / Design Guide / Driver Programming Techniques / + /// Logging Errors. + /// + /// + [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] + public static extern NTStatus NtRemoveProcessDebug(HPROCESS ProcessHandle, IntPtr DebugObjectHandle); - /// - /// - /// [ NtQueryInformationProcess may be altered or unavailable in future versions of Windows. Applications should use the - /// alternate functions listed in this topic.] - /// - /// Retrieves information about the specified process. - /// - /// A handle to the process for which information is to be retrieved. - /// - /// - /// The type of process information to be retrieved. This parameter can be one of the following values from the - /// PROCESSINFOCLASS enumeration. - /// - /// - /// - /// Value - /// Meaning - /// - /// - /// ProcessBasicInformation
0
- /// - /// Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a - /// unique value used by the system to identify the specified process. Use the CheckRemoteDebuggerPresent and GetProcessId functions - /// to obtain this information. - /// - ///
- /// - /// ProcessDebugPort
7
- /// - /// Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process - /// is being run under the control of a ring 3 debugger. Use the CheckRemoteDebuggerPresent or IsDebuggerPresent function. - /// - ///
- /// - /// ProcessWow64Information
26
- /// - /// Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based - /// applications to run on 64-bit Windows). Use the IsWow64Process2 function to obtain this information. - /// - ///
- /// - /// ProcessImageFileName
27
- /// - /// Retrieves a UNICODE_STRING value containing the name of the image file for the process. Use the QueryFullProcessImageName or - /// GetProcessImageFileName function to obtain this information. - /// - ///
- /// - /// ProcessBreakOnTermination
29
- /// Retrieves a ULONG value indicating whether the process is considered critical. - ///
- /// - /// ProcessSubsystemInformation
75
- /// - /// Retrieves a SUBSYSTEM_INFORMATION_TYPE value indicating the subsystem type of the process. The buffer pointed to by the - /// ProcessInformation parameter should be large enough to hold a single SUBSYSTEM_INFORMATION_TYPE enumeration. - /// - ///
- ///
- /// - /// - /// - /// A pointer to a buffer supplied by the calling application into which the function writes the requested information. The size of - /// the information written varies depending on the data type of the ProcessInformationClass parameter: - /// - /// PROCESS_BASIC_INFORMATION - /// - /// When the ProcessInformationClass parameter is ProcessBasicInformation, the buffer pointed to by the ProcessInformation - /// parameter should be large enough to hold a single PROCESS_BASIC_INFORMATION structure having the following layout: - /// - /// - /// - /// The UniqueProcessId member points to the system's unique identifier for this process. Use the GetProcessId function to - /// retrieve this information. - /// - /// The PebBaseAddress member points to a PEB structure. - /// The other members of this structure are reserved for internal use by the operating system. - /// ULONG_PTR - /// - /// When the ProcessInformationClass parameter is ProcessWow64Information, the buffer pointed to by the ProcessInformation - /// parameter should be large enough to hold a ULONG_PTR. If this value is nonzero, the process is running in a WOW64 - /// environment; otherwise, if the value is equal to zero, the process is not running in a WOW64 environment. - /// - /// Use the IsWow64Process2 function to determine whether a process is running in the WOW64 environment. - /// UNICODE_STRING - /// - /// When the ProcessInformationClass parameter is ProcessImageFileName, the buffer pointed to by the ProcessInformation - /// parameter should be large enough to hold a UNICODE_STRING structure as well as the string itself. The string stored in - /// the Buffer member is the name of the image file. - /// - /// - /// If the buffer is too small, the function fails with the STATUS_INFO_LENGTH_MISMATCH error code and the ReturnLength parameter is - /// set to the required buffer size. - /// - /// - /// The size of the buffer pointed to by the ProcessInformation parameter, in bytes. - /// - /// A pointer to a variable in which the function returns the size of the requested information. If the function was successful, - /// this is the size of the information written to the buffer pointed to by the ProcessInformation parameter, but if the buffer was - /// too small, this is the minimum size of buffer needed to receive the information successfully. - /// - /// - /// The function returns an NTSTATUS success or error code. - /// - /// The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the DDK, and are - /// described in the DDK documentation under Kernel-Mode Driver Architecture / Design Guide / Driver Programming Techniques / - /// Logging Errors. - /// - /// - /// - /// - /// The NtQueryInformationProcess function and the structures that it returns are internal to the operating system and - /// subject to change from one release of Windows to another. To maintain the compatibility of your application, it is better to use - /// public functions mentioned in the description of the ProcessInformationClass parameter instead. - /// - /// - /// If you do use NtQueryInformationProcess, access the function through run-time dynamic linking. This gives your code an - /// opportunity to respond gracefully if the function has been changed or removed from the operating system. Signature changes, - /// however, may not be detectable. - /// - /// - /// This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll. - /// - /// - // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess __kernel_entry NTSTATUS - // NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN - // ULONG ProcessInformationLength, OUT PULONG ReturnLength ); - [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] + /// + /// + /// [ NtQueryInformationProcess may be altered or unavailable in future versions of Windows. Applications should use the + /// alternate functions listed in this topic.] + /// + /// Retrieves information about the specified process. + /// + /// A handle to the process for which information is to be retrieved. + /// + /// + /// The type of process information to be retrieved. This parameter can be one of the following values from the + /// PROCESSINFOCLASS enumeration. + /// + /// + /// + /// Value + /// Meaning + /// + /// + /// ProcessBasicInformation
0
+ /// + /// Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a + /// unique value used by the system to identify the specified process. Use the CheckRemoteDebuggerPresent and GetProcessId functions + /// to obtain this information. + /// + ///
+ /// + /// ProcessDebugPort
7
+ /// + /// Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process + /// is being run under the control of a ring 3 debugger. Use the CheckRemoteDebuggerPresent or IsDebuggerPresent function. + /// + ///
+ /// + /// ProcessWow64Information
26
+ /// + /// Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based + /// applications to run on 64-bit Windows). Use the IsWow64Process2 function to obtain this information. + /// + ///
+ /// + /// ProcessImageFileName
27
+ /// + /// Retrieves a UNICODE_STRING value containing the name of the image file for the process. Use the QueryFullProcessImageName or + /// GetProcessImageFileName function to obtain this information. + /// + ///
+ /// + /// ProcessBreakOnTermination
29
+ /// Retrieves a ULONG value indicating whether the process is considered critical. + ///
+ /// + /// ProcessSubsystemInformation
75
+ /// + /// Retrieves a SUBSYSTEM_INFORMATION_TYPE value indicating the subsystem type of the process. The buffer pointed to by the + /// ProcessInformation parameter should be large enough to hold a single SUBSYSTEM_INFORMATION_TYPE enumeration. + /// + ///
+ ///
+ /// + /// + /// + /// A pointer to a buffer supplied by the calling application into which the function writes the requested information. The size of + /// the information written varies depending on the data type of the ProcessInformationClass parameter: + /// + /// PROCESS_BASIC_INFORMATION + /// + /// When the ProcessInformationClass parameter is ProcessBasicInformation, the buffer pointed to by the ProcessInformation + /// parameter should be large enough to hold a single PROCESS_BASIC_INFORMATION structure having the following layout: + /// + /// + /// + /// The UniqueProcessId member points to the system's unique identifier for this process. Use the GetProcessId function to + /// retrieve this information. + /// + /// The PebBaseAddress member points to a PEB structure. + /// The other members of this structure are reserved for internal use by the operating system. + /// ULONG_PTR + /// + /// When the ProcessInformationClass parameter is ProcessWow64Information, the buffer pointed to by the ProcessInformation + /// parameter should be large enough to hold a ULONG_PTR. If this value is nonzero, the process is running in a WOW64 + /// environment; otherwise, if the value is equal to zero, the process is not running in a WOW64 environment. + /// + /// Use the IsWow64Process2 function to determine whether a process is running in the WOW64 environment. + /// UNICODE_STRING + /// + /// When the ProcessInformationClass parameter is ProcessImageFileName, the buffer pointed to by the ProcessInformation + /// parameter should be large enough to hold a UNICODE_STRING structure as well as the string itself. The string stored in + /// the Buffer member is the name of the image file. + /// + /// + /// If the buffer is too small, the function fails with the STATUS_INFO_LENGTH_MISMATCH error code and the ReturnLength parameter is + /// set to the required buffer size. + /// + /// + /// The size of the buffer pointed to by the ProcessInformation parameter, in bytes. + /// + /// A pointer to a variable in which the function returns the size of the requested information. If the function was successful, + /// this is the size of the information written to the buffer pointed to by the ProcessInformation parameter, but if the buffer was + /// too small, this is the minimum size of buffer needed to receive the information successfully. + /// + /// + /// The function returns an NTSTATUS success or error code. + /// + /// The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the DDK, and are + /// described in the DDK documentation under Kernel-Mode Driver Architecture / Design Guide / Driver Programming Techniques / + /// Logging Errors. + /// + /// + /// + /// + /// The NtQueryInformationProcess function and the structures that it returns are internal to the operating system and + /// subject to change from one release of Windows to another. To maintain the compatibility of your application, it is better to use + /// public functions mentioned in the description of the ProcessInformationClass parameter instead. + /// + /// + /// If you do use NtQueryInformationProcess, access the function through run-time dynamic linking. This gives your code an + /// opportunity to respond gracefully if the function has been changed or removed from the operating system. Signature changes, + /// however, may not be detectable. + /// + /// + /// This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess __kernel_entry NTSTATUS + // NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN + // ULONG ProcessInformationLength, OUT PULONG ReturnLength ); + [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] [PInvokeData("winternl.h", MSDNShortId = "0eae7899-c40b-4a5f-9e9c-adae021885e7")] public static extern NTStatus NtQueryInformationProcess([In] HPROCESS ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, [Out] IntPtr ProcessInformation, uint ProcessInformationLength, out uint ReturnLength); diff --git a/UnitTests/PInvoke/NtDll/WinternlTests.cs b/UnitTests/PInvoke/NtDll/WinternlTests.cs index 2aa3cb4d..3ce72704 100644 --- a/UnitTests/PInvoke/NtDll/WinternlTests.cs +++ b/UnitTests/PInvoke/NtDll/WinternlTests.cs @@ -5,124 +5,124 @@ using static Vanara.PInvoke.NtDll; namespace Vanara.PInvoke.Tests { - [TestFixture] - public partial class WinternlTests - { - [Test] - public void NtQueryInformationProcessTest() - { - HPROCESS hProc = Kernel32.GetCurrentProcess(); - var procIsWow64 = hProc.IsWow64(); - var procIs64 = Environment.Is64BitProcess; - var osIs64 = Environment.Is64BitOperatingSystem; + [TestFixture] + public partial class WinternlTests + { + [Test] + public void NtQueryInformationProcessTest() + { + HPROCESS hProc = Kernel32.GetCurrentProcess(); + var procIsWow64 = hProc.IsWow64(); + var procIs64 = Environment.Is64BitProcess; + var osIs64 = Environment.Is64BitOperatingSystem; - using var pbi = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessBasicInformation); - Assert.That(pbi, ResultIs.ValidHandle); - // Can get pointer here since PROCESS_BASIC_INFORMATION has no managed types - unsafe - { - var rpbi = (PROCESS_BASIC_INFORMATION*)pbi; - Assert.That(rpbi->UniqueProcessId.ToInt32(), Is.EqualTo(Kernel32.GetCurrentProcessId())); - Assert.That(rpbi->PebBaseAddress, Is.Not.EqualTo(IntPtr.Zero)); - // Have to use ToStructure here since PEB has managed types - var peb = rpbi->PebBaseAddress.ToStructure(); - // Have to use ToStructure here since RTL_USER_PROCESS_PARAMETERS has managed types - var upp = peb.ProcessParameters.ToStructure(); - Assert.That(upp.CommandLine.ToString(hProc), Is.Not.Empty); - TestContext.WriteLine($"Img: {upp.ImagePathName.ToString(hProc)}; CmdLine: {upp.CommandLine.ToString(hProc)}"); - } + using var pbi = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessBasicInformation); + Assert.That(pbi, ResultIs.ValidHandle); + // Can get pointer here since PROCESS_BASIC_INFORMATION has no managed types + unsafe + { + var rpbi = (PROCESS_BASIC_INFORMATION*)pbi; + Assert.That(rpbi->UniqueProcessId.ToInt32(), Is.EqualTo(Kernel32.GetCurrentProcessId())); + Assert.That(rpbi->PebBaseAddress, Is.Not.EqualTo(IntPtr.Zero)); + // Have to use ToStructure here since PEB has managed types + var peb = rpbi->PebBaseAddress.ToStructure(); + // Have to use ToStructure here since RTL_USER_PROCESS_PARAMETERS has managed types + var upp = peb.ProcessParameters.ToStructure(); + Assert.That(upp.CommandLine.ToString(hProc), Is.Not.Empty); + TestContext.WriteLine($"Img: {upp.ImagePathName.ToString(hProc)}; CmdLine: {upp.CommandLine.ToString(hProc)}"); + } - NtQueryResult pdp = null; - Assert.That(() => pdp = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessDebugPort), Throws.Nothing); - Assert.That(pdp, ResultIs.ValidHandle); - TestContext.WriteLine($"DbgPort: {pdp.Value.ToInt64()}"); + NtQueryResult pdp = null; + Assert.That(() => pdp = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessDebugPort), Throws.Nothing); + Assert.That(pdp, ResultIs.ValidHandle); + TestContext.WriteLine($"DbgPort: {pdp.Value.ToInt64()}"); - NtQueryResult pwi = null; - Assert.That(() => pwi = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessWow64Information), Throws.Nothing); - Assert.That(pwi, ResultIs.ValidHandle); - Assert.That(pwi.Value.Value, Is.True); + NtQueryResult pwi = null; + Assert.That(() => pwi = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessWow64Information), Throws.Nothing); + Assert.That(pwi, ResultIs.ValidHandle); + Assert.That(pwi.Value.Value, Is.True); - NtQueryResult pfn = null; - Assert.That(() => pfn = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessImageFileName), Throws.Nothing); - Assert.That(pfn, ResultIs.ValidHandle); - TestContext.WriteLine($"Fn: {pfn.Value.ToString(hProc)}"); + NtQueryResult pfn = null; + Assert.That(() => pfn = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessImageFileName), Throws.Nothing); + Assert.That(pfn, ResultIs.ValidHandle); + TestContext.WriteLine($"Fn: {pfn.Value.ToString(hProc)}"); - NtQueryResult pbt = null; - Assert.That(() => pbt = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessBreakOnTermination), Throws.Nothing); - Assert.That(pbt, ResultIs.ValidHandle); - Assert.That(pbt.Value.Value, Is.False); + NtQueryResult pbt = null; + Assert.That(() => pbt = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessBreakOnTermination), Throws.Nothing); + Assert.That(pbt, ResultIs.ValidHandle); + Assert.That(pbt.Value.Value, Is.False); - NtQueryResult psi = null; - // This is documented, but fails on Win10 - Assert.That(() => psi = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessSubsystemInformation), Throws.ArgumentException); - //Assert.That(psi, ResultIs.ValidHandle); - //Assert.That(Enum.IsDefined(typeof(SUBSYSTEM_INFORMATION_TYPE), psi.Value), Is.True); - //TestContext.WriteLine($"SubSys: {psi.Value}"); + NtQueryResult psi = null; + // This is documented, but fails on Win10 + Assert.That(() => psi = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessSubsystemInformation), Throws.ArgumentException); + //Assert.That(psi, ResultIs.ValidHandle); + //Assert.That(Enum.IsDefined(typeof(SUBSYSTEM_INFORMATION_TYPE), psi.Value), Is.True); + //TestContext.WriteLine($"SubSys: {psi.Value}"); - // Try undocumented fetch - NtQueryResult ppb = null; - Assert.That(() => ppb = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessPriorityBoost), Throws.Nothing); - TestContext.WriteLine($"Priority boost: {ppb.Value}"); - } + // Try undocumented fetch + NtQueryResult ppb = null; + Assert.That(() => ppb = NtQueryInformationProcess(hProc, PROCESSINFOCLASS.ProcessPriorityBoost), Throws.Nothing); + TestContext.WriteLine($"Priority boost: {ppb.Value}"); + } - [Test] - public void DbgUiSetThreadDebugObjectAndNtRemoveProcessDebugTest() - { - Kernel32.STARTUPINFO StartInfo = new Kernel32.STARTUPINFO - { - dwFlags = Kernel32.STARTF.STARTF_USESHOWWINDOW, - wShowWindow = (ushort)ShowWindowCommand.SW_HIDE - }; + [Test] + public void DbgUiSetThreadDebugObjectAndNtRemoveProcessDebugTest() + { + Kernel32.STARTUPINFO StartInfo = new() + { + dwFlags = Kernel32.STARTF.STARTF_USESHOWWINDOW, + wShowWindow = (ushort)ShowWindowCommand.SW_HIDE + }; - Assert.IsTrue(Kernel32.CreateProcess("notepad.exe", dwCreationFlags: Kernel32.CREATE_PROCESS.DEBUG_PROCESS | Kernel32.CREATE_PROCESS.CREATE_UNICODE_ENVIRONMENT, lpStartupInfo: StartInfo, lpProcessInformation: out Kernel32.SafePROCESS_INFORMATION Information)); + Assert.That(Kernel32.CreateProcess(@"C:\Program Files\Notepad++\notepad++.exe", dwCreationFlags: Kernel32.CREATE_PROCESS.DEBUG_PROCESS | Kernel32.CREATE_PROCESS.CREATE_UNICODE_ENVIRONMENT, lpStartupInfo: StartInfo, lpProcessInformation: out Kernel32.SafePROCESS_INFORMATION Information), ResultIs.Successful); - using (Information) - using (NtQueryResult DebugObjectHandleQueryResult = NtQueryInformationProcess(Information.hProcess, PROCESSINFOCLASS.ProcessDebugObjectHandle)) - { - Assert.That(DebugObjectHandleQueryResult, ResultIs.ValidHandle); - Assert.That(DebugObjectHandleQueryResult.Value, ResultIs.ValidHandle); + using (Information) + using (NtQueryResult DebugObjectHandleQueryResult = NtQueryInformationProcess(Information.hProcess, PROCESSINFOCLASS.ProcessDebugObjectHandle)) + { + Assert.That(DebugObjectHandleQueryResult, ResultIs.ValidHandle); + Assert.That(DebugObjectHandleQueryResult.Value, ResultIs.ValidHandle); - try - { - Assert.DoesNotThrow(() => DbgUiSetThreadDebugObject(DebugObjectHandleQueryResult.Value).ThrowIfFailed()); + try + { + Assert.That(DbgUiSetThreadDebugObject(DebugObjectHandleQueryResult.Value), ResultIs.Successful); - try - { - Kernel32.SafeHPROCESS DebugProcessHandle = Kernel32.SafeHPROCESS.Null; + try + { + Kernel32.SafeHPROCESS DebugProcessHandle = Kernel32.SafeHPROCESS.Null; - try - { - while (true) - { - Assert.IsTrue(Kernel32.WaitForDebugEvent(out Kernel32.DEBUG_EVENT Event, Kernel32.INFINITE)); + try + { + while (true) + { + Assert.That(Kernel32.WaitForDebugEvent(out Kernel32.DEBUG_EVENT Event, Kernel32.INFINITE), ResultIs.Successful); - if (Event.dwDebugEventCode == Kernel32.DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT) - { - DebugProcessHandle = new Kernel32.SafeHPROCESS(Event.u.CreateProcessInfo.hProcess); - break; - } + if (Event.dwDebugEventCode == Kernel32.DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT) + { + DebugProcessHandle = new Kernel32.SafeHPROCESS(Event.u.CreateProcessInfo.hProcess); + break; + } - Assert.IsTrue(Kernel32.ContinueDebugEvent(Event.dwProcessId, Event.dwThreadId, Kernel32.DEBUG_CONTINUE.DBG_CONTINUE)); - } + Assert.That(Kernel32.ContinueDebugEvent(Event.dwProcessId, Event.dwThreadId, Kernel32.DEBUG_CONTINUE.DBG_CONTINUE), ResultIs.Successful); + } - Assert.AreNotEqual(Kernel32.SafeHPROCESS.Null, DebugProcessHandle); - } - finally - { - DebugProcessHandle.Dispose(); - } - } - finally - { - Assert.DoesNotThrow(() => DbgUiSetThreadDebugObject(IntPtr.Zero).ThrowIfFailed()); - } - } - finally - { - Assert.IsTrue(Kernel32.TerminateProcess(Information.hProcess, 0)); - Assert.DoesNotThrow(() => NtRemoveProcessDebug(Information.hProcess, DebugObjectHandleQueryResult.Value).ThrowIfFailed()); - } - } - } - } + Assert.False(DebugProcessHandle.IsNull); + } + finally + { + DebugProcessHandle.Dispose(); + } + } + finally + { + Assert.That(DbgUiSetThreadDebugObject(IntPtr.Zero), ResultIs.Successful); + } + } + finally + { + Assert.That(Kernel32.TerminateProcess(Information.hProcess, 0), ResultIs.Successful); + Assert.That(NtRemoveProcessDebug(Information.hProcess, DebugObjectHandleQueryResult.Value), ResultIs.Successful); + } + } + } + } } \ No newline at end of file