A tiny, dependency-free Windows system-tray app that changes your primary monitor instantly with a global hotkey.
Switching the primary (main) display is what moves the taskbar, newly opened windows and the Alt-Tab switcher onto that screen. Windows has no built-in shortcut for it — Monitor Switcher adds one.
Built for the classic multi-monitor split: a desk monitor and a TV on the couch. Tap a hotkey to make the TV your main screen when you sit down, and tap another to hand it back to the desk when you get up.
- Global hotkeys —
Ctrl+Alt+1…Ctrl+Alt+4make that monitor primary from anywhere. - Tray menu — right-click to pick a display, identify monitors, toggle start-with-Windows, or exit.
- Identify monitors — flashes a big number on each screen so you know which index maps to which physical display (double-click the tray icon, too).
- No third-party dependencies — pure Win32 APIs. No AutoHotkey, no drivers, no downloads.
- Reliable on Windows 10/11 — uses the modern CCD display API
(
SetDisplayConfig), which actually commits the change where the legacyChangeDisplaySettingsExpath silently fails on modern Windows. - Lightweight — single instance, ~150 KB exe, sits quietly in the tray.
- Windows 10 or 11.
- .NET SDK 9 to build the project, or just
the in-box .NET Framework (the
build.ps1fallback needs nothing extra).
Either build the .NET project:
dotnet build MonitorSwitcher.sln -c Release
# output: bin\Release\net9.0-windows\MonitorSwitcher.exe…or, if you don't have the SDK, use the zero-dependency script that compiles with the C# compiler already shipped in Windows:
powershell -ExecutionPolicy Bypass -File .\build.ps1
# output: .\MonitorSwitcher.exeRun MonitorSwitcher.exe. A small monitor icon appears in the system tray.
| Action | What it does |
|---|---|
Ctrl+Alt+1 … Ctrl+Alt+4 |
Make monitor #1–#4 the primary display |
| Right-click tray icon | Open the menu (pick a monitor, identify, startup, exit) |
| Double-click tray icon | Identify monitors (flash each screen's number) |
Not sure which number is which screen? Use Identify monitors — the index it
shows is the same number you press with Ctrl+Alt.
Right-click the tray icon → Start with Windows. This adds a per-user entry
under HKCU\…\Run (no admin required); toggle it off to remove it.
| Concern | Approach |
|---|---|
| Enumerate displays | EnumDisplayDevices + EnumDisplaySettings |
| Set the primary monitor | CCD API: QueryDisplayConfig → shift every source so the target lands at (0,0) → SetDisplayConfig |
| Global hotkeys | RegisterHotKey on a message-only window |
| Identify overlay | Borderless top-most WinForms windows, auto-closed by a timer |
| Run at login | HKCU\Software\Microsoft\Windows\CurrentVersion\Run |
On Windows 10/11 the legacy ChangeDisplaySettingsEx call validates a mode but
refuses to commit a primary/topology change (returns DISP_CHANGE_FAILED),
so this app uses the newer Connecting and Configuring Displays (CCD) API
instead. The primary display is, by definition, the source at coordinate
(0,0), so making a monitor primary means translating every display so the
chosen one sits at the origin.
The hotkeys and how many of them are registered live in
TrayContext.cs:
MaxHotkeys— how manyCtrl+Alt+<n>hotkeys to register.- The
keysarray (Keys.D1 … Keys.D4) and themods(MOD_CONTROL | MOD_ALT) — change these to use different keys or modifiers.
Rebuild after editing.
| File | Responsibility |
|---|---|
Program.cs |
Entry point, single-instance guard, DPI awareness |
TrayContext.cs |
Tray icon, menu, hotkey wiring |
MonitorService.cs |
Enumerate displays and set the primary |
CcdNative.cs |
Modern CCD (SetDisplayConfig) implementation |
HotKeyManager.cs |
Global hotkeys via a message-only window |
NativeMethods.cs |
Win32 P/Invoke signatures and structs |
IdentifyOverlay.cs |
The "identify monitors" overlay |
StartupManager.cs |
Run-at-login toggle |
build.ps1 |
SDK-free build using the in-box C# compiler |