Jump to content

Recommended Posts

Posted (edited)

 Image-Search-DLL-Test-Suite.png

Source ImageSearch Automation Suite:

#RequireAdmin
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_Res_Comment=ImageSearch Test Suite by Dao Van Trong
#AutoIt3Wrapper_Res_Description=ImageSearch Test Suite v3.3
#AutoIt3Wrapper_Res_Fileversion=1.0.0.0
#AutoIt3Wrapper_Res_ProductName=ImageSearch Test Suite
#AutoIt3Wrapper_Res_ProductVersion=1.0.0.0
#AutoIt3Wrapper_Res_CompanyName=TRONG.PRO
#AutoIt3Wrapper_Res_LegalCopyright=Dao Van Trong - TRONG.PRO
#AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker
#AutoIt3Wrapper_UseX64=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
;~ #pragma compile(x64, true) ; false
#cs ----------------------------------------------------------------------------
    ;
    ;    Title .........: ImageSearch Test Suite v3.3
    ;    AutoIt Version : 3.3.16.1+
    ;    Author ........: Dao Van Trong (TRONG.PRO)
    ;    UDF Version ...: 3.3
    ;
    ;    Note ..........: This script is a graphical user interface (GUI) front-end for the
    ;                     ImageSearchDLL_UDF.au3 and its underlying ImageSearch.dll.
    ;                     It allows for visual configuration of image search tasks on the screen
    ;                     or within another image file, and now includes a file browser for
    ;                     selecting existing images.
    ;
    ; -------------------------------------------------------------------------------------------------------------------------------
    ; #SECTION# SCRIPT OVERVIEW
    ; -------------------------------------------------------------------------------------------------------------------------------
    ;
    ; This enhanced script provides a powerful and user-friendly interface for performing complex image search and automation tasks.
    ; It acts as a control panel for the high-performance ImageSearch UDF, allowing you to visually configure, execute,
    ; and log search operations without writing complex code. It supports searching images on the screen or within another image.
    ; NEW: Added "Browse" buttons to select existing image files instead of only capturing from screen.
    ;
    ; -------------------------------------------------------------------------------------------------------------------------------
    ; #SECTION# FIRST-TIME SETUP
    ; -------------------------------------------------------------------------------------------------------------------------------
    ;
    ; Before you can start a search, you need to provide the images you want to find.
    ;
    ; 1. RUN THE SCRIPT: The main window will appear. On the right side, you will see 12 empty "Image Target" slots.
    ;
    ; 2. CREATE OR SELECT AN IMAGE:
    ;    - Click the "Create" button next to slot #1 to capture a region from the screen.
    ;    - OR click the "Browse" button next to slot #1 to select an existing image file.
    ;
    ; 3. FOR CREATE: The script window will hide. Your mouse cursor will turn into a crosshair.
    ;    Click and drag a rectangle around the object on the screen you want to find. When you release the mouse button,
    ;    a bitmap image named "Search_1.bmp" will be saved in the same directory as the script.
    ;
    ; 4. FOR BROWSE: A file dialog will open allowing you to select BMP, JPG, PNG, or other image files.
    ;    The selected image will be copied to the script directory as "Search_X.bmp".
    ;
    ; 5. PREVIEW UPDATES: The image you just captured/selected will now appear in the preview panel for that slot.
    ;
    ; 6. REPEAT: Repeat this process for any other images you need to find (up to 12).
    ;
    ; 7. IMAGE-IN-IMAGE SEARCH: To search within an image, select "Search in Image" mode, then specify a source image file
    ;    using the "Browse Source Image" button.
    ;
#ce ----------------------------------------------------------------------------

; === INCLUDES ===
#include <Array.au3>
#include <GDIPlus.au3>
#include <ScreenCapture.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <EditConstants.au3>
#include <StaticConstants.au3>
#include <ButtonConstants.au3>
#include <Date.au3>
#include <Misc.au3>
#include <Math.au3>
#include <GuiEdit.au3>
#include <GuiStatusBar.au3>
#include <GuiComboBox.au3>
#include <ComboConstants.au3>
#include <File.au3>
#include "ImageSearchDLL_UDF.au3" ; Core image search functionality v3.3

; === GLOBAL CONSTANTS ===
Global Const $MAX_IMAGES = 12 ; Maximum number of image targets the GUI supports.
Global Const $g_sImgEmptySlots = @TempDir & "\" & 'Default_UIF.jpg' ; Default image to show in empty slots.
Global Const $g_sPlaceholderPath = _Deploy_ImgEmptySlots() ? $g_sImgEmptySlots : @WindowsDir & "\Web\Wallpaper\Windows\img0.jpg"

; === GLOBAL VARIABLES ===
Global $g_asImagePaths[$MAX_IMAGES] ; Array to store the file paths for the 12 target images.
Global $g_nMsg ; Stores the message code from GUIGetMsg() for the main event loop.
Global $g_hMainGUI ; Handle for the main GUI window.
Global $g_hLog ; Handle for the activity log Edit control.
Global $g_hStatusBar ; Handle for the status bar at the bottom of the GUI.

; --- GUI Control IDs ---
Global $g_idBtnStart, $g_idBtnSelectAll, $g_idBtnDeselectAll, $g_idBtnSelectArea, $g_idSystemInfo
Global $g_idInputDelay, $g_idChkMoveMouse
Global $g_idRadNoClick, $g_idRadSingleClick, $g_idRadDoubleClick
Global $g_idChkWait, $g_idInputWaitTime
Global $g_idChkUseArea, $g_idInputLeft, $g_idInputTop, $g_idInputRight, $g_idInputBottom
Global $g_idChkMultiSearch, $g_idChkFindAll, $g_idChkUseTolerance, $g_idInputTolerance, $g_idChkEnableDebug, $g_idChkUseCache
Global $g_aidPic[$MAX_IMAGES], $g_aidChkSearch[$MAX_IMAGES], $g_aidBtnCreate[$MAX_IMAGES], $g_aidBtnBrowse[$MAX_IMAGES]
Global $g_idRadSearchOnScreen, $g_idRadSearchInImage, $g_idInputSourceImage, $g_idBtnBrowseSource
; NEW v3.3: Monitor selection controls
Global $g_idChkUseSpecificMonitor, $g_idComboMonitor
Global $g_iMonitorCount = 0

_Main()

; #FUNCTION# ====================================================================================================================
; Name...........: _Main
; Description....: Main program entry point. Initializes all necessary components and enters the GUI message loop to handle user interactions.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _Main()
    ; Explicitly initialize the ImageSearch library. This is a critical first step.
    If Not _ImageSearch_Startup() Then
        MsgBox(16, "Fatal Error", "Failed to initialize the ImageSearch DLL. @error: " & @error & @CRLF & "The script will now exit.")
        Exit
    EndIf

    ; Start GDI+ for image processing and create the GUI
    _GDIPlus_Startup()
    _InitializeImagePaths()
    _EnumerateMonitors() ; NEW: Get monitor list using UDF
    _CreateGUI()
    _UpdateAllImagePreviews()
    _RefreshImageTooltips()

    ; Main event loop to handle GUI events. The script will wait here for user input.
    While 1
        $g_nMsg = GUIGetMsg()
        Switch $g_nMsg
            Case $GUI_EVENT_CLOSE
                Exit ; Exit the loop and terminate the script.

            Case $g_idRadSearchInImage, $g_idRadSearchOnScreen
                _UpdateSearchModeControls()

            Case $g_idChkUseSpecificMonitor
                _UpdateMonitorControls()

            Case $g_idBtnStart
                _ExecuteSearch() ; Begin the image search process.

            Case $g_idBtnSelectAll
                _SelectAll(True) ; Check all image target checkboxes.

            Case $g_idBtnDeselectAll
                _SelectAll(False) ; Uncheck all image target checkboxes.

            Case $g_idBtnSelectArea
                _SelectAreaOnScreen() ; Allow user to define a search area on the screen.

            Case $g_idBtnBrowseSource
                _BrowseSourceImage() ; Open file dialog to select the source image for image-in-image search.

                ; Event handlers for the "Create" and "Browse" buttons for each image slot.
            Case $g_aidBtnCreate[0] To $g_aidBtnCreate[$MAX_IMAGES - 1]
                _HandleImageCreation($g_nMsg)

            Case $g_aidBtnBrowse[0] To $g_aidBtnBrowse[$MAX_IMAGES - 1]
                _HandleImageBrowse($g_nMsg)
        EndSwitch
    WEnd

    _Exit()
EndFunc   ;==>_Main

; #FUNCTION# ====================================================================================================================
; Name...........: _InitializeImagePaths
; Description....: Populates the global array '$g_asImagePaths' with default file paths for the search images (e.g., Search_1.bmp, Search_2.bmp).
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _InitializeImagePaths()
    For $i = 0 To $MAX_IMAGES - 1
        $g_asImagePaths[$i] = @ScriptDir & "\Search_" & $i + 1 & ".bmp"
    Next
EndFunc   ;==>_InitializeImagePaths

; #FUNCTION# ====================================================================================================================
; Name...........: _EnumerateMonitors
; Description....: Enumerates all monitors using UDF function and stores count
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _EnumerateMonitors()
    ; Use UDF's built-in monitor enumeration
    _ImageSearch_Monitor_GetList()
    $g_iMonitorCount = $g_aMonitorList[0][0]

    If $g_bImageSearch_Debug Then
        ConsoleWrite("Detected " & $g_iMonitorCount & " monitor(s)" & @CRLF)
        For $i = 1 To $g_iMonitorCount
            Local $sInfo = "Monitor " & $i & ": " & $g_aMonitorList[$i][5] & "x" & $g_aMonitorList[$i][6]
            If $g_aMonitorList[$i][7] Then $sInfo &= " (Primary)"
            ConsoleWrite("  " & $sInfo & @CRLF)
        Next
    EndIf
EndFunc   ;==>_EnumerateMonitors

; #FUNCTION# ====================================================================================================================
; Name...........: _PopulateMonitorCombo
; Description....: Populates the monitor selection combobox with detected monitors
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _PopulateMonitorCombo()
    If $g_iMonitorCount = 0 Then Return

    Local $sMonitorList = ""
    For $i = 1 To $g_iMonitorCount
        Local $sLabel = "Monitor " & $i
        If $g_aMonitorList[$i][7] Then $sLabel &= " (Primary)"
        $sLabel &= " - " & $g_aMonitorList[$i][5] & "x" & $g_aMonitorList[$i][6]
        $sMonitorList &= ($i > 1 ? "|" : "") & $sLabel
    Next

    ; Set data and select primary monitor by default
    GUICtrlSetData($g_idComboMonitor, $sMonitorList)

    ; Find and select primary monitor
    For $i = 1 To $g_iMonitorCount
        If $g_aMonitorList[$i][7] Then
            _GUICtrlComboBox_SetCurSel($g_idComboMonitor, $i - 1)
            Return
        EndIf
    Next
    _GUICtrlComboBox_SetCurSel($g_idComboMonitor, 0)
EndFunc   ;==>_PopulateMonitorCombo

Func _CreateSlot(ByRef $aChk, ByRef $aPic, ByRef $aBtnCreate, ByRef $aBtnBrowse, $idx, $col, $row, _
        $baseLeft, $baseTop, $colSpacing, $rowSpacing, $slotGroupW, $slotGroupH, $slotChkW, $slotChkH, $slotPicW, $slotPicH, $slotBtnW, $slotBtnH)
    Local $leftGroup = $baseLeft + ($col * $colSpacing)
    Local $topGroup = $baseTop + ($row * $rowSpacing)
    GUICtrlCreateGroup("Slot " & ($idx + 1), $leftGroup, $topGroup, $slotGroupW, $slotGroupH)
    Local $ox_chk = 5, $oy_chk = 13
    Local $ox_pic = 10, $oy_pic = 36
    Local $ox_btnCreate = 5, $oy_btnCreate = 142
    Local $ox_btnBrowse = $ox_btnCreate + $slotBtnW + 6
    $aChk[$idx] = GUICtrlCreateCheckbox("Search", $leftGroup + $ox_chk, $topGroup + $oy_chk, $slotChkW, $slotChkH)
    $aPic[$idx] = GUICtrlCreatePic("", $leftGroup + $ox_pic, $topGroup + $oy_pic, $slotPicW, $slotPicH)
    GUICtrlSetState(-1, $GUI_DISABLE)
    $aBtnCreate[$idx] = GUICtrlCreateButton("Create", $leftGroup + $ox_btnCreate, $topGroup + $oy_btnCreate, $slotBtnW, $slotBtnH)
    GUICtrlSetTip(-1, "Capture an image from the screen for slot " & ($idx + 1) & ".")
    $aBtnBrowse[$idx] = GUICtrlCreateButton("Browse", $leftGroup + $ox_btnBrowse, $topGroup + $oy_btnCreate, $slotBtnW, $slotBtnH)
    GUICtrlSetTip(-1, "Select an existing image file for slot " & ($idx + 1) & ".")
    GUICtrlCreateGroup("", -99, -99, 1, 1)
EndFunc   ;==>_CreateSlot

Func _CreateGUI()
    Local $winW = 1024, $winH = 791
    Local $cfgX = 10, $cfgY = 2, $cfgW = 460, $cfgH = 350
    Local $smX = 20, $smY = 22, $smW = 210, $smH = 310
    Local $smInnerX = 30, $smInnerY = 42
    Local $inputSourceW = 196, $inputSourceH = 23
    Local $btnBrowseX = 167, $btnBrowseY = 76, $btnBrowseW = 55, $btnBrowseH = 23
    Local $paramX = 240, $paramY = 22, $paramW = 220, $paramH = 120
    Local $labelW = 90, $labelH = 20
    Local $inputW = 80, $inputH = 23
    Local $actX = 240, $actY = 152, $actW = 220, $actH = 132
    Local $actLabelW = 40, $actLabelH = 20
    Local $actRadioW = 60, $actRadioH = 20
    Local $areaX = 10, $areaY = 356, $areaW = 460, $areaH = 80
    Local $areaChkX = 23, $areaChkY = 373, $areaChkW = 150, $areaChkH = 20
    Local $areaLabelW = 45, $areaLabelH = 20
    Local $areaInputW = 70, $areaInputH = 23
    Local $btnSelectAreaX = 191, $btnSelectAreaY = 373, $btnSelectAreaW = 268, $btnSelectAreaH = 25
    Local $imgX = 474, $imgY = 10, $imgW = 556, $imgH = 546
    Local $slotGroupW = 127, $slotGroupH = 172
    Local $slotChkW = 100, $slotChkH = 20
    Local $slotPicW = 100, $slotPicH = 100
    Local $slotBtnW = 55, $slotBtnH = 22
    Local $logX = 10, $logY = 604, $logW = 1008, $logH = 158
    Local $logEditX = 15, $logEditY = 619, $logEditW = 1000, $logEditH = 136
    Local $sysX = 10, $sysY = 440, $sysW = 460, $sysH = 160
    Local $sysLabelW = 440, $sysLabelH = 20
    Local $sysInfoLabelH = 40
    Local $btnSelectAllX = 477, $btnSelectAllY = 560, $btnSelectAllW = 100, $btnSelectAllH = 38
    Local $btnStartX = 580, $btnStartY = 560, $btnStartW = 324, $btnStartH = 38
    Local $btnDeselectAllX = 912, $btnDeselectAllY = 560, $btnDeselectAllW = 100, $btnDeselectAllH = 38

    ; Create main window
    $g_hMainGUI = GUICreate("ImageSearch Automation by Dao Van Trong - TRONG.PRO", $winW, $winH)
    GUISetFont(9, 400, 0, "Segoe UI", $g_hMainGUI, 5)
    GUISetBkColor(0xF3F3F3, $g_hMainGUI)
    GUICtrlCreateGroup("Configuration", $cfgX, $cfgY, $cfgW, $cfgH)
    GUICtrlCreateGroup("Search Mode", $smX, $smY, $smW, $smH)
    $g_idRadSearchOnScreen = GUICtrlCreateRadio("Search on All Screens", $smInnerX, $smInnerY, 180, 20)
    GUICtrlSetState(-1, $GUI_CHECKED)
    GUICtrlSetTip(-1, "Search across all monitors (virtual desktop)")

    ; NEW v3.3: Specific monitor selection
    $g_idChkUseSpecificMonitor = GUICtrlCreateCheckbox("Use Specific Monitor", $smInnerX + 10, $smInnerY + 25, 160, 20)
    GUICtrlSetTip(-1, "Search on a specific monitor only (2-3x faster!)")
    $g_idComboMonitor = GUICtrlCreateCombo("", $smInnerX + 10, $smInnerY + 50, 180, 25, BitOR($CBS_DROPDOWNLIST, $CBS_AUTOHSCROLL))
    GUICtrlSetTip(-1, "Select which monitor to search on")
    GUICtrlSetState($g_idComboMonitor, $GUI_DISABLE)
    _PopulateMonitorCombo()

    $g_idRadSearchInImage = GUICtrlCreateRadio("Search in Image", $smInnerX, $smInnerY + 85, 132, 20)
    GUICtrlSetTip(-1, "Search for images within a specified source image file.")
    $g_idInputSourceImage = GUICtrlCreateInput("", $smInnerX, $smInnerY + 115, $inputSourceW, $inputSourceH, BitOR($GUI_SS_DEFAULT_INPUT, $ES_READONLY))
    $g_idBtnBrowseSource = GUICtrlCreateButton("Browse", $btnBrowseX, $smInnerY + 115, $btnBrowseW, $btnBrowseH)
    GUICtrlSetTip(-1, "Select the source image file to search within.")
    $g_idChkMultiSearch = GUICtrlCreateCheckbox("Multi Search (All at once)", $smInnerX, $smInnerY + 150, 200, 20)
    GUICtrlSetTip(-1, "Finds the FIRST occurrence of ANY of the selected images.")
    $g_idChkFindAll = GUICtrlCreateCheckbox("Find All Occurrences", $smInnerX, $smInnerY + 175, 200, 20)
    GUICtrlSetState(-1, $GUI_CHECKED)
    $g_idChkWait = GUICtrlCreateCheckbox("Wait for Image Found", $smInnerX, $smInnerY + 200, 200, 20)
    $g_idChkUseTolerance = GUICtrlCreateCheckbox("Use Tolerance", $smInnerX, $smInnerY + 225, 200, 20)
    GUICtrlSetState(-1, $GUI_CHECKED)
    $g_idChkEnableDebug = GUICtrlCreateCheckbox("Enable DLL Debug", $smInnerX, $smInnerY + 250, 200, 20)
    If Not @Compiled Then GUICtrlSetState(-1, $GUI_CHECKED)

    ; NEW v3.3: Cache system
    $g_idChkUseCache = GUICtrlCreateCheckbox("Enable Cache (v3.3)", $smInnerX, $smInnerY + 275, 200, 20)
    GUICtrlSetTip(-1, "Enable persistent cache for 30-50% speed boost on repeated searches")
    GUICtrlCreateGroup("", -99, -99, 1, 1)

    GUICtrlCreateGroup("Parameters", $paramX, $paramY, $paramW, $paramH)
    GUICtrlCreateLabel("Timeout (ms):", $paramX + 10, $paramY + 22, $labelW, $labelH, $SS_RIGHT)
    $g_idInputWaitTime = GUICtrlCreateInput("5000", $paramX + 115, $paramY + 20, $inputW, $inputH)
    GUICtrlCreateLabel("Tolerance:", $paramX + 10, $paramY + 52, $labelW, $labelH, $SS_RIGHT)
    $g_idInputTolerance = GUICtrlCreateInput("10", $paramX + 115, $paramY + 50, $inputW, $inputH)
    GUICtrlCreateLabel("Delay (ms)", $paramX + 10, $paramY + 82, $labelW, $labelH, $SS_RIGHT)
    $g_idInputDelay = GUICtrlCreateInput("500", $paramX + 115, $paramY + 80, $inputW, $inputH)
    GUICtrlCreateGroup("", -99, -99, 1, 1)

    GUICtrlCreateGroup("Actions on Found", $actX, $actY, $actW, $actH)
    $g_idChkMoveMouse = GUICtrlCreateCheckbox("Move Mouse", $actX + 10, $actY + 20, 120, 20)
    GUICtrlSetState(-1, $GUI_CHECKED)
    GUICtrlCreateLabel("Click:", $actX + 10, $actY + 45, $actLabelW, $actLabelH)
    $g_idRadNoClick = GUICtrlCreateRadio("None", $actX + 65, $actY + 45, $actRadioW, $actRadioH)
    GUICtrlSetState(-1, $GUI_CHECKED)
    $g_idRadSingleClick = GUICtrlCreateRadio("Single", $actX + 10, $actY + 70, $actRadioW, $actRadioH)
    $g_idRadDoubleClick = GUICtrlCreateRadio("Double", $actX + 70, $actY + 70, $actRadioW, $actRadioH)
    GUICtrlCreateGroup("", -99, -99, 1, 1)

    GUICtrlCreateGroup("Search Area", $areaX, $areaY, $areaW, $areaH)
    $g_idChkUseArea = GUICtrlCreateCheckbox("Use Custom Area", $areaChkX, $areaChkY, $areaChkW, $areaChkH)
    ; All 4 inputs in one row
    GUICtrlCreateLabel("Left:", $areaX + 13, $areaY + 50, $areaLabelW, $areaLabelH)
    $g_idInputLeft = GUICtrlCreateInput("0", $areaX + 58, $areaY + 48, $areaInputW, $areaInputH)
    GUICtrlCreateLabel("Top:", $areaX + 135, $areaY + 50, $areaLabelW, $areaLabelH)
    $g_idInputTop = GUICtrlCreateInput("0", $areaX + 175, $areaY + 48, $areaInputW, $areaInputH)
    GUICtrlCreateLabel("Right:", $areaX + 252, $areaY + 50, $areaLabelW, $areaLabelH)
    $g_idInputRight = GUICtrlCreateInput(@DesktopWidth, $areaX + 297, $areaY + 48, $areaInputW, $areaInputH)
    GUICtrlCreateLabel("Bottom:", $areaX + 374, $areaY + 50, $areaLabelW, $areaLabelH)
    $g_idInputBottom = GUICtrlCreateInput(@DesktopHeight, $areaX + 420, $areaY + 48, $areaInputW, $areaInputH)
    $g_idBtnSelectArea = GUICtrlCreateButton("Select Area on Screen", $btnSelectAreaX, $btnSelectAreaY, $btnSelectAreaW, $btnSelectAreaH)
    GUICtrlCreateGroup("", -99, -99, 1, 1)
    ; RIGHT COLUMN: Image Targets group
    GUICtrlCreateGroup("Image Targets", $imgX, $imgY, $imgW, $imgH)
    Local $baseLeft = 479, $baseTop = 27
    Local $colSpacing = 135
    Local $rowSpacing = 172
    Dim $g_aidChkSearch[12], $g_aidPic[12], $g_aidBtnCreate[12], $g_aidBtnBrowse[12]
    Local $idx = 0
    For $r = 0 To 2
        For $c = 0 To 3
            _CreateSlot($g_aidChkSearch, $g_aidPic, $g_aidBtnCreate, $g_aidBtnBrowse, $idx, $c, $r, _
                    $baseLeft, $baseTop, $colSpacing, $rowSpacing, _
                    $slotGroupW, $slotGroupH, $slotChkW, $slotChkH, $slotPicW, $slotPicH, $slotBtnW, $slotBtnH)
            $idx += 1
        Next
    Next
    ; BOTTOM: Activity Log
    GUICtrlCreateGroup("Activity Log", $logX, $logY, $logW, $logH)
    $g_hLog = GUICtrlCreateEdit("", $logEditX, $logEditY, $logEditW, $logEditH, BitOR($ES_MULTILINE, $ES_READONLY, $WS_VSCROLL, $ES_AUTOVSCROLL))
    GUICtrlSetFont(-1, 9, 0, 0, "Segoe UI")
    GUICtrlCreateGroup("", -99, -99, 1, 1)

    ; System Information
    GUICtrlCreateGroup("System Information", $sysX, $sysY, $sysW, $sysH)
    GUICtrlCreateLabel("OS: " & @OSVersion & " (" & @OSArch & ")" & "   |   AutoIt: " & @AutoItVersion & (@AutoItX64 ? " (x64)" : ""), $sysX + 10, $sysY + 20, $sysLabelW, $sysLabelH)
    GUICtrlCreateLabel("UDF Version: " & $IMGS_UDF_VERSION & "   |   DLL: " & _ImageSearch_GetVersion(), $sysX + 10, $sysY + 45, $sysLabelW, $sysLabelH)
    GUICtrlCreateLabel("DLL Path: " & $g_sImgSearchDLL_Path, $sysX + 10, $sysY + 70, $sysLabelW, $sysLabelH)
    GUICtrlCreateLabel("Screen: " & @DesktopWidth & "x" & @DesktopHeight & "   |   Monitors: " & $g_iMonitorCount, $sysX + 10, $sysY + 95, $sysLabelW, $sysLabelH)
    $g_idSystemInfo = GUICtrlCreateLabel("System: " & _ImageSearch_GetSysInfo(), $sysX + 10, $sysY + 120, $sysLabelW, $sysInfoLabelH, $SS_LEFT)
    GUICtrlCreateGroup("", -99, -99, 1, 1)

    ; Action buttons
    $g_idBtnSelectAll = GUICtrlCreateButton("Select All", $btnSelectAllX, $btnSelectAllY, $btnSelectAllW, $btnSelectAllH)
    $g_idBtnStart = GUICtrlCreateButton("Start Search", $btnStartX, $btnStartY, $btnStartW, $btnStartH, $BS_DEFPUSHBUTTON)
    GUICtrlSetFont(-1, 12, 800, 0, "Segoe UI")
    $g_idBtnDeselectAll = GUICtrlCreateButton("Deselect All", $btnDeselectAllX, $btnDeselectAllY, $btnDeselectAllW, $btnDeselectAllH)

    ; Status bar
    $g_hStatusBar = _GUICtrlStatusBar_Create($g_hMainGUI)
    _UpdateStatus("Ready - ImageSearch Test Suite v3.3")

    GUISetState(@SW_SHOW)
    _UpdateSearchModeControls()

    ; Log initial info
    _LogWrite("═══════════════════════════════════════════════════════════")
    _LogWrite("ImageSearch Test Suite v3.3 - Initialized Successfully")
    _LogWrite("UDF Version: " & $IMGS_UDF_VERSION & "  |  DLL: " & _ImageSearch_GetVersion())
    _LogWrite("Detected " & $g_iMonitorCount & " monitor(s)")
    If $g_iMonitorCount > 0 Then
        For $i = 1 To $g_iMonitorCount
            Local $sMonInfo = "  Monitor " & $i & ": " & $g_aMonitorList[$i][5] & "x" & $g_aMonitorList[$i][6]
            If $g_aMonitorList[$i][7] Then $sMonInfo &= " (Primary)"
            _LogWrite($sMonInfo)
        Next
    EndIf
    _LogWrite("═══════════════════════════════════════════════════════════")
    _LogWrite("Ready to search. Select images and click 'Start Search'")
EndFunc   ;==>_CreateGUI

Func _UpdateGUI_SystemInfo()
    GUICtrlSetData($g_idSystemInfo, "System Info: " & _ImageSearch_GetSysInfo())
EndFunc   ;==>_UpdateGUI_SystemInfo

; #FUNCTION# ====================================================================================================================
; Name...........: _GetControlPos
; Description....: Calculates the X and Y coordinates for a control within a grid layout based on its index.
; Parameters.....: $iIndex     - The zero-based index of the control in the grid.
;                  $iX_Start   - The starting X coordinate of the grid.
;                  $iY_Start   - The starting Y coordinate of the grid.
;                  $iColWidth  - The width of each column.
;                  $iRowHeight - The height of each row.
;                  $iX         - [ByRef] Returns the calculated X coordinate.
;                  $iY         - [ByRef] Returns the calculated Y coordinate.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _GetControlPos($iIndex, $iX_Start, $iY_Start, $iColWidth, $iRowHeight, ByRef $iX, ByRef $iY)
    $iX = $iX_Start + ($iColWidth * Mod($iIndex, 4)) ; 4 controls per row
    $iY = $iY_Start + ($iRowHeight * Int($iIndex / 4))
EndFunc   ;==>_GetControlPos

; #FUNCTION# ====================================================================================================================
; Name...........: _CreateCheckboxGrid
; Description....: Creates a grid of checkboxes for selecting which image slots to search.
; Parameters.....: $aidArray   - Array to store control IDs.
;                  $iCount     - Number of checkboxes to create.
;                  $iX_Start   - Starting X coordinate.
;                  $iY_Start   - Starting Y coordinate.
;                  $iColWidth  - Width of each column.
;                  $iRowHeight - Height of each row.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _CreateCheckboxGrid(ByRef $aidArray, $iCount, $iX_Start, $iY_Start, $iColWidth, $iRowHeight)
    For $i = 0 To $iCount - 1
        Local $iX, $iY
        _GetControlPos($i, $iX_Start, $iY_Start, $iColWidth, $iRowHeight, $iX, $iY)
        $aidArray[$i] = GUICtrlCreateCheckbox("Search " & ($i + 1), $iX, $iY, 100, 20)
    Next
EndFunc   ;==>_CreateCheckboxGrid

; #FUNCTION# ====================================================================================================================
; Name...........: _CreateBtnCreateGrid
; Description....: Creates a grid of "Create" buttons for capturing images from the screen.
; Parameters.....: $aidArray   - Array to store control IDs.
;                  $iCount     - Number of buttons to create.
;                  $iX_Start   - Starting X coordinate.
;                  $iY_Start   - Starting Y coordinate.
;                  $iColWidth  - Width of each column.
;                  $iRowHeight - Height of each row.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _CreateBtnCreateGrid(ByRef $aidArray, $iCount, $iX_Start, $iY_Start, $iColWidth, $iRowHeight)
    For $i = 0 To $iCount - 1
        Local $iX, $iY
        _GetControlPos($i, $iX_Start, $iY_Start, $iColWidth, $iRowHeight, $iX, $iY)
        $aidArray[$i] = GUICtrlCreateButton("Create", $iX, $iY + 125, 45, 20)
        GUICtrlSetTip(-1, "Capture an image from the screen for slot " & ($i + 1) & ".")
    Next
EndFunc   ;==>_CreateBtnCreateGrid

; #FUNCTION# ====================================================================================================================
; Name...........: _CreateBtnBrowseGrid
; Description....: Creates a grid of "Browse" buttons for selecting existing image files.
; Parameters.....: $aidArray   - Array to store control IDs.
;                  $iCount     - Number of buttons to create.
;                  $iX_Start   - Starting X coordinate.
;                  $iY_Start   - Starting Y coordinate.
;                  $iColWidth  - Width of each column.
;                  $iRowHeight - Height of each row.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _CreateBtnBrowseGrid(ByRef $aidArray, $iCount, $iX_Start, $iY_Start, $iColWidth, $iRowHeight)
    For $i = 0 To $iCount - 1
        Local $iX, $iY
        _GetControlPos($i, $iX_Start, $iY_Start, $iColWidth, $iRowHeight, $iX, $iY)
        $aidArray[$i] = GUICtrlCreateButton("Browse", $iX + 50, $iY + 125, 45, 20)
        GUICtrlSetTip(-1, "Select an existing image file for slot " & ($i + 1) & ".")
    Next
EndFunc   ;==>_CreateBtnBrowseGrid

; #FUNCTION# ====================================================================================================================
; Name...........: _CreatePicGrid
; Description....: Creates a grid of image preview controls to display the target images.
; Parameters.....: $aidArray   - Array to store control IDs.
;                  $iCount     - Number of controls to create.
;                  $iX_Start   - Starting X coordinate.
;                  $iY_Start   - Starting Y coordinate.
;                  $iColWidth  - Width of each column.
;                  $iRowHeight - Height of each row.
;                  $iPicWidth  - Width of each image preview.
;                  $iPicHeight - Height of each image preview.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _CreatePicGrid(ByRef $aidArray, $iCount, $iX_Start, $iY_Start, $iColWidth, $iRowHeight, $iPicWidth, $iPicHeight)
    For $i = 0 To $iCount - 1
        Local $iX, $iY
        _GetControlPos($i, $iX_Start, $iY_Start, $iColWidth, $iRowHeight, $iX, $iY)
        $aidArray[$i] = GUICtrlCreatePic("", $iX, $iY, $iPicWidth, $iPicHeight)
        GUICtrlSetState(-1, $GUI_DISABLE)
    Next
EndFunc   ;==>_CreatePicGrid

; #FUNCTION# ====================================================================================================================
; Name...........: _UpdateSearchModeControls
; Description....: Updates the state (enabled/disabled) of GUI controls based on the selected search mode (screen or image).
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _UpdateSearchModeControls()
    Local $bIsScreenSearch = __IsChecked($g_idRadSearchOnScreen)
    GUICtrlSetState($g_idInputSourceImage, ($bIsScreenSearch ? $GUI_DISABLE : $GUI_ENABLE))
    GUICtrlSetState($g_idBtnBrowseSource, ($bIsScreenSearch ? $GUI_DISABLE : $GUI_ENABLE))
    GUICtrlSetState($g_idChkMoveMouse, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idRadNoClick, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idRadSingleClick, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idRadDoubleClick, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idChkUseArea, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idInputLeft, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idInputTop, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idInputRight, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idInputBottom, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idBtnSelectArea, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idChkWait, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    GUICtrlSetState($g_idInputWaitTime, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))

    ; NEW v3.3: Monitor selection controls
    GUICtrlSetState($g_idChkUseSpecificMonitor, ($bIsScreenSearch ? $GUI_ENABLE : $GUI_DISABLE))
    If Not $bIsScreenSearch Then
        GUICtrlSetState($g_idComboMonitor, $GUI_DISABLE)
    Else
        _UpdateMonitorControls()
    EndIf
EndFunc   ;==>_UpdateSearchModeControls

; #FUNCTION# ====================================================================================================================
; Name...........: _UpdateMonitorControls
; Description....: Updates monitor selection controls based on checkbox state
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _UpdateMonitorControls()
    Local $bScreenSearch = __IsChecked($g_idRadSearchOnScreen)
    Local $bUseSpecific = __IsChecked($g_idChkUseSpecificMonitor)

    If $bScreenSearch And $bUseSpecific Then
        GUICtrlSetState($g_idComboMonitor, $GUI_ENABLE)
    Else
        GUICtrlSetState($g_idComboMonitor, $GUI_DISABLE)
    EndIf
EndFunc   ;==>_UpdateMonitorControls

; #REGION# === EVENT HANDLERS ===

; #FUNCTION# ====================================================================================================================
; Name...........: _HandleImageCreation
; Description....: Handles the "Create" button click for an image slot by capturing a screen region and saving it to a file.
; Parameters.....: $iControlID - The control ID of the clicked "Create" button.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _HandleImageCreation($iControlID)
    For $i = 0 To $MAX_IMAGES - 1
        If $g_aidBtnCreate[$i] = $iControlID Then
            GUISetState(@SW_HIDE, $g_hMainGUI)
            _UpdateStatus("Capturing image for slot " & ($i + 1) & "...")
            Local $iResult = _CaptureRegion_free($g_asImagePaths[$i])
            GUISetState(@SW_SHOW, $g_hMainGUI)
            If $iResult = 0 Then
                _LogWrite("Captured image for slot " & ($i + 1) & ": " & $g_asImagePaths[$i])
                _UpdateSingleImagePreview($i)
                _RefreshImageTooltips()
                _UpdateStatus("Image captured for slot " & ($i + 1))
            ElseIf $iResult = -2 Then
                _LogWrite("Image capture cancelled for slot " & ($i + 1))
                _UpdateStatus("Capture cancelled")
            Else
                _LogWrite("ERROR: Failed to capture image for slot " & ($i + 1))
                _UpdateStatus("Capture failed")
            EndIf
            ExitLoop
        EndIf
    Next
EndFunc   ;==>_HandleImageCreation

; #FUNCTION# ====================================================================================================================
; Name...........: _HandleImageBrowse
; Description....: Handles the "Browse" button click for an image slot by opening a file dialog and copying the selected image.
; Parameters.....: $iControlID - The control ID of the clicked "Browse" button.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _HandleImageBrowse($iControlID)
    For $i = 0 To $MAX_IMAGES - 1
        If $g_aidBtnBrowse[$i] = $iControlID Then
            Local $sFile = FileOpenDialog("Select Image for Slot " & ($i + 1), @ScriptDir, "Images (*.bmp;*.jpg;*.jpeg;*.png;*.gif)", 1)
            If @error Or Not _ValidateImageFile($sFile) Then
                _LogWrite("ERROR: Invalid or no image selected for slot " & ($i + 1))
                _UpdateStatus("Invalid image selected")
                Return
            EndIf
            If FileExists($g_asImagePaths[$i]) Then FileDelete($g_asImagePaths[$i])
            If FileCopy($sFile, $g_asImagePaths[$i], 1) Then
                _LogWrite("Selected image for slot " & ($i + 1) & ": " & $g_asImagePaths[$i])
                _UpdateSingleImagePreview($i)
                _RefreshImageTooltips()
                _UpdateStatus("Image selected for slot " & ($i + 1))
            Else
                _LogWrite("ERROR: Failed to copy image for slot " & ($i + 1))
                _UpdateStatus("Failed to copy image")
            EndIf
            ExitLoop
        EndIf
    Next
EndFunc   ;==>_HandleImageBrowse

; #FUNCTION# ====================================================================================================================
; Name...........: _BrowseSourceImage
; Description....: Opens a file dialog to select the source image for image-in-image search and updates the GUI.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _BrowseSourceImage()
    Local $sFile = FileOpenDialog("Select Source Image", @ScriptDir, "Images (*.bmp;*.jpg;*.jpeg;*.png;*.gif)", 1)
    If @error Or Not _ValidateImageFile($sFile) Then
        _LogWrite("ERROR: Invalid or no source image selected.")
        _UpdateStatus("Invalid source image")
        Return
    EndIf
    GUICtrlSetData($g_idInputSourceImage, $sFile)
    _LogWrite("Selected source image: " & $sFile)
    _UpdateStatus("Source image selected")
EndFunc   ;==>_BrowseSourceImage

; #FUNCTION# ====================================================================================================================
; Name...........: _ExecuteSearch
; Description....: Executes the image search based on user-specified parameters from the GUI and processes the results.
; Parameters.....: None (uses global GUI control states)
; Return values..: True if search executed successfully, False otherwise.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _ExecuteSearch()
    _UpdateGUI_SystemInfo()
    ; Get search parameters from GUI controls
    Local $bMultiSearch = __IsChecked($g_idChkMultiSearch)
    Local $bFindAll = __IsChecked($g_idChkFindAll)
    Local $bWait = __IsChecked($g_idChkWait)
    Local $bUseTolerance = __IsChecked($g_idChkUseTolerance)
    Local $bEnableDebug = __IsChecked($g_idChkEnableDebug)
    Local $bUseCache = __IsChecked($g_idChkUseCache)
    Local $bUseArea = __IsChecked($g_idChkUseArea)
    Local $bMoveMouse = __IsChecked($g_idChkMoveMouse)
    Local $bSearchInImage = __IsChecked($g_idRadSearchInImage)
    Local $iTimeout = Number(GUICtrlRead($g_idInputWaitTime))
    Local $iTolerance = ($bUseTolerance ? Number(GUICtrlRead($g_idInputTolerance)) : 0)
    Local $iDelay = Number(GUICtrlRead($g_idInputDelay))
    Local $iLeft = ($bUseArea ? Number(GUICtrlRead($g_idInputLeft)) : 0)
    Local $iTop = ($bUseArea ? Number(GUICtrlRead($g_idInputTop)) : 0)
    Local $iRight = ($bUseArea ? Number(GUICtrlRead($g_idInputRight)) : 0)
    Local $iBottom = ($bUseArea ? Number(GUICtrlRead($g_idInputBottom)) : 0)

    ; NEW v3.3: Monitor selection
    Local $iScreen = -1  ; Default: all monitors (virtual desktop)
    Local $bUseSpecificMonitor = __IsChecked($g_idChkUseSpecificMonitor)
    If $bUseSpecificMonitor And Not $bSearchInImage Then
        ; Parse monitor number from combobox selection
        Local $sMonitorText = GUICtrlRead($g_idComboMonitor)
        Local $aMatch = StringRegExp($sMonitorText, "Monitor (\d+)", 1)
        If Not @error And IsArray($aMatch) Then
            $iScreen = Number($aMatch[0])
            _LogWrite("Using specific monitor: " & $iScreen & " (faster search)")
        EndIf
    EndIf

    ; Cache parameter (v3.3 uses $IMGS_ENABLED_CACHE constant from UDF)
    Local $iUseCache = $bUseCache ? $IMGS_ENABLED_CACHE : 0
    If $bUseCache Then _LogWrite("Cache enabled for performance boost")

    Local $iCenterPos = 1 ; Return center coordinates
    Local $fMinScale = 1.0, $fMaxScale = 1.0, $fScaleStep = 0.1
    Local $iReturnDebug = ($bEnableDebug ? 1 : 0)
    Local $sClickType = (__IsChecked($g_idRadSingleClick) ? "single" : (__IsChecked($g_idRadDoubleClick) ? "double" : "none"))
    Local $iClicks = ($sClickType = "single" ? 1 : ($sClickType = "double" ? 2 : 0))

    ; Build list of images to search with detailed logging
    Local $sImagePath = ""
    Local $sValidImages = ""
    Local $sInvalidImages = ""
    _LogWrite("Building list of images to search...")
    For $i = 0 To $MAX_IMAGES - 1
        If __IsChecked($g_aidChkSearch[$i]) Then
            If FileExists($g_asImagePaths[$i]) Then
                $sImagePath &= ($sImagePath = "" ? "" : "|") & $g_asImagePaths[$i]
                $sValidImages &= ($sValidImages = "" ? "" : ", ") & "Slot " & ($i + 1)
            Else
                GUICtrlSetState($g_aidChkSearch[$i], $GUI_UNCHECKED) ; Uncheck if image does not exist
                $sInvalidImages &= ($sInvalidImages = "" ? "" : ", ") & "Slot " & ($i + 1)
            EndIf
        EndIf
    Next
    If $sValidImages <> "" Then
        _LogWrite("Valid images selected: " & $sValidImages)
    Else
        _LogWrite("No valid images found.")
    EndIf
    If $sInvalidImages <> "" Then
        _LogWrite("Invalid or missing images (unchecked): " & $sInvalidImages)
    EndIf

    If $sImagePath = "" Then
        _LogWrite("ERROR: No valid images selected for search.")
        _UpdateStatus("Error: No images selected")
        Return False
    EndIf

    ; Initialize result array
    Local $aResult
    Local $bSuccess = False

    If $bSearchInImage Then
        ; Search within a source image
        Local $sSourceImage = GUICtrlRead($g_idInputSourceImage)
        If Not FileExists($sSourceImage) Then
            _LogWrite("ERROR: Source image file does not exist: " & $sSourceImage)
            _UpdateStatus("Error: Invalid source image")
            Return False
        EndIf

        _LogWrite("Starting image-in-image search for: " & $sImagePath)
        _UpdateStatus("Searching in image...")

        ; Execute search
        $aResult = _ImageSearch_InImage($sSourceImage, $sImagePath, $iTolerance, ($bFindAll ? $MAX_IMAGES : 1), $iCenterPos, $fMinScale, $fMaxScale, $fScaleStep, $iReturnDebug, $iUseCache)
        If $bEnableDebug Then
            _LogWrite("DLL Raw Return: " & _ImageSearch_GetLastResult())
        EndIf
        If @error Then
            _LogSearchError(@error)
            _UpdateStatus("Search failed")
            Return False
        EndIf

        ; Process results (v3.3: array format is [row][col] where col: 0=X, 1=Y, 2=W, 3=H)
        If $aResult[0][0] > 0 Then
            For $i = 1 To $aResult[0][0]
                _LogWrite("Found match " & $i & ": X=" & $aResult[$i][0] & ", Y=" & $aResult[$i][1] & ", Width=" & $aResult[$i][2] & ", Height=" & $aResult[$i][3])
                $bSuccess = True
            Next
            _UpdateStatus("Found " & $aResult[0][0] & " match(es)")
        Else
            _LogWrite("No matches found.")
            _UpdateStatus("No matches found")
        EndIf
    Else
        ; Screen search
        _LogWrite("Starting screen search for: " & $sImagePath)
        _UpdateStatus("Searching on screen...")

        ; v3.3: All searches now use _ImageSearch with proper parameters
        If $bWait Then
            ; Wait for image to appear (same params for area or full screen)
            $aResult = _ImageSearch_Wait($iTimeout, $sImagePath, $iLeft, $iTop, $iRight, $iBottom, $iScreen, $iTolerance, ($bFindAll ? $MAX_IMAGES : 1), $iCenterPos, $fMinScale, $fMaxScale, $fScaleStep, $iReturnDebug, $iUseCache)
        Else
            ; Immediate search (replaced _ImageSearch_Area with _ImageSearch)
            $aResult = _ImageSearch($sImagePath, $iLeft, $iTop, $iRight, $iBottom, $iScreen, $iTolerance, ($bFindAll ? $MAX_IMAGES : 1), $iCenterPos, $fMinScale, $fMaxScale, $fScaleStep, $iReturnDebug, $iUseCache)
        EndIf

        If $bEnableDebug Then
            _LogWrite("DLL Raw Return: " & _ImageSearch_GetLastResult())
        EndIf
        If @error Then
            _LogSearchError(@error)
            _UpdateStatus("Search failed")
            Return False
        EndIf

        ; Process results (v3.3: array format is [row][col] where col: 0=X, 1=Y, 2=W, 3=H)
        If $aResult[0][0] > 0 Then
            For $i = 1 To $aResult[0][0]
                _LogWrite("Found match " & $i & ": X=" & $aResult[$i][0] & ", Y=" & $aResult[$i][1] & ", Width=" & $aResult[$i][2] & ", Height=" & $aResult[$i][3])
                If $bMoveMouse Then
                    _UpdateStatus("MouseMove X:"&$aResult[$i][0] &" Y:"& $aResult[$i][1])
                    ; Use DLL MouseMove to support negative coordinates
                    _ImageSearch_MouseMove($aResult[$i][0], $aResult[$i][1], 0, $iScreen)
                    _HighlightFoundArea($aResult[$i][0], $aResult[$i][1], $aResult[$i][2], $aResult[$i][3])
                EndIf
                If $iClicks > 0 Then
                    _UpdateStatus("MouseClick 'left' X:"&$aResult[$i][0] &" Y:"& $aResult[$i][1])
                    ; Use DLL MouseClick with fallback support for negative coordinates
                    _ImageSearch_MouseClick("left", $aResult[$i][0], $aResult[$i][1], $iClicks, 0, $iScreen)
                EndIf
                Sleep($iDelay)
                $bSuccess = True
            Next
            _UpdateStatus("Found " & $aResult[0][0] & " match(es)")
        Else
            _LogWrite("No matches found.")
            _UpdateStatus("No matches found")
        EndIf
    EndIf

    Return $bSuccess
EndFunc   ;==>_ExecuteSearch


; #FUNCTION# ====================================================================================================================
; Name...........: _SelectAreaOnScreen
; Description....: Allows the user to select a rectangular area on the screen by dragging the mouse and updates the GUI inputs.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _SelectAreaOnScreen()
    GUISetState(@SW_HIDE, $g_hMainGUI)
    _UpdateStatus("Selecting search area...")
    Local $aArea = _CaptureRegion_free()
    GUISetState(@SW_SHOW, $g_hMainGUI)
    If IsArray($aArea) Then
        GUICtrlSetData($g_idInputLeft, $aArea[0])
        GUICtrlSetData($g_idInputTop, $aArea[1])
        GUICtrlSetData($g_idInputRight, $aArea[2])
        GUICtrlSetData($g_idInputBottom, $aArea[3])
        GUICtrlSetState($g_idChkUseArea, $GUI_CHECKED)
        _LogWrite("Search area selected: Left=" & $aArea[0] & ", Top=" & $aArea[1] & ", Right=" & $aArea[2] & ", Bottom=" & $aArea[3])
        _UpdateStatus("Search area selected")
    ElseIf $aArea = -2 Then
        _LogWrite("Area selection cancelled.")
        _UpdateStatus("Area selection cancelled")
    Else
        _LogWrite("ERROR: Failed to select search area.")
        _UpdateStatus("Area selection failed")
    EndIf
EndFunc   ;==>_SelectAreaOnScreen

; #REGION# === SCREEN CAPTURE & AREA SELECTION ===

; #FUNCTION# ====================================================================================================================
; Name...........: _CaptureRegion_free
; Description....: Captures a user-defined rectangular region of the screen or returns the coordinates of the selected area.
; Parameters.....: $sFilePath - [optional] The file path to save the captured image. If empty, returns coordinates.
; Return values..: If $sFilePath is provided:
;                  | 0 = Success (image saved)
;                  | -1 = Capture failed
;                  | -2 = User cancelled
;                  If $sFilePath is empty:
;                  | Array [Left, Top, Right, Bottom] = Success
;                  | -1 = Capture failed
;                  | -2 = User cancelled
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _CaptureRegion_free($sFilePath = "")
    Local $sTitle = "Select Region (" & ($sFilePath = "" ? "Area" : "Image") & ")"
    Local $hUserDLL = DllOpen("user32.dll")
    If $hUserDLL = -1 Then Return -1

    ; Create a fullscreen, transparent window to capture mouse events.
    Local $hCrossGUI = GUICreate($sTitle, @DesktopWidth, @DesktopHeight, 0, 0, $WS_POPUP, $WS_EX_TOPMOST)
    GUISetBkColor(0x000001) ; A color that is unlikely to be on screen.
    WinSetTrans($hCrossGUI, "", 1) ; Make it almost fully transparent.
    GUISetState(@SW_SHOW, $hCrossGUI)
    GUISetCursor(3, 1, $hCrossGUI) ; Set crosshair cursor.

    _UpdateStatus("Drag the mouse to select an area. Press ESC to cancel.")
    ToolTip("Drag the mouse to select an area. Press ESC to cancel.", 0, 0)

    ; Wait for the user to press the left mouse button.
    While Not _IsPressed("01", $hUserDLL)
        If _IsPressed("1B", $hUserDLL) Then ; Check for ESC key press to cancel.
            ToolTip("")
            GUIDelete($hCrossGUI)
            DllClose($hUserDLL)
            GUISetState(@SW_SHOW, $g_hMainGUI)
            Return -2
        EndIf
        Sleep(20)
    WEnd
    ToolTip("")

    Local $aStartPos = MouseGetPos()
    Local $iX1 = $aStartPos[0], $iY1 = $aStartPos[1]
    Local $hRectGUI

    ; While the mouse button is held down, draw a feedback rectangle.
    While _IsPressed("01", $hUserDLL)
        Local $aCurrentPos = MouseGetPos()
        Local $iX2 = $aCurrentPos[0], $iY2 = $aCurrentPos[1]
        If IsHWnd($hRectGUI) Then GUIDelete($hRectGUI)

        Local $iLeft = ($iX1 < $iX2 ? $iX1 : $iX2)
        Local $iTop = ($iY1 < $iY2 ? $iY1 : $iY2)
        Local $iWidth = Abs($iX1 - $iX2)
        Local $iHeight = Abs($iY1 - $iY2)

        $hRectGUI = GUICreate("", $iWidth, $iHeight, $iLeft, $iTop, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOPMOST))
        GUISetBkColor(0xFF0000) ; Red feedback rectangle.
        _WinAPI_SetLayeredWindowAttributes($hRectGUI, 0, 100) ; Set transparency.
        GUISetState(@SW_SHOWNOACTIVATE, $hRectGUI)
        Sleep(10)
    WEnd

    Local $aEndPos = MouseGetPos()
    Local $iX2 = $aEndPos[0], $iY2 = $aEndPos[1]

    ; Clean up the temporary GUIs.
    GUIDelete($hCrossGUI)
    If IsHWnd($hRectGUI) Then GUIDelete($hRectGUI)
    DllClose($hUserDLL)

    ; Final coordinate calculation.
    Local $iLeft = ($iX1 < $iX2 ? $iX1 : $iX2)
    Local $iTop = ($iY1 < $iY2 ? $iY1 : $iY2)
    Local $iRight = ($iX1 > $iX2 ? $iX1 : $iX2)
    Local $iBottom = ($iY1 > $iY2 ? $iY1 : $iY2)

    GUISetState(@SW_SHOW, $g_hMainGUI)

    ; If no area was selected (no drag), treat as a cancel.
    If $iLeft = $iRight Or $iTop = $iBottom Then Return -2

    ; If a file path was provided, capture the screen area to that file.
    If $sFilePath <> "" Then
        Local $aMousePos = MouseGetPos()
        MouseMove(0, 0, 0) ; Move mouse out of the way for a clean capture.
        Sleep(250)
        Local $hBitmap = _ScreenCapture_Capture("", $iLeft, $iTop, $iRight, $iBottom, False)
        If @error Then
            MouseMove($aMousePos[0], $aMousePos[1], 0)
            Return -1 ; Return error if capture failed.
        EndIf
        Local $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
        _GDIPlus_ImageSaveToFile($hImage, $sFilePath)
        _GDIPlus_BitmapDispose($hImage)
        _WinAPI_DeleteObject($hBitmap)
        MouseMove($aMousePos[0], $aMousePos[1], 0) ; Restore mouse position.
        Return 0 ; Success.
    Else
        ; If no file path, return the coordinates array.
        Local $aReturn[4] = [$iLeft, $iTop, $iRight, $iBottom]
        Return $aReturn
    EndIf
EndFunc   ;==>_CaptureRegion_free

; === END SCREEN CAPTURE & AREA SELECTION ===

; #REGION# === UTILITY & HELPER FUNCTIONS ===

; #FUNCTION# ====================================================================================================================
; Name...........: _HighlightFoundArea
; Description....: Creates a temporary, semi-transparent GUI to visually highlight a found image location on the screen.
; Parameters.....: $iX, $iY    - The TOP-LEFT coordinates of the found area (NOT center).
;                  $iWidth     - The width of the highlight rectangle.
;                  $iHeight    - The height of the highlight rectangle.
;                  $iColor     - [optional] The color of the highlight rectangle in 0xRRGGBB format. Default is green.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _HighlightFoundArea($iX, $iY, $iWidth, $iHeight, $iColor = 0xFF00FF00)
    ; FIX: $iX/$iY are already top-left coordinates, not center
    Local $hGUI = GUICreate("", $iWidth, $iHeight, $iX, $iY, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOPMOST, $WS_EX_TOOLWINDOW))
    GUISetBkColor($iColor)
    _WinAPI_SetLayeredWindowAttributes($hGUI, 0, 128) ; 50% transparency.
    GUISetState(@SW_SHOWNOACTIVATE)
    Sleep(500) ; Display the highlight for half a second.
    GUIDelete($hGUI)
EndFunc   ;==>_HighlightFoundArea

; #FUNCTION# ====================================================================================================================
; Name...........: _LogWrite
; Description....: Writes a timestamped message to the activity log Edit control and ensures it scrolls to the latest entry.
; Parameters.....: $sMessage - The string message to log.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _LogWrite($sMessage)
    GUICtrlSetData($g_hLog, "[" & _NowTime(5) & "] " & $sMessage & @CRLF, 1) ; The '1' appends the text.
    _GUICtrlEdit_SetSel(GUICtrlGetHandle($g_hLog), 0x7FFFFFFF, 0x7FFFFFFF) ; Scroll to the end.
    ConsoleWrite($sMessage&@CRLF)
EndFunc   ;==>_LogWrite

; #FUNCTION# ====================================================================================================================
; Name...........: _LogSearchError
; Description....: Translates a numerical error code from the ImageSearch UDF into a human-readable message and logs it.
; Parameters.....: $iErrorCode - The status code returned by an _ImageSearch* function.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _LogSearchError($iErrorCode)
    Switch $iErrorCode
        Case 0
            _LogWrite("No matches found.")
        Case $IMGSE_FAILED_TO_GET_SCREEN_DC
            _LogWrite("ERROR: Failed to capture screen.")
        Case $IMGSE_INVALID_PARAMETERS
            _LogWrite("ERROR: Invalid parameters.")
        Case $IMGSE_INVALID_SEARCH_REGION
            _LogWrite("ERROR: Invalid search region.")
        Case $IMGSE_INVALID_Source_BITMAP
            _LogWrite("ERROR: Invalid Source bitmap.")
        Case $IMGSE_RESULT_TOO_LARGE
            _LogWrite("ERROR: Too many results.")
        Case Else
            _LogWrite("ERROR: Error code " & $iErrorCode)
    EndSwitch
EndFunc   ;==>_LogSearchError

; #FUNCTION# ====================================================================================================================
; Name...........: _UpdateAllImagePreviews
; Description....: Iterates through all image slots and calls the function to update their preview images.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _UpdateAllImagePreviews()
    For $i = 0 To $MAX_IMAGES - 1
        _UpdateSingleImagePreview($i)
    Next
EndFunc   ;==>_UpdateAllImagePreviews

; #FUNCTION# ====================================================================================================================
; Name...........: _UpdateSingleImagePreview
; Description....: Updates a single image preview slot. If the target image file exists, it's displayed. Otherwise, a placeholder is shown.
; Parameters.....: $iIndex - The index (0-11) of the image slot to update.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _UpdateSingleImagePreview($iIndex)
    If FileExists($g_asImagePaths[$iIndex]) Then
        GUICtrlSetImage($g_aidPic[$iIndex], $g_asImagePaths[$iIndex])
    Else
        ; Use a default Windows wallpaper as a placeholder if available.
        If FileExists($g_sPlaceholderPath) Then
            GUICtrlSetImage($g_aidPic[$iIndex], $g_sPlaceholderPath)
        Else
            ; Fallback to a generic icon from shell32.dll if the wallpaper is also missing.
            GUICtrlSetImage($g_aidPic[$iIndex], "shell32.dll", 22)
        EndIf
    EndIf
EndFunc   ;==>_UpdateSingleImagePreview

; #FUNCTION# ====================================================================================================================
; Name...........: _SelectAll
; Description....: Checks or unchecks all image target checkboxes simultaneously.
; Parameters.....: $bState - True to check all, False to uncheck all.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _SelectAll($bState)
    Local $iCheckState = ($bState ? $GUI_CHECKED : $GUI_UNCHECKED)

    For $i = 0 To $MAX_IMAGES - 1
        GUICtrlSetState($g_aidChkSearch[$i], $iCheckState)
    Next
EndFunc   ;==>_SelectAll

; #FUNCTION# ====================================================================================================================
; Name...........: _UpdateStatus
; Description....: Sets the text of the status bar at the bottom of the GUI.
; Parameters.....: $sMessage - The message to display.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _UpdateStatus($sMessage)
    _GUICtrlStatusBar_SetText($g_hStatusBar, $sMessage)
EndFunc   ;==>_UpdateStatus

; #FUNCTION# ====================================================================================================================
; Name...........: __IsChecked
; Description....: A helper function to check the state of a checkbox or radio button in a more readable way.
; Parameters.....: $iControlID - The control ID of the checkbox or radio button.
; Return values..: True if the control is checked, False otherwise.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func __IsChecked($iControlID)
    Return BitAND(GUICtrlRead($iControlID), $GUI_CHECKED) = $GUI_CHECKED
EndFunc   ;==>__IsChecked

; #FUNCTION# ====================================================================================================================
; Name...........: _GetImageDimensions
; Description....: Gets the width and height of an image file using GDI+.
; Parameters.....: $sImagePath - Path to the image file.
; Return values..: On success, a 2-element array [Width, Height]. On failure, returns False.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _GetImageDimensions($sImagePath)
    If Not FileExists($sImagePath) Then Return False

    Local $hImage = _GDIPlus_ImageLoadFromFile($sImagePath)
    If Not $hImage Then Return False

    Local $iWidth = _GDIPlus_ImageGetWidth($hImage)
    Local $iHeight = _GDIPlus_ImageGetHeight($hImage)
    _GDIPlus_ImageDispose($hImage)

    Local $aDimensions[2] = [$iWidth, $iHeight]
    Return $aDimensions
EndFunc   ;==>_GetImageDimensions

; #FUNCTION# ====================================================================================================================
; Name...........: _ValidateImageFile
; Description....: Validates if a file is a valid image that can be processed by GDI+. Checks for existence, size, and basic integrity.
; Parameters.....: $sImagePath - Path to the image file.
; Return values..: True if the image is valid, False otherwise.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _ValidateImageFile($sImagePath)
    If Not FileExists($sImagePath) Then Return False

    ; Check file size (must be > 0 and < 50MB for reasonable processing)
    Local $iFileSize = FileGetSize($sImagePath)
    If $iFileSize <= 0 Or $iFileSize > 50 * 1024 * 1024 Then Return False

    ; Try to load with GDI+ to validate it's a real, uncorrupted image.
    Local $hImage = _GDIPlus_ImageLoadFromFile($sImagePath)
    If Not $hImage Then Return False

    ; Check minimum dimensions (at least 1x1 pixel).
    Local $iWidth = _GDIPlus_ImageGetWidth($hImage)
    Local $iHeight = _GDIPlus_ImageGetHeight($hImage)
    _GDIPlus_ImageDispose($hImage)

    Return ($iWidth > 0 And $iHeight > 0)
EndFunc   ;==>_ValidateImageFile

; #FUNCTION# ====================================================================================================================
; Name...........: _CreateImageInfoTooltip
; Description....: Creates a detailed tooltip string for an image slot, showing file name, dimensions, file size, and full path.
; Parameters.....: $iIndex - The index (0-11) of the image slot.
; Return values..: A formatted string containing the image's information for use in a tooltip.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _CreateImageInfoTooltip($iIndex)
    Local $sImagePath = $g_asImagePaths[$iIndex]
    Local $sTooltip = "Slot " & ($iIndex + 1) & ":" & @CRLF

    If FileExists($sImagePath) Then
        Local $aDims = _GetImageDimensions($sImagePath)
        Local $iFileSize = FileGetSize($sImagePath)
        Local $sFileSize = ""

        If $iFileSize < 1024 Then
            $sFileSize = $iFileSize & " B"
        ElseIf $iFileSize < 1024 * 1024 Then
            $sFileSize = Round($iFileSize / 1024, 1) & " KB"
        Else
            $sFileSize = Round($iFileSize / (1024 * 1024), 2) & " MB"
        EndIf

        $sTooltip &= "File: " & StringRegExpReplace($sImagePath, ".*\\", "") & @CRLF
        If IsArray($aDims) Then
            $sTooltip &= "Size: " & $aDims[0] & " x " & $aDims[1] & " px" & @CRLF
        EndIf
        $sTooltip &= "File Size: " & $sFileSize & @CRLF
        $sTooltip &= "Path: " & $sImagePath
    Else
        $sTooltip &= "No image file." & @CRLF
        $sTooltip &= "Click 'Create' or 'Browse'."
    EndIf

    Return $sTooltip
EndFunc   ;==>_CreateImageInfoTooltip

; #FUNCTION# ====================================================================================================================
; Name...........: _RefreshImageTooltips
; Description....: Updates the tooltips for all image preview controls with the latest file information.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _RefreshImageTooltips()
    For $i = 0 To $MAX_IMAGES - 1
        GUICtrlSetTip($g_aidPic[$i], _CreateImageInfoTooltip($i))
    Next
EndFunc   ;==>_RefreshImageTooltips

; #FUNCTION# ====================================================================================================================
; Name...........: _Exit
; Description....: Performs cleanup operations (like shutting down GDI+) and exits the script cleanly.
; Author.........: Dao Van Trong (TRONG.PRO)
; ===============================================================================================================================
Func _Exit()
    _GDIPlus_Shutdown()
    _ImageSearch_Shutdown()
    Exit
EndFunc   ;==>_Exit


Func _ImgEmptySlots()
    ; This function holds the hex data for default_img.jpg
    ; File size: 8.2 KB
    ; Architecture: Not PE
    ; Generated by AutoIt Embedded File Generator
    Local $sHexData = '0xFFD8FFE000104A46494600010101006000600000FFE1005A4578696600004D4D002A00000008000503010005000000010000004A03030001000000010000000051100001000000010100000051110004000000010000000051120004000000010000000000000000000186A00000B18FFFDB0043000201010201010202020202020202030503030303030604040305070607070706070708090B0908080A0807070A0D0A0A0B0C0C0C0C07090E0F0D0C0E0B0C0C0CFFDB004301020202030303060303060C0807080C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0CFFC00011080156015603012200021101031101FFC4001F0000010501010101010100000000000000000102030405060708090A0BFFC400B5100002010303020403050504040000017D01020300041105122131410613516107227114328191A1082342B1C11552D1F02433627282090A161718191A25262728292A3435363738393A434445464748494A535455565758595A636465666768696A737475767778797A838485868788898A92939495969798999AA2A3A4A5A6A7A8A9AAB2B3B4B5B6B7B8B9BAC2C3C4C5C6C7C8C9CAD2D3D4D5D6D7D8D9DAE1E2E3E4E5E6E7E8E9EAF1F2F3F4F5F6F7F8F9FAFFC4001F01000301010101010101'
    $sHexData &= '01010000000000000102030405060708090A0BFFC400B51100020102040403040705040400010277000102031104052131061241510761711322328108144291A1B1C109233352F0156272D10A162434E125F11718191A262728292A35363738393A434445464748494A535455565758595A636465666768696A737475767778797A82838485868788898A92939495969798999AA2A3A4A5A6A7A8A9AAB2B3B4B5B6B7B8B9BAC2C3C4C5C6C7C8C9CAD2D3D4D5D6D7D8D9DAE2E3E4E5E6E7E8E9EAF2F3F4F5F6F7F8F9FAFFDA000C03010002110311003F00F90E8A28AFED03F2F0A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A28'
    $sHexData &= '00A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A28'
    $sHexData &= '00A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A2A4B3B39B50BB8ADEDE29279E7711C71C6A59E462701401C924F000AFB3A3FD927E0F7EC23E14D3752FDA16E355F167C44D4A25BA83E1DE8178B10D3E221597FB42E14E632C0FDD4607FBBBC648F3730CD68E1396124E5397C318ABC9DB7B2D124BAB6D25D5EA7450C34AADDAD12DDBD97F5D96A7C5B457D8337FC14DBE1DD84A61D27F655F82D069AC70D1DFDA9BEB9DBD809CA2907DF6D68E85E2EFD96FF6D8BB1A2EAFE1597F676F195E46D1D8EB7A7DFF00DA7C38F2F3B05D4726DF254F4CA851DDA415E74B3BC5525ED313849C61D5A709B5E6E31937FF0080A91B2C25397BB4EAA6FE6BF16ADF7D8F8B68AF40FDA67F665F167EC95F15EEFC1FE31B38EDF508116E2DEE2DDCCB69A95B3E7CBB9B793037C4D8383804156560ACACA3CFEBDEA15E9D7A71AD464A5192BA6B668E39C250938C959A0A2BF487FE0DE3F857E1AF88FAF7C5F7F10681A46B6D6BA6585BC3F6EB44B8114731BAF355438380DB1338EBB457E7BFC45D3E1D23E20EBB696D1AC56F6BA8DC45120E888B230007D0015E4E0F3B862331C465CA2D3A2A177D1F3ABFE074D5C238508576FE3BFE0CC6A2B574CF02EB7AD786EF759B3D1B55BBD234C60B797D0DA4925B5A93D049201B509ED922B7FC21FB36FC45F883E1FF00ED6D03C03E35D734AC13F6'
    $sHexData &= 'DD3F43B9B9B7C0EBFBC442BFAD7AB53134609B9C92B69AB5BF639E34E4F448E2E8A92EED25B0BA9609E29219E1731C91C8A55E360705483C820F635D4F82BE01F8EBE24E95F6FF000E782BC5BAFD88629F68D3748B8BA8B70E08DD1A119FC6AAA56A74E3CF52492EEDD8518CA4ED157392A2AEF88BC35A97843579B4FD5B4FBDD2EFEDCE25B6BB81A09A33E8C8C011F88AAD676736A37715BDBC524F3CEE238E38D4B3C8C4E028039249E00154A49AE64F4134EF623A2B7D7E1478A5FC69FF0008D8F0D6BE7C459C7F658D3E6FB6E719FF0053B77F4E7A74AB9E3DF80FE38F855671DC78A3C19E2BF0DDBCA42A4BAA69171668E4F2003222826B3FACD1E651E7577B2BAD7D0AF672B376D8E528A29F6D6D25E5C470C31BCB2CAC111114B33B1380001D4935B10328AEDF55FD997E24685A23EA77DF0FBC6F67A7449E63DD4FA15D47022E33B8B940A063BE6B88ACA957A7515E9C93B76772A50947E2560A28A2B52428ADAF047C36F117C4CD45ECFC37A06B5E20BB8977BC1A6D8CB77222FA958D4902A5F1D7C2AF147C2FB88A1F13786F5FF0ECB38CC49A9E9F35A3483D5448A33F8565EDE9F3FB3E65CDDAFAFDC5724ADCD6D0C0A28AD7D4FC01AF689A3E9BA8DEE89ABDA69FAC8CD85CCD672470DF76FDD39187FF0080935729C5349BDC4937B1914576FAF7ECCFF11FC2DE156D7753F87FE3'
    $sHexData &= '7D3B4444F31B50BAD0AEA1B555FEF19590263DF35C4A234AE154166638000C926A2957A751374E49A5D9DC7284A3A49584A2BB997F661F897068A7527F879E394D3847E69BA6D06E84213FBDBFCBDB8F7CD70D452AF4EA5FD9C93B7677094251F895828A28AD490A28A2800A28A2800A28A2800A28A2800A28A2800A28A2803EB2FF008269693A77C1FF0008FC50F8FDAD58DADF2FC2BD2E383C3B0DCA878E4D72EDFCBB57DBC93E572C78E37060415C8F1EF8437FE13F8E5FB405CEA5F1ABC69E20D234CD69A7BCD4B5BB5B437D772DCB7CC32A01203313C8538C0180391ECDF0BA54D43FE08C3F142DACC6DBCD3BE2169B77A8375DF6D24091C4BED89431FC6BE48AF99C050FACE23195652719F37B34D5AF18C6316AD74D6F272DBAAD343D0AD3F670A514AEADCD6E8DB6F7F9248FD07F0E7FC13DBF653F167C0AF117C49B1F8CDF1065F07F856F21B0D4AF0E89B5A09A66458D44660DED92E9CAA9033EC6BE56FDADBC0DF06FC11AC68B1FC1FF001BF88BC6B67710CADA949AAE98D646D5C32F961372216DC0B67E5E303939C0F71FD9FBFE509DF1EFFEC6CD23FF00475A57C615C9C3F87C4CB135E55B13526A9547049F2D9AE48BD6D14EF793D9AE9A77D31B3A6A9C1469A5CD1BDD5FBB5A6BE47D75E09F88563FB5CFFC13B3C4BE0CF13DF69CFE3BF827147AC783EEEEE454BBBAD1F245'
    $sHexData &= 'DD8AB9399162501D530480100E178F916BB9F0E7C0CB8F11FC05F1178F93C43E16B6B7F0E5FDB5849A45C5FECD56F0CDD24860C7CE8BDCE470AFD769AE1ABDCCB30D4A84EB4684AF1736F97F95B49C92F26FDEF59339311525350735ADB7EEBA7F97C8FDC2FF008236FF00C140752FDAF3C19AF7872FBC29A178757E1DE97A7C11CBA6B32C779BC4C99F2C8C4607920E013F78D7C2BFF0515FF82B56B3FB5BF80752F87377E05F0E68D6D63ABEE6D42399E7B83E43B01E5E4011962393F37048F7AF65FF00836BA743E23F8C506E1E74B63A632A77601AEC13F9B0FCEBF39BE31D9CBA77C5DF155BCF1BC53C1AC5DC72230C323099C1047A835F9E645C3996C789F1A9525FBAF67286AF46E376F7D6EFBDD1EDE331D5DE5F4BDEF8B993DB5B3D0FD6CFF823A6A3E18F0D7FC1287C61A9F8BAC6DEFBC31677BAA5D6AF6F244245BAB78E2467565FE20557183C76E95F37F8BFFE0E1EF8ADFF000934A3C29E16F01E87E1BB76D96361716535C491C23855775990138FEE2A81D2BD7FF603FF0094107C61FF00AF1D7FFF00496BF282AB21E1ECBF30CCF31AD8EA6AA38D56927B2D35696D77A6BE418DC756A187A11A32B5E3D0FD18FF008262FECEF65FF050FF00DA1FE207C77F8BB67A55DE89A35CFDA6E2C522F2EC2E6F0C61CEF424E628E35525493B8B0CE79CDCF8EDFF00070CF8B34BF1D5C6'
    $sHexData &= '9BF0A3C29E12D37C1DA631B7B27D52D25967BA8D7857091C91AC4840E1304818C9EC3B2FF821B88FE26FEC19F1BBC03A6DD44BE23BCB9B892388B7CC12E6C52189B1E85E2719F6AFCB7F12F86AFF00C1BE21BED2755B49EC352D3677B6BAB69976C904884AB2B0EC4106B6C065183CCF3AC5D2CC23CD1C3F2469D37F0C62E3BA5E7DFF00E0115B13570F84A52A0ECE77727D5BBED73F5EFE067C60F017FC174BE03F883C27E39F0CE99A07C4FF000DDA79B6F7F6684987770B716EED9758F7801E16661823939047C11FF04F9F01DE7C3DFF00829E780FC37AC408BA8687E287B1BA8C8CA8962F314E33DB72E41FA1AFA07FE0DD3F84FABEA5FB46F8AFC70239A1F0F689A249A64D3B29114B3CD244E10374255622C476DCA78C8CF9F7C17F1BD87C48FF0082E3D9EB7A590DA7DFF8EEE5A0704112283228718E30DB723D88AE7C3D28E07119A657846FD846973257BA84A51778AF5DEC5CE4EB430F88ABF1B95AFDD26B5FD0FB8BFE0A9DFF000536B3FD81FE20C1A5782FC2DA0EA9F123C47611DCDFEA5A844C63B5B45664855F615790921F6AEF0ABC9E49C1F29FD85BFE0B55A97ED5BF16AD3E17FC62F09784EF34AF1A16D3EDEE6CAD5D60F3187CB15C432BC8AE8FCAE4118257839247847FC1C27FF27EB6BFF62B58FF00E8DB9AF97BF63BFF0093B8F859FF00637E93FF00A5B0D6592F06'
    $sHexData &= 'E575F86E188A94EF5654F9B9EEF993B5D59DF44B4B2DB42F179AE2218F708CBDD52B5BA5BFE09EA7FF000564FD90F4EFD8EBF6B7D4345D062687C33AE5B26AFA5C24922D5246657841272423AB633FC256BED6F813E05F00FF00C11CFF00616D27E2CF8A3C3B6FE21F8A9E2E8633671CBB44D14B3279896B1B907CA4441BA47504920F5F9457947FC1C81FF2723E04FF00B179FF00F4A1ABD47FE0B6BE19BDF8E3FB05FC25F1F786637D53C3DA5AC3757925BFEF160867B65093363F84300A4E382E3A735854C756CCB2ECA70B8C9BF675DB551DEDCDCABDD8B7FDE7BF765468C70F5F135292F7A1F0F95F77F23C7FC31FF0719FC59B5F1AC373ACF84FC0B7DA019B33D8DAC1736F71E57A24CD3380C3D59083CF033C75DFF055CFD973C03FB40FECABA37ED35F0B74E8F4C5BF58A5D6ED6DE111ADCC72BEC32BC6BC2CD14A76B91D4124E76835F9915FB09E06F845ACFC1EFF00837DBC43A6F886DA4B4BFBDD16F35516D3290F6F1CF71E6C40AB0055B6156231C1635E8E7F95E0722C4E0F199645529CAA460E29E9384B7BAEB6D35F3F430C1622B6329D5A5887CC945B4DF46B63F1EEBD77F618FD962EBF6C9FDA67C3BE0586792CECEF9DAE351BA400B5B5A46374ACA0F1B88C28EBF338C822BC8ABEEEFF0083786357FDBBB54240253C237A57D8FDA6D07F226BEDB89B1F5705956231547E'
    $sHexData &= '28C5B5E4FA3F91E4E5F46357130A73D9B47BEFED97FF000536F0FF00FC13235083E0BFC09F06786E3BAF0F451FF6A5E5E46CF6F04ACA0EC2B1B2BCD395219E477E09030C738A7FB197FC15DB4EFDBB7C5A9F087E3C7833C29776BE2E736D61756B6CE2D5E6C656396395DCAB920EC951810DB4601F9ABE35FF0082B87C25D73E15FEDEBE3D6D62DAE23B7F105F1D574EB975C25DC12804329E876B650F7056B8FF00F827DFC20D6FE36FED91F0F748D0ADE596E2DB5AB5D4AE6545256D2DEDE6496495880768017009E37328EF5F134384B27A9912C6CFF88E1CEEADDF3735B9B9AF7E8FA7EA7AD3CCF14B19EC97C37B72DB4B5ED6B1B3FF00052AFD8EC7EC4DFB526ABE15B379A7F0F5E44BA968B2CA4B39B690902366C72C8CACA7D40527AD7EAF7ED17FB4BF85BF635FF827DFC30F1C6ADE18D3FC49AD69961A7C1E19B4B8402386FA4B223CC0D83B36C42424A8DD8C818CE6BE29FF00838AFC77A77883F6ADF0BE8B6AD1C97BA06823ED857AA34D2B32213EA14671FED8F5AF59FF0082CFFF00CA2DBE06FF00D7F699FF00A6B9EBCAC6B9E6D86C9BEBF76EA37CDAD9B56F2FE64B5F2674D2B61A78BF63F652B796BFA1E01E2EFF0082FE7C6FF18F87F57D32E34DF87D15A6AF6D25AB08F4A9CB408EA54942D3919C1FE20C3DABDFBF628F841F0FFF00E0995FB07DBFED05E3DD0E2D73C6FE'
    $sHexData &= '21823B8D2A095434B6EB367ECF043B87EEDD97E777C6E032390307F286BF597FE0A49E1EBBFDA4BFE08E5F0A3C5BE178A5BDD3FC311586A3A8451AEF78225B57B790B05E9E5BB7CC7A0009E8335EC71365183C1BC2E5D8582A347115146A38E9CC92D22DFF007B6397018AAB57DA57A8F9A508DE37D6DDDFC8F20B4FF838CFE2EC7E321753F84FC052E87E7EE6D3D20B9498C59FBA27F38E1F1FC5B08CF3B7B57A37FC1457F67FF87DFB747EC470FED2DF0D3488344D76CE3FB56BB6F1C4B1BDDC6ADB2E1660B853344DCF99D5957BE463F2CEBF59BF66DF0D5D7ECF3FF0411F88379E2C0FA68F1769FA8CBA7C17276B1176A20B7C03FF003D1B0C0752181A9E21C9B0592D4C2E372A8FB2A8EA46168B7EFC65BA6AFAFAFF00C01E071557171A94B12F9A3CADEBD1AD99F933451457EA47CE8514514005145140051451400514514005145140051451401F477FC13AFE367867C29E24F177C37F883A8369BF0F3E2EE95FD89A8DE900A69376AE1ECEF98120011499C93C00D93C035E75FB56FECADE2CFD8F7E316A5E0EF1659490CD6AE5ECAF56322D756B627E4B8818F0C8C3AE0E55B72B619481E6D5F4BFC06FF82893786BE14DB7C37F8AFE0CD33E2F7C3DB371FD9F6DA95C3C3A9682BB7611677432D1A85C613B6D001519AF9FC561B1585C4CB1B838F3A9DB9E1749B6B4528B765CD6D1A6'
    $sHexData &= 'D2692D535AF6D3A94EA5354AABB35B3FD1F975BAD8C7F861FB57681E09FF00827A7C4CF8497563AC4BE21F1A6B963A959DD451C66CA18A07859C48C5C3863E51C0542391922BC02BEAEB87FD8A35B9DAEF6FED21A2283B9EC21FEC99D587F76376248FAB9A7EB7FB687C19F825E0AD4745F82FF07619F52D56DDEDE6F147C405B7D5EFE247C0222B5DAD6E8703AF20E7953DF9B0B8C74E73FAA616A73549734B9AD149D92BB6DED64B48A93F234A94B992F6B52368AB2B6AFABDBE7D6C7837C6BD2FE1EE989E17FF00840754F126A6D3E876F2EBFF00DAF6F1C42DB5339F3A28367588718CE4FF00B47B70F4B2399246638058E4E0003F21C0A4AFA4A34DD3828393979BDFF438272E677B58F67FD857F6D3D7FF00617F8E1078BB46B74D4AD2784DA6A9A649298E3BFB7241DBB803B5C100AB60E0F6C122BEA1FDB87FE0A39FB347ED43F067C4E349F839AA58FC4CD7A3468F5B9F4AB1B7682E015CC8D7314C65930063053E6E01C57E7BD15E2637867038AC6C3309A71AB1B6B1938DD2774A56DD7FC31D74B1F5A9D274159C5F46AF6F43ED1FD98FFE0A55E13F82BFF04DCF885F06B53D0FC4373E22F1443A84161776AB0B5928BA8447BA566915D4A9C9C2A36401C8CF1F175145776072AC3E0EA56AB4159D59734B5EBB7C8C6B626756318CF68AB2F43D4BF643FDAEFC5DFB16FC5FB5F177'
    $sHexData &= '84AE23F3157C8BEB19F26DB52809C98A40083EE1872A4023B83F76F89BFE0A61FB1FFED4F343AEFC57F831AC41E2C48D64B99EDAD63945C3AF44F3E19A29251C71E6A018E2BF3028AF3F35E17C163EB2C54F9A1552B73C24E32B766D6FF337C36635A8C3D9AB38F66AE8FD05FDAA3FE0B4DA45E7C1293E1AFC02F04C9F0DFC33770B5BDC5E491436D7491BE43A43140CC91B367994BB31C9C60FCD5F217EC81F1B2CBF671FDA6BC17E38D46D2EAFEC3C37A925DDC416DB7CE923C156D9B8805B04900900E3A8EB5E6F456B80E1CC060F0B530942168D4BF336DB94AEACDB93D6FF0097426B63EB55A91AB37AC76ECADE47D19FF0544FDB13C3FF00B707ED383C67E19D3B59D33498B48B7D3523D51234B8768DA46662B1BBA8199303E63D3DF15E39F03BC7B07C2BF8D5E0FF00145D412DD5B786F5BB2D525862203CA904E92B2AE78C90A40CFAD72D45776132CA186C1C703497B918F2AD75B6DB98D5C44EA5575A5BB773EA8FF82B07EDDDE1AFDBCFE32E81AEF85749D734BD3345D2BEC27FB55228E7964323393B637750A32003BB279E057ACFEC19FF00050FF1F7EC4FF00EC34CF89FF0E3C47E29F823E206923D2EFA6B12042AF92F144D2810CF1372446CCBFC5B588040FCFEAFBE7F619FF82BAF87BC1BF03E1F83FF001CBC283C63E00861169677515BA4F2DAC00FCB14B0B101D13F85'
    $sHexData &= 'D4875006031C11F299F6454E86514F0186C37B6A506AF1E66A696BEF41FF00326FBEAB43D2C1E3253C54AB54A9C927D6DA5FB35D8ED2F3F6FBFD897E1B6A7FF097783FE04EA7A878B997CFB6B5BAB08A0B3B4997E64CAB4F245110C7EF4313118F615EF5FB42FC7BF11F8BFF00E08BBE30F1AFC48862D275AF1FC323595844ACAB6D0DCDC2A5A42AADF371105639EB96638CD78CE93E37FF0082717C36D563F1369DA76A9ACDFD91F3EDF4992D757B840E3240F2EE310BFD2472BD2BE66FF82957FC14EB58FDBCB5EB1D32C34E7F0D78074272FA7E965C34D71263689A72BF2EE0BC2A2F0A09E589CD7C8E1B21FED0C650F6187AD08D39294AA576F9AD1D54209B7A37BB5D91E9CF19EC294F9E706E49A5186DAF567ADFFC1353F6E2FD9F7F67DFD917C5FE18F889E1A173E27D45EE1A6FF8938BC6D7A164C47089082136F230E55467703926A8FF00C1BDD34571FB7D6B92410FD9E07F0A5F34716F2FE529BAB4C2E4F27038CF7AF82ABEB7FF00822E7ED21E0DFD983F6C0B9D7BC73ACA683A35F787AEB4E4BB9219248D26696091436C56201113738C671EB5F5BC43C3F1A396E61570BCF3A95D5DABB96ABA457FC3FDC79982C6B957A11A964A0F7DBEF67D4FFB447FC150BE17DFFC6FF1CFC2EFDA07E180F1AE8BE16D76E2DF48D56C608A5BA8232E70ACACF1B215040DF1C80B01CA923270'
    $sHexData &= '9BFE0B09F003F649F026A3A6FECF7F09AEAD35ABD8C47F6ED42D92DA1638F95A593CD92E26DA4FDC62A09CFCC339AF83BF6D5F899A47C64FDAC7E2078A3409DEEB45D6F599EEACA678DA333445BE56DAC0119C670403CF205797D6581E03CB6A61692ACA6935172A7CF25072B2BDE37DEFBAD0BAD9CD78D4972357BBB4ACAF6F537FE28FC4DD6FE337C43D5FC53E23BE9352D735CB96BABBB87EAEE7B01D9400000380001DABEB2FDBD3FE0A55E13FDAC7F630F865F0EB47D0FC43A76B9E119AD67D467BC5845A3186CE4B72B0B248CEE097CE591381D2BE2EA2BEAF1393616BD4A15671B3A2EF0B689696DBB58F329E2AA42338A7F1EE15F5B7FC139FFE0AB7AFFEC436573E17D634A1E30F879A948D24DA6492ED9AC99F87680B6570C325A361B58F3952493F24D15AE6795E1730C3BC2E321CD07D3F54D6A9F9A270F88A9426AA527667E9C41FB767EC23A66B4BE2EB6F81DAD7FC241BF72D8FF635BF931B02486F24DD7D940CF390B9E9C71C7CDDFF000517FF0082A4F89BF6EFBCB4D1EDEC07857C07A4CBE6DA69114BE63DCC8321659DC001881F7500DAB93F78F35F2C515E365FC1F80C2E2238A6E75271F85CE4E5CBFE1BE8BF33AEBE695AA41D3D229EF6495FD428A28AFA93CE0A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A'
    $sHexData &= '28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A'
    $sHexData &= '28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2800A28A2803FFFD9'

    Return $sHexData
EndFunc   ;==>_ImgEmptySlots

; =================================================================
; Helper function to deploy the file from hex data.
; =================================================================
Func _Deploy_ImgEmptySlots()
    Local $sHexData = _ImgEmptySlots()
    Local $Deploy_Dir = @TempDir
    If $sHexData = "" Then Return SetError(1, 0, False)
    Local $hFile = FileOpen($g_sImgEmptySlots, 2 + 8 + 16)
    If $hFile = -1 Then Return SetError(2, 0, False)
    FileWrite($hFile, Binary($sHexData))
    FileClose($hFile)
    If Not FileExists($g_sImgEmptySlots) Then Return SetError(3, 0, False)
    Return True
EndFunc   ;==>_Deploy_ImgEmptySlots


; === END UTILITY & HELPER FUNCTIONS ===

 

This program is an example of using ImageSearchUDF.

Download ImageSearchUDF here:

 

 

Download ImageSearch Automation Suite HERE: 

 

Edited by Trong
Update document!

Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal

Posted

thanks

If there is more than one identical shape with the same background in the image, it will only find one in the search results, and often the one defined in the "target image" selected by the mouse.

Posted (edited)
On 7/24/2025 at 3:27 AM, Parsix said:

thanks

If there is more than one identical shape with the same background in the image, it will only find one in the search results, and often the one defined in the "target image" selected by the mouse.

Updated code, try it out!

Edited by Trong

Enjoy my work? Buy me a 🍻 or tip via ❤️ PayPal

  • Trong changed the title to ImageSearchUDF Test Suite v3.3

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
×
×
  • Create New...