Jump to content

Unblocking a Blocked Message Handler


LarsJ
 Share

Recommended Posts

Test-00.au3
If you have used ArrayDisplay regularly, you know that it's possible to show more arrays at once in this way (click "Run User Func" button):

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6

#include <ArrayDisplay142-1.au3>

ShowArray0( 0, 0 )

Func ShowArray0( $p1, $p2 )
  Local $aArray0 = [ 00, 01, 02, 03, 04, 05, 06, 07, 08, 09 ]
  _ArrayDisplay142( $aArray0, "$aArray0", "", 0, Default, Default, Default, Default, ShowArray1 )
  #forceref $p1, $p2
EndFunc

Func ShowArray1( $p1, $p2 )
  Local $aArray1 = [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 11, 12, 13, 14 ]
  _ArrayDisplay142( $aArray1, "$aArray1", "", 0, Default, Default, Default, Default, ShowArray2 )
  #forceref $p1, $p2
EndFunc

Func ShowArray2( $p1, $p2 )
  Local $aArray2 = [ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]
  _ArrayDisplay142( $aArray2, "$aArray2", "", 0, Default, Default, Default, Default, ShowArray0 )
  #forceref $p1, $p2
EndFunc

 

How does this work? The ArrayDisplay GUI is running in MessageLoop mode. Will the message loop in a later created GUI not block the message loop in a previously created GUI? Yes, it will. But it's important to know exactly what is blocked and what is not blocked.

This is the message loop in a slightly modified version of ArrayDisplay from AutoIt 3.3.14.2:

While 1
  $iMsg = GUIGetMsg() ; Variable needed to check which "Copy" button was pressed
  Switch $iMsg
    Case $idCopy_ID, $idCopy_Data
      ; Count selected rows
      Local $iSel_Count = GUICtrlSendMsg($idListView, $LVM_GETSELECTEDCOUNT, 0, 0)
      ; Display splash dialog if required
      If $iVerbose And (Not $iSel_Count) And ($iItem_End - $iItem_Start) * ($iSubItem_End - $iSubItem_Start) > 10000 Then
        SplashTextOn("ArrayDisplay", "Copying data" & @CRLF & @CRLF & "Please be patient", 300, 100)
      EndIf
      ; Generate clipboard text
      Local $sClip = "", $sItem, $aSplit
      ; Add items
      For $i = 0 To $iItem_End - $iItem_Start
        ; Skip if copying selected rows and item not selected
        If $iSel_Count And Not (GUICtrlSendMsg($idListView, $LVM_GETITEMSTATE, $i, $LVIS_SELECTED)) Then
          ContinueLoop
        EndIf
        $sItem = $avArrayText[$i]
        If $iMsg = $idCopy_Data Then
          ; Remove row ID if required
          $sItem = StringRegExpReplace($sItem, "^\[\d+\].(.*)$", "$1")
        EndIf
        If $iCW_ColWidth Then
          ; Expand columns
          $aSplit = StringSplit($sItem, $sAD_Separator)
          $sItem = ""
          For $j = 1 To $aSplit[0]
            $sItem &= StringFormat("%-" & $iCW_ColWidth + 1 & "s", StringLeft($aSplit[$j], $iCW_ColWidth))
          Next
        Else
          ; Use defined separator
          $sItem = StringReplace($sItem, $sAD_Separator, $vUser_Separator)
        EndIf
        $sClip &= $sItem & @CRLF
      Next
      ; Add header line if required
      If $iMsg = $idCopy_ID Then
        If $iCW_ColWidth Then
          $aSplit = StringSplit($sHeader, $sAD_Separator)
          $sItem = ""
          For $j = 1 To $aSplit[0]
            $sItem &= StringFormat("%-" & $iCW_ColWidth + 1 & "s", StringLeft($aSplit[$j], $iCW_ColWidth))
          Next
        Else
          $sItem = StringReplace($sHeader, $sAD_Separator, $vUser_Separator)
        EndIf
        $sClip = $sItem & @CRLF & $sClip
      EndIf
      ;Send to clipboard
      ClipPut($sClip)
      ; Remove splash if used
      SplashOff()
      ; Refocus ListView
      GUICtrlSetState($idListView, $GUI_FOCUS)

    Case $idUser_Func
      ; Get selected indices
      Local $aiSelItems[$iRowLimit] = [0]
      If GUICtrlSendMsg($idListView, $LVM_GETSELECTEDCOUNT, 0, 0) Then
        For $i = 0 To GUICtrlSendMsg($idListView, $LVM_GETITEMCOUNT, 0, 0)
          If GUICtrlSendMsg($idListView, $LVM_GETITEMSTATE, $i, $LVIS_SELECTED) Then
            $aiSelItems[0] += 1
            $aiSelItems[$aiSelItems[0]] = $i + $iItem_Start
          EndIf
        Next
      EndIf
      ReDim $aiSelItems[$aiSelItems[0] + 1]
      ; Pass array and selection to user function
      Opt("GUIDataSeparatorChar", $sCurr_Separator)
      $hUser_Function($aArray, $aiSelItems)
      Opt("GUIDataSeparatorChar", $sAD_Separator)
      GUICtrlSetState($idListView, $GUI_FOCUS)

    Case $idExit_Script
      ; Clear up
      GUIDelete($hGUI)
      Exit

    Case $GUI_EVENT_CLOSE
      ExitLoop
  EndSwitch
WEnd

 

And this is the part of the message loop that handles "Run User Func" button clicks:

Case $idUser_Func
  ; Get selected indices
  Local $aiSelItems[$iRowLimit] = [0]
  If GUICtrlSendMsg($idListView, $LVM_GETSELECTEDCOUNT, 0, 0) Then
    For $i = 0 To GUICtrlSendMsg($idListView, $LVM_GETITEMCOUNT, 0, 0)
      If GUICtrlSendMsg($idListView, $LVM_GETITEMSTATE, $i, $LVIS_SELECTED) Then
        $aiSelItems[0] += 1
        $aiSelItems[$aiSelItems[0]] = $i + $iItem_Start
      EndIf
    Next
  EndIf
  ReDim $aiSelItems[$aiSelItems[0] + 1]
  ; Pass array and selection to user function
  Opt("GUIDataSeparatorChar", $sCurr_Separator)
  $hUser_Function($aArray, $aiSelItems) ; <<<<<<<< ; The message loop is blocked
  Opt("GUIDataSeparatorChar", $sAD_Separator)      ; until the function returns.
  GUICtrlSetState($idListView, $GUI_FOCUS)

 

It's the message loop that's blocked. It's blocked by a function that's still running. And which events does this loop handle? It handles button click events and $GUI_EVENT_CLOSE event. So button clicks and $GUI_EVENT_CLOSE does not work.

But are there any listview events in the loop? No there are not. Since there are no AutoIt events to handle the listview, the listview is handled entirely through Windows events. And of course Windows events are not blocked. This means that the listview is fully functional.

Note that the message loop isn't blocked in the last created GUI. Button clicks and $GUI_EVENT_CLOSE does work in the last created GUI.

Is it possible to unblock the blocked message handlers in the ArrayDisplay GUIs that were created before the last GUI?

Before we look at ArrayDisplay let's look at some more simple examples.

 

Test-01.au3
Test-01.au3 is similar to Test-00.au3. Only the last created GUI is responsive. The message loop in the other GUIs is blocked.

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6

#include <GUIConstantsEx.au3>

Opt( "MustDeclareVars", 1 )

Global $aGuiInfo[6+1][2] ; 6 rows for GUIs + 1 row for general info
$aGuiInfo[0][0] = 0 ; Current number of GUIs
$aGuiInfo[0][1] = 6 ; Max number of GUIs

Example()

Func Example()
  ; Max number of GUIs is $aGuiInfo[0][1]
  If $aGuiInfo[0][0] = $aGuiInfo[0][1] Then Return

  ; Increase number of GUIs
  $aGuiInfo[0][0] += 1

  ; Find first available index
  For $iIdx = 1 To $aGuiInfo[0][1]
    If Not $aGuiInfo[$iIdx][0] Then ExitLoop
  Next

  ; Create GUI
  Local $hGui = GUICreate( "GUI " & $iIdx, 300, 200, 100 + 350 * Mod($iIdx-1,Int($aGuiInfo[0][1]/2)), 100 + 270 * Int(($iIdx-1)/Int($aGuiInfo[0][1]/2)) )
  Local $idNew  = GUICtrlCreateButton( "Create new GUI", 20,  45, 260, 30 )
  Local $idExit = GUICtrlCreateButton( "Exit script",    20, 120, 260, 30 )

  ; Store GUI info
  $aGuiInfo[$iIdx][0] = $iIdx

  ; Show GUI
  GUISetState()

  ; Main loop
  While 1
    Switch GUIGetMsg()
      Case $idNew
        Example()
      Case $idExit
        Exit
      Case $GUI_EVENT_CLOSE
        $aGuiInfo[$iIdx][0] = 0 ; $iIdx
        $aGuiInfo[0][0] -= 1 ; Decrease number of GUIs
        ExitLoop
    EndSwitch
  WEnd

  GUIDelete( $hGui )
EndFunc

 

Test-02.au3
Test-02.au3 is a non-blocked version of Test-01.au3:

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <ButtonConstants.au3>
#include <MenuConstants.au3>

Opt( "MustDeclareVars", 1 )

Global $aGuiInfo[6+1][5] ; 6 rows for GUIs + 1 row for general info
  $aGuiInfo[0][0] = 0 ; Current number of GUIs
  $aGuiInfo[0][1] = 6 ; Max number of GUIs
; $aGuiInfo[0][2] = $iIdx, $idNew click event
; $aGuiInfo[0][3] = $iIdx, $idExit click event
; $aGuiInfo[0][4] = $iIdx, $GUI_EVENT_CLOSE, GUI close event

; $aGuiInfo[$iIdx][0] = $iIdx, $iIdx > 0
; $aGuiInfo[$iIdx][1] = $hGui
; $aGuiInfo[$iIdx][2] = $idNew
; $aGuiInfo[$iIdx][3] = $idExit
; $aGuiInfo[$iIdx][4] = $pMsgHandler

Example()

Func Example()
  ; Max number of GUIs is $aGuiInfo[0][1]
  If $aGuiInfo[0][0] = $aGuiInfo[0][1] Then Return

  ; Increase number of GUIs
  $aGuiInfo[0][0] += 1

  ; Find first available index
  For $iIdx = 1 To $aGuiInfo[0][1]
    If Not $aGuiInfo[$iIdx][0] Then ExitLoop
  Next

  ; Create GUI
  Local $hGui = GUICreate( "GUI " & $iIdx, 300, 200, 100 + 350 * Mod($iIdx-1,Int($aGuiInfo[0][1]/2)), 100 + 270 * Int(($iIdx-1)/Int($aGuiInfo[0][1]/2)) )
  Local $idNew  = GUICtrlCreateButton( "Create new GUI", 20,  45, 260, 30 )
  Local $idExit = GUICtrlCreateButton( "Exit script",    20, 120, 260, 30 )

  ; Register non-blocked message handler
  Local $pMsgHandler = DllCallbackGetPtr( DllCallbackRegister( "MsgHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
  DllCall( "comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $hGui, "ptr", $pMsgHandler, "uint_ptr", $iIdx, "dword_ptr", 0 ) ; $iSubclassId = $iIdx, $pData = 0

  ; Store GUI info
  $aGuiInfo[$iIdx][0] = $iIdx
  $aGuiInfo[$iIdx][1] = $hGui
  $aGuiInfo[$iIdx][2] = $idNew
  $aGuiInfo[$iIdx][3] = $idExit
  $aGuiInfo[$iIdx][4] = $pMsgHandler

  ; Show GUI
  GUISetState()

  ; Main loop
  While Sleep(10)
    If $aGuiInfo[0][0] = 0 Then ExitLoop

    For $iMsg = 2 To 4
      If $aGuiInfo[0][$iMsg] Then ExitLoop
    Next
    If $iMsg = 5 Then ContinueLoop
    $iIdx = $aGuiInfo[0][$iMsg]
    $aGuiInfo[0][$iMsg] = 0
  
    Switch $iMsg
      ; $idNew click event
      Case 2
        Example()

      ; $idExit click event
      Case 3
        For $iIdx = 1 To $aGuiInfo[0][1]
          If Not $aGuiInfo[$iIdx][0] Then ContinueLoop
          DllCall( "comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", $aGuiInfo[$iIdx][1], "ptr", $aGuiInfo[$iIdx][4], "uint_ptr", $iIdx )
          GUIDelete( $aGuiInfo[$iIdx][1] ) ; GUIDelete( $hGui )
        Next
        Exit

      ; GUI close event
      Case 4
        DllCall( "comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", $aGuiInfo[$iIdx][1], "ptr", $aGuiInfo[$iIdx][4], "uint_ptr", $iIdx )
        GUIDelete( $aGuiInfo[$iIdx][1] ) ; GUIDelete( $hGui )
        $aGuiInfo[$iIdx][0] = 0 ; $iIdx
        $aGuiInfo[0][0] -= 1 ; Decrease number of GUIs
        If $aGuiInfo[0][0] = 0 Then ExitLoop
    EndSwitch
  WEnd
EndFunc

Func MsgHandler( $hWnd, $iMsg, $wParam, $lParam, $iIdx, $pData ) ; $iSubclassId = $iIdx
  If $iMsg <> $WM_COMMAND And $iMsg <> $WM_SYSCOMMAND Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  Switch $iMsg
    Case $WM_COMMAND
      Switch BitAND( $wParam, 0xFFFF ) ; LoWord
        Case $aGuiInfo[$iIdx][2] ; $idNew
          Switch BitShift( $wParam, 16 ) ; HiWord
            Case $BN_CLICKED
              $aGuiInfo[0][2] = $iIdx ; $idNew click event
          EndSwitch
        Case $aGuiInfo[$iIdx][3] ; $idExit
          Switch BitShift( $wParam, 16 ) ; HiWord
            Case $BN_CLICKED
              $aGuiInfo[0][3] = $iIdx ; $idExit click event
          EndSwitch
      EndSwitch

    Case $WM_SYSCOMMAND
      Switch $hWnd
        Case $aGuiInfo[$iIdx][1] ; $hGui
          Switch $wParam
            Case $SC_CLOSE
              $aGuiInfo[0][4] = $iIdx ; GUI close event
          EndSwitch
      EndSwitch
  EndSwitch

  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $pData
EndFunc

; $aGuiInfo[0][2] = $iIdx, $idNew click event
; $aGuiInfo[0][3] = $iIdx, $idExit click event
; $aGuiInfo[0][4] = $iIdx, $GUI_EVENT_CLOSE, GUI close event

; $aGuiInfo[$iIdx][0] = $iIdx, $iIdx > 0
; $aGuiInfo[$iIdx][1] = $hGui
; $aGuiInfo[$iIdx][2] = $idNew
; $aGuiInfo[$iIdx][3] = $idExit
; $aGuiInfo[$iIdx][4] = $pMsgHandler

 

Note that Test-02.au3 is neither running in MessageLoop Mode (GUIGetMsg isn't called) nor in OnEvent Mode (Opt("GUIOnEventMode", 1) isn't set). Instead, messages are handled through subclassing.

And of course, the main loop is still blocked in all GUIs except in the last created GUI. But the chain of GUIs and subclasses means that a message is propagated from one GUI to the next. When the message reaches the last created GUI, where the main loop isn't blocked, the message is handled. The information in the global $aGuiInfo array makes it possible to execute the corresponding code against the proper (active) GUI.

 

Test-03.au3
Test-03.au3 is a non-blocked version of Test-00.au3:

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6

#include <ArrayDisplay142-2.au3>

ShowArray0( 0, 0 )

Func ShowArray0( $p1, $p2 )
  Local $aArray0 = [ 00, 01, 02, 03, 04, 05, 06, 07, 08, 09 ]
  _ArrayDisplay142( $aArray0, "$aArray0", "", 0, Default, Default, Default, Default, ShowArray1 )
  #forceref $p1, $p2
EndFunc

Func ShowArray1( $p1, $p2 )
  Local $aArray1 = [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 11, 12, 13, 14 ]
  _ArrayDisplay142( $aArray1, "$aArray1", "", 0, Default, Default, Default, Default, ShowArray2 )
  #forceref $p1, $p2
EndFunc

Func ShowArray2( $p1, $p2 )
  Local $aArray2 = [ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]
  _ArrayDisplay142( $aArray2, "$aArray2", "", 0, Default, Default, Default, Default, ShowArray0 )
  #forceref $p1, $p2
EndFunc

ArrayDisplay142-2.au3 is included instead of ArrayDisplay142-1.au3.

After the GUI is created in ArrayDisplay142-2.au3, it's subclassed and information is stored in $aGuiInfo array:

; Register concurrent GUIs
; Increase number of GUIs
$aGuiInfo[0][0] += 1
; Find first available index
For $iIdx = 1 To $aGuiInfo[0][1]
  If Not $aGuiInfo[$iIdx][0] Then ExitLoop
Next
; Register non-blocked message handler
Local $pMsgHandler = DllCallbackGetPtr( DllCallbackRegister( "MsgHandler", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr" ) )
DllCall( "comctl32.dll", "bool", "SetWindowSubclass", "hwnd", $hGUI, "ptr", $pMsgHandler, "uint_ptr", $iIdx, "dword_ptr", 0 ) ; $iSubclassId = $iIdx, $pData = 0
; Store GUI info
$aGuiInfo[$iIdx][0]  = $iIdx
$aGuiInfo[$iIdx][1]  = $hGUI
$aGuiInfo[$iIdx][2]  = $idListView
$aGuiInfo[$iIdx][3]  = $idCopy_ID
$aGuiInfo[$iIdx][4]  = $idCopy_Data
$aGuiInfo[$iIdx][5]  = $idUser_Func
$aGuiInfo[$iIdx][6]  = $idExit_Script
$aGuiInfo[$iIdx][7]  = $pMsgHandler
$aGuiInfo[$iIdx][8]  = $avArrayText
$aGuiInfo[$iIdx][9]  = $aArray
$aGuiInfo[$iIdx][10] = $iItem_Start
$aGuiInfo[$iIdx][11] = $iItem_End
$aGuiInfo[$iIdx][12] = $iSubItem_Start
$aGuiInfo[$iIdx][13] = $iSubItem_End
$aGuiInfo[$iIdx][14] = $iVerbose
$aGuiInfo[$iIdx][15] = $iCW_ColWidth
$aGuiInfo[$iIdx][16] = $sHeader
$aGuiInfo[$iIdx][17] = $vUser_Separator
$aGuiInfo[$iIdx][18] = $hUser_Function

 

This is the new main loop:

While Sleep(10)
  If $aGuiInfo[0][0] = 0 Then ExitLoop

  ;$iMsg = GUIGetMsg() ; Variable needed to check which "Copy" button was pressed
  For $iMsg = 3 To 7
    If $aGuiInfo[0][$iMsg] Then ExitLoop
  Next
  If $iMsg = 8 Then ContinueLoop
  $iIdx = $aGuiInfo[0][$iMsg]
  $aGuiInfo[0][$iMsg] = 0

  Switch $iMsg
    ;Case $idCopy_ID, $idCopy_Data
    Case 3, 4
      $idListView = $aGuiInfo[$iIdx][2]
      $avArrayText = $aGuiInfo[$iIdx][8]
      $iItem_Start = $aGuiInfo[$iIdx][10]
      $iItem_End = $aGuiInfo[$iIdx][11]
      $iSubItem_Start = $aGuiInfo[$iIdx][12]
      $iSubItem_End = $aGuiInfo[$iIdx][13]
      $iVerbose = $aGuiInfo[$iIdx][14]
      $iCW_ColWidth = $aGuiInfo[$iIdx][15]
      $sHeader = $aGuiInfo[$iIdx][16]
      $vUser_Separator = $aGuiInfo[$iIdx][17]
      ; Count selected rows
      Local $iSel_Count = GUICtrlSendMsg($idListView, $LVM_GETSELECTEDCOUNT, 0, 0)
      ; Display splash dialog if required
      If $iVerbose And (Not $iSel_Count) And ($iItem_End - $iItem_Start) * ($iSubItem_End - $iSubItem_Start) > 10000 Then
        SplashTextOn("ArrayDisplay", "Copying data" & @CRLF & @CRLF & "Please be patient", 300, 100)
      EndIf
      ; Generate clipboard text
      Local $sClip = "", $sItem, $aSplit
      ; Add items
      For $i = 0 To $iItem_End - $iItem_Start
        ; Skip if copying selected rows and item not selected
        If $iSel_Count And Not (GUICtrlSendMsg($idListView, $LVM_GETITEMSTATE, $i, $LVIS_SELECTED)) Then
          ContinueLoop
        EndIf
        $sItem = $avArrayText[$i]
        ;If $iMsg = $idCopy_Data Then
        If $iMsg = 4 Then
          ; Remove row ID if required
          $sItem = StringRegExpReplace($sItem, "^\[\d+\].(.*)$", "$1")
        EndIf
        If $iCW_ColWidth Then
          ; Expand columns
          $aSplit = StringSplit($sItem, $sAD_Separator)
          $sItem = ""
          For $j = 1 To $aSplit[0]
            $sItem &= StringFormat("%-" & $iCW_ColWidth + 1 & "s", StringLeft($aSplit[$j], $iCW_ColWidth))
          Next
        Else
          ; Use defined separator
          $sItem = StringReplace($sItem, $sAD_Separator, $vUser_Separator)
        EndIf
        $sClip &= $sItem & @CRLF
      Next
      ; Add header line if required
      ;If $iMsg = $idCopy_ID Then
      If $iMsg = 3 Then
        If $iCW_ColWidth Then
          $aSplit = StringSplit($sHeader, $sAD_Separator)
          $sItem = ""
          For $j = 1 To $aSplit[0]
            $sItem &= StringFormat("%-" & $iCW_ColWidth + 1 & "s", StringLeft($aSplit[$j], $iCW_ColWidth))
          Next
        Else
          $sItem = StringReplace($sHeader, $sAD_Separator, $vUser_Separator)
        EndIf
        $sClip = $sItem & @CRLF & $sClip
      EndIf
      ;Send to clipboard
      ClipPut($sClip)
      ; Remove splash if used
      SplashOff()
      ; Refocus ListView
      GUICtrlSetState($idListView, $GUI_FOCUS)

    ;Case $idUser_Func
    Case 5
      ; Get selected indices
      $idListView = $aGuiInfo[$iIdx][2]
      $iItem_Start = $aGuiInfo[$iIdx][10]
      Local $aiSelItems[$iRowLimit] = [0]
      If GUICtrlSendMsg($idListView, $LVM_GETSELECTEDCOUNT, 0, 0) Then
        For $i = 0 To GUICtrlSendMsg($idListView, $LVM_GETITEMCOUNT, 0, 0)
          If GUICtrlSendMsg($idListView, $LVM_GETITEMSTATE, $i, $LVIS_SELECTED) Then
            $aiSelItems[0] += 1
            $aiSelItems[$aiSelItems[0]] = $i + $iItem_Start
          EndIf
        Next
      EndIf
      ReDim $aiSelItems[$aiSelItems[0] + 1]
      ; Pass array and selection to user function
      Opt("GUIDataSeparatorChar", $sCurr_Separator)
      ;$hUser_Function($aArray, $aiSelItems)
      $aGuiInfo[$iIdx][18]($aGuiInfo[$iIdx][9], $aiSelItems)
      Opt("GUIDataSeparatorChar", $sAD_Separator)
      GUICtrlSetState($idListView, $GUI_FOCUS)

    ;Case $idExit_Script
    Case 6
      ; Clear up
      For $iIdx = 1 To $aGuiInfo[0][1]
        If Not $aGuiInfo[$iIdx][0] Then ContinueLoop
        DllCall( "comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", $aGuiInfo[$iIdx][1], "ptr", $aGuiInfo[$iIdx][7], "uint_ptr", $iIdx )
        GUIDelete( $aGuiInfo[$iIdx][1] ) ; GUIDelete( $hGui )
        $aGuiInfo[$iIdx][8] = 0 ; $avArrayText
        $aGuiInfo[$iIdx][9] = 0 ; $aArray
      Next
      Exit

    ;Case $GUI_EVENT_CLOSE
    Case 7
      DllCall( "comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", $aGuiInfo[$iIdx][1], "ptr", $aGuiInfo[$iIdx][7], "uint_ptr", $iIdx )
      GUIDelete( $aGuiInfo[$iIdx][1] ) ; GUIDelete( $hGui )
      $aGuiInfo[$iIdx][8] = 0 ; $avArrayText
      $aGuiInfo[$iIdx][9] = 0 ; $aArray
      $aGuiInfo[$iIdx][0] = 0 ; $iIdx
      $aGuiInfo[0][0] -= 1 ; Decrease number of GUIs
      If $aGuiInfo[0][0] = 0 Then ExitLoop
  EndSwitch
WEnd

 

And the subclass function:

Func MsgHandler( $hWnd, $iMsg, $wParam, $lParam, $iIdx, $pData ) ; $iSubclassId = $iIdx
  If $iMsg <> $WM_COMMAND And $iMsg <> $WM_SYSCOMMAND Then Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]

  Switch $iMsg
    Case $WM_COMMAND
      Switch BitAND( $wParam, 0xFFFF ) ; LoWord
        Case $aGuiInfo[$iIdx][3] ; $idCopy_ID
          Switch BitShift( $wParam, 16 ) ; HiWord
            Case $BN_CLICKED
              $aGuiInfo[0][3] = $iIdx ; $idCopy_ID click event
          EndSwitch
        Case $aGuiInfo[$iIdx][4] ; $idCopy_Data
          Switch BitShift( $wParam, 16 ) ; HiWord
            Case $BN_CLICKED
              $aGuiInfo[0][4] = $iIdx ; $idCopy_Data click event
          EndSwitch
        Case $aGuiInfo[$iIdx][5] ; $idUser_Func
          Switch BitShift( $wParam, 16 ) ; HiWord
            Case $BN_CLICKED
              $aGuiInfo[0][5] = $iIdx ; $idUser_Func click event
          EndSwitch
        Case $aGuiInfo[$iIdx][6] ; $idExit_Script
          Switch BitShift( $wParam, 16 ) ; HiWord
            Case $BN_CLICKED
              $aGuiInfo[0][6] = $iIdx ; $idExit_Script click event
          EndSwitch
      EndSwitch

    Case $WM_SYSCOMMAND
      Switch $hWnd
        Case $aGuiInfo[$iIdx][1] ; $hGUI
          Switch $wParam
            Case $SC_CLOSE
              $aGuiInfo[0][7] = $iIdx ; GUI close event
          EndSwitch
      EndSwitch
  EndSwitch

  Return DllCall( "comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hWnd, "uint", $iMsg, "wparam", $wParam, "lparam", $lParam )[0]
  #forceref $pData
EndFunc

 

Edited by LarsJ
New zip-file next post
Link to comment
Share on other sites

  • 4 months later...

In first code box in first post, it's shown how to open three _ArrayDisplays at once. The rest of first post is about how to make all three _ArrayDisplays responsive.

But it would be much easier if you could open three _ArrayDisplays at once this way (Test-05.au3):

#include <ArrayDisplay142-2.au3>

Global $aArray0 = [ 00, 01, 02, 03, 04, 05, 06, 07, 08, 09 ]
Global $aArray1 = [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 11, 12, 13, 14 ]
Global $aArray2 = [ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]

; Show all three arrays at once
_ArrayDisplay142( $aArray0, "$aArray0", "", 128 ) ; 128 => $bMainLoop = False
_ArrayDisplay142( $aArray1, "$aArray1", "", 128 ) ; 128 => $bMainLoop = False
_ArrayDisplay142( $aArray2, "$aArray2", "",   0 ) ;   0 => $bMainLoop = True

It's very very easy. I simply cannot understand why I didn't add the few extra code lines needed, already in first post.

The idea is to use a flag value to determine whether the mainloop should run:

While $bMainLoop And Sleep(10)
  ; ...
WEnd

Default is $bMainLoop = True.

What happens if $bMainLoop = False? GUI and ListView have been created before mainloop. In my version of _ArrayDisplay I have carefully ensured that all GUI delete code runs inside the mainloop.

If there is no mainloop, GUI and ListView will be created at the start of the function. And then the function ends without the GUI being deleted.

In the example, both $aArray0 and $aArray1 are shown without running mainloops, but $aArray2 is shown with a running mainloop to make the GUIs responsive and to ensure that the program doesn't end.

Additionally, a little bit of code is added that is executed when $aArray2 is shown. This code sets $bMainLoop = True for $aArray0 and $aArray1, so that one of these GUIs can take over mainloop if $aArray2 GUI is deleted. But by far the majority of this code is already implemented in first post. That's why it's so easy.

And of course you can still use code like this (Test-06.au3):

#include <ArrayDisplay142-2.au3>

Global $aArray0 = [ 00, 01, 02, 03, 04, 05, 06, 07, 08, 09 ]
Global $aArray1 = [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 11, 12, 13, 14 ]
Global $aArray2 = [ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]

; Show all three arrays at once
_ArrayDisplay142( $aArray0, "$aArray0", "", 128, Default, Default, Default, Default, ShowArray1 ) ; 128 => $bMainLoop = False
_ArrayDisplay142( $aArray1, "$aArray1", "", 128, Default, Default, Default, Default, ShowArray2 ) ; 128 => $bMainLoop = False
_ArrayDisplay142( $aArray2, "$aArray2", "",   0, Default, Default, Default, Default, ShowArray0 ) ;   0 => $bMainLoop = True

Func ShowArray0( $p1, $p2 )
  _ArrayDisplay142( $aArray0, "$aArray0", "", 0, Default, Default, Default, Default, ShowArray1 )
  #forceref $p1, $p2
EndFunc

Func ShowArray1( $p1, $p2 )
  _ArrayDisplay142( $aArray1, "$aArray1", "", 0, Default, Default, Default, Default, ShowArray2 )
  #forceref $p1, $p2
EndFunc

Func ShowArray2( $p1, $p2 )
  _ArrayDisplay142( $aArray2, "$aArray2", "", 0, Default, Default, Default, Default, ShowArray0 )
  #forceref $p1, $p2
EndFunc

See example Test-04.au3 to study the code. This is a continuation of Test-02.au3 in the first post.
 

Zip-file
The zip-file contains all examples and include files.

You need AutoIt 3.3.10 or later. Tested on Windows 10 and Windows 7.

Comments are welcome. Let me know if there are any issues.

NonBlocked.7z

Edited by LarsJ
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...