Debugx.au3

Last modified:   Thursday, 5 June 2008

;*******************************************************************************
;
;   Function List
;         _DebugArrayDisplay()
;         _DebugLastErrorMessage()
;         _DebugOut()
;         _DebugSetup()
;
;*******************************************************************************

#include-once

;===============================================================================
; Name...........: _DebugArrayDisplay
; Description ...: Displays given 1D or 2D array array in a listview.
; Syntax.........: _ArrayDisplay(Const ByRef $avArray[, $sTitle = "Array: ListView Display"[, $iItemLimit = -1[, $iTranspose = 0[, $sSeparator = ""[, $sReplace = "|"]]]]])
; Parameters ....: $avArray    - Array to display
;                  $sTitle     - [optional] Title to use for window
;                  $iItemLimit - [optional] Maximum number of listview items (rows) to show
;                  $iTranspose - [optional] If set differently than default, will transpose the array if 2D
;                  $sSeparator - [optional] Change Opt("GUIDataSeparatorChar") on-the-fly
;                  $sReplace   - [optional] String to replace any occurrence of $sSeparator with in each array element
; Return values .: Success - 1
;                  Failure - 0, sets @error:
;                  |1 - $avArray is not an array
;                  |2 - $avArray has too many dimensions (only up to 2D supported)
; Author ........: randallc, Ultima
; Modified.......: Gary Frost (gafrost) / Ultima: modified to be self-contained (no longer depends on "GUIListView.au3")
; Remarks .......:
; Related .......:
; Link ..........;
; Example .......; Yes
;===============================================================================
Func _DebugArrayDisplay(Const ByRef $avArray, $sTitle = "Array: ListView Display", $iItemLimit = -1, $iTranspose = 0, $sSeparator = "", $sReplace = "|")
   If Not IsArray($avArray) Then Return SetError(1, 0, 0)

   ; Dimension checking
   Local $iDimension = UBound($avArray, 0), $iUBound = UBound($avArray, 1) - 1, $iSubMax = UBound($avArray, 2) - 1
   If $iDimension > 2 Then Return SetError(2, 0, 0)

   ; Separator handling
   If $sSeparator = "" Then $sSeparator = Chr(1)

   ; Declare variables
   Local $i, $j, $vTmp, $aItem, $avArrayText, $sHeader = "Row", $iBuffer = 64
   Local $iColLimit = 250, $iLVIAddUDFThreshold = 4000, $iWidth = 640, $iHeight = 480
   Local $iOnEventMode = Opt("GUIOnEventMode", 0), $sDataSeparatorChar = Opt("GUIDataSeparatorChar", $sSeparator)

   ; Swap dimensions if transposing
   If $iSubMax < 0 Then $iSubMax = 0
   If $iTranspose Then
      $vTmp = $iUBound
      $iUBound = $iSubMax
      $iSubMax = $vTmp
   EndIf

   ; Set limits for dimensions
   If $iSubMax > $iColLimit Then $iSubMax = $iColLimit
   If $iItemLimit = 1 Then $iItemLimit = $iLVIAddUDFThreshold
   If $iItemLimit < 1 Then $iItemLimit = $iUBound
   If $iUBound > $iItemLimit Then $iUBound = $iItemLimit
   If $iLVIAddUDFThreshold > $iUBound Then $iLVIAddUDFThreshold = $iUBound

   ; Set header up
   For $i = 0 To $iSubMax
      $sHeader &= $sSeparator & "Col " & $i
   Next

   ; Convert array into text for listview
   Local $avArrayText[$iUBound + 1]
   For $i = 0 To $iUBound
      $avArrayText[$i] = "[" & $i & "]"
      For $j = 0 To $iSubMax
         ; Get current item
         If $iDimension = 1 Then
            If $iTranspose Then
               $vTmp = $avArray[$j]
            Else
               $vTmp = $avArray[$i]
            EndIf
         Else
            If $iTranspose Then
               $vTmp = $avArray[$j][$i]
            Else
               $vTmp = $avArray[$i][$j]
            EndIf
         EndIf

         ; Add to text array
         $vTmp = StringReplace($vTmp, $sSeparator, $sReplace, 0, 1)
         $avArrayText[$i] &= $sSeparator & $vTmp

         ; Set max buffer size
         $vTmp = StringLen($vTmp)
         If $vTmp > $iBuffer Then $iBuffer = $vTmp
      Next
   Next
   $iBuffer += 1

   ; GUI Constants
   Local Const $_ARRAYCONSTANT_GUI_DOCKBORDERS = 0x66
   Local Const $_ARRAYCONSTANT_GUI_DOCKBOTTOM = 0x40
   Local Const $_ARRAYCONSTANT_GUI_DOCKHEIGHT = 0x0200
   Local Const $_ARRAYCONSTANT_GUI_DOCKLEFT = 0x2
   Local Const $_ARRAYCONSTANT_GUI_DOCKRIGHT = 0x4
   Local Const $_ARRAYCONSTANT_GUI_EVENT_CLOSE = -3
   Local Const $_ARRAYCONSTANT_LVIF_PARAM = 0x4
   Local Const $_ARRAYCONSTANT_LVIF_TEXT = 0x1
   Local Const $_ARRAYCONSTANT_LVM_GETCOLUMNWIDTH = (0x1000 + 29)
   Local Const $_ARRAYCONSTANT_LVM_GETITEMCOUNT = (0x1000 + 4)
   Local Const $_ARRAYCONSTANT_LVM_GETITEMSTATE = (0x1000 + 44)
   Local Const $_ARRAYCONSTANT_LVM_INSERTITEMA = (0x1000 + 7)
   Local Const $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE = (0x1000 + 54)
   Local Const $_ARRAYCONSTANT_LVM_SETITEMA = (0x1000 + 6)
   Local Const $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT = 0x20
   Local Const $_ARRAYCONSTANT_LVS_EX_GRIDLINES = 0x1
   Local Const $_ARRAYCONSTANT_LVS_SHOWSELALWAYS = 0x8
   Local Const $_ARRAYCONSTANT_WS_EX_CLIENTEDGE = 0x0200
   Local Const $_ARRAYCONSTANT_WS_MAXIMIZEBOX = 0x00010000
   Local Const $_ARRAYCONSTANT_WS_MINIMIZEBOX = 0x00020000
   Local Const $_ARRAYCONSTANT_WS_SIZEBOX = 0x00040000
   Local Const $_ARRAYCONSTANT_tagLVITEM = "int Mask;int Item;int SubItem;int State;int StateMask;ptr Text;int TextMax;int Image;int Param;int Indent;int GroupID;int Columns;ptr pColumns"

   Local $iAddMask = BitOR($_ARRAYCONSTANT_LVIF_TEXT, $_ARRAYCONSTANT_LVIF_PARAM)
   Local $tBuffer = DllStructCreate("char Text[" & $iBuffer & "]"), $pBuffer = DllStructGetPtr($tBuffer)
   Local $tItem = DllStructCreate($_ARRAYCONSTANT_tagLVITEM), $pItem = DllStructGetPtr($tItem)
   DllStructSetData($tItem, "Param", 0)
   DllStructSetData($tItem, "Text", $pBuffer)
   DllStructSetData($tItem, "TextMax", $iBuffer)

   ; Set interface up
   Local $hGUI = GUICreate($sTitle, $iWidth, $iHeight, Default, Default, BitOR($_ARRAYCONSTANT_WS_SIZEBOX, $_ARRAYCONSTANT_WS_MINIMIZEBOX, $_ARRAYCONSTANT_WS_MAXIMIZEBOX))
   Local $aiGUISize = WinGetClientSize($hGUI)
   Local $hListView = GUICtrlCreateListView($sHeader, 0, 0, $aiGUISize[0], $aiGUISize[1] - 26, $_ARRAYCONSTANT_LVS_SHOWSELALWAYS)
   Local $hCopy = GUICtrlCreateButton("Copy Selected", 3, $aiGUISize[1] - 23, $aiGUISize[0] - 6, 20)
   GUICtrlSetResizing($hListView, $_ARRAYCONSTANT_GUI_DOCKBORDERS)
   GUICtrlSetResizing($hCopy, $_ARRAYCONSTANT_GUI_DOCKLEFT + $_ARRAYCONSTANT_GUI_DOCKRIGHT + $_ARRAYCONSTANT_GUI_DOCKBOTTOM + $_ARRAYCONSTANT_GUI_DOCKHEIGHT)
   GUICtrlSendMsg($hListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_LVS_EX_GRIDLINES, $_ARRAYCONSTANT_LVS_EX_GRIDLINES)
   GUICtrlSendMsg($hListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT, $_ARRAYCONSTANT_LVS_EX_FULLROWSELECT)
   GUICtrlSendMsg($hListView, $_ARRAYCONSTANT_LVM_SETEXTENDEDLISTVIEWSTYLE, $_ARRAYCONSTANT_WS_EX_CLIENTEDGE, $_ARRAYCONSTANT_WS_EX_CLIENTEDGE)

   ; Fill listview
   For $i = 0 To $iLVIAddUDFThreshold
      GUICtrlCreateListViewItem($avArrayText[$i], $hListView)
   Next
   For $i = ($iLVIAddUDFThreshold + 1) To $iUBound
      $aItem = StringSplit($avArrayText[$i], $sSeparator)
      DllStructSetData($tBuffer, "Text", $aItem[1])

      ; Add listview item
      DllStructSetData($tItem, "Item", $i)
      DllStructSetData($tItem, "SubItem", 0)
      DllStructSetData($tItem, "Mask", $iAddMask)
      GUICtrlSendMsg($hListView, $_ARRAYCONSTANT_LVM_INSERTITEMA, 0, $pItem)

      ; Set listview subitem text
      DllStructSetData($tItem, "Mask", $_ARRAYCONSTANT_LVIF_TEXT)
      For $j = 2 To $aItem[0]
         DllStructSetData($tBuffer, "Text", $aItem[$j])
         DllStructSetData($tItem, "SubItem", $j - 1)
         GUICtrlSendMsg($hListView, $_ARRAYCONSTANT_LVM_SETITEMA, 0, $pItem)
      Next
   Next
   
   ; ajust window width
   $iWidth = 0
   For $i = 0 to $iSubMax+1
      $iWidth += GUICtrlSendMsg($hListView, $_ARRAYCONSTANT_LVM_GETCOLUMNWIDTH, $i, 0)
   Next
   If $iWidth<250 Then $iWidth = 230
   WinMove($hGUI,"", Default,Default, $iWidth+20)

   ; Show dialog
   GUISetState(@SW_SHOW, $hGUI)

   While 1
      Switch GUIGetMsg()
         Case $_ARRAYCONSTANT_GUI_EVENT_CLOSE
            ExitLoop

         Case $hCopy
            Local $sClip = ""

            ; Get selected indices [ _GUICtrlListView_GetSelectedIndices($hListView, True) ]
            Local $aiCurItems[1] = [0]
            For $i = 0 To GUICtrlSendMsg($hListView, $_ARRAYCONSTANT_LVM_GETITEMCOUNT, 0, 0)
               If GUICtrlSendMsg($hListView, $_ARRAYCONSTANT_LVM_GETITEMSTATE, $i, 0x2) Then
                  $aiCurItems[0] += 1
                  ReDim $aiCurItems[$aiCurItems[0] + 1]
                  $aiCurItems[$aiCurItems[0]] = $i
               EndIf
            Next

            ; Generate clipboard text
            If Not $aiCurItems[0] Then
               For $sItem In $avArrayText
                  $sClip &= $sItem & @CRLF
               Next
            Else
               For $i = 1 To UBound($aiCurItems) - 1
                  $sClip &= $avArrayText[$aiCurItems[$i]] & @CRLF
               Next
            EndIf
            ClipPut($sClip)
      EndSwitch
   WEnd
   GUIDelete($hGUI)

   Opt("GUIOnEventMode", $iOnEventMode)
   Opt("GUIDataSeparatorChar", $sDataSeparatorChar)

   Return 1
EndFunc   ;<===>_DebugArrayDisplay

;>> Private Functions <<

Func _DebugBugReportEnv($err=@error, $ext=@extended)
   Local $AutoItX64 = ""
   If @AutoItX64 then $AutoItX64 = " X64"
   Local $AutoItAnsi = " Ansi"
   If @AutoItUnicode then $AutoItAnsi = ""
   Local $Compiled = ""
   If @Compiled then $Compiled= " Compiled"
   Local $OsServicePack = ""
   If @OSServicePack Then $OsServicePack = "/" & @OSServicePack
   Return SetError($err, $ext, "Environment = " & @AutoItVersion & $AutoItX64 & $AutoItAnsi & _
   $Compiled & " under  " & @OSVersion & $OSServicePack & " " & @ProcessorArch)
EndFunc

;===============================================================================
; Name...........: _DebugLastErrorMessage()
; Description ...: Returns the last error message
; Syntax.........: 
; Parameters ....: 
; Return values .: Success - Last windows error
; Author ........: Zedna
; Modified.......: 
; Remarks .......:
;===============================================================================

;Run('calc2.exe')
;If @error Then MsgBox(16, "Error", "Run() produced and error:" & @CRLF & _GetLastErrorMessage ())

Func _DebugLastErrorMessage($DisplayMsgBox="")
    Local $ret,$s
    Local $p    = DllStructCreate("char[4096]")
    Local Const $FORMAT_MESSAGE_FROM_SYSTEM        = 0x00001000

    If @error Then Return ""

    $ret    = DllCall("Kernel32.dll","int","GetLastError")

    $ret    = DllCall("kernel32.dll","int","FormatMessage", _
                        "int",$FORMAT_MESSAGE_FROM_SYSTEM, _
                        "ptr",0, _
                        "int",$ret[0], _
                        "int",0, _
                        "ptr",DllStructGetPtr($p), _
                        "int",4096, _
                        "ptr",0)
    $s    = DllStructGetData($p,1)
    $p = 0
    If $DisplayMsgBox <> "" Then MsgBox(0,"_GetLastErrorMessage",$DisplayMsgBox & @CRLF & $s)
    return $s
EndFunc

;===============================================================================
; Name...........: _DebugOut
; Description ...: Outputs a string to the Notepad window setup by _DebugSetup.
; Syntax.........: _DebugOut(Const $sOutput, Const $bActivate)
; Parameters ....: $sOutput = The string (or other printable value) to be output to the Notepad window.
;                  $bActivate = (Optional) True/False flag that inidicates that the Notepad window should be 
;                  activated before sending characters.  This is needed if another window is activated during the main script.
; Return values .: Success - Returns 1.
;                  Failure - Returns 0 and Sets @Error:
;                  |0 - No error.
;                  |1 - $sOutput is an incompatible type.
;                  |2 - $bActivate is an incompatible type.
;                  |3 - _DebugSetup() did not run properly.  Make sure _DebugSetup() ran properly before calling this function.
;                  |4 - The Notepad window has been closed.  Output can not occur.
; Author ........: David Nuttall (Nutster)
; Modified.......:
; Remarks .......: Before calling this function, _DebugSetup must be called first to create the Notepad window.
; Related .......: _DebugSetup
; Link ..........;
; Example .......; Yes
;===============================================================================

Func _DebugOut(Const $sOutput, Const $bActivate = False, Const $curerr = @error, Const $curext = @extended)
   If IsNumber($sOutput) = 0 And IsString($sOutput) = 0 And IsBool($sOutput) = 0 Then
      Return SetError(1, 0, 0)   ; $sOutput can not be printed
   ElseIf IsBool($bActivate) = False And IsNumber($bActivate) = False Then
      Return SetError(2, 0, 0)   ; The $bActivate flag is set to an invalid type.  Must be able to convert to Bool.
   ElseIf IsHWnd($g_hWndDbg) = 0 Then
      Return SetError(3, 0, 0)   ; Window was not assigned.
   ElseIf WinExists($g_hWndDbg) = 0 Then
      Return SetError(4, 0, 0)   ; The Notepad window no longer exists
   Else
      If $bActivate Then WinActivate($g_hWndDbg)
      ControlCommand($g_hWndDbg, "", "Edit1", "EditPaste", String($sOutput) & @CRLF)
      Return SetError($curerr, $curext, 1)   ; Return @error and @extende as before calling _DebugOut()
   EndIf
EndFunc   ;==>_DebugOut

;===============================================================================
; Name...........: _DebugSetup
; Description ...: Sets up a debug session using a Microsoft Notepad window as the output target.
; Syntax.........: _DebugSetup(Const $sTitle)
; Parameters ....: $sTitle = (Optional) Title to be displayed on the Notepad window.  Default value is "Debug Info".
;                  $bBugReportInfos = (Optional) Display BugReport infos.  Default value is false.
; Return values .: Success - Returns 1.
;                  Failure - Returns 0 and Sets @Error:
;                  |0 - No error.
;                  |1 - $sTitle is an incompatable type.
;                  |2 - Another debug session is open.  Use it instead.
;                  |3 - Another Untitled MS-Notepad window is already open.  Save it or close it before continuing.
; Author ........: David Nuttall (Nutster)
; Modified.......: Jean-Paul Mesnage (jpm)
; Remarks .......: This must be called in your program before any _DebugOut() calls.
;                  Microsoft Notepad in the %PATH%
; Related .......: _DebugOut
;===============================================================================

Func _DebugSetup(Const $sTitle = "Debug Info", Const $bBugReportInfos = false)
   Local $pNotepad ; process ID of the Notepad started by this function
   
   If $g_hWndDbg = 0 And WinExists($sTitle) Then
         ; notepad started by an another script use it
         $g_hWndDbg = WinGetHandle($sTitle)
   EndIf
   If IsNumber($sTitle) = 0 And IsString($sTitle) = 0 And IsBool($sTitle) = 0 Then
      Return SetError(1, 0, 0)      ; Not any of the acceptable types.
   ElseIf IsHWnd($g_hWndDbg) Then
      ; Another session already started
      If WinExists($g_hWndDbg) Then
         Return SetError(2, 0, 0)   ; The session is still active
      Else 
         ; Reset the session and assign new hWnd.
      EndIf
   EndIf
   
   $pNotepad = Run("Notepad.exe")
   WinWait("[CLASS:Notepad]")
   $g_hWndDbg = WinGetHandle("[CLASS:Notepad]")
   If $pNotepad <> WinGetProcess($g_hWndDbg) Then
      Return SetError(3, 0, 0)
   EndIf

   WinActivate($g_hWndDbg)
   WinSetTitle($g_hWndDbg, "", String($sTitle))
   If $bBugReportInfos Then _DebugOut(_DebugBugReportEnv() & @CRLF)
   Return 1
EndFunc   ;==>_DebugSetup