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
Post a Comment