mirror of https://github.com/dahall/Vanara.git
Moved functions from WinBase.Debugging.cs to DebugApi.cs and added some unit tests
parent
724164ceee
commit
fba697592c
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vanara.Extensions;
|
||||
using Vanara.InteropServices;
|
||||
|
||||
namespace Vanara.PInvoke
|
||||
{
|
||||
|
@ -189,6 +188,70 @@ namespace Vanara.PInvoke
|
|||
[PInvokeData("WinBase.h", MSDNShortId = "ms679297")]
|
||||
public static extern void DebugBreak();
|
||||
|
||||
/// <summary>
|
||||
/// Causes a breakpoint exception to occur in the specified process. This allows the calling thread to signal the debugger to handle
|
||||
/// the exception.
|
||||
/// </summary>
|
||||
/// <param name="Process">A handle to the process.</param>
|
||||
/// <returns>
|
||||
/// <para>If the function succeeds, the return value is nonzero.</para>
|
||||
/// <para>If the function fails, the return value is zero. To get extended error information, call <c>GetLastError</c>.</para>
|
||||
/// </returns>
|
||||
// BOOL WINAPI DebugBreakProcess( _In_ HANDLE Process); https://msdn.microsoft.com/en-us/library/windows/desktop/ms679298(v=vs.85).aspx
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)]
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679298")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DebugBreakProcess([In] HPROCESS Process);
|
||||
|
||||
/// <summary>Sets the action to be performed when the calling thread exits.</summary>
|
||||
/// <param name="KillOnExit">
|
||||
/// If this parameter is <c>TRUE</c>, the thread terminates all attached processes on exit (note that this is the default).
|
||||
/// Otherwise, the thread detaches from all processes being debugged on exit.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <para>If the function succeeds, the return value is nonzero.</para>
|
||||
/// <para>If the function fails, the return value is zero. To get extended error information, call <c>GetLastError</c>.</para>
|
||||
/// </returns>
|
||||
// BOOL WINAPI DebugSetProcessKillOnExit( _In_ BOOL KillOnExit); https://msdn.microsoft.com/en-us/library/windows/desktop/ms679307(v=vs.85).aspx
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)]
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679307")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DebugSetProcessKillOnExit([MarshalAs(UnmanagedType.Bool)] bool KillOnExit);
|
||||
|
||||
/// <summary>
|
||||
/// Transfers execution control to the debugger. The behavior of the debugger thereafter is specific to the type of debugger used.
|
||||
/// </summary>
|
||||
/// <param name="ExitCode">The error code associated with the exit.</param>
|
||||
/// <returns>This function does not return a value.</returns>
|
||||
// void WINAPI FatalExit( _In_ int ExitCode); https://msdn.microsoft.com/en-us/library/windows/desktop/ms679337(v=vs.85).aspx
|
||||
[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679337")]
|
||||
public static extern void FatalExit(int ExitCode);
|
||||
|
||||
/// <summary>Retrieves a descriptor table entry for the specified selector and thread.</summary>
|
||||
/// <param name="hThread">
|
||||
/// A handle to the thread containing the specified selector. The handle must have THREAD_QUERY_INFORMATION access. For more
|
||||
/// information, see Thread Security and Access Rights.
|
||||
/// </param>
|
||||
/// <param name="dwSelector">The global or local selector value to look up in the thread's descriptor tables.</param>
|
||||
/// <param name="lpSelectorEntry">
|
||||
/// A pointer to an <c>LDT_ENTRY</c> structure that receives a copy of the descriptor table entry if the specified selector has an
|
||||
/// entry in the specified thread's descriptor table. This information can be used to convert a segment-relative address to a linear
|
||||
/// virtual address.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <para>
|
||||
/// If the function succeeds, the return value is nonzero. In that case, the structure pointed to by the lpSelectorEntry parameter
|
||||
/// receives a copy of the specified descriptor table entry.
|
||||
/// </para>
|
||||
/// <para>If the function fails, the return value is zero. To get extended error information, call <c>GetLastError</c>.</para>
|
||||
/// </returns>
|
||||
// BOOL WINAPI GetThreadSelectorEntry( _In_ HANDLE hThread, _In_ DWORD dwSelector, _Out_ LPLDT_ENTRY lpSelectorEntry); https://msdn.microsoft.com/en-us/library/windows/desktop/ms679363(v=vs.85).aspx
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)]
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679363")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetThreadSelectorEntry([In] HTHREAD hThread, uint dwSelector, out LDT_ENTRY lpSelectorEntry);
|
||||
|
||||
/// <summary>Determines whether the calling process is being debugged by a user-mode debugger.</summary>
|
||||
/// <returns>
|
||||
/// <para>If the current process is running in the context of a debugger, the return value is nonzero.</para>
|
||||
|
@ -246,6 +309,11 @@ namespace Vanara.PInvoke
|
|||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool WaitForDebugEventEx(out DEBUG_EVENT lpDebugEvent, uint dwMilliseconds);
|
||||
|
||||
private static string ReadString(IntPtr data, ushort fUni, ushort len)
|
||||
{
|
||||
return StringHelper.GetString(data, fUni != 0 ? CharSet.Unicode : CharSet.Ansi, len);
|
||||
}
|
||||
|
||||
/// <summary>Describes a debugging event.</summary>
|
||||
// typedef struct _DEBUG_EVENT { DWORD dwDebugEventCode; DWORD dwProcessId; DWORD dwThreadId; union { EXCEPTION_DEBUG_INFO Exception;
|
||||
// CREATE_THREAD_DEBUG_INFO CreateThread; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; EXIT_THREAD_DEBUG_INFO ExitThread;
|
||||
|
@ -400,8 +468,8 @@ namespace Vanara.PInvoke
|
|||
public struct EXCEPTION_DEBUG_INFO
|
||||
{
|
||||
/// <summary>
|
||||
/// An <c>EXCEPTION_RECORD</c> structure with information specific to the exception. This includes the exception code,
|
||||
/// flags, address, a pointer to a related exception, extra parameters, and so on.
|
||||
/// An <c>EXCEPTION_RECORD</c> structure with information specific to the exception. This includes the exception code, flags,
|
||||
/// address, a pointer to a related exception, extra parameters, and so on.
|
||||
/// </summary>
|
||||
public EXCEPTION_RECORD ExceptionRecord;
|
||||
|
||||
|
@ -409,31 +477,30 @@ namespace Vanara.PInvoke
|
|||
/// A value that indicates whether the debugger has previously encountered the exception specified by the
|
||||
/// <c>ExceptionRecord</c> member. If the <c>dwFirstChance</c> member is nonzero, this is the first time the debugger has
|
||||
/// encountered the exception. Debuggers typically handle breakpoint and single-step exceptions when they are first
|
||||
/// encountered. If this member is zero, the debugger has previously encountered the exception. This occurs only if,
|
||||
/// during the search for structured exception handlers, either no handler was found or the exception was continued.
|
||||
/// encountered. If this member is zero, the debugger has previously encountered the exception. This occurs only if, during
|
||||
/// the search for structured exception handlers, either no handler was found or the exception was continued.
|
||||
/// </summary>
|
||||
public uint dwFirstChance;
|
||||
}
|
||||
|
||||
/// <summary>Contains thread-creation information that can be used by a debugger.</summary>
|
||||
// typedef struct _CREATE_THREAD_DEBUG_INFO { HANDLE hThread; LPVOID lpThreadLocalBase; LPTHREAD_START_ROUTINE
|
||||
// lpStartAddress;} CREATE_THREAD_DEBUG_INFO,
|
||||
// typedef struct _CREATE_THREAD_DEBUG_INFO { HANDLE hThread; LPVOID lpThreadLocalBase; LPTHREAD_START_ROUTINE lpStartAddress;} CREATE_THREAD_DEBUG_INFO,
|
||||
// *LPCREATE_THREAD_DEBUG_INFO; https://msdn.microsoft.com/en-us/library/windows/desktop/ms679287(v=vs.85).aspx
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679287")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CREATE_THREAD_DEBUG_INFO
|
||||
{
|
||||
/// <summary>
|
||||
/// A handle to the thread whose creation caused the debugging event. If this member is <c>NULL</c>, the handle is not
|
||||
/// valid. Otherwise, the debugger has THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, and THREAD_SUSPEND_RESUME access to the
|
||||
/// thread, allowing the debugger to read from and write to the registers of the thread and control execution of the thread.
|
||||
/// A handle to the thread whose creation caused the debugging event. If this member is <c>NULL</c>, the handle is not valid.
|
||||
/// Otherwise, the debugger has THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, and THREAD_SUSPEND_RESUME access to the thread,
|
||||
/// allowing the debugger to read from and write to the registers of the thread and control execution of the thread.
|
||||
/// </summary>
|
||||
public HTHREAD hThread;
|
||||
|
||||
/// <summary>
|
||||
/// A pointer to a block of data. At offset 0x2C into this block is another pointer, called ThreadLocalStoragePointer,
|
||||
/// that points to an array of per-module thread local storage blocks. This gives a debugger access to per-thread data in
|
||||
/// the threads of the process being debugged using the same algorithms that a compiler would use.
|
||||
/// A pointer to a block of data. At offset 0x2C into this block is another pointer, called ThreadLocalStoragePointer, that
|
||||
/// points to an array of per-module thread local storage blocks. This gives a debugger access to per-thread data in the
|
||||
/// threads of the process being debugged using the same algorithms that a compiler would use.
|
||||
/// </summary>
|
||||
public IntPtr lpThreadLocalBase;
|
||||
|
||||
|
@ -457,8 +524,8 @@ namespace Vanara.PInvoke
|
|||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// A handle to the process's image file. If this member is <c>NULL</c>, the handle is not valid. Otherwise, the debugger
|
||||
/// can use the member to read from and write to the image file.
|
||||
/// A handle to the process's image file. If this member is <c>NULL</c>, the handle is not valid. Otherwise, the debugger can
|
||||
/// use the member to read from and write to the image file.
|
||||
/// </para>
|
||||
/// <para>When the debugger is finished with this file, it should close the handle using the <c>CloseHandle</c> function.</para>
|
||||
/// </summary>
|
||||
|
@ -472,9 +539,9 @@ namespace Vanara.PInvoke
|
|||
|
||||
/// <summary>
|
||||
/// A handle to the initial thread of the process identified by the <c>hProcess</c> member. If <c>hThread</c> param is
|
||||
/// <c>NULL</c>, the handle is not valid. Otherwise, the debugger has <c>THREAD_GET_CONTEXT</c>,
|
||||
/// <c>THREAD_SET_CONTEXT</c>, and <c>THREAD_SUSPEND_RESUME</c> access to the thread, allowing the debugger to read from
|
||||
/// and write to the registers of the thread and to control execution of the thread.
|
||||
/// <c>NULL</c>, the handle is not valid. Otherwise, the debugger has <c>THREAD_GET_CONTEXT</c>, <c>THREAD_SET_CONTEXT</c>,
|
||||
/// and <c>THREAD_SUSPEND_RESUME</c> access to the thread, allowing the debugger to read from and write to the registers of
|
||||
/// the thread and to control execution of the thread.
|
||||
/// </summary>
|
||||
public HTHREAD hThread;
|
||||
|
||||
|
@ -507,24 +574,24 @@ namespace Vanara.PInvoke
|
|||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// A pointer to the file name associated with the <c>hFile</c> member. This parameter may be <c>NULL</c>, or it may
|
||||
/// contain the address of a string pointer in the address space of the process being debugged. That address may, in
|
||||
/// turn, either be <c>NULL</c> or point to the actual filename. If <c>fUnicode</c> is a nonzero value, the name string
|
||||
/// is Unicode; otherwise, it is ANSI.
|
||||
/// A pointer to the file name associated with the <c>hFile</c> member. This parameter may be <c>NULL</c>, or it may contain
|
||||
/// the address of a string pointer in the address space of the process being debugged. That address may, in turn, either be
|
||||
/// <c>NULL</c> or point to the actual filename. If <c>fUnicode</c> is a nonzero value, the name string is Unicode;
|
||||
/// otherwise, it is ANSI.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This member is strictly optional. Debuggers must be prepared to handle the case where <c>lpImageName</c> is
|
||||
/// <c>NULL</c> or * <c>lpImageName</c> (in the address space of the process being debugged) is <c>NULL</c>.
|
||||
/// Specifically, the system does not provide an image name for a create process event, and will not likely pass an image
|
||||
/// name for the first DLL event. The system also does not provide this information in the case of debug events that
|
||||
/// originate from a call to the <c>DebugActiveProcess</c> function.
|
||||
/// This member is strictly optional. Debuggers must be prepared to handle the case where <c>lpImageName</c> is <c>NULL</c>
|
||||
/// or * <c>lpImageName</c> (in the address space of the process being debugged) is <c>NULL</c>. Specifically, the system
|
||||
/// does not provide an image name for a create process event, and will not likely pass an image name for the first DLL
|
||||
/// event. The system also does not provide this information in the case of debug events that originate from a call to the
|
||||
/// <c>DebugActiveProcess</c> function.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public IntPtr lpImageName;
|
||||
|
||||
/// <summary>
|
||||
/// A value that indicates whether a file name specified by the <c>lpImageName</c> member is Unicode or ANSI. A nonzero
|
||||
/// value indicates Unicode; zero indicates ANSI.
|
||||
/// A value that indicates whether a file name specified by the <c>lpImageName</c> member is Unicode or ANSI. A nonzero value
|
||||
/// indicates Unicode; zero indicates ANSI.
|
||||
/// </summary>
|
||||
public ushort fUnicode;
|
||||
}
|
||||
|
@ -558,8 +625,8 @@ namespace Vanara.PInvoke
|
|||
{
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// A handle to the loaded DLL. If this member is <c>NULL</c>, the handle is not valid. Otherwise, the member is opened
|
||||
/// for reading and read-sharing in the context of the debugger.
|
||||
/// A handle to the loaded DLL. If this member is <c>NULL</c>, the handle is not valid. Otherwise, the member is opened for
|
||||
/// reading and read-sharing in the context of the debugger.
|
||||
/// </para>
|
||||
/// <para>When the debugger is finished with this file, it should close the handle using the <c>CloseHandle</c> function.</para>
|
||||
/// </summary>
|
||||
|
@ -569,9 +636,9 @@ namespace Vanara.PInvoke
|
|||
public IntPtr lpBaseOfDll;
|
||||
|
||||
/// <summary>
|
||||
/// The offset to the debugging information in the file identified by the <c>hFile</c> member, in bytes. The system
|
||||
/// expects the debugging information to be in CodeView 4.0 format. This format is currently a derivative of Common
|
||||
/// Object File Format (COFF).
|
||||
/// The offset to the debugging information in the file identified by the <c>hFile</c> member, in bytes. The system expects
|
||||
/// the debugging information to be in CodeView 4.0 format. This format is currently a derivative of Common Object File
|
||||
/// Format (COFF).
|
||||
/// </summary>
|
||||
public uint dwDebugInfoFileOffset;
|
||||
|
||||
|
@ -582,24 +649,23 @@ namespace Vanara.PInvoke
|
|||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// A pointer to the file name associated with <c>hFile</c>. This member may be <c>NULL</c>, or it may contain the
|
||||
/// address of a string pointer in the address space of the process being debugged. That address may, in turn, either be
|
||||
/// <c>NULL</c> or point to the actual filename. If <c>fUnicode</c> is a nonzero value, the name string is Unicode;
|
||||
/// otherwise, it is ANSI.
|
||||
/// A pointer to the file name associated with <c>hFile</c>. This member may be <c>NULL</c>, or it may contain the address of
|
||||
/// a string pointer in the address space of the process being debugged. That address may, in turn, either be <c>NULL</c> or
|
||||
/// point to the actual filename. If <c>fUnicode</c> is a nonzero value, the name string is Unicode; otherwise, it is ANSI.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This member is strictly optional. Debuggers must be prepared to handle the case where <c>lpImageName</c> is
|
||||
/// <c>NULL</c> or * <c>lpImageName</c> (in the address space of the process being debugged) is <c>NULL</c>.
|
||||
/// Specifically, the system will never provide an image name for a create process event, and it will not likely pass an
|
||||
/// image name for the first DLL event. The system will also never provide this information in the case of debugging
|
||||
/// events that originate from a call to the <c>DebugActiveProcess</c> function.
|
||||
/// This member is strictly optional. Debuggers must be prepared to handle the case where <c>lpImageName</c> is <c>NULL</c>
|
||||
/// or * <c>lpImageName</c> (in the address space of the process being debugged) is <c>NULL</c>. Specifically, the system
|
||||
/// will never provide an image name for a create process event, and it will not likely pass an image name for the first DLL
|
||||
/// event. The system will also never provide this information in the case of debugging events that originate from a call to
|
||||
/// the <c>DebugActiveProcess</c> function.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public IntPtr lpImageName;
|
||||
|
||||
/// <summary>
|
||||
/// A value that indicates whether a filename specified by <c>lpImageName</c> is Unicode or ANSI. A nonzero value for
|
||||
/// this member indicates Unicode; zero indicates ANSI.
|
||||
/// A value that indicates whether a filename specified by <c>lpImageName</c> is Unicode or ANSI. A nonzero value for this
|
||||
/// member indicates Unicode; zero indicates ANSI.
|
||||
/// </summary>
|
||||
public ushort fUnicode;
|
||||
}
|
||||
|
@ -622,14 +688,13 @@ namespace Vanara.PInvoke
|
|||
public struct OUTPUT_DEBUG_STRING_INFO
|
||||
{
|
||||
/// <summary>
|
||||
/// The debugging string in the calling process's address space. The debugger can use the <c>ReadProcessMemory</c>
|
||||
/// function to retrieve the value of the string.
|
||||
/// The debugging string in the calling process's address space. The debugger can use the <c>ReadProcessMemory</c> function
|
||||
/// to retrieve the value of the string.
|
||||
/// </summary>
|
||||
public IntPtr lpDebugStringData;
|
||||
|
||||
/// <summary>
|
||||
/// The format of the debugging string. If this member is zero, the debugging string is ANSI; if it is nonzero, the
|
||||
/// string is Unicode.
|
||||
/// The format of the debugging string. If this member is zero, the debugging string is ANSI; if it is nonzero, the string is Unicode.
|
||||
/// </summary>
|
||||
public ushort fUnicode;
|
||||
|
||||
|
@ -685,9 +750,98 @@ namespace Vanara.PInvoke
|
|||
}
|
||||
}
|
||||
|
||||
private static string ReadString(IntPtr data, ushort fUni, ushort len)
|
||||
/// <summary>Describes an entry in the descriptor table. This structure is valid only on x86-based systems.</summary>
|
||||
// typedef struct _LDT_ENTRY { WORD LimitLow; WORD BaseLow; union { struct { BYTE BaseMid; BYTE Flags1; BYTE Flags2; BYTE BaseHi; }
|
||||
// Bytes; struct { DWORD BaseMid :8; DWORD Type :5; DWORD Dpl :2; DWORD Pres :1; DWORD LimitHi :4; DWORD Sys :1; DWORD Reserved_0 :1;
|
||||
// DWORD Default_Big :1; DWORD Granularity :1; DWORD BaseHi :8; } Bits; } HighWord;} LDT_ENTRY, *PLDT_ENTRY; https://msdn.microsoft.com/en-us/library/windows/desktop/ms680348(v=vs.85).aspx
|
||||
[PInvokeData("WinNT.h", MSDNShortId = "ms680348")]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct LDT_ENTRY
|
||||
{
|
||||
return StringHelper.GetString(data, fUni != 0 ? CharSet.Unicode : CharSet.Ansi, len);
|
||||
/// <summary>The low-order part of the address of the last byte in the segment.</summary>
|
||||
public ushort LimitLow;
|
||||
|
||||
/// <summary>The low-order part of the base address of the segment.</summary>
|
||||
public ushort BaseLow;
|
||||
|
||||
private uint bits;
|
||||
|
||||
/// <summary>The middle bits (16–23) of the base address of the segment.</summary>
|
||||
public byte BaseMid { get => GetBits(0, 8); set => SetBits(0, 8, value); }
|
||||
|
||||
/// <summary>
|
||||
/// The type of segment. This member can be one of the following values:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <term>0: Read-only data segment</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>1: Read-write data segment</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>2: Unused segment</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>3: Read-write expand-down data segment</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>4: Execute-only code segment</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>5: Executable-readable code segment</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>6: Execute-only "conforming" code segment</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>7: Executable-readable "conforming" code segment</term>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public byte Type { get => GetBits(8, 5); set => SetBits(8, 5, value); }
|
||||
|
||||
/// <summary>
|
||||
/// The privilege level of the descriptor. This member is an integer value in the range 0 (most privileged) through 3 (least privileged).
|
||||
/// </summary>
|
||||
public byte Dpl { get => GetBits(13, 2); set => SetBits(13, 2, value); }
|
||||
|
||||
/// <summary>The present flag. This member is 1 if the segment is present in physical memory or 0 if it is not.</summary>
|
||||
public byte Pres { get => GetBits(15, 1); set => SetBits(15, 1, value); }
|
||||
|
||||
/// <summary>The high bits (16–19) of the address of the last byte in the segment.</summary>
|
||||
public byte LimitHi { get => GetBits(16, 4); set => SetBits(16, 4, value); }
|
||||
|
||||
/// <summary>
|
||||
/// The space that is available to system programmers. This member might be used for marking segments in some system-specific way.
|
||||
/// </summary>
|
||||
public byte Sys { get => GetBits(20, 1); set => SetBits(20, 1, value); }
|
||||
|
||||
/// <summary>Reserved.</summary>
|
||||
public byte Reserved_0 { get => GetBits(21, 1); set => SetBits(21, 1, value); }
|
||||
|
||||
/// <summary>
|
||||
/// The size of segment. If the segment is a data segment, this member contains 1 if the segment is larger than 64 kilobytes (K)
|
||||
/// or 0 if the segment is smaller than or equal to 64K.
|
||||
/// <para>
|
||||
/// If the segment is a code segment, this member contains 1 if the segment is a code segment and runs with the default (native
|
||||
/// mode) instruction set.This member contains 0 if the code segment is an 80286 code segment and runs with 16-bit offsets and
|
||||
/// the 80286-compatible instruction set.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public byte Default_Big { get => GetBits(22, 1); set => SetBits(22, 1, value); }
|
||||
|
||||
/// <summary>The granularity. This member contains 0 if the segment is byte granular, 1 if the segment is page granular.</summary>
|
||||
public byte Granularity { get => GetBits(23, 1); set => SetBits(23, 1, value); }
|
||||
|
||||
/// <summary>The high bits (24–31) of the base address of the segment.</summary>
|
||||
public byte BaseHi { get => GetBits(24, 8); set => SetBits(24, 8, value); }
|
||||
|
||||
private byte GetBits(int offset, int len) => (byte)((bits & (((1 << len) - 1) << offset)) >> offset);
|
||||
|
||||
private void SetBits(int offset, int len, byte value)
|
||||
{
|
||||
var mask = (uint)(((1 << len) - 1) << offset); bits = (bits & ~mask) | (uint)(value << offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Vanara.PInvoke
|
||||
{
|
||||
public static partial class Kernel32
|
||||
{
|
||||
/// <summary>
|
||||
/// Causes a breakpoint exception to occur in the specified process. This allows the calling thread to signal the debugger to handle the exception.
|
||||
/// </summary>
|
||||
/// <param name="Process">A handle to the process.</param>
|
||||
/// <returns>
|
||||
/// <para>If the function succeeds, the return value is nonzero.</para>
|
||||
/// <para>If the function fails, the return value is zero. To get extended error information, call <c>GetLastError</c>.</para>
|
||||
/// </returns>
|
||||
// BOOL WINAPI DebugBreakProcess( _In_ HANDLE Process);
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679298(v=vs.85).aspx
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)]
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679298")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DebugBreakProcess([In] HPROCESS Process);
|
||||
|
||||
/// <summary>Sets the action to be performed when the calling thread exits.</summary>
|
||||
/// <param name="KillOnExit">
|
||||
/// If this parameter is <c>TRUE</c>, the thread terminates all attached processes on exit (note that this is the default). Otherwise, the thread
|
||||
/// detaches from all processes being debugged on exit.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <para>If the function succeeds, the return value is nonzero.</para>
|
||||
/// <para>If the function fails, the return value is zero. To get extended error information, call <c>GetLastError</c>.</para>
|
||||
/// </returns>
|
||||
// BOOL WINAPI DebugSetProcessKillOnExit( _In_ BOOL KillOnExit); https://msdn.microsoft.com/en-us/library/windows/desktop/ms679307(v=vs.85).aspx
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)]
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679307")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DebugSetProcessKillOnExit([MarshalAs(UnmanagedType.Bool)] bool KillOnExit);
|
||||
|
||||
/// <summary>Transfers execution control to the debugger. The behavior of the debugger thereafter is specific to the type of debugger used.</summary>
|
||||
/// <param name="ExitCode">The error code associated with the exit.</param>
|
||||
/// <returns>This function does not return a value.</returns>
|
||||
// void WINAPI FatalExit( _In_ int ExitCode);
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679337(v=vs.85).aspx
|
||||
[DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)]
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679337")]
|
||||
public static extern void FatalExit(int ExitCode);
|
||||
|
||||
/// <summary>Retrieves a descriptor table entry for the specified selector and thread.</summary>
|
||||
/// <param name="hThread">
|
||||
/// A handle to the thread containing the specified selector. The handle must have THREAD_QUERY_INFORMATION access. For more information, see Thread
|
||||
/// Security and Access Rights.
|
||||
/// </param>
|
||||
/// <param name="dwSelector">The global or local selector value to look up in the thread's descriptor tables.</param>
|
||||
/// <param name="lpSelectorEntry">
|
||||
/// A pointer to an <c>LDT_ENTRY</c> structure that receives a copy of the descriptor table entry if the specified selector has an entry in the specified
|
||||
/// thread's descriptor table. This information can be used to convert a segment-relative address to a linear virtual address.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <para>
|
||||
/// If the function succeeds, the return value is nonzero. In that case, the structure pointed to by the lpSelectorEntry parameter receives a copy of the
|
||||
/// specified descriptor table entry.
|
||||
/// </para>
|
||||
/// <para>If the function fails, the return value is zero. To get extended error information, call <c>GetLastError</c>.</para>
|
||||
/// </returns>
|
||||
// BOOL WINAPI GetThreadSelectorEntry( _In_ HANDLE hThread, _In_ DWORD dwSelector, _Out_ LPLDT_ENTRY lpSelectorEntry);
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms679363(v=vs.85).aspx
|
||||
[DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)]
|
||||
[PInvokeData("WinBase.h", MSDNShortId = "ms679363")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetThreadSelectorEntry([In] HTHREAD hThread, uint dwSelector, out LDT_ENTRY lpSelectorEntry);
|
||||
|
||||
/// <summary>Describes an entry in the descriptor table. This structure is valid only on x86-based systems.</summary>
|
||||
// typedef struct _LDT_ENTRY { WORD LimitLow; WORD BaseLow; union { struct { BYTE BaseMid; BYTE Flags1; BYTE Flags2; BYTE BaseHi; } Bytes; struct { DWORD BaseMid :8; DWORD Type :5; DWORD Dpl :2; DWORD Pres :1; DWORD LimitHi :4; DWORD Sys :1; DWORD Reserved_0 :1; DWORD Default_Big :1; DWORD Granularity :1; DWORD BaseHi :8; } Bits; } HighWord;} LDT_ENTRY, *PLDT_ENTRY;
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms680348(v=vs.85).aspx
|
||||
[PInvokeData("WinNT.h", MSDNShortId = "ms680348")]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
public struct LDT_ENTRY
|
||||
{
|
||||
/// <summary>The low-order part of the address of the last byte in the segment.</summary>
|
||||
public ushort LimitLow;
|
||||
/// <summary>The low-order part of the base address of the segment.</summary>
|
||||
public ushort BaseLow;
|
||||
|
||||
private uint bits;
|
||||
|
||||
/// <summary>The middle bits (16–23) of the base address of the segment.</summary>
|
||||
public byte BaseMid { get => GetBits(0, 8); set => SetBits(0, 8, value); }
|
||||
|
||||
/// <summary>The type of segment. This member can be one of the following values:
|
||||
/// <list type="bullet">
|
||||
/// <item><term>0: Read-only data segment</term></item>
|
||||
/// <item><term>1: Read-write data segment</term></item>
|
||||
/// <item><term>2: Unused segment</term></item>
|
||||
/// <item><term>3: Read-write expand-down data segment</term></item>
|
||||
/// <item><term>4: Execute-only code segment</term></item>
|
||||
/// <item><term>5: Executable-readable code segment</term></item>
|
||||
/// <item><term>6: Execute-only "conforming" code segment</term></item>
|
||||
/// <item><term>7: Executable-readable "conforming" code segment</term></item>
|
||||
/// </list></summary>
|
||||
public byte Type { get => GetBits(8, 5); set => SetBits(8, 5, value); }
|
||||
|
||||
/// <summary>The privilege level of the descriptor. This member is an integer value in the range 0 (most privileged) through 3 (least privileged).</summary>
|
||||
public byte Dpl { get => GetBits(13, 2); set => SetBits(13, 2, value); }
|
||||
|
||||
/// <summary>The present flag. This member is 1 if the segment is present in physical memory or 0 if it is not.</summary>
|
||||
public byte Pres { get => GetBits(15, 1); set => SetBits(15, 1, value); }
|
||||
|
||||
/// <summary>The high bits (16–19) of the address of the last byte in the segment.</summary>
|
||||
public byte LimitHi { get => GetBits(16, 4); set => SetBits(16, 4, value); }
|
||||
|
||||
/// <summary>The space that is available to system programmers. This member might be used for marking segments in some system-specific way.</summary>
|
||||
public byte Sys { get => GetBits(20, 1); set => SetBits(20, 1, value); }
|
||||
|
||||
/// <summary>Reserved.</summary>
|
||||
public byte Reserved_0 { get => GetBits(21, 1); set => SetBits(21, 1, value); }
|
||||
|
||||
/// <summary>The size of segment. If the segment is a data segment, this member contains 1 if the segment is larger than 64 kilobytes (K) or 0 if the segment is smaller than or equal to 64K.
|
||||
/// <para>If the segment is a code segment, this member contains 1 if the segment is a code segment and runs with the default (native mode) instruction set.This member contains 0 if the code segment is an 80286 code segment and runs with 16-bit offsets and the 80286-compatible instruction set.</para></summary>
|
||||
public byte Default_Big { get => GetBits(22, 1); set => SetBits(22, 1, value); }
|
||||
|
||||
/// <summary>The granularity. This member contains 0 if the segment is byte granular, 1 if the segment is page granular.</summary>
|
||||
public byte Granularity { get => GetBits(23, 1); set => SetBits(23, 1, value); }
|
||||
|
||||
/// <summary>The high bits (24–31) of the base address of the segment.</summary>
|
||||
public byte BaseHi { get => GetBits(24, 8); set => SetBits(24, 8, value); }
|
||||
|
||||
private byte GetBits(int offset, int len) => (byte)((bits & (((1 << len) - 1) << offset)) >> offset);
|
||||
|
||||
private void SetBits(int offset, int len, byte value) { var mask = (uint)(((1 << len) - 1) << offset); bits = (bits & ~mask) | (uint)(value << offset); }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,19 +8,45 @@ namespace Vanara.PInvoke.Tests
|
|||
[TestFixture]
|
||||
public class DebugApiTests
|
||||
{
|
||||
[Test]
|
||||
public void CheckRemoteDebuggerPresentTest()
|
||||
{
|
||||
Assert.That(CheckRemoteDebuggerPresent(GetCurrentProcess(), out var present), ResultIs.Successful);
|
||||
Assert.That(present, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDebuggerPresentTest()
|
||||
{
|
||||
Assert.That(IsDebuggerPresent(), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OutputDebugStringTest()
|
||||
{
|
||||
OutputDebugString("Hello");
|
||||
}
|
||||
|
||||
// TODO: Figure out how WaitForDebugEvent works
|
||||
// [Test]
|
||||
public void TestMethod()
|
||||
{
|
||||
TestContext.WriteLine($"EXCEPTION_RECORD: {Marshal.SizeOf(typeof(EXCEPTION_RECORD))}");
|
||||
TestContext.WriteLine($"DEBUG_EVENT.EXCEPTION_DEBUG_INFO: {Marshal.SizeOf(typeof(DEBUG_EVENT.EXCEPTION_DEBUG_INFO))}");
|
||||
TestContext.WriteLine($"DEBUG_EVENT.EXCEPTION_INFO: {Marshal.SizeOf(typeof(DEBUG_EVENT.EXCEPTION_INFO))}");
|
||||
var pid = GetCurrentProcessId();
|
||||
DebugActiveProcess(pid);
|
||||
{
|
||||
var p = CSharpRunner.RunProcess(typeof(DebugProcess));
|
||||
var pid = (uint)p.Id;
|
||||
Assert.That(DebugActiveProcess(pid), ResultIs.Successful);
|
||||
//Assert.That(ContinueDebugEvent(pid, ))
|
||||
Assert.That(WaitForDebugEvent(out var evt, 2000));
|
||||
DebugActiveProcessStop(pid);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DebugProcess
|
||||
{
|
||||
public static int Main()
|
||||
{
|
||||
Sleep(2000);
|
||||
DebugBreak();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue