Public/Show-IntuneWinAppUtilGui.ps1

# Show-Gui.ps1

function Show-IntuneWinAppUtilGui {
    [CmdletBinding()]
    param ()

    Add-Type -AssemblyName PresentationFramework
    Add-Type -AssemblyName System.Windows.Forms

    $configPath = Join-Path -Path $env:APPDATA -ChildPath "IntuneWinAppUtilGUI\config.json"
    $xamlPath = Join-Path -Path $PSScriptRoot -ChildPath "..\UI\UI.xaml"
    $iconPath = Join-Path -Path $PSScriptRoot -ChildPath "..\Assets\Intune.ico"

    if (-not (Test-Path $xamlPath)) {
        Write-Error "XAML file not found: $xamlPath"
        return
    }

    $xaml = Get-Content $xamlPath -Raw
    $window = [Windows.Markup.XamlReader]::Parse($xaml)

    $SourceFolder = $window.FindName("SourceFolder")
    $SetupFile = $window.FindName("SetupFile")
    $OutputFolder = $window.FindName("OutputFolder")
    $ToolPathBox = $window.FindName("ToolPathBox")
    $FinalFilename = $window.FindName("FinalFilename")
    
    $BrowseSource = $window.FindName("BrowseSource")
    $BrowseSetup = $window.FindName("BrowseSetup")
    $BrowseOutput = $window.FindName("BrowseOutput")
    $BrowseTool = $window.FindName("BrowseTool")
    
    $RunButton = $window.FindName("RunButton")
    $ClearButton = $window.FindName("ClearButton")
    $ExitButton = $window.FindName("ExitButton")

    # Preload config.json if it exists
    if (Test-Path $configPath) {
        try {
            $cfg = Get-Content $configPath -Raw | ConvertFrom-Json
            if ($cfg.ToolPath -and (Test-Path $cfg.ToolPath)) {
                $ToolPathBox.Text = $cfg.ToolPath
            }
        } catch {}
    }

    # Eventi Browse
    $BrowseSource.Add_Click({
        $dlg = New-Object System.Windows.Forms.FolderBrowserDialog
        if ($dlg.ShowDialog() -eq 'OK') { $SourceFolder.Text = $dlg.SelectedPath }
    })

    # Browse for Setup File
    $BrowseSetup.Add_Click({
        $dialog = New-Object System.Windows.Forms.OpenFileDialog
        $dialog.Filter = "Executable or MSI (*.exe;*.msi)|*.exe;*.msi"
        if ($dialog.ShowDialog() -eq 'OK') {
            $selectedPath = $dialog.FileName
            $sourceRoot = $SourceFolder.Text.Trim()

            if (-not [string]::IsNullOrWhiteSpace($sourceRoot) -and (Test-Path $sourceRoot)) {
                try {
                    $relativePath = [System.IO.Path]::GetRelativePath($sourceRoot, $selectedPath)
                    if (-not ($relativePath.StartsWith(".."))) {
                        # File is inside source folder or subdir
                        $SetupFile.Text = $relativePath
                    } else {
                        # Outside of source folder
                        $SetupFile.Text = $selectedPath
                    }
                } catch {
                    # If relative path fails (e.g. bad format), fallback
                    $SetupFile.Text = $selectedPath
                }
            } else {
                # Source folder not set or invalid, fallback
                $SetupFile.Text = $selectedPath
            }
        }
    })

    $BrowseOutput.Add_Click({
        $dlg = New-Object System.Windows.Forms.FolderBrowserDialog
        if ($dlg.ShowDialog() -eq 'OK') { $OutputFolder.Text = $dlg.SelectedPath }
    })

    $BrowseTool.Add_Click({
        $dlg = New-Object System.Windows.Forms.OpenFileDialog
        $dlg.Filter = "IntuneWinAppUtil.exe|IntuneWinAppUtil.exe"
        if ($dlg.ShowDialog() -eq 'OK') { $ToolPathBox.Text = $dlg.FileName }
    })

    $RunButton.Add_Click({
        $c = $SourceFolder.Text.Trim()
        $s = $SetupFile.Text.Trim()
        $o = $OutputFolder.Text.Trim()
        $f = $FinalFilename.Text.Trim()

        $f = -join ($f.ToCharArray() | Where-Object { [System.IO.Path]::GetInvalidFileNameChars() -notcontains $_ })

        if (-not (Test-Path $c)) { [System.Windows.MessageBox]::Show("Invalid source folder path.", "Error", "OK", "Error"); return }
        if (-not (Test-Path $s)) {
            $s = Join-Path $c $s
            if (-not (Test-Path $s)) { [System.Windows.MessageBox]::Show("Setup file not found.", "Error", "OK", "Error"); return }
        }
        if (-not (Test-Path $o)) { [System.Windows.MessageBox]::Show("Invalid output folder path.", "Error", "OK", "Error"); return }

        # IntuneWinAppUtil.exe path check (or download if not set)
        $toolPath = $ToolPathBox.Text.Trim()
        $downloadDir = Join-Path $env:APPDATA "IntuneWinAppUtilGUI\bin"
        $exePath = Join-Path $downloadDir "IntuneWinAppUtil.exe"

        if ([string]::IsNullOrWhiteSpace($toolPath) -or -not (Test-Path $toolPath)) {
            if (-not (Test-Path $exePath)) {
                try {
                    Add-Type -AssemblyName System.IO.Compression.FileSystem

                    $url = "https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool/archive/refs/heads/master.zip"
                    $zipPath = Join-Path $env:TEMP "IntuneWinAppUtil-master.zip"
                    $extractPath = Join-Path $env:TEMP "IntuneExtract"

                    if (Test-Path $extractPath) { Remove-Item $extractPath -Recurse -Force }

                    Invoke-WebRequest -Uri $url -OutFile $zipPath -UseBasicParsing
                    [System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $extractPath, $true)
                    Remove-Item $zipPath -Force

                    # Find the executable in the extracted structure
                    $sourceExe = Get-ChildItem -Path $extractPath -Recurse -Filter "IntuneWinAppUtil.exe" | Select-Object -First 1

                    if (-not $sourceExe) {
                        throw "IntuneWinAppUtil.exe not found in extracted archive."
                    }

                    # Copy to destination
                    New-Item -ItemType Directory -Force -Path $downloadDir | Out-Null
                    Copy-Item -Path $sourceExe.FullName -Destination $exePath -Force

                    [System.Windows.MessageBox]::Show("Tool downloaded and extracted to:`n$exePath", "Download Complete", "OK", "Info")
                } catch {
                    [System.Windows.MessageBox]::Show("Failed to download or extract the archive:`n$($_.Exception.Message)", "Download Error", "OK", "Error")
                    return
                }
            }

            if (Test-Path $exePath) {
                $toolPath = $exePath
                $ToolPathBox.Text = $toolPath
            }
        }

        if (-not (Test-Path $toolPath)) {
            [System.Windows.MessageBox]::Show("IntuneWinAppUtil.exe not found at:`n$toolPath", "Error", "OK", "Error")
            return
        }

        $IWAUtilargs = "-c `"$c`" -s `"$s`" -o `"$o`""
        Start-Process -FilePath $toolPath -ArgumentList $IWAUtilargs -WorkingDirectory (Split-Path $toolPath) -WindowStyle Normal -Wait

        Start-Sleep -Seconds 1
        $defaultName = [System.IO.Path]::GetFileNameWithoutExtension($s) + ".intunewin"
        $defaultPath = Join-Path $o $defaultName

        if (Test-Path $defaultPath) {
            $newName = if ([string]::IsNullOrWhiteSpace($f)) {
            (Split-Path $c -Leaf) + ".intunewin"
            } else {
                $f + ".intunewin"
            }

            try {
                Rename-Item -Path $defaultPath -NewName $newName -Force
                $fullPath = Join-Path $o $newName
                $resp = [System.Windows.MessageBox]::Show("Package created and renamed to:`n$newName`n`nOpen folder?", "Success", [System.Windows.MessageBoxButton]::YesNo, [System.Windows.MessageBoxImage]::Information)
                if ($resp -eq "Yes") { Start-Process explorer.exe "/select,`"$fullPath`"" }
            } catch {
                [System.Windows.MessageBox]::Show("Renaming failed: $($_.Exception.Message)", "Warning", "OK", "Warning")
            }
        } else {
            [System.Windows.MessageBox]::Show("Output file not found.", "Warning", "OK", "Warning")
        }
    })

    # Clear button: reset all except ToolPath if loaded from config
    $ClearButton.Add_Click({
        $SourceFolder.Clear()
        $SetupFile.Clear()
        $OutputFolder.Clear()
        $FinalFilename.Clear()
    })

    # Exit button: close the window
    $ExitButton.Add_Click({
        $window.Close()
    })

    $window.Add_KeyDown({
        param($sender, $e)
        switch ($e.Key) {
            'Escape' {
                if ([System.Windows.MessageBox]::Show("Exit the tool?", "Confirm", "YesNo", "Question") -eq "Yes") {
                    $window.Close()
                }
            }
            'Return' {
                $RunButton.RaiseEvent((New-Object System.Windows.RoutedEventArgs ([System.Windows.Controls.Button]::ClickEvent)))
            }
        }
    })

    $window.Add_Closed({
        if (-not (Test-Path (Split-Path $configPath))) {
            New-Item -Path (Split-Path $configPath) -ItemType Directory -Force | Out-Null
        }
        $cfg = @{ ToolPath = $ToolPathBox.Text.Trim() }
        $cfg | ConvertTo-Json | Set-Content $configPath -Encoding UTF8
    })

    if (Test-Path $iconPath) {
        $window.Icon = [System.Windows.Media.Imaging.BitmapFrame]::Create((New-Object System.Uri $iconPath, [System.UriKind]::Absolute))
    }

    $window.AddHandler([ System.Windows.Documents.Hyperlink]::RequestNavigateEvent,
        [System.Windows.Navigation.RequestNavigateEventHandler] {
            param($sender, $e)
            Start-Process $e.Uri.AbsoluteUri
            $e.Handled = $true
        })

    $window.ShowDialog() | Out-Null
}

Export-ModuleMember -Function Show-IntuneWinAppUtilGui