Skip to content

Instantly share code, notes, and snippets.

@meatcar
Last active July 19, 2023 06:53
Show Gist options
  • Save meatcar/98e9bd0f122dddac5221857bdc0a7592 to your computer and use it in GitHub Desktop.
Save meatcar/98e9bd0f122dddac5221857bdc0a7592 to your computer and use it in GitHub Desktop.
GPU System-Tray Toggler

GPU System-Tray Toggler

Using an eGPU requires occasional disabling and enabling of certain devices. This script adds an icon in your system tray to allow that to be done faster than opening Device Mangager.

Technically, you can use this to toggle any device shown in Device Manager, but I've found it most useful for GPUs so far.

Installation

  1. Download gpu-systray.ps1 and save it somewhere.
  2. Edit it and change/add/remove the graphics card names in the $devices variable right at the top.
  3. Right-click it and click "Run with Powershell"
  4. To have this start at boot, schedule a task as follows
    • Run with Highest Privileges
    • Triggered to run at logon of any user
    • Run `powershell.exe -File '<path-to/gpu-systray.ps1>'
[CmdletBinding()]
# Run Get-PnpDevice to find the full device names
$devices = @{
InternalGpu = "Intel(R) UHD Graphics 630"
DiscreteGpu = "NVIDIA GeForce GTX 1050 Ti with Max-Q Design"
ExternalGpu = "AMD Radeon RX 5700 XT"
}
# Relaunch as an elevated process:
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Start-Process powershell.exe "-File", ('"{0}"' -f $MyInvocation.MyCommand.Path) -Verb RunAs
exit
}
# Make PowerShell Disappear
$windowcode = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
$asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru
$null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)
function Get-Devices {
$names = $devices.GetEnumerator() | % { $_.Value }
Get-PnpDevice | Where-Object {
$_.friendlyName -in $names
}
}
function Get-Device {
param ( [string]$deviceName )
$input | Where-Object { $_.friendlyname -like $deviceName }
}
function Disable-Device {
param ( [string]$deviceName )
Write-Verbose "Disabling $deviceName"
Get-Devices | Get-Device $deviceName | Disable-PnpDevice -Confirm:$false
Start-Sleep -s 1
}
function Enable-Device {
param ( [string]$deviceName )
Write-Verbose "Enabling $deviceName"
Get-Devices | Get-Device $deviceName | Enable-PnpDevice -Confirm:$false
Start-Sleep -s 1
}
function Switch-Device {
param ( [string]$deviceName )
$status = (Get-Devices | Get-Device $deviceName).status
if ($status -like "error") {
# disabled
Enable-Device $deviceName
}
elseif ($status -like "ok") {
# enabled
Disable-Device $deviceName
}
else {
Write-Verbose "Not toggling, status: $status"
}
}
function Get-Device-Status {
param ( [string]$deviceName )
$allDevices = $input
if ($null -eq $allDevices) {
$allDevices = (Get-Devices)
}
$status = (($allDevices | Get-Device $deviceName).status)
return $status
}
[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('System.Drawing') | out-null
[System.Reflection.Assembly]::LoadWithPartialName('WindowsFormsIntegration') | out-null
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("C:\Windows\System32\resmon.exe")
# Create an application context for it to all run within.
# This helps with responsiveness, especially when clicking Exit.
$appContext = New-Object System.Windows.Forms.ApplicationContext
# ----------------------------------------------------
# Add the systray menu
# ----------------------------------------------------
$Main_Tool_Icon = New-Object System.Windows.Forms.NotifyIcon
$Main_Tool_Icon.Text = "GPU Systray"
$Main_Tool_Icon.Icon = $icon
$Main_Tool_Icon.Visible = $true
$gpu_Menus = $devices.GetEnumerator() | ForEach-Object {
$menu = New-Object System.Windows.Forms.MenuItem
$menu.Text = $_.Value
$menu.add_Click( { Switch-Device $this.Text })
return $menu
}
$Menu_Exit = New-Object System.Windows.Forms.MenuItem
$Menu_Exit.Text = "Exit"
$contextmenu = New-Object System.Windows.Forms.ContextMenu
$Main_Tool_Icon.ContextMenu = $contextmenu
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($gpu_Menus)
$Main_Tool_Icon.contextMenu.MenuItems.AddRange($Menu_Exit)
$Main_Tool_Icon.ContextMenu.Add_Popup( {
$devices = Get-Devices
foreach ($menu in $gpu_Menus) {
$menu.Checked = ($devices | Get-Device-Status $menu.Text) -like "ok"
}
})
# When Exit is clicked, close everything and kill the PowerShell process
$Menu_Exit.add_Click( {
$Main_Tool_Icon.Visible = $false
$Main_Tool_Icon.Dispose()
$appContext.ExitThread()
exit
})
# Force garbage collection just to start slightly lower RAM usage.
[System.GC]::Collect()
[void][System.Windows.Forms.Application]::Run($appContext)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment