Page 1 of 1

dmp() - Advanced debug function for AutoHotKey

Posted: 02 May 2015, 10:50
by 0biWanKenobi
I wrote this debug function for AutoHotKey to have a better handling with my AutoHotKey debugging workflow.

It depends on AutoHotKey_L.

Usage: dmp(yourVar)

Result:
http://guresicpark.de/sites/default/fil ... es/dmp.png

Features:
- multidimensioal arrays are displayed serialized
- dumped lines can be copied & directly pasted in your AutoHotKey script
- ability to cancel dmp-loop (via Button Cancel or via ESC-Key)
- continue dmp-loop via Button Continue or via Enter-Key



How to install:

In your main script directory create a folder \lib and place dmp.ahk there.

Example:
C:\myscript\lib\dmp.ahk

After you have finished this you can use dmp() anywhere in your script.

Example code:

Code: Select all

car1 := {model: "Toyota", color:"red", speed:100}
car2 := {model: "Chrysler", color:"white", speed:200}
car3 := {model: "BMW", color:"Black", speed:180}

driver1 := {name: "Peter", age:"31", salary:1800, car:car3}
driver2 := {name: "Jeff", age:"38", salary:2200, car:car1}
driver3 := {name: "Jim", age:"40", salary:1950, car:nil}
driver4 := {name: "Frank", age:"22", salary:1650, car:car2}

drivers := [driver1, driver2, driver3, driver4]

for deltaDriver, driverItem in drivers
{
    dmp(driverItem)
}

dmp(drivers)
Download: dmp.ahk

Homepage: http://guresicpark.de/autohotkey/dmp

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 02 May 2015, 11:35
by guest3456
I'm interested in you _dmpListLines() func

It looks like you are overwriting the memory addresses so SetForegroundWindow and ShowWindow won't be called, so the end user doesn't see the ListLines window.

But what are those values that you are using? Where did you get them and what do they represent?

Code: Select all

; Overwrite SetForegroundWindow() and ShowWindow() temporarily:
    NumPut(0x0004C200000001B8, pSFW+0, 0, "int64")  ; return TRUE
    NumPut(0x0008C200000001B8, pSW+0, 0, "int64")   ; return TRUE
edit/
whoops further searching reveals that Lexikos originally wrote this function, maybe he can chime in

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 03 May 2015, 01:25
by vasili111
Thank you for script. Useful in debugging!

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 03 May 2015, 08:46
by Guest
dmp 1.2

- bugfixes
- output improved
- new feature: now you can output multiple variables with one dmp call, example:

Code: Select all

aNames := ["Jeff", "Peter", "Jim"]
sText := " Lorem ipusm "

dmp(aNames, sText, Trim(sText))
Result: http://guresicpark.de/sites/default/fil ... s/dmp2.png

Download: dmp.ahk
Homepage: http://guresicpark.de/autohotkey/dmp

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 04 May 2015, 07:36
by joedf
Cool, sorta like PHP's vardump

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 04 May 2015, 08:34
by jNizM
@guest3456
Better ask Lexikos about 0x0004C200000001B8
ref: ListVars

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 04 May 2015, 11:18
by toralf
Could you imagine a way to show if a var contains also space characters? E.g. Add >< around the content of var when any amount of white space exists at the start or end of the content.

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 04 May 2015, 11:58
by 0biWanKenobi
dmp 1.21
- nicer output
- performance tweaks
- small code clean up

Download: dmp.ahk
Homepage: http://guresicpark.de/autohotkey/dmp

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 04 May 2015, 12:10
by 0biWanKenobi
Hello toralf,
it is already implented with quotes -> "
Try this:

Code: Select all

sText1 := "Lorem ipsum dolor sit amet"

sText2 := "    Lorem ipsum dolor sit amet"

sText3 := "Lorem ipsum dolor sit amet    "

sText4 := "    Lorem ipsum dolor sit amet    "

sText5=
(join`r`n

	Lorem ipsum dolor sit amet,
	consetetur sadipscing elitr,
	sed diam nonumy eirmod tempor.
)

dmp(sText1, sText2, sText3, sText4, sText5)
And dmp should show you this result:

Code: Select all

>>>>> sText1

    sText1 := "Lorem ipsum dolor sit amet"

<<<<<



>>>>> sText2

    sText2 := "    Lorem ipsum dolor sit amet"

<<<<<



>>>>> sText3

    sText3 := "Lorem ipsum dolor sit amet    "

<<<<<



>>>>> sText4

    sText4 := "    Lorem ipsum dolor sit amet    "

<<<<<



>>>>> sText5

    sText5 :=
"
	Lorem ipsum dolor sit amet,
	consetetur sadipscing elitr,
	sed diam nonumy eirmod tempor."

<<<<<
You can even copy dmp result and copy it in your AutoHotKey script.

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 04 May 2015, 12:37
by 0biWanKenobi
dmp 1.22
- bugfix

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 05 May 2015, 12:38
by 0biWanKenobi
dmp 1.23
- code cleanup
- New feature: alphabetically sorted output, example:

>>>>> drivers

drivers[1]["age"] := "31"
drivers[1]["car"]["color"] := "Black"
drivers[1]["car"]["model"] := "BMW"
drivers[1]["car"]["speed"] := 180
drivers[1]["name"] := "Peter"
drivers[1]["salary"] := 1800

6 elements for drivers <<<<<

Download: dmp.ahk

Homepage: http://guresicpark.de/autohotkey/dmp

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 17 May 2015, 06:02
by 0biWanKenobi
dmp 1.25
- window is now resizeable
- added button "Reload scirpt" for restarting debugged script after modifying script file

Download: dmp.ahk
Homepage: http://guresicpark.de/autohotkey/dmp

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 19 Aug 2020, 22:33
by pmobin
Hi all,

Does dmp.ahk exist anywhere? The .de site is down.

Thanks,
FM

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 21 Aug 2020, 07:34
by joedf
From: https://web.archive.org/web/20160121062531/http://guresicpark.de/autohotkey/dmp
but the download link was not archived... :(

EDIT: I found a copy here:
https://github.com/Ixiko/AHK-libs-and-classes-collection/blob/eef70b9dc1a5a712c41f2d218dd625de93dc67fc/lib-a_to_h/dmp.ahk
Included as a code in the bottom for archival purposes.

function dmp() 1.25 for AutoHotKey

Usage: dmp(yourVar)

Result:
dmp.png
dmp.png (10.02 KiB) Viewed 3382 times


Features:
  • multidimensioal arrays are displayed serialized
  • dumped lines can be copied & directly pasted in your AutoHotKey script
  • ability to cancel dmp-loop (via Button Cancel or via ESC-Key)
  • continue dmp-loop via Button Continue or via Enter-Key
How to install:

In your main script directory create a folder \lib and place dmp.ahk there.

Example:

C:\myscript\lib\dmp.ahk

After you have finished this you can use dmp() anywhere in your script.

Example code:

Code: Select all

car1 := {model: "Toyota", color:"red", speed:100}
car2 := {model: "Chrysler", color:"white", speed:200}
car3 := {model: "BMW", color:"Black", speed:180}

driver1 := {name: "Peter", age:"31", salary:1800, car:car3}
driver2 := {name: "Jeff", age:"38", salary:2200, car:car1}
driver3 := {name: "Jim", age:"40", salary:1950, car:nil}
driver4 := {name: "Frank", age:"22", salary:1650, car:car2}

drivers := [driver1, driver2, driver3, driver4]

for deltaDriver, driverItem in drivers
{
    dmp(driverItem)
}

dmp(drivers)
Download dmp.ahk

Code: Select all

; http://the-automator.com/download/dmp.ahk
; dmp Version 1.25
; By ObiWanKenobi
; Visit guresicpark.de for dmp updates
; May 8, 2015
;~ http://guresicpark.de/autohotkey/dmp

dmp(pVar*) {
    static _iCallCount := 0

    ; set x as basic variable name
    aVarnames := ["x"]

    ; now search variable name
	sLines := _dmpListLines()
    aLines := StrSplit(sLines, "`n", "`r")
    iLinesLength := aLines.Length()
    for deltaLines, LinesItem in aLines
    {
        sCurrentLine := aLines[(aLines.Length()+1)-deltaLines]
        if RegExMatch(sCurrentLine, "i)dmp\s*\(.*\)", match)
        {
            RegExMatch(sCurrentLine, "^(\d+)\:", match)
            iLineNumber := match1 + 0
            aVarnames := _dmpGetVarnames(sCurrentLine)
            break
        }
    }

    ; start dump
    _iCallCount++
    for deltaVar, VarItem in pVar
    {
        global gliRowCount := 0
        global glsRet=
        _dmp(VarItem, aVarnames[deltaVar])
        glsRet := LTrim(glsRet, "`r`n")
        glsRet := StrReplace(glsRet, "%", "``%")
        if (sDmpOutput!="")
            sDmpOutput .= "`r`n`r`n`r`n`r`n"
        sDmpOutput .= ">>>>> " . aVarnames[deltaVar]
        sDmpOutput .= "`r`n`r`n"
        sDmpOutput .= glsRet
        sDmpOutput .= "`r`n`r`n"
        if (gliRowCount > 1)
            sDmpOutput .= gliRowCount . " elements for " . aVarnames[deltaVar] . " "
        sDmpOutput .= "<<<<<"
    }

    pidCurrent := DllCall("GetCurrentProcessId")

    sScriptPopup =
    (ltrim join`r`n
        #NoEnv

        sDmpOutput=
        `(join``r``n
            %sDmpOutput%
        `)

        OnMessage(0x0111, "On_EN_SETFOCUS")
        OnMessage(0x100, "OnKeyDown")

        Gui +AlwaysOnTop +ToolWindow +Resize +MinSize400x300
        Gui, Add, Edit, +Readonly w780 h570 vedit, `% sDmpOutput
        Gui, Add, Button, xm w100 y+10 gButtonContinue vButtonContinue, Continue
        Gui, Add, Button, xp+120 yp+0 w100 gButtonReload vButtonReload, Reload script
        Gui, Add, Button, xp+120 yp+0 w100 gButtonKill vButtonKill, Kill script

        gosub Guisize

        Gui, Show, Center w800 h600, call #%_iCallCount% in line %iLineNumber% - dmp 1.25
        Return

        Guisize:
            glsEditWidth := "w" . A_GuiWidth - 20
            glsEditHeight := "h" . A_GuiHeight - 50
            GuiControl, Move, edit, `% glsEditWidth . " " . glsEditHeight
            GuiControl, Move, ButtonContinue, `%  "y" . (A_GuiHeight - 30)
            GuiControl, Move, ButtonReload, `%  "y" . (A_GuiHeight - 30)
            GuiControl, Move, ButtonKill, `%  "y" . (A_GuiHeight - 30)
        return

        ButtonReload:
            process, close, `% %pidCurrent%
            Sleep, 120
            Run, %A_ScriptFullPath%
            ExitApp
        return

        ButtonKill:
            process, close, `% %pidCurrent%
        GUIEscape:
        GuiClose:
        ButtonContinue:
        ExitApp

        On_EN_SETFOCUS(wParam, lParam) {
           Static EM_SETSEL   := 0x00B1
           Static EN_SETFOCUS := 0x0100
           Critical
           If ((wParam >> 16) = EN_SETFOCUS) {
              DllCall("User32.dll\HideCaret", "Ptr", lParam)
              PostMessage, `%EM_SETSEL`%, -1, 0, , ahk_id `%lParam`%
           }
        }

        OnKeyDown(wParam)
        {
            if (wParam = 13)
                ExitApp
        }
    )

    pidScriptPopup := _dmpDynaRun(sScriptPopup)
    Process, WaitClose, %pidScriptPopup%
}

_dmp(pVar, _psPrefix, _piLevel:=0) {
    global gliRowCount
    global glsRet

    if (IsObject(pVar))
    {
        if (_dmpArrayEmpty(pVar))
        {
            gliRowCount++
            glsRet .= "`r`n" . _psPrefix . " := []"
        }
        else
        {
            for deltaVar, VarItem in pVar
            {
                _dmp(VarItem, _psPrefix . "[" . _dmpGetQuotes(deltaVar) . "]", _piLevel + 1)
            }
        }
    }
    else
    {
        gliRowCount++
        glsRet .= "`r`n" . _psPrefix . " := " . _dmpGetQuotes(pVar)
    }
}

_dmpArrayEmpty(paArray) {
    for deltaArray, ArrayItem in paArray
        return false
    return true
}

_dmpGetQuotes(pVar) {
    sRet=
    if !IsObject(pVar)
    {
        if (ObjGetCapacity([pVar], 1) != "")
        {
            if (RegExMatch(pVar, "^\d+\.\d+$"))
                sRet := pVar
            else
            {
                if (StrSplit(pVar, "`n", "`r").Length() > 1)
                    sRet := "`n"
                sRet .= """" . pVar . """"
            }
        }
        else
        {
            if !pVar
                sRet := "0"
            else
                sRet := pVar
        }
    }
    return sRet
}

_dmpRecursiveBracketNeutralizer(ByRef psText, paBracket, piStartLevel:=0, piStartPos:=1, _piLevel:=0) {
	iPos := piStartPos
	while (iPos <= StrLen(psText))
	{
		if (SubStr(psText, iPos, StrLen(paBracket[1]))==paBracket[1])
		{
			iPos := _dmpRecursiveBracketNeutralizer(psText, paBracket, piStartLevel, iPos+1, _piLevel+1)
		}
		else if (SubStr(psText, iPos, StrLen(paBracket[2]))==paBracket[2])
		{
			if (piStartLevel<_piLevel)
			{
				; do replacement
				iDiff:=iPos+StrLen(paBracket[2])-piStartPos
				sReplacement=
				loop, % (iDiff+1)
					sReplacement .= "*"
				psText:=_dmpReplaceSE(psText, piStartPos-1, (iPos-1)+StrLen(paBracket[2]), sReplacement)

				; calculate difference between old found string and replacement for iPos reset
				iPos+=(StrLen(sReplacement)-iDiff)
			}
			return iPos++
		}
		else
			iPos++
	}
}

; *** string functions

_dmpStringSplitPositions(psText, psNeedle, piStartPos:=1) {
	aRet := []
	if (psText != "")
	{
		iStartPos := piStartPos
		while (iPos := InStr(psText, psNeedle, false, iStartPos))
		{
			aEl := []
			aEl["iStartPos"] := iStartPos
			aEl["iEndPos"] := iPos-1
			aRet.Push(aEl)
			iStartPos := iPos + StrLen(psNeedle)
		}
		aEl := []
		aEl["iStartPos"] := iStartPos
		aEl["iEndPos"] := StrLen(psText)
		aRet.Push(aEl)
	}
	return aRet
}

_dmpReplaceSE(psText, piPosStart, piPosEnd:="", psReplacement:="") {
	if (psText!="")
	{
		piPosEnd := piPosEnd != "" ? piPosEnd : StrLen(psText)
		sBefore := SubStr(psText, 1, piPosStart-1)
		sAfter := SubStr(psText, piPosEnd+1, StrLen(psText))
		return sBefore . psReplacement . sAfter
	}
	else
	{
		return psReplacement
	}
}

_dmpExtractSE(psText, piPosStart, piPosEnd:="") {
	if (psText != "")
	{
		piPosEnd := piPosEnd != "" ? piPosEnd : StrLen(psText)
		return SubStr(psText, piPosStart, piPosEnd-(piPosStart-1))
	}
}

_dmpGetVarContent(psText) {
	sRet=
	if RegExMatch(psText, "i)dmp\s*\((.*)\)", match)
	{
		sRet := match1
	}
	return sRet
}

_dmpGetVarnames(psText) {
	aRet := []
	if (psText!="")
	{
		sTextSaved := psText

		_dmpRecursiveBracketNeutralizer(psText, ["{", "}"])
		_dmpRecursiveBracketNeutralizer(psText, ["[", "]"])
		_dmpRecursiveBracketNeutralizer(psText, ["(", ")"], 1)

		sVarContent := _dmpGetVarContent(psText)
		aVarContentPositions := _dmpStringSplitPositions(sVarContent, ",")
		sVarContentSaved := _dmpGetVarContent(sTextSaved)
		for deltaVarContentPositions, VarContentPositionsItem in aVarContentPositions
		{
			sFound := _dmpExtractSE(sVarContentSaved, VarContentPositionsItem["iStartPos"], VarContentPositionsItem["iEndPos"])
			sFound=%sFound%
			aRet.Push(sFound)
		}
	}
	return aRet
}

; *** external functions

_dmpDynaRun(s, pn:="", pr:=""){ ; s=Script,pn=PipeName,n:=,pr=Parameters,p1+p2=hPipes,P=PID
   static AhkPath:="""" A_AhkPath """" (A_IsCompiled||(A_IsDll&&DllCall(A_AhkPath "\ahkgetvar","Str","A_IsCompiled"))?" /E":"") " """
   if (-1=p1 := DllCall("CreateNamedPipe","str",pf:="\\.\pipe\" (pn!=""?pn:"AHK" A_TickCount),"uint",2,"uint",0,"uint",255,"uint",0,"uint",0,"Ptr",0,"Ptr",0))
      || (-1=p2 := DllCall("CreateNamedPipe","str",pf,"uint",2,"uint",0,"uint",255,"uint",0,"uint",0,"Ptr",0,"Ptr",0))
      Return 0
   ; allow compiled executable to execute dynamic scripts. Requires AHK_H
   Run, % AhkPath pf """ " pr,,UseErrorLevel HIDE, P
   If ErrorLevel
      return 0,DllCall("CloseHandle","Ptr",p1),DllCall("CloseHandle","Ptr",p2)
   DllCall("ConnectNamedPipe","Ptr",p1,"Ptr",0),DllCall("CloseHandle","Ptr",p1),DllCall("ConnectNamedPipe","Ptr",p2,"Ptr",0)
   if !DllCall("WriteFile","Ptr",p2,"Wstr",s:=(A_IsUnicode?chr(0xfeff):"") s,"UInt",StrLen(s)*(A_IsUnicode ? 2 : 1)+(A_IsUnicode?4:6),"uint*",0,"Ptr",0)
      P:=0
   DllCall("CloseHandle","Ptr",p2)
   Return P
}

_dmpListLines() {
    static hwndEdit, pSFW, pSW, bkpSFW, bkpSW
    ListLines Off

    if !hwndEdit
    {
        ; Retrieve the handle of our main window's Edit control:
        dhw := A_DetectHiddenWindows
        DetectHiddenWindows, On
        Process, Exist
        ControlGet, hwndEdit, Hwnd,, Edit1, ahk_class AutoHotkey ahk_pid %ErrorLevel%
        DetectHiddenWindows, %dhw%

        astr := A_IsUnicode ? "astr" : "str"
        hmod := DllCall("GetModuleHandle", "str", "user32.dll")
        ; Get addresses of these two functions:
        pSFW := DllCall("GetProcAddress", "uint", hmod, astr, "SetForegroundWindow")
        pSW := DllCall("GetProcAddress", "uint", hmod, astr, "ShowWindow")
        ; Make them writable:
        DllCall("VirtualProtect", "uint", pSFW, "uint", 8, "uint", 0x40, "uint*", 0)
        DllCall("VirtualProtect", "uint", pSW, "uint", 8, "uint", 0x40, "uint*", 0)
        ; Save initial values of the first 8 bytes:
        bkpSFW := NumGet(pSFW+0, 0, "int64")
        bkpSW := NumGet(pSW+0, 0, "int64")
    }

    ; Overwrite SetForegroundWindow() and ShowWindow() temporarily:
    NumPut(0x0004C200000001B8, pSFW+0, 0, "int64")  ; return TRUE
    NumPut(0x0008C200000001B8, pSW+0, 0, "int64")   ; return TRUE

    ; Dump ListLines into hidden AutoHotkey main window:
    ListLines

    ; Restore SetForegroundWindow() and ShowWindow():
    NumPut(bkpSFW, pSFW+0, 0, "int64")
    NumPut(bkpSW, pSW+0, 0, "int64")

    ; Retrieve ListLines text and strip out some unnecessary stuff:
    ControlGetText, ListLinesText,, ahk_id %hwndEdit%
    RegExMatch(ListLinesText, ".*`r`n`r`n\K[\s\S]*(?=`r`n`r`n.*$)", ListLinesText)
    StringReplace, ListLinesText, ListLinesText, `r`n, `n, All

    ListLines On
    return ListLinesText  ; This line appears in ListLines if we're called more than once.
}

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 21 Aug 2020, 07:45
by gregster
Joe Glines has a copy of it: https://www.the-automator.com/download/dmp.ahk
Edit: Oh, I see you already found it :thumbup:

Code: Select all

; http://the-automator.com/download/dmp.ahk
; dmp Version 1.25
; By ObiWanKenobi
; Visit guresicpark.de for dmp updates 
; May 8, 2015  
;~ http://guresicpark.de/autohotkey/dmp

dmp(pVar*)
{
    static _iCallCount := 0
    
    ; set x as basic variable name
    aVarnames := ["x"]
    
    ; now search variable name
	sLines := _dmpListLines()
    aLines := StrSplit(sLines, "`n", "`r")
    iLinesLength := aLines.Length()
    for deltaLines, LinesItem in aLines
    {
        sCurrentLine := aLines[(aLines.Length()+1)-deltaLines]
        if RegExMatch(sCurrentLine, "i)dmp\s*\(.*\)", match)
        {
            RegExMatch(sCurrentLine, "^(\d+)\:", match)
            iLineNumber := match1 + 0
            aVarnames := _dmpGetVarnames(sCurrentLine)
            break
        }
    }
    
    ; start dump
    _iCallCount++
    for deltaVar, VarItem in pVar
    {
        global gliRowCount := 0
        global glsRet=
        _dmp(VarItem, aVarnames[deltaVar])
        glsRet := LTrim(glsRet, "`r`n")
        glsRet := StrReplace(glsRet, "%", "``%")
        if (sDmpOutput!="")
            sDmpOutput .= "`r`n`r`n`r`n`r`n"
        sDmpOutput .= ">>>>> " . aVarnames[deltaVar]
        sDmpOutput .= "`r`n`r`n"
        sDmpOutput .= glsRet
        sDmpOutput .= "`r`n`r`n"
        if (gliRowCount > 1)
            sDmpOutput .= gliRowCount . " elements for " . aVarnames[deltaVar] . " "
        sDmpOutput .= "<<<<<"
    }
    
    pidCurrent := DllCall("GetCurrentProcessId")
    
    sScriptPopup =
    (ltrim join`r`n
        #NoEnv
        
        sDmpOutput=
        `(join``r``n
            %sDmpOutput%
        `)

        OnMessage(0x0111, "On_EN_SETFOCUS")
        OnMessage(0x100, "OnKeyDown")
        
        Gui +AlwaysOnTop +ToolWindow +Resize +MinSize400x300
        Gui, Add, Edit, +Readonly w780 h570 vedit, `% sDmpOutput
        Gui, Add, Button, xm w100 y+10 gButtonContinue vButtonContinue, Continue
        Gui, Add, Button, xp+120 yp+0 w100 gButtonReload vButtonReload, Reload script
        Gui, Add, Button, xp+120 yp+0 w100 gButtonKill vButtonKill, Kill script
        
        gosub Guisize
        
        Gui, Show, Center w800 h600, call #%_iCallCount% in line %iLineNumber% - dmp 1.25
        Return
        
        Guisize:
            glsEditWidth := "w" . A_GuiWidth - 20
            glsEditHeight := "h" . A_GuiHeight - 50
            GuiControl, Move, edit, `% glsEditWidth . " " . glsEditHeight
            GuiControl, Move, ButtonContinue, `%  "y" . (A_GuiHeight - 30)	
            GuiControl, Move, ButtonReload, `%  "y" . (A_GuiHeight - 30)	
            GuiControl, Move, ButtonKill, `%  "y" . (A_GuiHeight - 30)	
        return
        
        ButtonReload:
            process, close, `% %pidCurrent%
            Sleep, 120
            Run, %A_ScriptFullPath%
            ExitApp
        return
        
        ButtonKill:
            process, close, `% %pidCurrent%
        GUIEscape:
        GuiClose:
        ButtonContinue:
        ExitApp
        
        On_EN_SETFOCUS(wParam, lParam) {
           Static EM_SETSEL   := 0x00B1
           Static EN_SETFOCUS := 0x0100
           Critical
           If ((wParam >> 16) = EN_SETFOCUS) {
              DllCall("User32.dll\HideCaret", "Ptr", lParam)
              PostMessage, `%EM_SETSEL`%, -1, 0, , ahk_id `%lParam`%
           }
        }
        
        OnKeyDown(wParam)
        {
            if (wParam = 13)
                ExitApp
        }
    )
    
    pidScriptPopup := _dmpDynaRun(sScriptPopup)
    Process, WaitClose, %pidScriptPopup%
}

_dmp(pVar, _psPrefix, _piLevel:=0)
{
    global gliRowCount
    global glsRet
    
    if (IsObject(pVar))
    {
        if (_dmpArrayEmpty(pVar))
        {
            gliRowCount++
            glsRet .= "`r`n" . _psPrefix . " := []"
        }
        else
        {
            for deltaVar, VarItem in pVar
            {
                _dmp(VarItem, _psPrefix . "[" . _dmpGetQuotes(deltaVar) . "]", _piLevel + 1)
            }
        }
    }
    else
    {
        gliRowCount++
        glsRet .= "`r`n" . _psPrefix . " := " . _dmpGetQuotes(pVar)
    }
}

_dmpArrayEmpty(paArray)
{
    for deltaArray, ArrayItem in paArray
        return false
    return true
}

_dmpGetQuotes(pVar)
{
    sRet=
    if !IsObject(pVar)
    {
        if (ObjGetCapacity([pVar], 1) != "")
        {
            if (RegExMatch(pVar, "^\d+\.\d+$"))
                sRet := pVar
            else
            {
                if (StrSplit(pVar, "`n", "`r").Length() > 1)
                    sRet := "`n"
                sRet .= """" . pVar . """"
            }
        }
        else
        {
            if !pVar
                sRet := "0"
            else
                sRet := pVar
        }
    }
    return sRet
}

_dmpRecursiveBracketNeutralizer(ByRef psText, paBracket, piStartLevel:=0, piStartPos:=1, _piLevel:=0)
{
	iPos := piStartPos
	while (iPos <= StrLen(psText))
	{
		if (SubStr(psText, iPos, StrLen(paBracket[1]))==paBracket[1])
		{
			iPos := _dmpRecursiveBracketNeutralizer(psText, paBracket, piStartLevel, iPos+1, _piLevel+1)
		}
		else if (SubStr(psText, iPos, StrLen(paBracket[2]))==paBracket[2])
		{
			if (piStartLevel<_piLevel)
			{
				; do replacement
				iDiff:=iPos+StrLen(paBracket[2])-piStartPos
				sReplacement=
				loop, % (iDiff+1)
					sReplacement .= "*"
				psText:=_dmpReplaceSE(psText, piStartPos-1, (iPos-1)+StrLen(paBracket[2]), sReplacement)
				
				; calculate difference between old found string and replacement for iPos reset
				iPos+=(StrLen(sReplacement)-iDiff)
			}
			return iPos++
		}
		else
			iPos++
	}
}

; *** string functions

_dmpStringSplitPositions(psText, psNeedle, piStartPos:=1)
{
	aRet := []
	if (psText != "")
	{
		iStartPos := piStartPos
		while (iPos := InStr(psText, psNeedle, false, iStartPos))
		{
			aEl := []
			aEl["iStartPos"] := iStartPos
			aEl["iEndPos"] := iPos-1
			aRet.Push(aEl)
			iStartPos := iPos + StrLen(psNeedle)
		}
		aEl := []
		aEl["iStartPos"] := iStartPos
		aEl["iEndPos"] := StrLen(psText)
		aRet.Push(aEl)
	}
	return aRet
}

_dmpReplaceSE(psText, piPosStart, piPosEnd:="", psReplacement:="")
{
	if (psText!="")
	{
		piPosEnd := piPosEnd != "" ? piPosEnd : StrLen(psText)
		sBefore := SubStr(psText, 1, piPosStart-1)
		sAfter := SubStr(psText, piPosEnd+1, StrLen(psText))
		return sBefore . psReplacement . sAfter
	}
	else
	{
		return psReplacement
	}	
}

_dmpExtractSE(psText, piPosStart, piPosEnd:="")
{
	if (psText != "")
	{
		piPosEnd := piPosEnd != "" ? piPosEnd : StrLen(psText)
		return SubStr(psText, piPosStart, piPosEnd-(piPosStart-1))
	}
}

_dmpGetVarContent(psText)
{
	sRet=	
	if RegExMatch(psText, "i)dmp\s*\((.*)\)", match)
	{
		sRet := match1
	}
	return sRet
}

_dmpGetVarnames(psText)
{
	aRet := []
	if (psText!="")
	{
		sTextSaved := psText
		
		_dmpRecursiveBracketNeutralizer(psText, ["{", "}"])
		_dmpRecursiveBracketNeutralizer(psText, ["[", "]"])
		_dmpRecursiveBracketNeutralizer(psText, ["(", ")"], 1)

		sVarContent := _dmpGetVarContent(psText)
		aVarContentPositions := _dmpStringSplitPositions(sVarContent, ",")
		sVarContentSaved := _dmpGetVarContent(sTextSaved)
		for deltaVarContentPositions, VarContentPositionsItem in aVarContentPositions
		{
			sFound := _dmpExtractSE(sVarContentSaved, VarContentPositionsItem["iStartPos"], VarContentPositionsItem["iEndPos"])
			sFound=%sFound%
			aRet.Push(sFound)
		}
	}
	return aRet
}

; *** external functions

_dmpDynaRun(s, pn:="", pr:=""){ ; s=Script,pn=PipeName,n:=,pr=Parameters,p1+p2=hPipes,P=PID
   static AhkPath:="""" A_AhkPath """" (A_IsCompiled||(A_IsDll&&DllCall(A_AhkPath "\ahkgetvar","Str","A_IsCompiled"))?" /E":"") " """
   if (-1=p1 := DllCall("CreateNamedPipe","str",pf:="\\.\pipe\" (pn!=""?pn:"AHK" A_TickCount),"uint",2,"uint",0,"uint",255,"uint",0,"uint",0,"Ptr",0,"Ptr",0))
      || (-1=p2 := DllCall("CreateNamedPipe","str",pf,"uint",2,"uint",0,"uint",255,"uint",0,"uint",0,"Ptr",0,"Ptr",0))
      Return 0
   ; allow compiled executable to execute dynamic scripts. Requires AHK_H
   Run, % AhkPath pf """ " pr,,UseErrorLevel HIDE, P
   If ErrorLevel
      return 0,DllCall("CloseHandle","Ptr",p1),DllCall("CloseHandle","Ptr",p2)
   DllCall("ConnectNamedPipe","Ptr",p1,"Ptr",0),DllCall("CloseHandle","Ptr",p1),DllCall("ConnectNamedPipe","Ptr",p2,"Ptr",0)
   if !DllCall("WriteFile","Ptr",p2,"Wstr",s:=(A_IsUnicode?chr(0xfeff):"") s,"UInt",StrLen(s)*(A_IsUnicode ? 2 : 1)+(A_IsUnicode?4:6),"uint*",0,"Ptr",0)
      P:=0
   DllCall("CloseHandle","Ptr",p2)
   Return P
}

_dmpListLines()
{
    static hwndEdit, pSFW, pSW, bkpSFW, bkpSW
    ListLines Off
    
    if !hwndEdit
    {
        ; Retrieve the handle of our main window's Edit control:
        dhw := A_DetectHiddenWindows
        DetectHiddenWindows, On
        Process, Exist
        ControlGet, hwndEdit, Hwnd,, Edit1, ahk_class AutoHotkey ahk_pid %ErrorLevel%
        DetectHiddenWindows, %dhw%
        
        astr := A_IsUnicode ? "astr" : "str"
        hmod := DllCall("GetModuleHandle", "str", "user32.dll")
        ; Get addresses of these two functions:
        pSFW := DllCall("GetProcAddress", "uint", hmod, astr, "SetForegroundWindow")
        pSW := DllCall("GetProcAddress", "uint", hmod, astr, "ShowWindow")
        ; Make them writable:
        DllCall("VirtualProtect", "uint", pSFW, "uint", 8, "uint", 0x40, "uint*", 0)
        DllCall("VirtualProtect", "uint", pSW, "uint", 8, "uint", 0x40, "uint*", 0)
        ; Save initial values of the first 8 bytes:
        bkpSFW := NumGet(pSFW+0, 0, "int64")
        bkpSW := NumGet(pSW+0, 0, "int64")
    }
    
    ; Overwrite SetForegroundWindow() and ShowWindow() temporarily:
    NumPut(0x0004C200000001B8, pSFW+0, 0, "int64")  ; return TRUE
    NumPut(0x0008C200000001B8, pSW+0, 0, "int64")   ; return TRUE
    
    ; Dump ListLines into hidden AutoHotkey main window:
    ListLines
    
    ; Restore SetForegroundWindow() and ShowWindow():
    NumPut(bkpSFW, pSFW+0, 0, "int64")
    NumPut(bkpSW, pSW+0, 0, "int64")
    
    ; Retrieve ListLines text and strip out some unnecessary stuff:
    ControlGetText, ListLinesText,, ahk_id %hwndEdit%
    RegExMatch(ListLinesText, ".*`r`n`r`n\K[\s\S]*(?=`r`n`r`n.*$)", ListLinesText)
    StringReplace, ListLinesText, ListLinesText, `r`n, `n, All

    ListLines On
    return ListLinesText  ; This line appears in ListLines if we're called more than once.
}

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 14 Feb 2021, 17:45
by mstrauss2021
I like the idea of dmp.ahk.

However, when I put it just before the last return of my macro and run my macro, I can't find where the dmp file is saved. I examined dmp.ahk but still cant find where the output is saved.

Anyone know?

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 15 Feb 2021, 09:52
by swagfag
thats because nothing is being saved anywhere, it just displays information in an edit-control.
if u want to save something, look at sDmpOutput inside dmp() and FileOpen(..., "w").Write(...) it to a file or something

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 16 May 2021, 07:54
by swagfag
guest3456 wrote:
02 May 2015, 11:35
It looks like you are overwriting the memory addresses so SetForegroundWindow and ShowWindow won't be called, so the end user doesn't see the ListLines window.

But what are those values that you are using? Where did you get them and what do they represent?

Code: Select all

; Overwrite SetForegroundWindow() and ShowWindow() temporarily:
    NumPut(0x0004C200000001B8, pSFW+0, 0, "int64")  ; return TRUE
    NumPut(0x0008C200000001B8, pSW+0, 0, "int64")   ; return TRUE
edit/
whoops further searching reveals that Lexikos originally wrote this function, maybe he can chime in
no, the functions are still called - theyre just stubbed to return true and do nothing else(which is why u dont end up seeing their actual effects, ie a window showing up)
the numbers are what u get when u compile:

Code: Select all

BOOL SetForegroundWindow(HWND hWnd) { return TRUE; }
BOOL ShowWindow(HWND hWnd, int  nCmdShow) { return TRUE; }
to bytecode

but u probably knew all that by now, other ppl might not

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 17 May 2021, 02:05
by guest3456
swagfag wrote:
16 May 2021, 07:54
no, the functions are still called - theyre just stubbed to return true and do nothing else(which is why u dont end up seeing their actual effects, ie a window showing up)
the numbers are what u get when u compile:

Code: Select all

BOOL SetForegroundWindow(HWND hWnd) { return TRUE; }
BOOL ShowWindow(HWND hWnd, int  nCmdShow) { return TRUE; }
to bytecode

but u probably knew all that by now, other ppl might not
i did not know that, thanks, very interesting. does that overwrite the calls to those functions only within the script itself? or also for all external windows too? i'm guessing its only the ahk script because i thought you needed to hook a dll to accomplish the latter

Re: dmp() - Advanced debug function for AutoHotKey

Posted: 17 May 2021, 21:06
by swagfag