function Get-QuickAccessItem {
<#
.SYNOPSIS
Retrieves the list of pinned items in Quick Access.
.DESCRIPTION
This function returns all items pinned in Quick Access, providing the following details for each:
- Its Name (as displayed in Quick Access),
- Its Full Path,
- A flag `IsSystem` indicating if it's a system folder,
- A flag `IsBroken` indicating an invalid path.
.EXAMPLE
Get-QuickAccessItem
.EXAMPLE
Get-QuickAccessItem | Where-Object { -not $_.IsSystem }
.AUTHOR
Jean-Philippe SOSSON
.VERSION
25.03.22.1801
#>
[CmdletBinding()]
param ()
try {
# Detecting standard system paths for the current user
Write-Verbose " Detecting standard system paths..."
# Use COM to retrieve the exact paths of system folders
$ShellApp = New-Object -ComObject Shell.Application
$SystemPaths = @{
Desktop = $ShellApp.Namespace('shell:Desktop').Self.Path
Documents = $ShellApp.Namespace('shell:Personal').Self.Path
Pictures = $ShellApp.Namespace('shell:My Pictures').Self.Path
Music = $ShellApp.Namespace('shell:My Music').Self.Path
Videos = $ShellApp.Namespace('shell:My Video').Self.Path
Downloads = $ShellApp.Namespace('shell:Downloads').Self.Path
}
Write-Verbose " Detected system paths:"
$SystemPaths.GetEnumerator() | ForEach-Object { Write-Verbose " - $($_.Key): $($_.Value)" }
# Load pinned Quick Access items
Write-Verbose " Loading pinned Quick Access items..."
$PinnedNamespace = $ShellApp.Namespace("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}")
if (-not $PinnedNamespace) {
throw "Unable to load pinned items in Quick Access. Ensure that Quick Access is properly configured."
}
$PinnedItems = $PinnedNamespace.Items()
# Analyze each Quick Access item
$Results = foreach ($PinnedItem in $PinnedItems) {
$Path = $PinnedItem.Path
$IsSystem = $false
# Check if the path EXACTLY matches a known system folder
foreach ($SystemPath in $SystemPaths.Values) {
if ($Path -eq $SystemPath) { # Strict comparison
Write-Verbose "✅ '$Path' identified as system."
$IsSystem = $true
break
}
}
# Construct an output object for each item
[PSCustomObject]@{
Name = $PinnedItem.Name
Path = $Path
IsSystem = $IsSystem
IsBroken = if (Test-Path $Path) { $false } else { $true }
}
}
return $Results
} catch {
# Error handling
Write-Error "❌ An error occurred: $($_.Exception.Message)"
}
}
function Remove-QuickAccessItem {
<#
.SYNOPSIS
Removes one or more Quick Access items by name or path.
.DESCRIPTION
This function removes Quick Access items based on their name, path, or invalid paths.
If `-Force` is used, no confirmation is required, and the function returns an array
of the removed items.
.PARAMETER Name
Name(s) or patterns of the target items (default: `*` for "all").
.PARAMETER Path
Full path(s) of the target items.
.PARAMETER IncludeSystem
Include system items (e.g., Desktop, Downloads, etc.).
.PARAMETER OnlyBrokenPaths
Remove only items pointing to invalid paths.
.PARAMETER Force
Deletes items without requesting confirmation and directly returns a table of removed items.
.EXAMPLES
Remove-QuickAccessItem -Name "Videos"
Remove-QuickAccessItem -Path "C:\Users\Support Technician\Videos"
Remove-QuickAccessItem -IncludeSystem -OnlyBrokenPaths
Remove-QuickAccessItem -Name "Downloads" -Force
.AUTHOR
Jean-Philippe SOSSON
.VERSION
25.03.22.1811
#>
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
param (
[string[]]$Name = '*', # Pattern(s) of target item names
[string[]]$Path, # Full path(s) of the target items
[switch]$IncludeSystem, # Include system items
[switch]$OnlyBrokenPaths, # Remove only invalid paths
[switch]$Force # Skip confirmation and directly return table
)
begin {
# Initialize COM object
$ShellApp = New-Object -ComObject Shell.Application
$QuickAccessFolder = $ShellApp.Namespace("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}")
if (-not $QuickAccessFolder) {
Write-Error "❌ Unable to access Quick Access. Check your system configuration."
return
}
# Get Quick Access items
$QuickAccessItems = Get-QuickAccessItem
}
process {
# Filter target items based on parameters
$TargetItems = @()
if ($OnlyBrokenPaths) {
$TargetItems = $QuickAccessItems | Where-Object {
$_.IsBroken -and ($IncludeSystem -or -not $_.IsSystem)
}
} elseif ($Path) {
$TargetItems = $QuickAccessItems | Where-Object {
$_.Path -in $Path -and ($IncludeSystem -or -not $_.IsSystem)
}
} elseif ($Name) {
$TargetItems = $QuickAccessItems | Where-Object {
$_.Name -like $Name -and ($IncludeSystem -or -not $_.IsSystem)
}
}
# Check if any items were found
if (-not $TargetItems) {
Write-Verbose "⚠️ No matching items found."
return
}
# If Force is enabled, no confirmation, just delete items
if ($Force) {
foreach ($Item in $TargetItems) {
try {
$QuickAccessFolder.Items() | Where-Object { $_.Path -eq $Item.Path } |
ForEach-Object { $_.InvokeVerb("unpinfromhome") }
Write-Verbose "✅ Removed: '$($Item.Name)'"
} catch {
Write-Error "❌ Failed: '$($Item.Name)' - $($_.Exception.Message)"
}
}
return $TargetItems # Return array of removed items
}
# show all TargetItems in console for deleting confirmation
$TargetItems | Format-Table -AutoSize | Out-String | Write-Host
# Non-Force mode: Ask for confirmation and process each item
if ($PSCmdlet.ShouldContinue("Do you really want to delete these Quick Accesses ?", "CONFIRMATION")) {
foreach ($Item in $TargetItems) {
try {
$QuickAccessFolder.Items() | Where-Object { $_.Path -eq $Item.Path } |
ForEach-Object { $_.InvokeVerb("unpinfromhome") }
Write-Host -ForegroundColor Green "✅ Removed: '$($Item.Name)'"
} catch {
Write-Error "❌ Failed: '$($Item.Name)' - $($_.Exception.Message)"
}
}
} else {
Write-Host -ForegroundColor Yellow "Action canceled by the user."
}
}
}
function Add-QuickAccessItem {
<#
.SYNOPSIS
Pins an item to Windows Explorer Quick Access.
.DESCRIPTION
This function pins the specified item (file or folder) to Windows Explorer's Quick Access,
and the function returns :
- Its Name (as displayed in Quick Access),
- Its Full Path,
- A flag `IsSystem` indicating if it's a system folder,
- A flag `IsBroken` indicating an invalid path.
If the item is already pinned to Quick Access, it skips the addition and returns the existing item.
.PARAMETER Path
The full path to the item you want to pin to Quick Access.
.EXAMPLE
Add-QuickAccessItem -Path "C:\MyFolder"
.AUTHOR
Jean-Philippe SOSSON
.VERSION
25.03.22.1837
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[string]$Path
)
Begin {
# Check if the path exists
if (-not (Test-Path -Path $Path)) {
Write-Error "❌ The path '$Path' does not exist."
return
}
}
Process {
try {
# Check if the item is already in Quick Access
$ExistingItem = Get-QuickAccessItem | Where-Object { $_.Path -eq $Path }
if ($ExistingItem) {
Write-Verbose "ℹ️ The item '$Path' is already pinned to Quick Access."
return $ExistingItem
}
# Create the Shell object
$Shell = New-Object -ComObject Shell.Application
# Get the folder or file object
$Item = Get-Item -Path $Path
# Get the namespace for the item
$Namespace = $Shell.Namespace($Item.Parent.FullName)
if (-not $Namespace) {
throw "Unable to access the parent folder of '$Path'."
}
# Get the item in the namespace
$ShellItem = $Namespace.ParseName($Item.Name)
if (-not $ShellItem) {
throw "Unable to access the item '$Path'."
}
# Pin the item to Quick Access
$ShellItem.InvokeVerb("pintohome")
Write-Verbose "✅ The item '$Path' has been successfully added to Quick Access."
# Return the Quick Access item details
return Get-QuickAccessItem | Where-Object { $_.Path -eq $Path }
}
catch {
Write-Error "❌ An error occurred while adding the item to Quick Access: $($_.Exception.Message)"
return
}
}
}