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; } } } }