Imports DevCase.Interop.Unmanaged.Win32
Imports DevCase.Interop.Unmanaged.Win32.Enums
Imports Microsoft.Win32.SafeHandles
Namespace DevCase.Core.IPC.Tools
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Contains related UI automation utilities.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<ImmutableObject(True)>
Public NotInheritable Class UIAutomationUtil
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Prevents a default instance of the <see cref="UIAutomationUtil"/> class from being created.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<DebuggerNonUserCode>
Private Sub New()
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Pauses the execution of all the threads of the specified process.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code>
''' ' Create and run a CMD process.
''' Dim p As New Process
''' p.StartInfo.FileName = "cmd.exe"
''' p.StartInfo.Arguments = "/C ""Dir /B /S /A C:\*"""
''' p.Start()
'''
''' Thread.Sleep(2000) ' Let the CMD run the job for some seconds...
'''
''' ' Pause the process.
''' Using ppo As PauseProcessObject = UIAutomationUtil.PauseProcess(p.Id)
''' ' Wait 5 seconds to let you observe that the process is paused...
''' Thread.Sleep(5000)
'''
''' ' Resume the process.
''' UIAutomationUtil.ResumeProcess(ppo)
''' End Using
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="pid">
''' The process id (PID).
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' The return value is a collection of openthread-handles to the thread handles of the process.
''' <para></para>
''' An openthread-handle could be used to suspend, resume or terminate a thread that the openthread-handle refers to.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
''' <remarks>
''' After pausing the threads of a process, call <see cref="ResumeProcess(PauseProcessObject)"/> function
''' to resume their threads.
''' <para></para>
''' To manually resume an openthread-handle, call <see cref="ResumeThread"/> function.
''' </remarks>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Win32Exception">
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Shared Function PauseProcess(pid As Integer) As PauseProcessObject
Dim ppo As New PauseProcessObject(pid)
For Each thread As ProcessThread In Process.GetProcessById(pid).Threads()
ppo.handles_.Add(UIAutomationUtil.PauseThread(thread.Id))
Next thread
Return ppo
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Resumes the execution of all the threads for the specified process.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code>
''' ' Create and run a CMD process.
''' Dim p As New Process
''' p.StartInfo.FileName = "cmd.exe"
''' p.StartInfo.Arguments = "/C ""Dir /B /S /A C:\*"""
''' p.Start()
'''
''' Thread.Sleep(2000) ' Let the CMD run the job for some seconds...
'''
''' ' Pause the process.
''' Using ppo As PauseProcessObject = UIAutomationUtil.PauseProcess(p.Id)
''' ' Wait 5 seconds to let you observe that the process is paused...
''' Thread.Sleep(5000)
'''
''' ' Resume the process.
''' UIAutomationUtil.ResumeProcess(ppo)
''' End Using
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="ppo">
''' A <see cref="PauseProcessObject"/> object containing all the process thread-handles.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <remarks>
''' After resuming the threads of a process, call the <see cref="PauseProcessObject.Dispose()"/> method
''' of the <paramref name="ppo"/> parameter to close their openthread-handles.
''' <para></para>
''' To manually close an openthread-handle, call <see cref="NativeMethods.CloseHandle(IntPtr)"/> function.
''' </remarks>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Win32Exception">
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Shared Sub ResumeProcess(ppo As PauseProcessObject)
For Each safeOpenThreadHandle As SafeAccessTokenHandle In ppo.handles_
UIAutomationUtil.ResumeThread(safeOpenThreadHandle)
Next safeOpenThreadHandle
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Pauses the execution of the specified thread.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code>
''' Dim threadId As ProcessThread = Process.GetProcessesByName("cmd.exe").FirstOrDefault.Threads(0).Id
'''
''' Dim openThreadHandle As SafeAccessTokenHandle = PauseThread(threadId)
'''
''' ' ResumeThread(openThreadHandle)
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="threadId">
''' The thread identifier.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' The return value is an openthread-handle to the thread handle.
''' <para></para>
''' An openthread-handle could be used to suspend, resume or terminate a thread that the open-handle refers to.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
''' <remarks>
''' After pausing a thread, call <see cref="ResumeThread"/> function to resume the thread and close the openthread-handle.
''' <para></para>
''' To manually close an openthread-handle, call <see cref="NativeMethods.CloseHandle(IntPtr)"/> function.
''' </remarks>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Win32Exception">
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Shared Function PauseThread(threadId As Integer) As SafeAccessTokenHandle
Const threadAccessRights As ThreadAccessRights = ThreadAccessRights.SuspendResume Or ThreadAccessRights.Terminate
Dim safeOpenThreadHandle As SafeAccessTokenHandle = NativeMethods.OpenThread(threadAccessRights, True, CUInt(threadId))
Dim win32Err As Integer = Marshal.GetLastWin32Error()
If safeOpenThreadHandle.IsInvalid() Then
Throw New Win32Exception(win32Err)
Else
NativeMethods.SuspendThread64(safeOpenThreadHandle)
Return safeOpenThreadHandle
End If
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Pauses the execution of the specified thread.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code>
''' Dim thread As ProcessThread = Process.GetProcessesByName("cmd.exe").FirstOrDefault.Threads(0)
''' Dim safeOpenThreadHandle As SafeAccessTokenHandle = PauseThread(thread)
'''
''' ' ResumeThread(openThreadHandle)
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="thread">
''' The thread.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' The return value is an openthread-handle to the thread handle.
''' <para></para>
''' An openthread-handle could be used to suspend, resume or terminate a thread that the open-handle refers to.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
''' <remarks>
''' After pausing a thread, call <see cref="ResumeThread"/> function to resume the thread and close the openthread-handle.
''' <para></para>
''' To manually close an openthread-handle, call <see cref="NativeMethods.CloseHandle(SafeHandle)"/> function.
''' </remarks>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Win32Exception">
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Shared Function PauseThread(thread As ProcessThread) As SafeAccessTokenHandle
Return UIAutomationUtil.PauseThread(thread.Id)
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Resumes the execution of the specified thread.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="safeOpenThreadHandle">
''' An open thread handle.
''' <para></para>
''' This handle can be obtained by calling the <see cref="UIAutomationUtil.PauseThread"/>
''' or <see cref="NativeMethods.OpenThread(ThreadAccessRights, Boolean, UInteger)"/> functions.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Win32Exception">
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Shared Sub ResumeThread(safeOpenThreadHandle As SafeAccessTokenHandle)
Dim result As Integer
Dim win32Err As Integer
If safeOpenThreadHandle.IsInvalid Then
Throw New NullReferenceException("Handle is invalid.")
End If
result = NativeMethods.ResumeThread(safeOpenThreadHandle)
win32Err = Marshal.GetLastWin32Error()
If (result = -1) Then
Throw New Win32Exception(win32Err)
End If
End Sub
End Class
End Namespace