PS1 search for existing edge/opened link before opening

<# Powershell.exe -NoProfile -ExecutionPolicy Bypass -File "\\gal-arc01\T\Script\PS1\Edge-Open-Link-conditional\Edge-Open-Link-Conditional.ps1" -Link "https://planner.cloud.microsoft/webui/plan/6MwYXy_J30-C38ToHv9SxmUAGUaG/view/board?tid=94e13abb-51f9-4cae-9154-1dc424fb2c80" File: launch-conditional-Edge.ps1 Goal: - If Edge is not launched (no visible Edge window): open Link in a new window. - If Edge is launched AND the Link is already open in any tab: focus that tab (do nothing else). - If Edge is launched BUT the Link is NOT open: open Link in a new tab. Usage: powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\launch-conditional-Edge.ps1 -Link "https://..." Notes: - UI Automation may fail if Edge is elevated but this script isn't (or vice versa). Run at same elevation. - Tab scanning is best-effort; if UIA cannot read the address bar, script will open a new tab. #> # --- Parameter declaration --- param( # Link to open if not already open [Parameter(Mandatory = $false)] [string]$Link ) # --- Preference declaration --- $ErrorActionPreference = "Stop" # --- Function declaration --- function Explain-Behavior { <# .SYNOPSIS Prints a concise explanation of what this script does. #> Write-Host "" Write-Host "Behavior:" Write-Host " 1) If Microsoft Edge has no visible window: opens the link in a NEW Edge window." Write-Host " 2) If Edge is running and the link is already open in any tab: focuses that tab." Write-Host " 3) If Edge is running but the link is NOT open: opens the link in a NEW tab." Write-Host "" Write-Host "Best-effort notes:" Write-Host " - Uses Windows UI Automation to scan tabs + read the address bar." Write-Host " - If UI Automation cannot read the URL, it falls back to opening a new tab." Write-Host " - If Edge is elevated, run this script elevated too." Write-Host "" } # --- Function declaration --- function Show-Help { <# .SYNOPSIS Prints help text and exits. #> Write-Host "" Write-Host "launch-conditional-Edge.ps1" Write-Host "" Write-Host "Usage:" Write-Host " powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\launch-conditional-Edge.ps1 -Link ""https://...""" Write-Host "" Write-Host "Parameters:" Write-Host " -Link (Required) The URL to ensure is open in Edge." Explain-Behavior } # --- Function declaration --- function Normalize-Url { param( # URL to normalize for comparison [Parameter(Mandatory = $true)][string]$Url ) # Local variable declaration $u = $Url.Trim() # Control structure: if/then if (($u.StartsWith('"') -and $u.EndsWith('"')) -or ($u.StartsWith("'") -and $u.EndsWith("'"))) { $u = $u.Substring(1, $u.Length - 2) } # Control structure: while loop while ($u.EndsWith("/")) { $u = $u.Substring(0, $u.Length - 1) } return $u.ToLowerInvariant() } # --- Function declaration --- function Resolve-EdgeExePath { # Local variable declaration $cmd = Get-Command "msedge.exe" -ErrorAction SilentlyContinue # Control structure: if/then if ($cmd) { return $cmd.Source } # Local variable declaration $candidates = @( "$env:ProgramFiles\Microsoft\Edge\Application\msedge.exe", "$env:ProgramFiles(x86)\Microsoft\Edge\Application\msedge.exe", "$env:LocalAppData\Microsoft\Edge\Application\msedge.exe" ) # Control structure: foreach foreach ($p in $candidates) { # Control structure: if/then if ($p -and (Test-Path $p)) { return $p } } # Local variable declaration (registry App Paths locations) $regKeys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe", "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\msedge.exe" ) # Control structure: foreach foreach ($k in $regKeys) { # Control structure: try/catch try { # Local variable declaration $val = (Get-ItemProperty -Path $k -ErrorAction Stop).'(default)' # Control structure: if/then if ($val -and (Test-Path $val)) { return $val } } catch { # Intentionally ignore missing keys/permissions } } # Control structure: return (soft-fail) return $null } # --- Function declaration --- ##Expanded to launch under bypass and search for MSEdge install function Open-EdgeUrl { param( # URL to open [Parameter(Mandatory = $true)][string]$Url, # Mode: new-window or new-tab (best-effort if msedge.exe is not found) [Parameter(Mandatory = $true)] [ValidateSet("new-window", "new-tab")] [string]$Mode ) # Local variable declaration $edgeExe = Resolve-EdgeExePath # Control structure: if/then/else if ($edgeExe) { # Control structure: if/then/else if ($Mode -eq "new-window") { Start-Process -FilePath $edgeExe -ArgumentList @("--new-window", $Url) | Out-Null } else { Start-Process -FilePath $edgeExe -ArgumentList @("--new-tab", $Url) | Out-Null } } else { # Fall back to the registered Edge protocol handler (works even if msedge.exe path isn't discoverable) Start-Process -FilePath ("microsoft-edge:" + $Url) | Out-Null } } # --- Function declaration --- function Add-User32 { # Control structure: try/catch try { Add-Type -Namespace Win32 -Name User32 -MemberDefinition @" using System; using System.Runtime.InteropServices; public static class User32 { [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); } "@ -ErrorAction Stop | Out-Null } catch { # Intentionally ignore if already added in this session } } # --- Function declaration --- # --- Function declaration --- function Focus-Window { param( # Window handle to focus [Parameter(Mandatory = $true)][IntPtr]$Handle ) # Control structure: try/catch (UIA assembly load) try { Ensure-UIAutomationLoaded } catch { return $false } # Local variable declaration (window automation element) $winElem = [System.Windows.Automation.AutomationElement]::FromHandle($Handle) # Control structure: if/then (handle -> element validation) if (-not $winElem) { return $false } # Control structure: try/catch (restore window if minimized) try { $wp = $winElem.GetCurrentPattern([System.Windows.Automation.WindowPattern]::Pattern) $wp.SetWindowVisualState([System.Windows.Automation.WindowVisualState]::Normal) } catch { # Ignore if pattern not available } # Control structure: try/catch (focus window) try { $winElem.SetFocus() return $true } catch { return $false } } # --- Function declaration --- function Get-EdgeWindows { # Return Edge processes with visible windows Get-Process -Name "msedge" -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowHandle -ne 0 } | Sort-Object Id } # --- Function declaration --- function Ensure-UIAutomationLoaded { # Load UI Automation assemblies for AutomationElement, patterns, etc. Add-Type -AssemblyName UIAutomationClient -ErrorAction Stop Add-Type -AssemblyName UIAutomationTypes -ErrorAction Stop } # --- Function declaration --- function Find-AddressBarElement { param( # UIA AutomationElement representing the Edge window [Parameter(Mandatory = $true)] [System.Windows.Automation.AutomationElement]$Window ) # Local variable declaration (UIA conditions) $editType = New-Object System.Windows.Automation.PropertyCondition( [System.Windows.Automation.AutomationElement]::ControlTypeProperty, [System.Windows.Automation.ControlType]::Edit ) # Local variable declaration (name heuristics for address bar; can vary) $name1 = New-Object System.Windows.Automation.PropertyCondition( [System.Windows.Automation.AutomationElement]::NameProperty, "Address and search bar" ) # Local variable declaration (alternate name heuristic) $name2 = New-Object System.Windows.Automation.PropertyCondition( [System.Windows.Automation.AutomationElement]::NameProperty, "Search or enter web address" ) # Local variable declaration (alternate automation id heuristic) $autoId = New-Object System.Windows.Automation.PropertyCondition( [System.Windows.Automation.AutomationElement]::AutomationIdProperty, "addressEditBox" ) # Local variable declaration (combine heuristics) $orNames = New-Object System.Windows.Automation.OrCondition($name1, $name2) $orAll = New-Object System.Windows.Automation.OrCondition($orNames, $autoId) $and = New-Object System.Windows.Automation.AndCondition($editType, $orAll) return $Window.FindFirst([System.Windows.Automation.TreeScope]::Descendants, $and) } # --- Function declaration --- function Get-AddressBarValue { param( # UIA AutomationElement representing the Edge window [Parameter(Mandatory = $true)] [System.Windows.Automation.AutomationElement]$Window ) # Local variable declaration $addr = Find-AddressBarElement -Window $Window # Control structure: if/then if (-not $addr) { return $null } # Control structure: try/catch try { $vp = $addr.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern) return $vp.Current.Value } catch { return $null } } # --- Function declaration --- function Get-TabItems { param( # UIA AutomationElement representing the Edge window [Parameter(Mandatory = $true)] [System.Windows.Automation.AutomationElement]$Window ) # Local variable declaration (condition for TabItem controls) $cond = New-Object System.Windows.Automation.PropertyCondition( [System.Windows.Automation.AutomationElement]::ControlTypeProperty, [System.Windows.Automation.ControlType]::TabItem ) return $Window.FindAll([System.Windows.Automation.TreeScope]::Descendants, $cond) } # --- Function declaration --- function Select-TabItem { param( # UIA AutomationElement representing a tab [Parameter(Mandatory = $true)] [System.Windows.Automation.AutomationElement]$TabItem ) # Control structure: try/catch try { $sel = $TabItem.GetCurrentPattern([System.Windows.Automation.SelectionItemPattern]::Pattern) $sel.Select() return $true } catch { # Control structure: nested try/catch try { $inv = $TabItem.GetCurrentPattern([System.Windows.Automation.InvokePattern]::Pattern) $inv.Invoke() return $true } catch { return $false } } } # --- Function declaration --- function Send-UrlToFocusedEdge { param( # URL to type into the currently focused Edge window [Parameter(Mandatory = $true)][string]$Url ) # Load SendKeys helper Add-Type -AssemblyName System.Windows.Forms | Out-Null Start-Sleep -Milliseconds 200 [System.Windows.Forms.SendKeys]::SendWait("^l") Start-Sleep -Milliseconds 80 [System.Windows.Forms.SendKeys]::SendWait($Url) Start-Sleep -Milliseconds 80 [System.Windows.Forms.SendKeys]::SendWait("{ENTER}") } # --- Function declaration --- function Open-EdgeUrl { param( # URL to open [Parameter(Mandatory = $true)][string]$Url, # Mode: new-window or new-tab [Parameter(Mandatory = $true)] [ValidateSet("new-window", "new-tab")] [string]$Mode ) # Local variable declaration $edgeExe = Resolve-EdgeExePath # Control structure: if/then/else if ($Mode -eq "new-window") { Start-Process -FilePath $edgeExe -ArgumentList @("--new-window", $Url) | Out-Null } else { Start-Process -FilePath $edgeExe -ArgumentList @("--new-tab", $Url) | Out-Null } } # --- Function declaration --- function Try-FocusExistingEdgeTabByUrl { param( # Target URL to find among Edge tabs [Parameter(Mandatory = $true)][string]$TargetUrl ) Ensure-UIAutomationLoaded # Local variable declarations $targetNorm = Normalize-Url -Url $TargetUrl $windows = Get-EdgeWindows # Control structure: if/then if (-not $windows -or $windows.Count -eq 0) { return $false } # Control structure: foreach foreach ($p in $windows) { # Local variable declaration $h = [IntPtr]$p.MainWindowHandle # Control structure: if/then if ($h -eq [IntPtr]::Zero) { continue } # Control structure: if/then (best-effort focus; continue even if it fails) if (-not (Focus-Window -Handle $h)) { # Proceed without hard focus; tab scanning may still work } Start-Sleep -Milliseconds 150 # Local variable declaration $winElem = [System.Windows.Automation.AutomationElement]::FromHandle($h) # Control structure: if/then if (-not $winElem) { continue } # Local variable declaration $curUrl = Get-AddressBarValue -Window $winElem # Control structure: if/then if ($curUrl -and (Normalize-Url -Url $curUrl) -eq $targetNorm) { return $true } # Local variable declaration $tabs = Get-TabItems -Window $winElem # Control structure: if/then if (-not $tabs -or $tabs.Count -eq 0) { continue } # Local variable declaration (cap scanning for safety) $maxTabs = [Math]::Min($tabs.Count, 80) # Control structure: for loop for ($i = 0; $i -lt $maxTabs; $i++) { # Local variable declaration $tab = $tabs.Item($i) # Control structure: if/then if (-not (Select-TabItem -TabItem $tab)) { continue } Start-Sleep -Milliseconds 120 # Local variable declaration $tabUrl = Get-AddressBarValue -Window $winElem # Control structure: if/then if (-not $tabUrl) { continue } # Control structure: if/then if ((Normalize-Url -Url $tabUrl) -eq $targetNorm) { return $true } } } return $false } # --- Main block declarations --- # Control structure: if/then (help gate) if ($PSBoundParameters.Count -eq 0) { Show-Help exit 1 } # Control structure: if/then (argument validation) if (-not $Link -or [string]::IsNullOrWhiteSpace($Link)) { Show-Help exit 1 } # Local variable declaration $target = $Link # Local variable declaration $found = $false # Control structure: try/catch try { $found = Try-FocusExistingEdgeTabByUrl -TargetUrl $target } catch { $found = $false } # Control structure: if/then/else if ($found) { exit 0 } else { # Local variable declaration $hasVisibleEdge = (Get-EdgeWindows | Measure-Object).Count -gt 0 # Control structure: if/then/else if (-not $hasVisibleEdge) { Open-EdgeUrl -Url $target -Mode "new-window" exit 0 } else { # Control structure: try/catch try { Open-EdgeUrl -Url $target -Mode "new-tab" exit 0 } catch { # Control structure: foreach (focus first available window then type URL) foreach ($p in (Get-EdgeWindows)) { # Local variable declaration $h = [IntPtr]$p.MainWindowHandle # Control structure: if/then if ($h -ne [IntPtr]::Zero) { # Control structure: if/then (best-effort focus; continue even if it fails) if (-not (Focus-Window -Handle $h)) { # Proceed without hard focus; tab scanning may still work } Send-UrlToFocusedEdge -Url $target break } } exit 0 } } }

Comments

Popular posts from this blog

Revit area plans adding new types and references (Gross and rentable)

Revit CSV file manager for families and re-exporting to a CSV file

PDQ Sticky notification - Systray and MSG box notificaitons for install complete for users to close