powershellvariablesscope

How do I fix a Powershell problem with button selection?


I used ChatGPT to generate the code to understand how to do it myself, and it mostly works. The script is to check my TV & Movie folders for missing files to make Plex work better (folder.jpg, theme.mp3 and so on) and show which are missing, and how many.

It works perfectly fine on the movies folder, but I can't (nor can the AI) work out how to fix it to click the TV or TV ended button and make it search those folders.

My best guess is the $pathMap variable on line 167 is the key and it's not updating correctly for this part - $global:fixedPath = $pathMap

Would anyone be able to correct it and explain how I could work out how to do it?

Windows 10 22H2 and Powershell PSVersion 5.1.19041.5737

    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    
    # Create form
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "Marion's Plex checker"
    $form.Size = New-Object System.Drawing.Size(700, 700)
    $form.StartPosition = "CenterScreen"
    
    # Fixed folder path
    $fixedPath = "D:\ServerFolders\Videos\02 - Movies"
    
    # Path label
    $label = New-Object System.Windows.Forms.Label
    $label.Text = "Checking subfolders in: $fixedPath"
    $label.Location = New-Object System.Drawing.Point(120, 20)
    $label.Size = New-Object System.Drawing.Size(550, 20)
    $form.Controls.Add($label)
    
    # Summary label
    $summaryLabel = New-Object System.Windows.Forms.Label
    $summaryLabel.Text = ""
    $summaryLabel.Location = New-Object System.Drawing.Point(120, 45)
    $summaryLabel.Size = New-Object System.Drawing.Size(550, 20)
    $form.Controls.Add($summaryLabel)
    
    # Output box
    $logBox = New-Object System.Windows.Forms.TextBox
    $logBox.Multiline = $true
    $logBox.ScrollBars = "Vertical"
    $logBox.Location = New-Object System.Drawing.Point(120, 75)
    $logBox.Size = New-Object System.Drawing.Size(550, 350)
    $logBox.Anchor = "Top,Bottom,Left,Right"
    $logBox.ReadOnly = $true
    $form.Controls.Add($logBox)
    
    # Search file buttons (on the left)
    $fileTypes = @("theme.mp3", "folder.jpg", "background.jpg", "poster.jpg")
    $fileButtons = @{}
    $yPos = 20
    $global:selectedFile = "theme.mp3"
    
    foreach ($file in $fileTypes) {
        $btn = New-Object System.Windows.Forms.Button
        $btn.Text = $file
        $btn.Tag = $file
        $btn.Location = New-Object System.Drawing.Point(10, $yPos)
        $btn.Size = New-Object System.Drawing.Size(100, 30)
        $form.Controls.Add($btn)
        $fileButtons[$file] = $btn
        $yPos += 40
    
        $btn.Add_Click({
            $global:selectedFile = $this.Tag
    
            # Reset styles
            foreach ($b in $fileButtons.Values) {
                $b.UseVisualStyleBackColor = $true
            }
    
            # Highlight selected
            # Reset styles
            foreach ($b in $fileButtons.Values) {
                $b.UseVisualStyleBackColor = $true
                $b.BackColor = [System.Drawing.SystemColors]::Control
            }
    
            # Highlight selected
            $this.UseVisualStyleBackColor = $false
            $this.BackColor = 'LightBlue'
    
            $summaryLabel.Text = "Selected file: $global:selectedFile"
        })
    }
    
    # Scan button
    $scanButton = New-Object System.Windows.Forms.Button
    $scanButton.Text = "Scan"
    $scanButton.Location = New-Object System.Drawing.Point(10, 440)
    $scanButton.Size = New-Object System.Drawing.Size(100, 30)
    $form.Controls.Add($scanButton)
    
    # Create a new button for Movies, positioned to the right of the Scan button
    $moviesButton = New-Object System.Windows.Forms.Button
    $moviesButton.Text = "Movies"
    $moviesButton.Size = New-Object System.Drawing.Size(100, 30)
    $moviesButton.Location = New-Object System.Drawing.Point(120, 440)
    
    # Create a new button for TV, positioned next to the Movies button
    $tvButton = New-Object System.Windows.Forms.Button
    $tvButton.Text = "TV"
    $tvButton.Size = New-Object System.Drawing.Size(100, 30)
    $tvButton.Location = New-Object System.Drawing.Point(230, 440)
    
    # Create a new button for TV Ended, positioned next to the TV button
    $tvEndedButton = New-Object System.Windows.Forms.Button
    $tvEndedButton.Text = "TV Ended"
    $tvEndedButton.Size = New-Object System.Drawing.Size(100, 30)
    $tvEndedButton.Location = New-Object System.Drawing.Point(340, 440)  # Right of the TV button
    $tvEndedButton.Add_Click({
        Start-Process "D:\ServerFolders\Videos\TV (Ended)"  # Link to the TV Ended path
    })
    $form.Controls.Add($tvEndedButton)
    
    # Path variables for Movies, TV, and TV Ended
    $pathMap = @{
        "Movies"     = "D:\ServerFolders\Videos\02 - Movies"
        "TV Current" = "D:\ServerFolders\Videos\01 - TV"
        "TV Ended"   = "D:\ServerFolders\Videos\TV (Ended)"
    }
    
    # Default path for Movies
    $global:fixedPath = $pathMap["Movies"]
    $pathLabel.Text = "Scanning path: $global:fixedPath"
    
    # Add click event for Movies button
    $moviesButton.Add_Click({
        $global:fixedPath = $pathMap["Movies"]
        $pathLabel.Text = "Scanning path: $global:fixedPath"
        # Change button colors to indicate active path
        $moviesButton.BackColor = 'LightGreen'
        $tvButton.BackColor = [System.Drawing.SystemColors]::Control
        $tvEndedButton.BackColor = [System.Drawing.SystemColors]::Control
        # Start scanning the Movies path
        Write-Host "Scanning Movies path: $global:fixedPath"
        Scan-Folder -path $global:fixedPath
    })
    
    # Add click event for TV button
    $tvButton.Add_Click({
        $global:fixedPath = $pathMap["TV Current"]
        $pathLabel.Text = "Scanning path: $global:fixedPath"
        # Change button colors to indicate active path
        $tvButton.BackColor = 'LightGreen'
        $moviesButton.BackColor = [System.Drawing.SystemColors]::Control
        $tvEndedButton.BackColor = [System.Drawing.SystemColors]::Control
        # Start scanning the TV Current path
        Write-Host "Scanning TV path: $global:fixedPath"
        Scan-Folder -path $global:fixedPath
    })
    
    # Add click event for TV Ended button
    $tvEndedButton.Add_Click({
        $global:fixedPath = $pathMap["TV Ended"]
        $pathLabel.Text = "Scanning path: $global:fixedPath"
        # Change button colors to indicate active path
        $tvEndedButton.BackColor = 'LightGreen'
        $moviesButton.BackColor = [System.Drawing.SystemColors]::Control
        $tvButton.BackColor = [System.Drawing.SystemColors]::Control
        # Start scanning the TV Ended path
        Write-Host "Scanning TV Ended path: $global:fixedPath"
        Scan-Folder -path $global:fixedPath
    })
    
    # Function to scan the folder
    function Scan-Folder {
        param(
            [string]$path
        )
        Write-Host "Scanning folder: $path"
        # Insert the actual folder scanning logic here
    }
    
    
    # Path variables for Movies, TV, and TV Ended
    $pathMap = @{
        "Movies"     = "D:\ServerFolders\Videos\02 - Movies"
        "TV Current" = "D:\ServerFolders\Videos\01 - TV"
        "TV Ended"   = "D:\ServerFolders\Videos\TV (Ended)"
    }
    
    # Default path for Movies
    $global:fixedPath = $pathMap["Movies"]
    
    # Add click event for Movies button
    $moviesButton.Add_Click({
        $global:fixedPath = $pathMap["Movies"]
        $pathLabel.Text = "Scanning path: $global:fixedPath"
        # Change button colors to indicate active path
        $moviesButton.BackColor = 'LightGreen'
        $tvButton.BackColor = [System.Drawing.SystemColors]::Control
        $tvEndedButton.BackColor = [System.Drawing.SystemColors]::Control
        # Trigger the scanning logic
        Scan-Folder -path $global:fixedPath
    })
    
    # Add click event for TV button
    $tvButton.Add_Click({
        $global:fixedPath = $pathMap["TV Current"]
        $pathLabel.Text = "Scanning path: $global:fixedPath"
        # Change button colors to indicate active path
        $tvButton.BackColor = 'LightGreen'
        $moviesButton.BackColor = [System.Drawing.SystemColors]::Control
        $tvEndedButton.BackColor = [System.Drawing.SystemColors]::Control
        # Trigger the scanning logic
        Scan-Folder -path $global:fixedPath
    })
    
    # Add click event for TV Ended button
    $tvEndedButton.Add_Click({
        $global:fixedPath = $pathMap["TV Ended"]
        $pathLabel.Text = "Scanning path: $global:fixedPath"
        # Change button colors to indicate active path
        $tvEndedButton.BackColor = 'LightGreen'
        $moviesButton.BackColor = [System.Drawing.SystemColors]::Control
        $tvButton.BackColor = [System.Drawing.SystemColors]::Control
        # Trigger the scanning logic
        Scan-Folder -path $global:fixedPath
    })
    
    # Function to scan the folder
    function Scan-Folder {
        param(
            [string]$path
        )
        Write-Host "Scanning folder: $path"
        # Insert the actual scanning logic here to process the folder
    }
    
      # Right of the Movies button
    $tvButton.Add_Click({
        Start-Process "D:\ServerFolders\Videos\01 - TV"  # Link to the TV path
    })
    $form.Controls.Add($tvButton)
      # Right of the Scan button
    $moviesButton.Add_Click({
        Start-Process "D:\ServerFolders\Videos\02 - Movies"  # Link to the Movies path
    })
    $form.Controls.Add($moviesButton)
    
    
    # Globals
    $global:missingItems = @()
    
    function Run-Scan {
        $logBox.Clear()
        $summaryLabel.Text = ""
        $log = @()
        $logPath = Join-Path $fixedPath "missing_${global:selectedFile}_log.txt"
        $csvPath = Join-Path $fixedPath "missing_${global:selectedFile}_report.csv"
        $global:missingItems = @()
    
        if (-not (Test-Path $fixedPath)) {
            [System.Windows.Forms.MessageBox]::Show("Invalid folder path.", "Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
            return
        }
    
        $folders = Get-ChildItem -Path $fixedPath -Directory
    
        foreach ($folder in $folders) {
            $targetFile = Join-Path $folder.FullName $global:selectedFile
            if (-not (Test-Path -LiteralPath $targetFile)) {
                $global:missingItems += $folder
                $logLine = "$($folder.Name)"
                $logBox.AppendText($logLine + "`r`n")
                $log += $logLine
            }
        }
    
        if ($global:missingItems.Count -eq 0) {
            $summaryLabel.Text = "All folders have $($global:selectedFile)."
        } else {
            $summaryLabel.Text = "Missing $($global:selectedFile) from [$($global:missingItems.Count)] folders."
        }
    
        $log | Out-File -FilePath $logPath -Encoding UTF8
    
        $logBox.AppendText("`r`nLog saved to: $logPath`r`n")
        $exportButton.Enabled = $global:missingItems.Count -gt 0
    }
    
    $scanButton.Add_Click({ Run-Scan })
    
    
    # Export to CSV button
    $exportButton = New-Object System.Windows.Forms.Button
    $exportButton.Text = "Export to CSV"
    $exportButton.Location = New-Object System.Drawing.Point(10, 400)
    $exportButton.Size = New-Object System.Drawing.Size(100, 30)
    $exportButton.Enabled = $false
    $form.Controls.Add($exportButton)
    
    $exportButton.Add_Click({
        $csvPath = Join-Path $fixedPath "missing_$($global:selectedFile)_report.csv"
    
        $csvPath = Join-Path $fixedPath "missing_$($global:selectedFile)_report.csv"
        $headerLines = @(
            "File type: $($global:selectedFile)",
            "Total missing: $($global:missingItems.Count)`r`n"
        )
        $headerLines | Out-File -FilePath $csvPath -Encoding UTF8
    
        $global:missingItems | ForEach-Object {
            [PSCustomObject]@{ FolderName = $_.Name; Path = $_.FullName }
        } | Export-Csv -Path $csvPath -Append -NoTypeInformation -Encoding UTF8
    
        $logBox.AppendText("CSV exported to: $csvPath`r`n")
    
    })
    
    
    $form.Topmost = $true
    $form.Add_Shown({ $form.Activate() })
    [void]$form.ShowDialog()


Solution

  • Your problem is one of variable scoping:

    You use both $fixedPath and $global:fixedPath to refer to what you think are the same variable, but they're not:


    For robustness and conceptual clarity I suggest always using a scope specifier to refer to variables designed for cross-scope use; however, do not use $global:, as global variables are invariably session-global, and therefore linger after your script exits, polluting the global namespace.

    Instead, use the $script: scope:
    That is, replace all occurrences of $global:fixedPath and $fixedPath with $script:fixedPath