Vanara/PInvoke/Security/AdvApi32/wct.cs

469 lines
20 KiB
C#

using System;
using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.InteropServices;
namespace Vanara.PInvoke
{
public static partial class AdvApi32
{
/// <summary/>
public const uint WCT_MAX_NODE_COUNT = 16;
/// <summary>
/// <para>
/// An application-defined callback function that receives a wait chain. Specify this address when calling the
/// OpenThreadWaitChainSession function.
/// </para>
/// <para>
/// The <c>PWAITCHAINCALLBACK</c> type defines a pointer to this callback function. WaitChainCallback is a placeholder for the
/// application-defined function name.
/// </para>
/// </summary>
/// <param name="WctHandle">A handle to the WCT session created by the OpenThreadWaitChainSession function.</param>
/// <param name="Context">A optional pointer to an application-defined context structure specified by the GetThreadWaitChain function.</param>
/// <param name="CallbackStatus">
/// The callback status. This parameter can be one of the following values, or one of the other system error codes.
/// </param>
/// <param name="NodeCount">
/// The number of nodes retrieved, up to WCT_MAX_NODE_COUNT. If the array cannot contain all the nodes of the wait chain, the
/// function fails, CallbackStatus is ERROR_MORE_DATA, and this parameter receives the number of array elements required to contain
/// all the nodes.
/// </param>
/// <param name="NodeInfoArray">An array of WAITCHAIN_NODE_INFO structures that receives the wait chain.</param>
/// <param name="IsCycle">If the function detects a deadlock, this variable is set to <c>TRUE</c>; otherwise, it is set to <c>FALSE</c>.</param>
/// <returns>This callback function does not return a value.</returns>
// https://docs.microsoft.com/en-us/windows/desktop/api/wct/nc-wct-pwaitchaincallback PWAITCHAINCALLBACK Pwaitchaincallback; void
// Pwaitchaincallback( HWCT WctHandle, DWORD_PTR Context, DWORD CallbackStatus, LPDWORD NodeCount, PWAITCHAIN_NODE_INFO
// NodeInfoArray, LPBOOL IsCycle ) {...}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
[PInvokeData("wct.h", MSDNShortId = "07d987b4-3ee4-4957-a6e8-542c427b94dd")]
public delegate void WaitChainCallback(HWCT WctHandle, IntPtr Context, Win32Error CallbackStatus, ref uint NodeCount, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] WAITCHAIN_NODE_INFO[] NodeInfoArray, [MarshalAs(UnmanagedType.Bool)] out bool IsCycle);
/// <summary>The wait chain retrieval options.</summary>
[PInvokeData("wct.h", MSDNShortId = "5b418fa6-1d07-465e-85ea-b7127264eebf")]
[Flags]
public enum WaitChainRetrievalOptions
{
/// <summary>
/// Follows the wait chain into other processes. Otherwise, the function reports the first thread in a different process but does
/// not retrieve additional information.
/// </summary>
WCT_OUT_OF_PROC_FLAG = 0x1,
/// <summary>Enumerates all threads of an out-of-proc MTA COM server to find the correct thread identifier.</summary>
WCT_OUT_OF_PROC_COM_FLAG = 0x2,
/// <summary>Retrieves critical-section information from other processes.</summary>
WCT_OUT_OF_PROC_CS_FLAG = 0x4,
/// <summary/>
WCT_NETWORK_IO_FLAG = 0x8,
/// <summary/>
WCTP_GETINFO_ALL_FLAGS = WCT_OUT_OF_PROC_FLAG | WCT_OUT_OF_PROC_COM_FLAG | WCT_OUT_OF_PROC_CS_FLAG,
}
/// <summary>The session type.</summary>
public enum WaitChainSessionType
{
/// <summary>A synchronous session.</summary>
WCT_SYNC_OPEN_FLAG = 0,
/// <summary>An asynchronous session.</summary>
WCT_ASYNC_OPEN_FLAG = 0x1,
}
/// <summary>The object status.</summary>
[PInvokeData("wct.h", MSDNShortId = "7a333924-79a3-4522-aa5a-4fc60690667d")]
public enum WCT_OBJECT_STATUS
{
/// <summary/>
WctStatusNoAccess = 1,
/// <summary/>
WctStatusRunning,
/// <summary/>
WctStatusBlocked,
/// <summary/>
WctStatusPidOnly,
/// <summary/>
WctStatusPidOnlyRpcss,
/// <summary/>
WctStatusOwned,
/// <summary/>
WctStatusNotOwned,
/// <summary/>
WctStatusAbandoned,
/// <summary/>
WctStatusUnknown,
/// <summary/>
WctStatusError,
/// <summary/>
WctStatusMax
}
/// <summary>The object type.</summary>
[PInvokeData("wct.h", MSDNShortId = "7a333924-79a3-4522-aa5a-4fc60690667d")]
public enum WCT_OBJECT_TYPE
{
/// <summary/>
WctCriticalSectionType = 1,
/// <summary/>
WctSendMessageType,
/// <summary/>
WctMutexType,
/// <summary/>
WctAlpcType,
/// <summary/>
WctComType,
/// <summary/>
WctThreadWaitType,
/// <summary/>
WctProcessWaitType,
/// <summary/>
WctThreadType,
/// <summary/>
WctComActivationType,
/// <summary/>
WctUnknownType,
/// <summary/>
WctSocketIoType,
/// <summary/>
WctSmbIoType,
/// <summary/>
WctMaxType
}
/// <summary>Closes the specified WCT session and cancels any outstanding asynchronous operations.</summary>
/// <param name="WctHandle">A handle to the WCT session created by the OpenThreadWaitChainSession function.</param>
/// <returns>This function does not return a value.</returns>
/// <remarks>
/// <para>
/// If the WCT session was opened in asynchronous mode (with WCT_ASYNC_OPEN_FLAG), the function cancels any outstanding operations
/// after their callback functions have been called and returned, and then it returns.
/// </para>
/// <para>Examples</para>
/// <para>For an example, see Using WCT.</para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/desktop/api/wct/nf-wct-closethreadwaitchainsession void CloseThreadWaitChainSession( HWCT
// WctHandle );
[DllImport(Lib.AdvApi32, SetLastError = false, ExactSpelling = true)]
[PInvokeData("wct.h", MSDNShortId = "dc288418-01e4-4737-9c63-e6e6b73b5d13")]
public static extern void CloseThreadWaitChainSession(HWCT WctHandle);
/// <summary>Retrieves the wait chain for the specified thread.</summary>
/// <param name="WctHandle">A handle to the WCT session created by the OpenThreadWaitChainSession function.</param>
/// <param name="Context">
/// A pointer to an application-defined context structure to be passed to the callback function for an asynchronous session.
/// </param>
/// <param name="Flags">The wait chain retrieval options. This parameter can be one of more of the following values.</param>
/// <param name="ThreadId">The identifier of the thread.</param>
/// <param name="NodeCount">
/// <para>
/// On input, a number from 1 to WCT_MAX_NODE_COUNT that specifies the number of nodes in the wait chain. On return, the number of
/// nodes retrieved. If the array cannot contain all the nodes of the wait chain, the function fails, GetLastError returns
/// ERROR_MORE_DATA, and this parameter receives the number of array elements required to contain all the nodes.
/// </para>
/// <para>
/// For asynchronous sessions, check the value that is passed to the callback function. Do not free the variable until the callback
/// function has returned.
/// </para>
/// </param>
/// <param name="NodeInfoArray">
/// <para>An array of WAITCHAIN_NODE_INFO structures that receives the wait chain.</para>
/// <para>
/// For asynchronous sessions, check the value that is passed to the callback function. Do not free the array until the callback
/// function has returned.
/// </para>
/// </param>
/// <param name="IsCycle">
/// <para>If the function detects a deadlock, this variable is set to <c>TRUE</c>; otherwise, it is set to <c>FALSE</c>.</para>
/// <para>
/// For asynchronous sessions, check the value that is passed to the callback function. Do not free the variable until the callback
/// function has returned.
/// </para>
/// </param>
/// <returns>
/// <para>If the function succeeds, the return value is nonzero.</para>
/// <para>If the function fails, the return value is zero. To retrieve extended error information, call GetLastError.</para>
/// <list type="table">
/// <listheader>
/// <term>Return code</term>
/// <term>Description</term>
/// </listheader>
/// <item>
/// <term>ERROR_ACCESS_DENIED</term>
/// <term>The caller did not have sufficient privilege to open a target thread.</term>
/// </item>
/// <item>
/// <term>ERROR_INVALID_PARAMETER</term>
/// <term>One of the input parameters is invalid.</term>
/// </item>
/// <item>
/// <term>ERROR_IO_PENDING</term>
/// <term>The WCT session was opened in asynchronous mode. The results will be returned through the WaitChainCallback callback function.</term>
/// </item>
/// <item>
/// <term>ERROR_MORE_DATA</term>
/// <term>
/// The NodeInfoArray buffer is not large enough to contain all the nodes in the wait chain. The NodeCount parameter contains the
/// number of nodes in the chain. The wait chain returned is still valid.
/// </term>
/// </item>
/// <item>
/// <term>ERROR_NOT_SUPPORTED</term>
/// <term>The operating system is not providing this service.</term>
/// </item>
/// <item>
/// <term>ERROR_OBJECT_NOT_FOUND</term>
/// <term>The specified thread could not be located.</term>
/// </item>
/// <item>
/// <term>ERROR_TOO_MANY_THREADS</term>
/// <term>The number of nodes exceeds WCT_MAX_NODE_COUNT. The wait chain returned is still valid.</term>
/// </item>
/// </list>
/// </returns>
/// <remarks>
/// <para>
/// If the session is asynchronous, the function returns <c>FALSE</c> and GetLastError returns ERROR_IO_PENDING. To obtain the
/// results, see the WaitChainCallback callback function.
/// </para>
/// <para>
/// If the specified thread is not blocked or is blocked on an unsupported synchronization element, the function returns a single
/// item in NodeInfoArray.
/// </para>
/// <para>
/// The caller must have the SE_DEBUG_NAME privilege. If the caller has insufficient privileges, the function fails if the first
/// thread cannot be accessed. Otherwise, the last node in the array will have its <c>ObjectStatus</c> member set to WctStatusNoAcces.
/// </para>
/// <para>If any subset of nodes in the array forms a cycle, the function sets the IsCycle parameter to <c>TRUE</c>.</para>
/// <para>
/// Wait chain information is dynamic; it was correct when the function was called but may be out-of-date by the time it is reviewed
/// by the caller.
/// </para>
/// <para>Examples</para>
/// <para>For an example, see Using WCT.</para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/desktop/api/wct/nf-wct-getthreadwaitchain BOOL GetThreadWaitChain( HWCT WctHandle,
// DWORD_PTR Context, DWORD Flags, DWORD ThreadId, LPDWORD NodeCount, PWAITCHAIN_NODE_INFO NodeInfoArray, LPBOOL IsCycle );
[DllImport(Lib.AdvApi32, SetLastError = true, ExactSpelling = true)]
[PInvokeData("wct.h", MSDNShortId = "5b418fa6-1d07-465e-85ea-b7127264eebf")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetThreadWaitChain(HWCT WctHandle, IntPtr Context, WaitChainRetrievalOptions Flags, uint ThreadId, ref uint NodeCount, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] WAITCHAIN_NODE_INFO[] NodeInfoArray, [MarshalAs(UnmanagedType.Bool)] out bool IsCycle);
/// <summary>Creates a new WCT session.</summary>
/// <param name="Flags">The session type. This parameter can be one of the following values.</param>
/// <param name="callback">If the session is asynchronous, this parameter can be a pointer to a WaitChainCallback callback function.</param>
/// <returns>
/// <para>If the function succeeds, the return value is a handle to the newly created session.</para>
/// <para>If the function fails, the return value is <c>NULL</c>. To get extended error information, call GetLastError.</para>
/// </returns>
/// <remarks>
/// <para>When you have finished using the session, call the CloseThreadWaitChainSession function.</para>
/// <para>Examples</para>
/// <para>For an example, see Using WCT.</para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/desktop/api/wct/nf-wct-openthreadwaitchainsession HWCT OpenThreadWaitChainSession( DWORD
// Flags, PWAITCHAINCALLBACK callback );
[DllImport(Lib.AdvApi32, SetLastError = true, ExactSpelling = true)]
[PInvokeData("wct.h", MSDNShortId = "405d9f3d-c11b-4e20-acc8-9c4f7989685d")]
public static extern SafeHWCT OpenThreadWaitChainSession(WaitChainSessionType Flags, [Optional, MarshalAs(UnmanagedType.FunctionPtr)] WaitChainCallback callback);
/// <summary>Register COM callback functions for WCT.</summary>
/// <param name="CallStateCallback">The address of the <c>CoGetCallState</c> function.</param>
/// <param name="ActivationStateCallback">The address of the <c>CoGetActivationState</c> function.</param>
/// <returns>This function does not return a value.</returns>
/// <remarks>
/// <para>
/// If a thread is blocked on a COM call, WCT can retrieve COM ownership information using these callback functions. If this function
/// is callback multiple times, only the last addresses retrieved are used.
/// </para>
/// <para>Examples</para>
/// <para>For an example, see Using WCT.</para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/desktop/api/wct/nf-wct-registerwaitchaincomcallback void RegisterWaitChainCOMCallback(
// PCOGETCALLSTATE CallStateCallback, PCOGETACTIVATIONSTATE ActivationStateCallback );
[DllImport(Lib.AdvApi32, SetLastError = false, ExactSpelling = true)]
[PInvokeData("wct.h", MSDNShortId = "f8adffa3-6e63-4fae-81e8-5f6643e988e9")]
public static extern void RegisterWaitChainCOMCallback(IntPtr CallStateCallback, IntPtr ActivationStateCallback);
/// <summary>Register COM callback functions for WCT. This method does the work of getting the method addresses and calling the Windows API function.</summary>
/// <returns>This function does not return a value.</returns>
/// <remarks>
/// <para>
/// If a thread is blocked on a COM call, WCT can retrieve COM ownership information using these callback functions. If this function
/// is callback multiple times, only the last addresses retrieved are used.
/// </para>
/// </remarks>
[PInvokeData("wct.h", MSDNShortId = "f8adffa3-6e63-4fae-81e8-5f6643e988e9")]
public static void RegisterWaitChainCOMCallback()
{
using (var hLib = Kernel32.LoadLibrary(Lib.Ole32))
{
if (hLib.IsInvalid) Win32Error.ThrowLastError();
var p1 = Kernel32.GetProcAddress(hLib, "CoGetCallState");
if (p1 == IntPtr.Zero) Win32Error.ThrowLastError();
var p2 = Kernel32.GetProcAddress(hLib, "CoGetActivationState");
if (p2 == IntPtr.Zero) Win32Error.ThrowLastError();
RegisterWaitChainCOMCallback(p1, p2);
}
}
/// <summary>Provides a handle to a thread wait chain.</summary>
[StructLayout(LayoutKind.Sequential)]
public struct HWCT : IHandle
{
private IntPtr handle;
/// <summary>Initializes a new instance of the <see cref="HWCT"/> struct.</summary>
/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
public HWCT(IntPtr preexistingHandle) => handle = preexistingHandle;
/// <summary>Returns an invalid handle by instantiating a <see cref="HWCT"/> object with <see cref="IntPtr.Zero"/>.</summary>
public static HWCT NULL => new HWCT(IntPtr.Zero);
/// <summary>Gets a value indicating whether this instance is a null handle.</summary>
public bool IsNull => handle == IntPtr.Zero;
/// <summary>Performs an explicit conversion from <see cref="HWCT"/> to <see cref="IntPtr"/>.</summary>
/// <param name="h">The handle.</param>
/// <returns>The result of the conversion.</returns>
public static explicit operator IntPtr(HWCT h) => h.handle;
/// <summary>Performs an implicit conversion from <see cref="IntPtr"/> to <see cref="HWCT"/>.</summary>
/// <param name="h">The pointer to a handle.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator HWCT(IntPtr h) => new HWCT(h);
/// <summary>Implements the operator !=.</summary>
/// <param name="h1">The first handle.</param>
/// <param name="h2">The second handle.</param>
/// <returns>The result of the operator.</returns>
public static bool operator !=(HWCT h1, HWCT h2) => !(h1 == h2);
/// <summary>Implements the operator ==.</summary>
/// <param name="h1">The first handle.</param>
/// <param name="h2">The second handle.</param>
/// <returns>The result of the operator.</returns>
public static bool operator ==(HWCT h1, HWCT h2) => h1.Equals(h2);
/// <inheritdoc/>
public override bool Equals(object obj) => obj is HWCT h ? handle == h.handle : false;
/// <inheritdoc/>
public override int GetHashCode() => handle.GetHashCode();
/// <inheritdoc/>
public IntPtr DangerousGetHandle() => handle;
}
/// <summary>Represents a node in a wait chain.</summary>
// https://docs.microsoft.com/en-us/windows/desktop/api/wct/ns-wct-waitchain_node_info typedef struct _WAITCHAIN_NODE_INFO {
// WCT_OBJECT_TYPE ObjectType; WCT_OBJECT_STATUS ObjectStatus; union { struct { WCHAR ObjectName[WCT_OBJNAME_LENGTH]; LARGE_INTEGER
// Timeout; BOOL Alertable; } LockObject; struct { DWORD ProcessId; DWORD ThreadId; DWORD WaitTime; DWORD ContextSwitches; }
// ThreadObject; }; } WAITCHAIN_NODE_INFO, *PWAITCHAIN_NODE_INFO;
[PInvokeData("wct.h", MSDNShortId = "7a333924-79a3-4522-aa5a-4fc60690667d")]
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct WAITCHAIN_NODE_INFO
{
/// <summary>The object type. This member is one of the following values from the <c>WCT_OBJECT_TYPE</c> enumeration type.</summary>
[FieldOffset(0)]
public WCT_OBJECT_TYPE ObjectType;
/// <summary>
/// The object status. This member is one of the following values from the <c>WCT_OBJECT_STATUS</c> enumeration type.
/// </summary>
[FieldOffset(4)]
public WCT_OBJECT_STATUS ObjectStatus;
/// <summary/>
[FieldOffset(8)]
public LOCKOBJECT LockObject;
/// <summary/>
[FieldOffset(8)]
public THREADOBJECT ThreadObject;
/// <summary/>
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Size = 272)]
public struct LOCKOBJECT
{
[FieldOffset(0)]
private long on0;
/// <summary>This member is reserved for future use.</summary>
[FieldOffset(256)]
public long Timeout;
/// <summary>This member is reserved for future use.</summary>
[MarshalAs(UnmanagedType.Bool)]
[FieldOffset(264)]
public bool Alertable;
/// <summary>
/// The name of the object. Object names are only available for certain object, such as mutexes. If the object does not have
/// a name, this member is an empty string.
/// </summary>
public string ObjectName
{
get
{
unsafe
{
fixed (void* pin = &on0)
return StringHelper.GetString((IntPtr)pin, CharSet.Unicode, 128);
}
}
set
{
unsafe
{
fixed (void* pin = &on0)
StringHelper.Write(value, (IntPtr)pin, out _, true, CharSet.Unicode, 128);
}
}
}
}
/// <summary/>
[StructLayout(LayoutKind.Sequential)]
public struct THREADOBJECT
{
/// <summary>The process identifier.</summary>
public uint ProcessId;
/// <summary>The thread identifier. For COM and ALPC, this member can be 0.</summary>
public uint ThreadId;
/// <summary>The wait time.</summary>
public uint WaitTime;
/// <summary>The number of context switches.</summary>
public uint ContextSwitches;
}
}
/// <summary>Provides a <see cref="SafeHandle"/> for <see cref="HWCT"/> that is disposed using <see cref="CloseThreadWaitChainSession"/>.</summary>
public class SafeHWCT : SafeHANDLE
{
/// <summary>Initializes a new instance of the <see cref="SafeHWCT"/> class and assigns an existing handle.</summary>
/// <param name="preexistingHandle">An <see cref="IntPtr"/> object that represents the pre-existing handle to use.</param>
/// <param name="ownsHandle">
/// <see langword="true"/> to reliably release the handle during the finalization phase; otherwise, <see langword="false"/> (not recommended).
/// </param>
public SafeHWCT(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { }
/// <summary>Initializes a new instance of the <see cref="SafeHWCT"/> class.</summary>
private SafeHWCT() : base() { }
/// <summary>Performs an implicit conversion from <see cref="SafeHWCT"/> to <see cref="HWCT"/>.</summary>
/// <param name="h">The safe handle instance.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator HWCT(SafeHWCT h) => h.handle;
/// <inheritdoc/>
protected override bool InternalReleaseHandle() { CloseThreadWaitChainSession(handle); return true; }
}
}
}