;;#Include Extensions.ahk
;#include %A_ScriptDir%\Lib\dbgp.ahk
;#include %A_ScriptDir%\Lib\HashTable.ahk
;#include %A_ScriptDir%\Lib\RemoteBuf.ahk
;SplitPath, A_AhkPath, ,ahkpath
;ahkpath=D:\scite\ahk\scite_ahk
#include %A_ScriptDir%\Lib\dbgp.ahk
#include %A_ScriptDir%\..\Lib\HashTable.ahk
#include %A_ScriptDir%\..\Lib\RemoteBuf.ahk
#Persistent
#NoTrayIcon
SetWorkingDir, %A_ScriptDir%
DetectHiddenWindows, On
debug_target := 0 ;; 0:remote, 1:local
WM_COMMAND := 0x111
WM_COPYDATA := 0x4a
ATM_OFFSET := 0x1000
ATM_GETSTATUS := ATM_OFFSET+6
ATM_AHKDBG := ATM_OFFSET+7
;;ATM_OFFSET := 0x1000
;;ATM_STARTDEBUG := ATM_OFFSET+0
; Do not run this script from SciTE, or any other editor that redirects console output.
;;DllCall("AllocConsole")
;;DllCall("AttachConsole")
;;WinGet, pid, PID, ahk_class ConsoleWindowClass
;;if (!DllCall("AttachConsole", int, -1, int))
;; if (!DllCall("AllocConsole", int))
;; return
;;DllCall("FreeConsole")
;DllCall("AllocConsole")
;sleep, 1000
SciTEPath := "=="
if 1 = 0 || 2 = 0 || 3 = 0
{
MsgBox, 16, SciTE4AutoHotkey Debugger, before You need run this script run scitetools
ExitApp
}
scitehwd=%1%
receiver=%2%
toolbar=%3%
dbgAddr=%4%
dbgPort=%5%
bIsAttach := false
if 6 = /attach
bIsAttach := true
;;_Director_Send("output:dbgp_console.ahk:dbgAddr:" dbgAddr ",dbgPort:" dbgPort "\n")
;;scitehwd=1446560
;;receiver=3018642
;;toolbar=26087118
sleep,50
;;MsgBox, 16, SciTE4AutoHotkey Toolbar,scitehwd=%scitehwd%:receiver=%receiver%:toolbar=%toolbar%
;DllCall("AttachConsole", int, -1, int)
;hStdin := DllCall("GetStdHandle", int, -10, int)
;;hStdout := DllCall("GetStdHandle", int, -11, int)
;;hStderror := DllCall("GetStdHandle", int, -12, int)
;;stdin := FileOpen( hStdin , "h `n")
;;stdout := FileOpen( hStdout , "h `n")
ThisScriptsHWND := WinExist("Ahk_PID " DllCall("GetCurrentProcessId"))
OnMessage(ATM_AHKDBG, "Msg_AhkDbg")
OnMessage(WM_COPYDATA, "_Director_Recv")
OnMessage(ATM_GETSTATUS, "Msg_GetStatus")
;_Director_Send("output:dbgp_console.ahk:_Director_Recv" "\n")
if A_IsCompiled
A_ExeName := A_ScriptName
else
SplitPath, A_AhkPath, A_ExeName
; Set up aliases:
alias=
(
r=run
in=step_into
ov=step_over
ou=step_out
fg=feature_get
fs=feature_set
bs=breakpoint_set
bg=breakpoint_get
br=breakpoint_remove
bl=breakpoint_list
sd=stack_depth
sg=stack_get
cn=context_names
cg=context_get
tg=typemap_get
pg=property_get
ps=property_set
pv=property_value
)
Loop, Parse, alias, `n
{
StringSplit, alias, A_LoopField, =
DllCall("AddConsoleAlias", "str", alias1, "str", alias2 " $*", "str", A_ExeName)
}
; Set up error code -> text map.
ErrorMap := {0: "OK"
, 1: "Parse error"
, 3: "Invalid options"
, 4: "Unimplemented command"
, 5: "Command unavailable"
, 100: "Can not open file"
, 201: "Breakpoint type not supported"
, 202: "Breakpoint invalid"
, 203: "No code on breakpoint line"
, 204: "Invalid breakpoint state"
, 205: "No such breakpoint"
, 300: "Unknown property"
, 301: "Invalid stack depth"
, 302: "Invalid context"}
;_Director_Send("output:dbgp_console.ahk:ErrorMap" "\n")
Dbg_BkList := new HashTable()
; Set event callback.
DBGp_OnBegin("DebuggerConnected")
DBGp_OnStream("DebuggerStream")
DBGp_OnEnd("DebuggerDisconnected")
Listen:
;;dbgAddr := "127.0.0.1"
;;dbgPort := 9002
;_Director_Send("output:dbgp_console.ahk: for ListenSocket" "\n")
ListenSocket := DBGp_StartListening(dbgAddr,dbgPort)
sleep,50
ConWriteLine("*** Listening on port " dbgPort " and Addr " dbgAddr " for a debugger connection.")
if bIsAttach
{
SelectAttachScript(AttachWin, Dbg_PID)
if AttachWin =
{
ExitApp
}
; Set the Last Found Window
WinWait, ahk_id %AttachWin%
; Get PID of the AutoHotkey window
WinGet, Dbg_PID, PID
; Tell AutoHotkey to debug
PostMessage, DllCall("RegisterWindowMessage", "str", "AHK_ATTACH_DEBUGGER"), DllCall("ws2_32\inet_addr", "astr", dbgAddr), dbgPort
}
return
DebuggerConnected(new_session)
{
global
; We can handle only one at a time, so stop listening for now.
DBGp_StopListening(ListenSocket), ListenSocket := -1
; Start the interactive loop in a new thread.
session := new_session
if ( debug_target == 1){
SetTimer, monitoring_prc, Off
}
SetTimer, Debug, -1
}
DebuggerStream(session, ByRef packet)
{
if RegExMatch(packet, "\K.*(?=)", stream)
{
ConAttrib(stream1="stdout" ? 7 : 14)
ConWriteLine(RegExReplace(DBGp_Base64UTF8Decode(stream),"`n$"))
ConAttrib(15)
}
}
DebuggerDisconnected()
{
; global session := 0
}
WriteResponse(session, ByRef response)
{
; Improve output formatting a little.
TidyPacket(response)
; Display response with base64-encoded data converted back to text:
b := 1
p := 1
while p := RegExMatch(response, "encoding=""base64""[^>]*?>\s*\K[^<]+?(?=\s*<)", base64, p)
{
ConWrite(SubStr(response, b, p-b))
ConAttrib(9)
ConWrite(DBGp_Base64UTF8Decode(base64))
ConAttrib(15)
b := p + StrLen(base64)
}
;;_Director_Send("output:" SubStr(response, b) "`n`n")
ConWrite(SubStr(response, b) "`n`n")
}
Debug:
ConWriteLine("
(C
*** Debugging " session.File " ; Path of main script file
ide_key: " session.IDEKey " ; DBGp_IDEKEY env var
session: " session.Cookie " ; DBGp_COOKIE env var
thread id: " session.Thread " ; Thread id of script
)`n")
return
SciTEMsgHandler(line){
global
; Display prompt.
;;ConWrite("> ")
; Wait for one line of input.
;;stdin := FileOpen("*", "r `n")
;;line := RTrim(stdin.ReadLine(), "`n")
;; FileReadLine, line, CONIN$, 1
; Support var=value
if RegExMatch(line, "^(.+?)\s*=\s*(.*)$", m)
line := "property_set -n " m1 " -- " DBGp_Base64UTF8Encode(m2)
; Support ?var
else if SubStr(line,1,1)="?"
line := "property_get -n " RegExReplace(line,"^\?\s*")
; Split the command and args.
if delim := InStr(line," ")
command := SubStr(line,1,delim-1), args := SubStr(line,delim+1)
else
command := line, args := ""
if command = d
{
ConWrite(DBGp_Base64UTF8Decode(args) "`n`n")
;continue
return
}
if ( command = "help" )
{
ConWriteLine("
(
*** autohotkey debug commands
property_get -m 200 -n
)`n")
return
}
if ( command = "breakpoint_remove" )
{
RegExMatch(args, "^(.+)\s+(.+)$", pat)
filepath := pat1
cline := pat2 + 0
StringLower, filepath, filepath
bk := Util_GetBk(filepath ":" cline)
args := "-d " bk.id
Util_RemoveBk( filepath ":" cline )
;;_Director_Send("output:dbgp_console.ahk:" command " " path " " line " " args "\n")
}
if bIsAttach
{
if ( command = "stop" )
{
MsgBox, 35, SciTE4AutoHotkey Debugger, Do you wish to stop the script (YES) or just stop debugging (NO)?
IfMsgBox, Cancel
return
IfMsgBox, No
{
command := "detach"
}
}
if ( command = "breakpoint_set" )
{
RegExMatch(args, "^\s*-t\s+line\s+-n\s+(\d+)\s+-f\s+(.+)$", pat)
filepath := pat2
cline := pat1 + 0
StringLower, filepath, filepath
bk := Util_GetBk(filepath ":" cline)
if ( bk.cond != "not" )
return
}
}
if DBGp(session, command, args, response) = 0
{
;;WriteResponse(session, response)
if IsLabel("_pos_" command)
gosub _pos_%command%
else
WriteResponse(session, response)
;;ConWrite("`n")
}
else
{
if session.Socket = -1 ; Disconnected.
ExitApp
gosub display_error
}
if InStr(response,"status=""stopped""") || session.Socket = -1
{
ConWriteLine("*** Stopped debugging.")
ExitApp
}
}
;;; If script has stopped, listen for the next connection.
;;Until InStr(response,"status=""stopped""") || session.Socket = -1
;;ConWriteLine("*** Stopped debugging.")
;;gosub Listen
return
display_error:
el := ErrorMap.HasKey(ErrorLevel) ? ErrorMap[ErrorLevel] : ErrorLevel
ConAttrib(12)
ConWrite("Error: " el "`n`n")
ConAttrib(15)
return
TidyPacket(ByRef xml) {
xml := RegExReplace(xml, "^<\?.*?\?>")
i := 1
while i := RegExMatch(xml, "s)(.*?)(<[^>]*>)", s, i)
{
if (s1 != "" && out != "")
out .= "`n" . indent
out .= s1 . "`n"
if is_end_tag := SubStr(s2,2,1)="/"
indent := SubStr(indent, 3)
out .= indent
if StrLen(indent . s2) > 100 && RegExMatch(s2, "^<\w+ ", n)
s2 := RegExReplace(s2, "\S+?=""[^""]*""\K\s(?!\s*>)"
, "`n" . indent . RegExReplace(n, "s).", " "))
out .= s2
if !is_end_tag && SubStr(s,-1,1) != "/"
indent .= " "
i += StrLen(s)
}
xml := out
}
;
; File encoding: UTF-8
; Author: Lexikos
;
StrPutVar(string, ByRef var, encoding)
{
; Ensure capacity.
VarSetCapacity( var, StrPut(string, encoding)
; StrPut returns char count, but VarSetCapacity needs bytes.
* ((encoding="utf-16"||encoding="cp1200") ? 2 : 1) )
; Copy or convert the string.
return StrPut(string, &var, encoding)
}
_Director_Send(msg)
{
global
len := StrPutVar(msg, msg_utf8, "UTF-8")
;;MsgBox, 16, SciTE4AutoHotkey Toolbar,% "====="msg_utf8
; This code was taken from the AHK help
VarSetCapacity(COPYDATASTRUCT, 3*A_PtrSize, 0)
NumPut(len, COPYDATASTRUCT, A_PtrSize, "UInt")
NumPut(&msg_utf8, COPYDATASTRUCT, 2*A_PtrSize)
;;MsgBox, 16, SciTE4AutoHotkey Toolbar,% "=receiverhwnd===="receiverhwnd "::"msg
SendMessage, WM_COPYDATA, 0, ©DATASTRUCT,, ahk_id %receiver%,,,,60000
;;MsgBox, 16, SciTE4AutoHotkey Toolbar, ===%ErrorLevel%
}
_Director_Recv(wParam, lParam, msg, hwnd)
{
Critical
global DirectorMsg, DirectorRcv, DirectorRetByArray
message := StrGet(NumGet(lParam + 2*A_PtrSize), NumGet(lParam + A_PtrSize, "UInt"), "UTF-8")
SciTEMsgHandler(message)
;;MsgBox, 16, SciTE4AutoHotkey Toolbar,% "=message===="message.type
return true
}
Msg_GetStatus(wParam, lParam, msg, hwnd)
{
global
CmdOK := wParam
}
Msg_AhkDbg(wParam, lParam, msg, hwnd)
{
global gdbcmd, initcmds, prc_id, debug_target
message := StrGet(lParam, "UTF-8")
;;_Director_Send("output:dbgp_console.ahk wParam=[" wParam "]\n")
;;_Director_Send("output:dbgp_console.ahk cmd_arg=[" message "]\n")
if ( wParam == 0)
{
gdbcmd := message
SetTimer, gdb_command, -1
}
else if ( wParam == 1 ) {
RegExMatch(message, "^(start_dbg)\s+(.+)$", pat)
cmd_code := pat1
cmd_arg := pat2
;;scite.SendDirectorMsg("output:Msg_Ahkdbg cmd=[" cmd_arg "]\n")
;;_Director_Send("output:dbgp_console.ahk cmd_arg=[" cmd_arg "]\n")
Run, %cmd_arg%,,,prc_id
debug_target := 1
SetTimer, monitoring_prc, 50
}
else if ( wParam == 2)
{
if message !=
{
initcmds := message
SetTimer, init_command, -1
}
}
else if ( wParam == 3)
{
if message =
initcmds := "step_into`n"
else
initcmds := message "run`n"
SetTimer, init_command, -1
}
}
init_command:
Loop, parse, initcmds, `n
{
exe_cmd := A_LoopField
if exe_cmd !=
{
gdbcmd := exe_cmd
SetTimer, gdb_command, -1
sleep, 10
_Director_Send("output: init=[" gdbcmd "]\n")
}
}
return
gdb_command:
;;_Director_Send("output:dbgp_console.ahk gdbcmd=[" gdbcmd "]\n")
SciTEMsgHandler(gdbcmd)
return
monitoring_prc:
Process, Exist, %prc_id%
if ErrorLevel = 0
{
;;MsgBox The document could not be launched.
ConWriteLine("*** Stopped debugging.")
ExitApp
}
return
scite_mgs(s) {
global send_str
send_str := s
;;StringReplace,str,s,\,\\,ALL
;;StringReplace, str, str, `n, \\n, All
;;_Director_Send("property:debug_msg=" str)
;;_Director_Send("output:debug_msg=" str "\n")
SetTimer, SendMsg, -1
}
SendMsg:
cmd := ":" ThisScriptsHWND+0 ":" send_str
len := StrPutVar(cmd, str_utf8, "UTF-8")
RemoteBuf_Open(hSciteBuf, scitehwd, len)
RemoteBuf_Write( hSciteBuf, str_utf8, len)
CmdOK := 0
addr_msg := RemoteBuf_Get(hSciteBuf, "adr")
SendMessage, WM_COMMAND, 1158, addr_msg ,, ahk_id %scitehwd%,,,,60000
;;_Director_Send("output:SendMessage:" CmdOK " \n")
while ( CmdOK == 0 ) {
sleep,50
SendMessage, WM_COMMAND, 1158, addr_msg,, ahk_id %scitehwd%,,,,60000
;_Director_Send("output:SendMessage:" CmdOK " \n")
}
RemoteBuf_Close( hSciteBuf )
return
ConWrite(s) {
;;global
scite_mgs(s)
;;stdout := FileOpen( hStdout , "h `n")
;stdout := FileOpen("*", "w `n")
;;FileAppend %s%, CONOUT$
;;stdout.Write(s)
;;stdout.Read(0)
;FileAppend %s%, *
}
ConWriteLine(s) {
;;global
;;stdout := FileOpen( hStdout , "h `n")
;stdout := FileOpen("*", "w `n")
;FileAppend %s%`n, CONOUT$
scite_mgs(s "`n")
;;stdout.WriteLine(s "`n")
;;stdout.Read(0)
;;FileAppend %s%`n, *
}
ConAttrib(attrib) {
;Console := FileOpen("*", "rw")
;;Console := FileOpen("*", "w `n")
;; return DllCall("SetConsoleTextAttribute", "ptr", Console.__Handle, "short", attrib)
;;return DllCall("SetConsoleTextAttribute", "ptr", stdout.__Handle, "short", attrib)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
loadXML(ByRef data)
{
o := ComObjCreate("MSXML2.DOMDocument")
o.async := false
o.setProperty("SelectionLanguage", "XPath")
o.loadXML(data)
return o
}
Util_RemoveBk( uri_line )
{
global Dbg_BkList
;;Dbg_BkList[uri].Remove(line)
Dbg_BkList.Delete(uri_line)
}
Util_AddBkToList(uri_line, _id, cond := "")
{
global Dbg_BkList
_value := { id: _id, cond: cond }
Dbg_BkList.Set(uri_line, _value)
;Dbg_BkList[uri, line] := { id: id, cond: cond }
}
Util_GetBk(uri_line)
{
global Dbg_BkList
;return Dbg_BkList[uri, line]
try {
return Dbg_BkList.Get(uri_line)
} catch e {
return { id: 0, cond: "not" }
}
}
_pos_breakpoint_remove:
return
_pos_breakpoint_list:
WriteResponse(session, response)
dom := loadXML(response)
msg := ""
for node in dom.selectSingleNode("/response").childNodes
{
bkID := node.getAttribute("id")
line := dom.selectSingleNode("/response/breakpoint[@id=" bkID "]/@lineno").text
cFNameURI := dom.selectSingleNode("/response/breakpoint[@id=" bkID "]/@filename").text
cFName := DBGp_DecodeFileURI(cFNameURI)
StringLower, cFName, cFName
Util_AddBkToList(cFName ":" line, bkID)
if ( cFNameURI != "" )
msg := msg "breakpoint " cFName " " line "`n"
}
/*
Loop {
line := dom.selectSingleNode("/response/breakpoint[@id=" bkID "]/@lineno").text
cFNameURI := dom.selectSingleNode("/response/breakpoint[@id=" bkID "]/@filename").text
cFName := DBGp_DecodeFileURI(cFNameURI)
Util_AddBkToList(cFName ":" line, bkID)
if ( cFNameURI != "" )
msg := msg "breakpoint " cFName " " line "`n"
bkID++
} Until ( cFNameURI == "" )
*/
if ( msg == "" )
msg := "breakpoint have 0`n"
ConWrite( msg)
return
_pos_breakpoint_set:
dom := loadXML(response)
bkID := dom.selectSingleNode("/response/@id").text
if DBGp(session, "breakpoint_get" , "-d " bkID, response) = 0
{
dom := loadXML(response)
line := dom.selectSingleNode("/response/breakpoint[@id=" bkID "]/@lineno").text
cFNameURI := dom.selectSingleNode("/response/breakpoint[@id=" bkID "]/@filename").text
cFName := DBGp_DecodeFileURI(cFNameURI)
StringLower, cFName, cFName
Util_AddBkToList(cFName ":" line, bkID)
;;_Director_Send("output:dbgp_console.ahk:Util_AddBkToList " cFName " " line " " bkID "\n")
}
return
_pos_property_get:
dom := loadXML(response)
check := dom.selectSingleNode("/response/property/@name").text
if check = (invalid)
return
Dbg_VarType := dom.selectSingleNode("/response/property/@type").text
if ( Dbg_VarType = "Object" )
{
ConWrite( "= ")
}else if ( Dbg_VarType = "undefined" )
{
ConWrite( "= null")
}else{
Dbg_VarData := DBGp_Base64UTF8Decode(dom.selectSingleNode("/response/property").text)
Dbg_VarSize := dom.selectSingleNode("/response/property/@size").text
if Dbg_VarSize > 200
Dbg_VarData .= "..."
;;ToolTip, %Dbg_VarName% = %Dbg_VarData%
if ( Dbg_VarType = "string" )
ConWrite("= [" Dbg_VarData "]" )
else if ( Dbg_VarType = "integer" )
ConWrite("= (" Dbg_VarData ")" )
;;ToolTip, %Dbg_VarName% is an object
}
return
_pos_run:
_pos_step_into:
_pos_step_over:
_pos_step_out:
if DBGp(session, "stack_get" , "", response) = 0
{
dom := loadXML(response)
cLine := dom.selectSingleNode("/response/stack[1]/@lineno").text
cFNameURI := dom.selectSingleNode("/response/stack[1]/@filename").text
cFName := DBGp_DecodeFileURI(cFNameURI)
StringReplace,cFNameURI,cFName,\,/,ALL
ConWrite( "Paused at file " cFNameURI " line " cLine)
}
return
SelectAttachScript(ByRef outwin, ByRef outpid)
{
global SciTEPath
oldTM := A_TitleMatchMode, oldHW := A_DetectHiddenWindows
SetTitleMatchMode, 2
DetectHiddenWindows, On
WinGet, w, List, - AutoHotkey ahk_class AutoHotkey,, %A_ScriptFullPath%
Gui, +LabelAttGui +ToolWindow +AlwaysOnTop
Gui, Add, ListView, x0 y0 w640 h240 +NoSortHdr -LV0x10 gAttGuiSelect, HWND|Name
i := 0
Loop, % w
{
hwnd := w%A_Index%
WinGetTitle, ov, ahk_id %hwnd%
if InStr(ov, SciTEPath) ; Do not allow debugging SciTE4AutoHotkey itself
continue
if !RegExMatch(ov, "v([0-9.]+)(-\S+)?$", q) ; Make sure it has a correctly-formed version number
continue
if q1 < 1.1.00.00 ; Make sure it is NOT a legacy AutoHotkey version
continue
LV_Add("", hwnd, ov)
i ++
}
if i = 0
{
MsgBox, 48, SciTE4AutoHotkey Debugger, There are no currently running debuggable AutoHotkey scripts!
return ""
}
LV_ModifyCol(1, "AutoHdr")
LV_ModifyCol(2, "AutoHdr")
Gui, Show, w640 h240, Select running script to debug
global attSelection, attWin
while !attSelection
Sleep, 100
if attSelection = -1
filename := "", outwin := "", outpid := ""
else
{
LV_GetText(filename, attSelection, 2)
LV_GetText(outwin, attSelection, 1)
WinGet, outpid, PID, ahk_id %outwin%
}
Gui, Destroy
DetectHiddenWindows, %oldTM%
SetTitleMatchMode, %oldTM%
return filename
}
AttGuiClose:
attSelection := -1
return
AttGuiSelect:
if A_GuiEvent != DoubleClick
return
attSelection := A_EventInfo
return