Attribute VB_Name = "modZBar"
Option Explicit
DefLng A-Z ' default variable data type to Long instead of Variant, if we've forgotten to explicitly code it somewhere

' ZAAP-specific types
Public Type typZBarHead
    Serial              As Integer      '    USHORT  SN;     // Binary sequence #  (incremented every new pamphlet issued)
    WindowHandle        As Long         '    HWND    ZHwnd;      // ZTree window handle
    ReturnFlag          As String * 1   '    UCHAR   ReturnFlag;    // Return flag (ZTree sets this character to a hyphen)
                                        '                           //  When an Y or Ctrl-Y command is issued,
                                        '                           //  the application must respond with a return flag:-
                                        '                           //  k  OK for handshake
                                        '                           //  t  tag the file
                                        '                           //  u  untag the file
                                        '                           //  e  file operation error
                                        '                           //  s  command syntax error
    ClientStatus        As String * 1   '    UCHAR   client;    // Ztree screen status
                                        '                       //  n - Normal screen is in use
                                        '                       //  l - Left pane is active in split-view
                                        '                       //  r - Right pane is active in split-view
                                        '                       //  7 - Auto-view is in use
                                        '                       //  v - Internal Viewer is in use (in standard mode)
                                        '                       //  x - Execute screen is in use
                                        '                       //  m - Menu screen is in use
                                        '                       //  s - Statistics screen is in use
    Command             As String * 1   '    UCHAR   cmd;   // Ztree Command
                                        '                   //  @  New filename in pamphlet
                                        '                   //  w  Screen status change (same filename)
                                        '                   //  ^  Shift/Ctrl-key entry
                                        '                   //  ~  Shift/Ctrl/Alt-key entry
                                        '                   //  Q  Ztree quitting
                                        '                   //  h  Hold pre-command
                                        '                   //  y  Assist pre-command
                                        '                   //  Y  Assist command
    KeyCode             As String * 1   '    UCHAR   cchr;  // Cmd Char or Keycode
                                        '                   //  s - simple Y command
                                        '                   //  t - Ctrl-Y command
                                        '                   //  Codes generated with the Sh/Ctrl or Sh/Ctrl/Alt keys
    Page                As String * 1   '    UCHAR   page;  // Page (D or F)
    buffer(1 To 512)    As Byte         '    char   szBuff[512];    // Full path (null terminated)
                                        '                           // ZTree "Y" command (null terminated)
                                        '                           // CheckSum
                                        '                           // Copy of sequence #
End Type

' Timer declarations
Private Declare Function SetTimer Lib "user32" ( _
    ByVal hWnd As Long, _
    ByVal nIDEvent As Long, _
    ByVal uElapse As Long, _
    ByVal lpTimerFunc As Long) _
    As Long
Private Declare Function KillTimer Lib "user32" ( _
    ByVal hWnd As Long, _
    ByVal nIDEvent As Long) _
    As Long

' Declarations for interprocess communications
Private Declare Function SendMessageTimeout Lib "user32" Alias "SendMessageTimeoutW" ( _
    ByVal hWnd As Long, _
    ByVal msg As Long, _
    ByVal wParam As Long, _
    ByVal lParam As Long, _
    ByVal fuFlags As Long, _
    ByVal uTimeout As Long, _
    ByRef lpdwResult As Long) _
    As Long
Private Const WM_SETTEXT = &HC
Private Const SMTO_BLOCK = &H1


' Declarations to get precise file date and time
Private Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type

Private Type WIN32_FILE_ATTRIBUTE_DATA
    dwFileAttributes As Long
    ftCreationTime As FILETIME
    ftLastAccessTime As FILETIME
    ftLastWriteTime As FILETIME
    nFileSizeHigh As Long
    nFileSizeLow As Long
End Type

Private Declare Function GetFileAttributesEx Lib "kernel32" Alias "GetFileAttributesExW" ( _
    ByVal lpFileNameString As Long, _
    ByVal fInfoLevelId As Long, _
    ByRef wData As WIN32_FILE_ATTRIBUTE_DATA) _
    As Long

Private Declare Function CompareFileTime Lib "kernel32" ( _
    ByRef lpFileTime1 As FILETIME, _
    ByRef lpFileTime2 As FILETIME) _
    As Long

' Declarations to ascertain a window's state
Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type WINDOWPLACEMENT
    Length As Long
    flags As Long
    showCmd As Long
    ptMinPosition As POINTAPI
    ptMaxPosition As POINTAPI
    rcNormalPosition As RECT
End Type

Private Declare Function GetWindowPlacement Lib "user32" ( _
    ByVal hWnd As Long, _
    ByRef lpWndpl As WINDOWPLACEMENT) _
    As Long

Private Const SW_HIDE = 0
Private Const SW_MAXIMIZE = 3
Private Const SW_MINIMIZE = 6
Private Const SW_RESTORE = 9
Private Const SW_SHOW = 5
Private Const SW_SHOWMAXIMIZED = 3
Private Const SW_SHOWMINIMIZED = 2
Private Const SW_SHOWMINNOACTIVE = 7
Private Const SW_SHOWNA = 8
Private Const SW_SHOWNOACTIVATE = 4
Private Const SW_SHOWNORMAL = 1

Private Declare Function GetClassName Lib "user32" Alias "GetClassNameW" ( _
    ByVal hWnd As Long, _
    ByVal lpClassNameString As Long, _
    ByVal nMaxCount As Long) _
    As Long


' Types to describe ZTree executables and their instances (windows)
Private Type typZTreeInstance
    WindowHandle    As Long
    LastZBar        As typZBarHead
    LastWindowState As Long
    ZBarUpdated     As Boolean
    Ordinal         As Long
End Type

Private Type typZTreeClass
    TimerID         As Long
    Interval        As Long
    Path            As String
    Version         As String
    Instances()     As typZTreeInstance
    LastWrite       As Date
    LastWriteFT     As FILETIME
    FormatNormal    As String
    FormatMax       As String
    FormatMin       As String
End Type

Private Const icstrZTreeExeName As String = "ZTW.EXE"

Private iudtZTExes() As typZTreeClass
Private ilngMaxOrdinal As Long

Public DebugMode As Boolean
Public DefaultInterval As Long
Public TrustFileTime As Boolean
Public SkipColonFix As Boolean

Public Function StartTracking(ByVal astrFormatNormal As String, _
                              Optional ByVal astrFormatMaximized As String = vbNullString, _
                              Optional ByVal astrFormatMinimized As String = vbNullString, _
                              Optional ByVal alngIntervalInMs As Long = 0) As Long
    If alngIntervalInMs = 0 Then
        alngIntervalInMs = DefaultInterval
    End If
    
    ' TODO: get a list of all windows, and register all corresponding paths that end on icstrZTreeExeName
    ' and return the number of paths that will be tracked
    Stop
End Function

Public Function StartTrackingPath(ByVal astrPath As String, _
                                  ByVal astrFormatNormal As String, _
                                  Optional ByVal astrFormatMaximized As String = vbNullString, _
                                  Optional ByVal astrFormatMinimized As String = vbNullString, _
                                  Optional ByVal alngIntervalInMs As Long = 0) As Boolean
    Dim lblnReturn As Boolean
    Dim llngZTIndex As Long
    
    On Error GoTo ArrayNotDimensioned
    llngZTIndex = UBound(iudtZTExes)
    On Error GoTo 0
    
    If alngIntervalInMs = 0 Then
        alngIntervalInMs = DefaultInterval
    End If
    
    ' See if we're not already tracking the given path
    ' (Note that GetIndexByPath expands the path to the full long path, and changes the parameter accordingly)
    llngZTIndex = GetIndexByPath(astrPath)
    If llngZTIndex = 0 Then
        llngZTIndex = UBound(iudtZTExes) + 1
        ReDim Preserve iudtZTExes(0 To llngZTIndex)
        With iudtZTExes(llngZTIndex)
            ReDim .Instances(0 To 0)
            .Path = GetLongPath(GetFullPath(astrPath))
            If Right$(.Path, 1) <> "\" Then
                .Path = .Path & "\"
            End If
            .Version = GetVersionByPath(.Path & icstrZTreeExeName)
            .FormatNormal = astrFormatNormal
            .FormatMax = astrFormatMaximized
            .FormatMin = astrFormatMinimized
            .Interval = alngIntervalInMs
            .TimerID = SetTimer(0, 0, alngIntervalInMs, AddressOf ZBarTimerProc)
            If .TimerID = 0 Then
                .Path = vbNullString
                RaiseSystemError , "SetTimer"
            End If
        End With
        lblnReturn = True
    End If
    
ExitHandler:
    StartTrackingPath = lblnReturn
    Exit Function
    
ArrayNotDimensioned:
    ReDim iudtZTExes(0 To 0)
    Resume Next
End Function

Public Sub StopTrackingPath(ByVal astrPath As String)
    Dim llngZTIndex As Long
    Dim llngInstance As Long
    
    ' See if we're already tracking the given path
    llngZTIndex = GetIndexByPath(astrPath)
    If llngZTIndex > 0 Then
        With iudtZTExes(llngZTIndex)
            ' First, kill the timer
            KillTimer 0, .TimerID
            .TimerID = 0
            
            ' Then, reset the titlebar caption for all the instances
            If LenB(.Version) = 0 Then
                .Version = GetVersionByPath(.Path & icstrZTreeExeName)
            End If
            If LenB(.Version) > 0 Then
                For llngInstance = 1 To UBound(.Instances)
                    SetWindowTitle .Instances(llngInstance).WindowHandle, .Version
                Next
            End If
            
            ' Clear some memory
            ReDim .Instances(0 To 0)
            .Path = vbNullString
            .Version = vbNullString
        End With
        
        ' And finally, purge the array as far as possible
        For llngZTIndex = UBound(iudtZTExes) To 1 Step -1
            If iudtZTExes(llngZTIndex).TimerID <> 0 Then
                Exit For
            End If
        Next
        If llngZTIndex < UBound(iudtZTExes) Then
            ReDim Preserve iudtZTExes(0 To llngZTIndex)
        End If
    End If
End Sub

Public Sub StopTracking()
    Dim llngZTIndex As Long
    
    ' Stop tracking each path
    For llngZTIndex = 1 To UBound(iudtZTExes)
        If iudtZTExes(llngZTIndex).TimerID <> 0 Then
            StopTrackingPath iudtZTExes(llngZTIndex).Path
        End If
    Next
    ilngMaxOrdinal = 0
End Sub


Private Function GetIndexByPath(ByRef astrPath As String) As Long
    Dim llngZTIndex As Long
    
    ' Note that the following calls ALTER the parameter if necessary!
    astrPath = GetLongPath(GetFullPath(astrPath))
    If Right$(astrPath, 1) <> "\" Then astrPath = astrPath & "\"
    
    For llngZTIndex = 1 To UBound(iudtZTExes)
        If StrComp(iudtZTExes(llngZTIndex).Path, astrPath, vbTextCompare) = 0 Then
            GetIndexByPath = llngZTIndex
            Exit For
        End If
    Next
End Function

Private Function GetIndexByTimerID(ByVal alngTimerID As Long) As Long
    Dim llngZTIndex As Long
    
    For llngZTIndex = 1 To UBound(iudtZTExes)
        If iudtZTExes(llngZTIndex).TimerID = alngTimerID Then
            GetIndexByTimerID = llngZTIndex
            Exit For
        End If
    Next
End Function

Private Function GetInstanceIndexByWindow(ByRef audtInstances() As typZTreeInstance, _
                                          ByVal alngHandle As Long) As Long
    Dim llngInstance As Long
    
    For llngInstance = 1 To UBound(audtInstances)
        If audtInstances(llngInstance).WindowHandle = alngHandle Then
            GetInstanceIndexByWindow = llngInstance
            Exit For
        End If
    Next
End Function



Private Sub ZBarTimerProc(ByVal hWnd As Long, _
                          ByVal uMsg As Long, _
                          ByVal idEvent As Long, _
                          ByVal dwTime As Long)
    Dim llngZTIndex As Long
    Dim lstrPath As String
    Dim lstrZBarPath As String
    Dim ludtW32FAD As WIN32_FILE_ATTRIBUTE_DATA
    Dim lblnZBarHasChanged As Boolean
    Dim ldtmLastWriteOfZBarDat As Date
    Dim lintFile As Integer
    Dim ludtZBar As typZBarHead
    Dim llngInstance As Long
    Dim lstrTitle As String
    Dim llngWindowState As Long
    Dim lblnWindowChangedState As Boolean

    llngZTIndex = GetIndexByTimerID(idEvent)
    If llngZTIndex > 0 Then
        With iudtZTExes(llngZTIndex)
            lstrZBarPath = .Path & "zbar.dat"
            
            ' Only continue if the file was modified since last we looked (depending on the file date/time)
            ' Configurable by option "General\TrustFileTime"
            If modZBar.TrustFileTime Then
                ' Use this API function for accuracy
                If GetFileAttributesEx(StrPtr("\\?\" & lstrZBarPath & vbNullChar), _
                                       0, _
                                       ludtW32FAD) Then
                    lblnZBarHasChanged = (CompareFileTime(ludtW32FAD.ftLastWriteTime, .LastWriteFT) > 0)
                ElseIf .Interval > 1000 Then
                    ' VB's Date is only accurate to a second; don't perform the check if the interval isn't at least that
                    ldtmLastWriteOfZBarDat = FileDateTime(lstrZBarPath)
                    lblnZBarHasChanged = (ldtmLastWriteOfZBarDat > .LastWrite)
                End If
            End If
            If lblnZBarHasChanged Or Not modZBar.TrustFileTime Then
                .LastWrite = ldtmLastWriteOfZBarDat
                .LastWriteFT = ludtW32FAD.ftLastWriteTime
                
                lintFile = FreeFile()
                Open lstrZBarPath For Binary Access Read Shared As #lintFile
                Get #lintFile, , ludtZBar
                Close #lintFile
                
                llngInstance = GetInstanceIndexByWindow(.Instances, ludtZBar.WindowHandle)
                If llngInstance = 0 Then
                    llngInstance = UBound(.Instances) + 1
                    ReDim Preserve .Instances(0 To llngInstance)
                    .Instances(llngInstance).WindowHandle = ludtZBar.WindowHandle
                    ilngMaxOrdinal = ilngMaxOrdinal + 1
                    .Instances(llngInstance).Ordinal = ilngMaxOrdinal
                End If
                
                'Check if this window is still open and valid!
                If GetWindowClass(.Instances(llngInstance).WindowHandle) <> "ConsoleWindowClass" Then
                    With .Instances(llngInstance)
                        .WindowHandle = 0
                        .ZBarUpdated = False
                    End With
                    
                    ' Try to purge the instances array
                    For llngInstance = UBound(.Instances) To 1 Step -1
                        If .Instances(llngInstance).WindowHandle <> 0 Then
                            Exit For
                        End If
                        ReDim Preserve .Instances(0 To llngInstance)
                    Next
                Else
                    ' Only continue if the serial has changed since last time
                    If ludtZBar.Serial <> .Instances(llngInstance).LastZBar.Serial Then
                        ' Remember this ZBar
                        .Instances(llngInstance).LastZBar = ludtZBar
                        .Instances(llngInstance).ZBarUpdated = True
                        
                        ' Determine the default title for this executable
                        If LenB(.Version) = 0 Then
                            .Version = GetVersionByWindow(ludtZBar.WindowHandle, .Path)
                        End If
                    End If
                End If
            End If
            
            ' Now check all known instances if they've changed state
            For llngInstance = 1 To UBound(.Instances)
                With .Instances(llngInstance)
                    If .WindowHandle <> 0 Then
                        llngWindowState = GetWindowState(.WindowHandle)
                        If llngWindowState <> .LastWindowState And llngWindowState <> -1 Then
                            lblnWindowChangedState = True
                        End If
                        If .ZBarUpdated Or lblnWindowChangedState Then
                            
                            ' Piece together the appropriate title
                            lstrTitle = CreateTitle(iudtZTExes(llngZTIndex), iudtZTExes(llngZTIndex).Instances(llngInstance))
                            
                            ' debug mode is configurable (General/Debug=<yes|allow|no>)
                            If DebugMode Then
                                With .LastZBar
                                    lstrTitle = .ClientStatus & "," & .Page & "," & .Command & "," & .KeyCode & " (" & Asc(.KeyCode) & "), " & lstrTitle
                                End With
                            End If
                            
                            ' Ensure that the caption either starts with 'ZTreeWin v', or there's a colon ':' in second position;
                            '  ZTreeWin uses this to ascertain whether the window is in mark/select mode
                            ' Configurable by option General/SkipColonFix
                            If modZBar.SkipColonFix = False And (Mid$(lstrTitle, 2, 1) <> ":" And Left$(lstrTitle, 10) <> "ZTreeWin v") Then
                                lstrTitle = "[:] " & lstrTitle
                            End If
                            
                            Debug.Print lstrTitle
                            
                            'Set the specified window's caption
                            If SetWindowTitle(.WindowHandle, lstrTitle) Then
                                ' and record that all went well
                                .LastWindowState = llngWindowState
                                .ZBarUpdated = False
                            End If
                        
                        End If
                    End If
                End With
            Next
        End With
    Else
        ' TODO: log that we've been called by an unknown timer...
        Debug.Print "Unknown timer called: " & idEvent
    End If
End Sub

Private Function GetVersionByWindow(ByVal alngHandle As Long, _
                                    Optional ByVal astrExecutableDir As String = vbNullString) As String
    Dim lstrExecutable As String
    
    ' Get the executable path and file that belongs to the given window handle
    '  and use that to retrieve the file version and description of the executable
    '  and finally append the file description and version to the title bar
    On Error Resume Next
    lstrExecutable = GetWindowExecutable(alngHandle)
    If LenB(astrExecutableDir) > 0 Then
        If LenB(lstrExecutable) = 0 Then
            lstrExecutable = astrExecutableDir & IIf(Right$(astrExecutableDir, 1) = "\", "", "\") & icstrZTreeExeName
        ElseIf InStr(lstrExecutable, "\") = 0 Then
            lstrExecutable = astrExecutableDir & lstrExecutable
        End If
    End If
    
    GetVersionByWindow = GetVersionByPath(lstrExecutable)
End Function

Private Function GetVersionByPath(ByVal astrExecutable As String) As String
    Dim lstrReturn As String
    
    On Error Resume Next
    If LenB(astrExecutable) > 0 And LenB(Dir$(astrExecutable)) > 0 Then
        lstrReturn = modFileVersion.GetFileDescription(astrExecutable) & " v" & modFileVersion.GetFileVersion(astrExecutable)
    End If
    If LenB(lstrReturn) = 0 Then
        lstrReturn = "ZTreeWin v?.??"
    End If
    
    GetVersionByPath = lstrReturn
End Function

Private Function CreateTitle(ByRef audtZTClass As typZTreeClass, _
                             ByRef audtZTInstance As typZTreeInstance) As String
    Dim lstrReturn As String
    Dim lstrSelectedItem As String
    Dim llngPos As Long
    Dim lstrScreen As String
    Dim llngResult As Long
    Dim lstrFormat As String
    Dim lblnVersioned As Boolean
    
    lstrReturn = audtZTClass.Version
    
    ' Read the currently selected dir/file name from the buffer
    lstrSelectedItem = StrConv(audtZTInstance.LastZBar.buffer, vbUnicode)
    llngPos = InStr(1, lstrSelectedItem, vbNullChar)
    If llngPos > 0 Then
        lstrSelectedItem = Left$(lstrSelectedItem, llngPos - 1)
    Else
        lstrSelectedItem = vbNullString
    End If
    
    ' Interpret the screen
    Select Case audtZTInstance.LastZBar.ClientStatus
    Case "7" ' Auto-view is in use
        lstrScreen = "AutoView"
    Case "v" ' Internal Viewer is in use (in standard mode)
        lstrScreen = "Viewer"
    Case "x" ' Execute screen is in use
        lstrScreen = "Execute"
    Case "m" ' Menu screen is in use
        lstrScreen = "Menu"
    Case "s" ' Statistics screen is in use
        lstrScreen = "Stats"
    Case Else
        lstrScreen = vbNullString
    End Select
    
    With audtZTClass
        Select Case GetWindowState(audtZTInstance.WindowHandle)
        Case SW_MAXIMIZE, SW_SHOWMAXIMIZED
            If LenB(.FormatMax) > 0 Then
                lstrFormat = .FormatMax
            ElseIf LenB(.FormatNormal) > 0 Then
                lstrFormat = .FormatNormal
            ElseIf LenB(.FormatMin) > 0 Then
                lstrFormat = .FormatMin
            Else
                lstrFormat = vbNullString
            End If
        Case SW_RESTORE, SW_SHOW, SW_SHOWNA, SW_SHOWNOACTIVATE, SW_SHOWNORMAL
            If LenB(.FormatNormal) > 0 Then
                lstrFormat = .FormatNormal
            ElseIf LenB(.FormatMax) > 0 Then
                lstrFormat = .FormatMax
            ElseIf LenB(.FormatMin) > 0 Then
                lstrFormat = .FormatMin
            Else
                lstrFormat = vbNullString
            End If
        Case Else 'SW_MINIMIZE, SW_SHOWMINIMIZED, SW_SHOWMINNOACTIVE, SW_HIDE
            If LenB(.FormatMin) > 0 Then
                lstrFormat = .FormatMin
            ElseIf LenB(.FormatNormal) > 0 Then
                lstrFormat = .FormatNormal
            ElseIf LenB(.FormatMax) > 0 Then
                lstrFormat = .FormatMax
            Else
                lstrFormat = vbNullString
            End If
        End Select
    End With
    lblnVersioned = lblnVersioned Or (InStr(1, lstrFormat, "%ztreeversion%", vbTextCompare) <> 0)
    
    lstrReturn = FormatTitle(audtZTClass.Path & icstrZTreeExeName, _
                             lstrSelectedItem, _
                             (audtZTInstance.LastZBar.Page = "F"), _
                             lstrFormat, _
                             "Ordinal", audtZTInstance.Ordinal, _
                             "Screen", lstrScreen, _
                             "ZTreeVersion", audtZTClass.Version)
    
    ' TODO: formatting based on file / dir window
    
    lblnVersioned = lblnVersioned Or (InStr(1, lstrFormat, "%ztreeversion%", vbTextCompare) <> 0)
    
    If Not lblnVersioned Then
        lstrReturn = lstrReturn & " - " & audtZTClass.Version
    End If

    ' Return the created title
    CreateTitle = lstrReturn
End Function

Private Function FormatTitle(ByRef astrExecutable As String, _
                             ByRef astrSelectedItem As String, _
                             ByVal ablnIsFile As Boolean, _
                             ByVal astrFormat As String, _
                             ParamArray avarExtraVariables() As Variant) As String
    Dim lstrReturn As String
    Dim llngNextPos As Long
    Dim llngPos As Long
    Dim lstrKey As String
    Dim llngKeyLength As Long
    Dim lintSize As Integer
    Dim lstrSeparator As String
    Dim lintIndex As Integer
    Dim lstrValue As String
    Dim lstrElements() As String
    Dim lblnVarFound As Boolean
    Dim llngVar As Long
    
    llngNextPos = InStr(astrFormat, "%")
    If llngNextPos = 0 Then
        lstrReturn = astrFormat
    Else
        Do Until llngNextPos = 0
            lstrReturn = lstrReturn & Mid$(astrFormat, llngPos + 1, llngNextPos - llngPos - 1)
            llngPos = InStr(llngNextPos + 1, astrFormat, "%")
            If llngPos = 0 Then
                lstrReturn = lstrReturn & Mid$(astrFormat, llngNextPos)
                Exit Do
            ElseIf llngPos - llngNextPos = 1 Then
                lstrReturn = lstrReturn & "%"
                lstrKey = vbNullString
            Else
                lstrKey = Mid$(astrFormat, llngNextPos + 1, llngPos - llngNextPos - 1)
                llngKeyLength = Len(lstrKey)
                lstrValue = vbNullString
                lstrSeparator = vbNullString
                
                llngPos = InStr(lstrKey, "@")
                If llngPos = 0 Then
                    lintSize = 0
                Else
                    lintSize = Val(Mid$(lstrKey, llngPos + 1))
                    lstrKey = Left$(lstrKey, llngPos - 1)
                End If
                
                llngPos = InStr(lstrKey, "#")
                If llngPos = 0 Then
                    lintIndex = 0
                Else
                    lintIndex = Val(Mid$(lstrKey, llngPos + 1))
                    lstrKey = Left$(lstrKey, llngPos - 1)
                End If
                
                Select Case LCase$(lstrKey)
                Case "fullpath"
                    lstrValue = astrSelectedItem
                Case "path"
                    llngPos = InStrRev(astrSelectedItem, "\")
                    If llngPos > 0 Then
                        lstrValue = Left$(astrSelectedItem, llngPos - 1)
                    Else
                        lstrValue = astrSelectedItem
                    End If
                Case "drivelesspath"
                    If Mid$(astrSelectedItem, 2, 1) = ":" Then
                        lstrValue = Mid$(astrSelectedItem, 3)
                    Else
                        lstrValue = astrSelectedItem
                    End If
                Case "drive"
                    If Mid$(astrSelectedItem, 2, 1) = ":" Then
                        lstrValue = Left$(astrSelectedItem, 2)
                    Else
                        lstrValue = vbNullString
                    End If
                Case "item"
                    If Right$(astrSelectedItem, 1) = "\" Then
                        llngPos = InStrRev(astrSelectedItem, "\", Len(astrSelectedItem) - 1)
                        If llngPos = 0 Then
                            lstrValue = Left$(astrSelectedItem, Len(astrSelectedItem) - 1)
                        Else
                            lstrValue = Mid$(astrSelectedItem, llngPos + 1, Len(astrSelectedItem) - llngPos - 1)
                        End If
                    Else
                        llngPos = InStrRev(astrSelectedItem, "\")
                        If llngPos = 0 Then
                            lstrValue = astrSelectedItem
                        Else
                            lstrValue = Mid$(astrSelectedItem, llngPos + 1)
                        End If
                    End If
                Case "file"
                    If ablnIsFile Then
                        llngPos = InStrRev(astrSelectedItem, "\")
                        If llngPos = 0 Then
                            lstrValue = astrSelectedItem
                        Else
                            lstrValue = Mid$(astrSelectedItem, llngPos + 1)
                        End If
                    Else
                        lstrValue = vbNullString
                    End If
                Case "folder", "dir", "directory"
                    lstrValue = astrSelectedItem
                    lstrSeparator = "\"
                    If Mid$(lstrValue, 2, 1) = ":" Then
                        lstrValue = Mid$(lstrValue, 3)
                    End If
                    If Left$(lstrValue, 1) = "\" Then
                        lstrValue = Mid$(lstrValue, 2)
                    End If
                    If ablnIsFile = False Then
                        If Left$(lstrValue, 1) = "\" Then
                            lstrValue = Mid$(lstrValue, 2)
                        End If
                    Else
                        llngPos = InStrRev(lstrValue, "\")
                        If llngPos > 0 Then
                            lstrValue = Left$(lstrValue, llngPos - 1)
                        End If
                    End If
                Case "zt_fullpath"
                    lstrValue = astrExecutable
                Case "zt_drivelesspath"
                    If Mid$(astrExecutable, 2, 1) = ":" Then
                        lstrValue = Mid$(astrExecutable, 3)
                    ElseIf Left$(astrExecutable, 2) = "\\" Then
                        llngPos = InStr(3, astrExecutable, "\")
                        If llngPos > 0 Then
                            llngPos = InStr(llngPos + 1, astrExecutable, "\")
                            If llngPos > 0 Then
                                astrExecutable = Mid$(astrExecutable, llngPos)
                            Else
                                lstrValue = vbNullString
                            End If
                        Else
                            lstrValue = vbNullString
                        End If
                    Else
                        lstrValue = astrExecutable
                    End If
                Case "zt_drive"
                    If Mid$(astrExecutable, 2, 1) = ":" Then
                        lstrValue = Left$(astrExecutable, 2)
                    ElseIf Left$(astrExecutable, 2) = "\\" Then
                        llngPos = InStr(3, astrExecutable, "\")
                        If llngPos > 0 Then
                            llngPos = InStr(llngPos + 1, astrExecutable, "\")
                            If llngPos > 0 Then
                                astrExecutable = Left$(astrExecutable, llngPos - 1)
                            Else
                                lstrValue = astrExecutable
                            End If
                        Else
                            lstrValue = astrExecutable
                        End If
                    Else
                        lstrValue = vbNullString
                    End If
                Case "zt_file"
                    lstrValue = icstrZTreeExeName
                    llngPos = InStrRev(lstrValue, "\")
                    If llngPos = 0 Then
                        lstrValue = lstrValue
                    Else
                        lstrValue = Mid$(lstrValue, llngPos + 1)
                    End If
                Case "zt_folder", "zt_dir", "zt_directory"
                    lstrValue = astrExecutable
                    lstrSeparator = "\"
                    If Mid$(lstrValue, 2, 1) = ":" Then
                        lstrValue = Mid$(lstrValue, 3)
                    ElseIf Left$(lstrValue, 2) = "\\" Then
                        llngPos = InStr(3, lstrValue, "\")
                        If llngPos > 0 Then
                            lstrValue = Mid$(lstrValue, llngPos + 1)
                        Else
                            lstrValue = Mid$(lstrValue, 3)
                        End If
                    End If
                    If Left$(lstrValue, 1) = "\" Then
                        lstrValue = Mid$(lstrValue, 2)
                    End If
                    llngPos = InStrRev(lstrValue, "\")
                    If llngPos > 0 Then
                        lstrValue = Left$(lstrValue, llngPos - 1)
                    End If
                Case Else
                    ' Also check if this key is provided in the extra variables
                    lblnVarFound = False
                    For llngVar = LBound(avarExtraVariables) To UBound(avarExtraVariables) Step 2
                        If StrComp(avarExtraVariables(llngVar), lstrKey, vbTextCompare) = 0 Then
                            lstrValue = avarExtraVariables(llngVar + 1)
                            lblnVarFound = True
                            Exit For
                        End If
                    Next
                    
                    ' If not, re-establish it as unparsed
                    If lblnVarFound = False Then
                        lstrValue = "%" & lstrKey & "%"
                    End If
                End Select
            
                If LenB(lstrSeparator) > 0 And LenB(lstrValue) > 0 Then
                    lstrElements() = Split(lstrValue, lstrSeparator)
                    If lintIndex = 0 Then
                        lintIndex = UBound(lstrElements)
                    ElseIf lintIndex > UBound(lstrElements) + 1 Then
                        lintIndex = UBound(lstrElements)
                    ElseIf lintIndex < LBound(lstrElements) + 1 Then
                        lintIndex = LBound(lstrElements)
                    Else
                        lintIndex = lintIndex - 1
                    End If
                    lstrValue = lstrElements(lintIndex)
                End If
                
                If lintSize > 0 Then
                    On Error Resume Next
                    lstrValue = CompactPathToChars(lstrValue, lintSize)
                    On Error GoTo 0
                End If
                
                lstrReturn = lstrReturn & lstrValue
            End If
            
            llngPos = llngNextPos + llngKeyLength + 1
            llngNextPos = InStr(llngPos + 1, astrFormat, "%")
        Loop
        If llngPos < Len(astrFormat) Then
            lstrReturn = lstrReturn & Mid$(astrFormat, llngPos + 1)
        End If
    End If
    
    FormatTitle = lstrReturn
End Function

Private Function SetWindowTitle(ByVal alngWindowHandle As Long, _
                                ByRef astrTitle As String) As Boolean
    SetWindowTitle = SendMessageTimeout(alngWindowHandle, _
                                        WM_SETTEXT, _
                                        0, _
                                        StrPtr(astrTitle & vbNullChar), _
                                        SMTO_BLOCK, _
                                        1000, _
                                        ByVal 0&) <> 0
End Function

Private Function GetWindowState(ByVal alngWindowHandle As Long) As Long
    Dim ludtWP As WINDOWPLACEMENT
    Dim llngResult As Long
    
    ludtWP.Length = Len(ludtWP)
    llngResult = GetWindowPlacement(alngWindowHandle, ludtWP)
    If llngResult = 0 Then
        RaiseSystemError
    Else
        GetWindowState = ludtWP.showCmd
    End If
End Function

Private Function GetWindowClass(ByVal alngWindowHandle As Long) As String
    Dim lstrReturn As String
    Dim llngResult As Long
    
    lstrReturn = String$(1024, 0)
    llngResult = GetClassName(alngWindowHandle, _
                              StrPtr(lstrReturn), _
                              Len(lstrReturn))
    GetWindowClass = Left$(lstrReturn, llngResult)
End Function
