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