using System;
using System.Runtime.InteropServices;
using Vanara.Extensions;
using Vanara.InteropServices;
namespace Vanara.PInvoke
{
public static partial class AdvApi32
{
///
public const uint WCT_MAX_NODE_COUNT = 16;
///
///
/// An application-defined callback function that receives a wait chain. Specify this address when calling the
/// OpenThreadWaitChainSession function.
///
///
/// The PWAITCHAINCALLBACK type defines a pointer to this callback function. WaitChainCallback is a placeholder for the
/// application-defined function name.
///
///
/// A handle to the WCT session created by the OpenThreadWaitChainSession function.
/// A optional pointer to an application-defined context structure specified by the GetThreadWaitChain function.
///
/// The callback status. This parameter can be one of the following values, or one of the other system error codes.
///
///
/// 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.
///
/// An array of WAITCHAIN_NODE_INFO structures that receives the wait chain.
/// If the function detects a deadlock, this variable is set to TRUE; otherwise, it is set to FALSE.
/// This callback function does not return a value.
// 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);
/// The wait chain retrieval options.
[PInvokeData("wct.h", MSDNShortId = "5b418fa6-1d07-465e-85ea-b7127264eebf")]
[Flags]
public enum WaitChainRetrievalOptions
{
///
/// Follows the wait chain into other processes. Otherwise, the function reports the first thread in a different process but does
/// not retrieve additional information.
///
WCT_OUT_OF_PROC_FLAG = 0x1,
/// Enumerates all threads of an out-of-proc MTA COM server to find the correct thread identifier.
WCT_OUT_OF_PROC_COM_FLAG = 0x2,
/// Retrieves critical-section information from other processes.
WCT_OUT_OF_PROC_CS_FLAG = 0x4,
///
WCT_NETWORK_IO_FLAG = 0x8,
///
WCTP_GETINFO_ALL_FLAGS = WCT_OUT_OF_PROC_FLAG | WCT_OUT_OF_PROC_COM_FLAG | WCT_OUT_OF_PROC_CS_FLAG,
}
/// The session type.
public enum WaitChainSessionType
{
/// A synchronous session.
WCT_SYNC_OPEN_FLAG = 0,
/// An asynchronous session.
WCT_ASYNC_OPEN_FLAG = 0x1,
}
/// The object status.
[PInvokeData("wct.h", MSDNShortId = "7a333924-79a3-4522-aa5a-4fc60690667d")]
public enum WCT_OBJECT_STATUS
{
///
WctStatusNoAccess = 1,
///
WctStatusRunning,
///
WctStatusBlocked,
///
WctStatusPidOnly,
///
WctStatusPidOnlyRpcss,
///
WctStatusOwned,
///
WctStatusNotOwned,
///
WctStatusAbandoned,
///
WctStatusUnknown,
///
WctStatusError,
///
WctStatusMax
}
/// The object type.
[PInvokeData("wct.h", MSDNShortId = "7a333924-79a3-4522-aa5a-4fc60690667d")]
public enum WCT_OBJECT_TYPE
{
///
WctCriticalSectionType = 1,
///
WctSendMessageType,
///
WctMutexType,
///
WctAlpcType,
///
WctComType,
///
WctThreadWaitType,
///
WctProcessWaitType,
///
WctThreadType,
///
WctComActivationType,
///
WctUnknownType,
///
WctSocketIoType,
///
WctSmbIoType,
///
WctMaxType
}
/// Closes the specified WCT session and cancels any outstanding asynchronous operations.
/// A handle to the WCT session created by the OpenThreadWaitChainSession function.
/// This function does not return a value.
///
///
/// 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.
///
/// Examples
/// For an example, see Using WCT.
///
// 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);
/// Retrieves the wait chain for the specified thread.
/// A handle to the WCT session created by the OpenThreadWaitChainSession function.
///
/// A pointer to an application-defined context structure to be passed to the callback function for an asynchronous session.
///
/// The wait chain retrieval options. This parameter can be one of more of the following values.
/// The identifier of the thread.
///
///
/// 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.
///
///
/// For asynchronous sessions, check the value that is passed to the callback function. Do not free the variable until the callback
/// function has returned.
///
///
///
/// An array of WAITCHAIN_NODE_INFO structures that receives the wait chain.
///
/// For asynchronous sessions, check the value that is passed to the callback function. Do not free the array until the callback
/// function has returned.
///
///
///
/// If the function detects a deadlock, this variable is set to TRUE; otherwise, it is set to FALSE.
///
/// For asynchronous sessions, check the value that is passed to the callback function. Do not free the variable until the callback
/// function has returned.
///
///
///
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To retrieve extended error information, call GetLastError.
///
///
/// Return code
/// Description
///
/// -
/// ERROR_ACCESS_DENIED
/// The caller did not have sufficient privilege to open a target thread.
///
/// -
/// ERROR_INVALID_PARAMETER
/// One of the input parameters is invalid.
///
/// -
/// ERROR_IO_PENDING
/// The WCT session was opened in asynchronous mode. The results will be returned through the WaitChainCallback callback function.
///
/// -
/// ERROR_MORE_DATA
///
/// 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.
///
///
/// -
/// ERROR_NOT_SUPPORTED
/// The operating system is not providing this service.
///
/// -
/// ERROR_OBJECT_NOT_FOUND
/// The specified thread could not be located.
///
/// -
/// ERROR_TOO_MANY_THREADS
/// The number of nodes exceeds WCT_MAX_NODE_COUNT. The wait chain returned is still valid.
///
///
///
///
///
/// If the session is asynchronous, the function returns FALSE and GetLastError returns ERROR_IO_PENDING. To obtain the
/// results, see the WaitChainCallback callback function.
///
///
/// If the specified thread is not blocked or is blocked on an unsupported synchronization element, the function returns a single
/// item in NodeInfoArray.
///
///
/// 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 ObjectStatus member set to WctStatusNoAcces.
///
/// If any subset of nodes in the array forms a cycle, the function sets the IsCycle parameter to TRUE.
///
/// 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.
///
/// Examples
/// For an example, see Using WCT.
///
// 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);
/// Creates a new WCT session.
/// The session type. This parameter can be one of the following values.
/// If the session is asynchronous, this parameter can be a pointer to a WaitChainCallback callback function.
///
/// If the function succeeds, the return value is a handle to the newly created session.
/// If the function fails, the return value is NULL. To get extended error information, call GetLastError.
///
///
/// When you have finished using the session, call the CloseThreadWaitChainSession function.
/// Examples
/// For an example, see Using WCT.
///
// 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);
/// Register COM callback functions for WCT.
/// The address of the CoGetCallState function.
/// The address of the CoGetActivationState function.
/// This function does not return a value.
///
///
/// 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.
///
/// Examples
/// For an example, see Using WCT.
///
// 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);
/// Register COM callback functions for WCT. This method does the work of getting the method addresses and calling the Windows API function.
/// This function does not return a value.
///
///
/// 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.
///
///
[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);
}
}
/// Provides a handle to a thread wait chain.
[StructLayout(LayoutKind.Sequential)]
public struct HWCT : IHandle
{
private IntPtr handle;
/// Initializes a new instance of the struct.
/// An object that represents the pre-existing handle to use.
public HWCT(IntPtr preexistingHandle) => handle = preexistingHandle;
/// Returns an invalid handle by instantiating a object with .
public static HWCT NULL => new HWCT(IntPtr.Zero);
/// Gets a value indicating whether this instance is a null handle.
public bool IsNull => handle == IntPtr.Zero;
/// Performs an explicit conversion from to .
/// The handle.
/// The result of the conversion.
public static explicit operator IntPtr(HWCT h) => h.handle;
/// Performs an implicit conversion from to .
/// The pointer to a handle.
/// The result of the conversion.
public static implicit operator HWCT(IntPtr h) => new HWCT(h);
/// Implements the operator !=.
/// The first handle.
/// The second handle.
/// The result of the operator.
public static bool operator !=(HWCT h1, HWCT h2) => !(h1 == h2);
/// Implements the operator ==.
/// The first handle.
/// The second handle.
/// The result of the operator.
public static bool operator ==(HWCT h1, HWCT h2) => h1.Equals(h2);
///
public override bool Equals(object obj) => obj is HWCT h ? handle == h.handle : false;
///
public override int GetHashCode() => handle.GetHashCode();
///
public IntPtr DangerousGetHandle() => handle;
}
/// Represents a node in a wait chain.
// 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
{
/// The object type. This member is one of the following values from the WCT_OBJECT_TYPE enumeration type.
[FieldOffset(0)]
public WCT_OBJECT_TYPE ObjectType;
///
/// The object status. This member is one of the following values from the WCT_OBJECT_STATUS enumeration type.
///
[FieldOffset(4)]
public WCT_OBJECT_STATUS ObjectStatus;
///
[FieldOffset(8)]
public LOCKOBJECT LockObject;
///
[FieldOffset(8)]
public THREADOBJECT ThreadObject;
///
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Size = 272)]
public struct LOCKOBJECT
{
[FieldOffset(0)]
private long on0;
/// This member is reserved for future use.
[FieldOffset(256)]
public long Timeout;
/// This member is reserved for future use.
[MarshalAs(UnmanagedType.Bool)]
[FieldOffset(264)]
public bool Alertable;
///
/// 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.
///
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);
}
}
}
}
///
[StructLayout(LayoutKind.Sequential)]
public struct THREADOBJECT
{
/// The process identifier.
public uint ProcessId;
/// The thread identifier. For COM and ALPC, this member can be 0.
public uint ThreadId;
/// The wait time.
public uint WaitTime;
/// The number of context switches.
public uint ContextSwitches;
}
}
/// Provides a for that is disposed using .
public class SafeHWCT : SafeHANDLE
{
/// Initializes a new instance of the class and assigns an existing handle.
/// An object that represents the pre-existing handle to use.
///
/// to reliably release the handle during the finalization phase; otherwise, (not recommended).
///
public SafeHWCT(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { }
/// Initializes a new instance of the class.
private SafeHWCT() : base() { }
/// Performs an implicit conversion from to .
/// The safe handle instance.
/// The result of the conversion.
public static implicit operator HWCT(SafeHWCT h) => h.handle;
///
protected override bool InternalReleaseHandle() { CloseThreadWaitChainSession(handle); return true; }
}
}
}