ption Explicit
Private isRunning As Boolean
Private ProcessID As Long
Private Const PROCESS_ALL_ACCESS = &H100FFF
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessId As Long
dwThreadId As Long
End Type
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Byte
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Enum ProcessCreationFlags
DEBUG_PROCESS = &H1
DEBUG_ONLY_THIS_PROCESS = &H2
CREATE_SUSPENDED = &H4
DETACHED_PROCESS = &H8
CREATE_NEW_CONSOLE = &H10
NORMAL_PRIORITY_CLASS = &H20
IDLE_PRIORITY_CLASS = &H40
HIGH_PRIORITY_CLASS = &H80
REALTIME_PRIORITY_CLASS = &H100
CREATE_NEW_PROCESS_GROUP = &H200
CREATE_UNICODE_ENVIRONMENT = &H400
CREATE_SEPARATE_WOW_VDM = &H800
CREATE_SHARED_WOW_VDM = &H1000
CREATE_FORCEDOS = &H2000
CREATE_DEFAULT_ERROR_MODE = &H4000000
CREATE_NO_WINDOW = &H8000000
End Enum
Private Declare Function CreateProcess Lib _
"kernel32" Alias "CreateProcessA" ( _
ByVal lpApplicationName As String, _
ByVal lpCommandLine As String, _
ByVal lpProcessAttributes As Long, _
ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, _
ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, _
ByVal lpCurrentDirectory As String, _
lpStartupInfo As STARTUPINFO, _
lpProcessInformation As PROCESS_INFORMATION _
) As Long
'Debugging event code that identifies the type of
' debugging event.
Private Const EXCEPTION_DEBUG_EVENT = 1
Private Const CREATE_THREAD_DEBUG_EVENT = 2
Private Const CREATE_PROCESS_DEBUG_EVENT = 3
Private Const EXIT_THREAD_DEBUG_EVENT = 4
Private Const LOAD_DLL_DEBUG_EVENT = 6
Private Const UNLOAD_DLL_DEBUG_EVENT = 7
Private Const OUTPUT_DEBUG_STRING_EVENT = 8
Private Const RIP_EVENT = 9
Private Const EXCEPTION_BREAKPOINT = &H80000003
Private Const DBG_CONTINUE = &H10002
Private Const DBG_TERMINATE_THREAD = &H40010003
Private Const DBG_TERMINATE_PROCESS = &H40010004
Private Const DBG_CONTROL_C = &H40010005
Private Const DBG_CONTROL_BREAK = &H40010008
Private Const DBG_EXCEPTION_NOT_HANDLED = &H80010001
Private Const EXIT_PROCESS_DEBUG_EVENT = 5
Private Const EXCEPTION_MAXIMUM_PARAMETERS = 15
Private Type DEBUG_EVENT
dwDebugEventCode As Long
dwProcessId As Long
dwThreadId As Long
union(0 To 87) As Byte
End Type
Private Type EXCEPTION_RECORD
ExceptionCode As Long
ExceptionFlags As Long
pExceptionRecord As Long ' Pointer to an EXCEPTION_RECORD structure
ExceptionAddress As Long
NumberParameters As Long
ExceptionInformation(EXCEPTION_MAXIMUM_PARAMETERS) As Long
End Type
Private Type EXCEPTION_DEBUG_INFO
pExceptionRecord As EXCEPTION_RECORD
dwFirstChance As Long
End Type
Private Declare Function OpenProcess Lib "kernel32" ( _
ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long _
) As Long
Private Declare Function WaitForDebugEvent Lib "kernel32" ( _
lpDebugEvent As DEBUG_EVENT, _
ByVal dwMilliseconds As Long _
) As Integer 'Bool
Private Declare Function DebugActiveProcess Lib "kernel32" ( _
ByVal dwProcessId As Long _
) As Long
Private Declare Function DebugActiveProcessStop Lib "kernel32" ( _
ByVal dwProcessId As Long _
) As Long
Private Declare Function ContinueDebugEvent Lib "kernel32" ( _
ByVal dwProcessId As Long, _
ByVal dwThreadId As Long, _
ByVal dwContinueStatus As Long _
) As Long
Private Declare Function CloseHandle Lib "kernel32" ( _
ByVal hObject As Long _
) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
Destination As Any, _
Source As Any, _
ByVal Length As Long _
)
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" ( _
ByVal hwnd As Long, _
lpdwProcessId As Long _
) As Long
Private Sub Command1_Click()
Dim hWndNtPD As Long
Dim hProc As Long
'get handle to Window if it is open
hWndNtPD = FindWindow("SciCalc", vbNullString)
'if program has not started then start it
If hWndNtPD = 0 Then
Dim PINFO As PROCESS_INFORMATION
Dim si As STARTUPINFO
si.cb = Len(si)
If CreateProcess(vbNullString, "C:\Windows\system32\calc.exe", 0&, 0&, 0&, _
DEBUG_ONLY_THIS_PROCESS, 0&, _
vbNullString, si, PINFO) Then
ProcessID = PINFO.dwProcessId
hProc = PINFO.hProcess
Else
MsgBox "CreateProcess failed"
Exit Sub
End If
Else
'program is already running, open its process
If GetWindowThreadProcessId(hWndNtPD, ProcessID) = 0 Then
MsgBox "Error Getting ProcessID"
Exit Sub
End If
hProc = OpenProcess(PROCESS_ALL_ACCESS, 0&, ProcessID)
If hProc = 0 Then
MsgBox "Error Opening process"
Exit Sub
End If
If DebugActiveProcess(ProcessID) = 0 Then
CloseHandle hProc
MsgBox "Error Attaching Debugger"
Exit Sub
End If
End If
' The debug loop. Runs until the debuggee terminates
Dim dbgevent As DEBUG_EVENT
Dim dwContinueStatus As Long
isRunning = True
Do
'wait here for 100ms for debug event
If WaitForDebugEvent(dbgevent, 100) Then
Debug.Print "Debug Event: "; dbgevent.dwDebugEventCode
If dbgevent.dwDebugEventCode = EXCEPTION_DEBUG_EVENT Then
dwContinueStatus = HandleException(dbgevent)
Else
dwContinueStatus = DBG_CONTINUE
End If
'continue debugging
Call ContinueDebugEvent(dbgevent.dwProcessId, _
dbgevent.dwThreadId, _
dwContinueStatus)
Else
'no debug event occurred
DoEvents
End If
Loop While (dbgevent.dwDebugEventCode <> EXIT_PROCESS_DEBUG_EVENT) And isRunning
isRunning = False
End Sub
Private Sub Form_Unload(Cancel As Integer)
isRunning = False
DebugActiveProcessStop ProcessID
End Sub
Private Function HandleException(dbgevent As DEBUG_EVENT) As Long
Dim dwContinueStatus As Long
Dim exceptRec As EXCEPTION_RECORD
Dim Exception As EXCEPTION_DEBUG_INFO
Call CopyMemory(Exception, dbgevent.union(0), Len(Exception))
Call CopyMemory(exceptRec, Exception.pExceptionRecord, Len(exceptRec))
Debug.Print
Debug.Print "Exception code: ";
Debug.Print Hex(exceptRec.ExceptionCode); " Addr: ";
Debug.Print Hex(exceptRec.ExceptionAddress)
' If it isn't a breakpoint, we don't want to know about it.
If exceptRec.ExceptionCode <> EXCEPTION_BREAKPOINT Then
HandleException = DBG_EXCEPTION_NOT_HANDLED
Exit Function
End If
dwContinueStatus = DBG_CONTINUE
HandleException = dwContinueStatus
End Function