This guide adds Steam game shortcuts to Windows. This lets you quickly & conveniently jump into games.
Disclaimer of Liability USE AT YOUR OWN RISK.
By running this script, you acknowledge and agree that you are doing so entirely at your own discretion and assume all associated risks. Always review and understand any script before executing it on your system.
Use of this script provides no guarantee about the safety or integrity of your Steam Cloud synced game save files.
Generated with Gemini 2.5 Flash
Save the following code as steam-shortcuts.ps1:
<-- Powershell script
<#
.SYNOPSIS
Finds installed Steam games, extracts names, and creates Start Menu shortcuts with game icons.
.DESCRIPTION
This script accurately parses Steam library files. It features an enhanced icon
detection routine that prioritizes finding a game-specific .ico file or the main
game executable (e.g., Hades2.exe) before falling back to less specific executables
or the generic steam.exe icon. It cleans up old shortcuts on each run.
.NOTES
- Run this script under the context of the user whose Start Menu you want to modify.
- To see detailed progress and troubleshooting information, run with the -Debug flag:
.\steamshortcuts.ps1 -Debug
#>
[CmdletBinding()]
param()
# --- 1. Define Target Shortcut Folder ---
# Target folder: C:\Users\<CurrentUser>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Steam
$StartMenuSteamFolder = Join-Path (
[System.Environment]::GetFolderPath('Programs')
) "Steam"
Write-Debug "Target Start Menu Folder: $StartMenuSteamFolder"
# --- 1.1. Create Target Folder ---
if (-not (Test-Path -Path $StartMenuSteamFolder -PathType Container)) {
Write-Host "Creating Start Menu folder: $StartMenuSteamFolder"
New-Item -Path $StartMenuSteamFolder -ItemType Directory | Out-Null
} else {
Write-Debug "Start Menu Folder already exists."
}
# --- 1.5. Clean Up Existing Shortcuts ---
Write-Host "Cleaning up old game shortcuts in '$StartMenuSteamFolder'..."
# Define the link to keep (Steam itself)
$SteamLinkToKeep = "Steam.lnk"
Get-ChildItem -Path $StartMenuSteamFolder -Filter "*.lnk" -ErrorAction SilentlyContinue |
Where-Object { $_.Name -ne $SteamLinkToKeep } |
Remove-Item -Force -ErrorAction SilentlyContinue | Out-Null
Write-Host "Cleanup complete. Old game links removed."
# --- End of Cleanup Logic ---
# --- 2. Find Steam Installation Path ---
Write-Host "Searching for Steam installation path..."
$SteamInstallPath = $null
$RegistryPaths = @(
'HKLM:\Software\Valve\Steam',
'HKLM:\Software\Wow6432Node\Valve\Steam'
)
foreach ($RegPath in $RegistryPaths) {
if (Test-Path $RegPath) {
$SteamInstallPath = (Get-ItemProperty -Path $RegPath -ErrorAction SilentlyContinue).InstallPath
if ($SteamInstallPath -and (Test-Path $SteamInstallPath)) {
Write-Host "Found Steam Install Path: $SteamInstallPath"
break
}
}
}
if (-not $SteamInstallPath) {
Write-Error "Steam installation path not found in registry."
exit 1
}
# --- 3. Get all Steam Library Folders ---
$Libraries = New-Object System.Collections.Generic.List[string]
# 3.1. Add the default library path
$DefaultLibraryPath = Join-Path $SteamInstallPath "steamapps"
if (Test-Path $DefaultLibraryPath) {
$Libraries.Add($DefaultLibraryPath) | Out-Null
Write-Debug "Added default library path: $DefaultLibraryPath"
}
# 3.2. Check 'libraryfolders.vdf' for additional libraries
$LibraryFoldersVdf = Join-Path $SteamInstallPath "steamapps\libraryfolders.vdf"
if (Test-Path $LibraryFoldersVdf) {
Write-Debug "Checking for additional libraries in $LibraryFoldersVdf"
# Read content as a single raw string
$VdfContent = Get-Content -Path $LibraryFoldersVdf -Raw
# Use regex to find all library paths (look for "1", "2", etc., keys)
$LibraryPaths = $VdfContent | Select-String -Pattern '"\d+"\s+"(?<library>[^"]+)"' -AllMatches |
ForEach-Object { $_.Matches.Groups['library'].Value }
foreach ($Path in $LibraryPaths) {
$NormalizedPath = $Path.Replace('\\', '\')
$AppManifestPath = Join-Path $NormalizedPath "steamapps"
if (Test-Path -Path $AppManifestPath -PathType Container) {
$Libraries.Add($AppManifestPath) | Out-Null
Write-Debug "Added external library path: $AppManifestPath"
} else {
Write-Debug "Skipped external library path (folder not found): $AppManifestPath"
}
}
}
$UniqueLibraries = $Libraries | Select-Object -Unique
Write-Host "Found $($UniqueLibraries.Count) Steam library location(s)."
# --- 4. Find Installed Games and Create Shortcuts (Enhanced Icon Detection) ---
Write-Host "Searching for installed games and creating shortcuts..."
$GamesFound = 0
$WshShell = New-Object -ComObject WScript.Shell
foreach ($LibraryPath in $UniqueLibraries) {
Write-Host "Processing library: $LibraryPath"
# Get all game manifest files (*.acf)
$ManifestFiles = Get-ChildItem -Path $LibraryPath -Filter "appmanifest_*.acf" -ErrorAction SilentlyContinue
foreach ($Manifest in $ManifestFiles) {
$AppID = ($Manifest.BaseName -split '_')[1]
Write-Debug "Found manifest for AppID: $AppID ($($Manifest.Name))"
# Read manifest content as a single raw string for multi-line regex matching
$ManifestContent = Get-Content -Path $Manifest.FullName -Raw
# Use robust regex patterns for VDF on raw content
$NamePattern = '"name"\s+"(?<name>[^"]+)"'
$DirPattern = '"installdir"\s+"(?<installdir>[^"]+)"'
# Safely extract Game Name and Install Directory
$GameName = $ManifestContent | Select-String -Pattern $NamePattern -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Groups | Where-Object { $_.Name -eq 'name' } | Select-Object -ExpandProperty Value -First 1
$InstallDir = $ManifestContent | Select-String -Pattern $DirPattern -AllMatches | Select-Object -ExpandProperty Matches | Select-Object -ExpandProperty Groups | Where-Object { $_.Name -eq 'installdir' } | Select-Object -ExpandProperty Value -First 1
if (-not $GameName) {
Write-Debug " Game Name not found in manifest. Skipping AppID $AppID."
continue
}
if (-not $InstallDir) {
Write-Debug " Install Dir not found in manifest. Skipping AppID $AppID."
continue
}
# Remove illegal characters for filenames
$GameNameClean = $GameName -replace '[<>:"/\\|?*]'
Write-Debug " Game Name: '$GameName' | Clean Name: '$GameNameClean' | Install Dir: '$InstallDir'"
# --- ENHANCED ICON DETECTION LOGIC ---
$GameIconPath = $null
$IconSource = "steam.exe" # Default fallback
# 4.1. Construct the game's actual root install directory path
$GameInstallPath = Join-Path $LibraryPath "common\$InstallDir"
Write-Debug " Game Install Path: $GameInstallPath"
# Define the executable/icon search paths (relative to $GameInstallPath)
$SearchPaths = @(
"", # Root folder (e.g., common\GameName\)
"Ship", # Common for Supergiant Games (e.g., Hades 2)
"Binaries\Win64", # Common Unreal/Unity
"Bin",
"Game"
)
# Define the priority of file types/names to look for
# Use $InstallDir (e.g., "Hades II") to find relevant files
$TargetFiles = @(
# Priority 1: Game-specific ICO
"$InstallDir.ico",
"game.ico",
"Game.ico",
# Priority 2: Game-specific EXE
"$InstallDir.exe",
"$InstallDir.ToLower().exe",
"$InstallDir.Replace(' ', '').exe", # e.g., HadesII.exe
# Priority 3: Fallback (any EXE)
"*.exe"
)
# 4.2. Iterate through search paths and target files
foreach ($Path in $SearchPaths) {
$CurrentSearchDir = Join-Path $GameInstallPath $Path
if (-not (Test-Path $CurrentSearchDir)) {
Write-Debug " Skipping missing search path: $Path"
continue
}
foreach ($TargetFile in $TargetFiles) {
# Special handling for *.exe to list all executables, otherwise search for specific file
if ($TargetFile -eq "*.exe") {
$Files = Get-ChildItem -Path $CurrentSearchDir -Filter "*.exe" -Recurse -Depth 0 -ErrorAction SilentlyContinue |
# Exclude common non-game executables (crash handlers, uninstallers)
Where-Object { $_.Name -notmatch "crashpad|uninstall" } |
Select-Object -First 1
} else {
$Files = Get-ChildItem -Path $CurrentSearchDir -Filter $TargetFile -ErrorAction SilentlyContinue | Select-Object -First 1
}
if ($Files) {
$GameIconPath = $Files.FullName
# FIX: Safely construct $IconSource for logging when $Path is empty (root directory)
if ([string]::IsNullOrWhiteSpace($Path)) {
$IconSource = $Files.Name
} else {
$IconSource = Join-Path $Path $Files.Name
}
Write-Debug " Icon found: $IconSource (Source Priority: $($TargetFile.Split('.')[1] -replace '(?i)ico|exe',''))"
break # Found the best match, stop searching targets
}
}
if ($GameIconPath) {
break # Found the best match, stop searching paths
}
}
# 4.4. Final Fallback: If nothing specific is found, use the generic steam.exe
if (-not $GameIconPath) {
$GameIconPath = $SteamInstallPath + "\steam.exe"
$IconSource = "steam.exe (Fallback)"
Write-Debug " Icon not found. Falling back to: $IconSource"
}
# --- Shortcut Creation ---
$ShortcutName = "$GameNameClean.lnk"
$ShortcutFile = Join-Path $StartMenuSteamFolder $ShortcutName
Write-Debug " Target Shortcut File: $ShortcutFile"
# Create/Update the shortcut
$Shortcut = $WshShell.CreateShortcut($ShortcutFile)
$Shortcut.TargetPath = $SteamInstallPath + "\steam.exe" # Target steam.exe
$Shortcut.Arguments = "-applaunch $AppID" # Pass AppID to launch the game
$Shortcut.WorkingDirectory = $SteamInstallPath
$Shortcut.IconLocation = $GameIconPath # Set icon path to game exe or ico file
# Save the shortcut file
$Shortcut.Save()
# Final existence check
if (Test-Path $ShortcutFile) {
Write-Host " ✅ Created shortcut for '$GameName' ($AppID). Icon from: $IconSource" -ForegroundColor Green
$GamesFound++
} else {
Write-Host " ❌ FAILED to create shortcut for '$GameName' ($AppID) at $ShortcutFile" -ForegroundColor Red
}
}
}
# --- 5. Ensure Steam.lnk exists or is created ---
Write-Host "Ensuring primary 'Steam.lnk' shortcut exists..."
$SteamShortcutFile = Join-Path $StartMenuSteamFolder $SteamLinkToKeep
if (-not (Test-Path $SteamShortcutFile)) {
Write-Host "Creating primary 'Steam.lnk' link..."
$Shortcut = $WshShell.CreateShortcut($SteamShortcutFile)
$Shortcut.TargetPath = $SteamInstallPath + "\steam.exe"
$Shortcut.Arguments = ""
$Shortcut.WorkingDirectory = $SteamInstallPath
$Shortcut.IconLocation = $SteamInstallPath + "\steam.exe"
$Shortcut.Save()
} else {
Write-Host "Primary 'Steam.lnk' already present."
}
Write-Host "---"
Write-Host "Script complete. Created $GamesFound new game shortcut(s) and ensured 'Steam.lnk' exists in '$StartMenuSteamFolder'"
# Clean up the COM object
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($WshShell) | Out-Null
Remove-Variable WshShellOpen a PowerShell window, type .\steam-shortcuts.ps1, and enter the command.
Navigate to %APPDATA%\Microsoft\Windows\Start Menu\Programs\Steam.
You will see shortcuts for all your installed Steam games.