A rewrite of a post I originally wrote back when the only way to do this was to compile a C++ .exe and host it on a file-sharing site. Times have changed — so has the method. (And if you're the kind of person who likes automating things on a computer just because you can, you might also like my Playwright TypeScript Automation Testing: E2E, API, AI, MCP course — automation is automation, whether it's a browser or a monitor.)
The Old Problem
Years ago I wanted a simple way to turn my laptop's screen off on demand — no such button exists on almost any laptop. Back then the fix was: write a tiny C++ program that calls SendMessage from User32.dll, compile it, and run the .exe. It worked, but it meant opening Visual Studio (or trusting a stranger's compiled binary from a file-sharing link) just to flip one bit.
Here's what that original C++ looked like:
#include <Windows.h>
int main(int argc, char* argv[]) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) 2);
// this turns the monitor off
return 0;
}
One line of real logic, wrapped in a whole compile-and-download workflow just to run it.
The 2026 Way: Just Use PowerShell
Every Windows machine already ships with PowerShell, and PowerShell can call the exact same Win32 API — SendMessage in user32.dll — directly, with no compiler and no .exe to distribute or trust. That's the real update to this post: the "download an executable" step is obsolete.
Quick one-liner, run from an elevated or regular PowerShell/Terminal prompt:
(Add-Type '[DllImport("user32.dll")]public static extern int SendMessage(int h,int m,int w,int l);' -Name Win32 -PassThru)::SendMessage(0xffff,0x0112,0xF170,2)
That single line compiles a throwaway C# stub in memory (via Add-Type) and immediately calls it. 0xffff is HWND_BROADCAST, 0x0112 is WM_SYSCOMMAND, 0xF170 is SC_MONITORPOWER, and 2 means "power off."
If you want something reusable — a function you can drop in your PowerShell profile, alias, or save as a .ps1 — here's the tidier version:
Add-Type -TypeDefinition '
using System;
using System.Runtime.InteropServices;
namespace Utilities {
public static class Display {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public static void PowerOff() {
SendMessage((IntPtr)0xffff, 0x0112, (IntPtr)0xF170, (IntPtr)2);
}
}
}'
[Utilities.Display]::PowerOff()
Save that as Turn-Off-Display.ps1, right-click → Run with PowerShell, or pin it (see below) — and your monitor goes dark, same as the old .exe, minus the download.
How does it come back on?
Same as before: Windows brings the display back automatically the moment you move the mouse or press a key — no code needed for that part. If you do want to trigger it programmatically (say, from a script that also wakes the screen), the correct wParam values for SC_MONITORPOWER are worth getting right, since the original post glossed over this:
2— power off1— low-power / standby state-1— fully on
So to force it back on rather than relying on mouse/keyboard input, call the same function with -1, not 1.
Why This Works (Still True in 2026)
The underlying Windows architecture hasn't changed: you never talk to an application directly. You send a message to the OS, and the OS routes it to whichever window (or, in this case, every top-level window, since we're broadcasting) is meant to handle it. Every Windows application runs a message loop that watches for these messages; if it doesn't recognize one, it just ignores it harmlessly.
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, 2) is really saying: "to whoever's listening — here's a system command related to monitor power." The only listener that cares is the same OS subsystem that already dims your display after your power-plan idle timeout. Nothing else on your system reacts to it, no ports are opened, and nothing is written to disk or the registry.
What has changed is that PowerShell's Add-Type cmdlet gives you a first-class, built-in way to reach that same Win32 API surface — you're no longer limited to C++ or hand-rolled C# with a separate build step. Add-Type compiles inline C# on the fly using .NET, so any P/Invoke call you could make from a compiled app, you can make from a script. It's this same "script something a compiled app used to do" instinct that pulled me toward test automation in the first place — if that clicks with you too, my Playwright course covers the same idea applied to browsers, APIs, and even AI/MCP-driven testing.
Since We're Here: What Else Can PowerShell Do to Your System?
Once you see that Add-Type unlocks any Win32 or WinRT API call from a script, "turn off the monitor" turns out to be one small example of a much bigger category. A few others worth knowing about in 2026:
Power and session control — sleep, hibernate, or lock the machine without touching the Start menu:
rundll32.exe powrprof.dll,SetSuspendState 0,1,0 # sleep
rundll32.exe user32.dll,LockWorkStation # lock the session
Screen brightness, via WMI on laptops with supported drivers:
(Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightnessMethods).WmiSetBrightness(1, 50)
Process and service management — inspect or kill anything running, or control Windows services:
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10
Stop-Process -Name "app" -Force
Get-Service | Where-Object Status -eq 'Running'
Finding and clearing duplicate files, by hashing rather than by filename:
Get-ChildItem -Recurse | Get-FileHash | Group-Object Hash | Where-Object Count -gt 1
Seeing (and removing) what Explorer hides, like the full startup program list or bundled Windows apps that don't have an uninstall button in Settings:
Get-CimInstance Win32_StartupCommand | Select-Object Name, Command, Location
Get-AppxPackage -AllUsers | Where-Object Name -like "*Bloatware*" | Remove-AppxPackage
Automating anything on a schedule, no separate Task Scheduler GUI needed:
Register-ScheduledTask -TaskName "NightlyBackup" -Trigger (New-ScheduledTaskTrigger -Daily -At 2am) -Action (New-ScheduledTaskAction -Execute "robocopy.exe" -Argument "C:\Data D:\Backup /MIR")
Managing other machines, not just the one in front of you — PowerShell remoting lets one script reach across a whole fleet:
Invoke-Command -ComputerName Server01,Server02 -ScriptBlock { Get-Service -Name spooler }
Talking to web APIs and cloud services directly, with JSON handled for you:
Invoke-RestMethod -Uri "https://api.example.com/status" -Method Get
And since PowerShell 7, it isn't even Windows-only anymore — pwsh runs the same language on macOS and Linux, so a chunk of this automation knowledge now travels with you across platforms, even though the Add-Type/user32.dll trick above is obviously Windows-specific. If scripting your OS into doing your bidding is fun for you, scripting a browser and an API into doing your bidding is the natural next step — that's most of what I teach in Playwright TypeScript Automation Testing: E2E, API, AI, MCP.
The Windows-Key Shortcut Trick Still Works Too
The old Windows 7 tip from the original post — pin something to the taskbar and launch it instantly with Win + [number], counting from the left — still applies today. Pin your Turn-Off-Display.ps1 shortcut (or a .lnk pointing at powershell.exe -File Turn-Off-Display.ps1) to the taskbar, note its position, and Win + 1 (or whichever slot it lands in) fires it instantly. No taskbar real estate wasted on a mystery .exe anymore — it's obviously "your" script if anyone ever looks at it.
Is This Actually Safe to Run?
Short answer: yes, but it's worth being precise about why, rather than just asserting it.
SC_MONITORPOWER isn't some obscure hack — it's the exact message Windows itself broadcasts every time your screen-saver or power-plan idle timer fires. Running it manually just triggers that same, already-trusted code path on demand. The call itself doesn't touch the network, doesn't read or write files, doesn't need admin rights, and doesn't persist anything — it's a single message asking the OS to change one power state, nothing more.
That said, "safe" isn't the same as "zero side effects in every situation." A few real edge cases worth knowing before you pin this to your taskbar:
- Fullscreen apps and games: because the message is broadcast to every top-level window (
HWND_BROADCAST), a handful of poorly-behaved fullscreen apps or older games have been known to minimize, stutter, or occasionally crash when they unexpectedly receive aWM_SYSCOMMAND. Well-written software just ignores it, same as it ignores the automatic idle-timeout version — but "well-written" isn't universal. - Multi-monitor setups: some GPU driver combinations don't reliably wake every display afterward — occasionally one monitor stays blank until you nudge the mouse harder, unplug/replug, or explicitly send the -1 ("fully on") message described above instead of waiting on Windows to do it.
- Remote Desktop / RDP sessions: this message targets a local display session, so running it inside an RDP window doesn't behave the same way (there's no local monitor for it to power down) and isn't a meaningful use case.
- Antivirus/EDR false positives: this is the one most worth flagging.
Add-Typecompiling C# on the fly is a completely legitimate .NET feature, but it's also a technique malware uses to slip code past static signature scanning. Strict corporate security tooling may quarantine the script or alert on it — not because it's doing anything harmful, but because the pattern looks the same either way from the outside. If you're on a managed corporate machine, expect friction here.
None of this means "don't run it" — it means: it's a narrow, well-documented, single-purpose API call, but it's still asking the whole desktop to listen for a moment, so test it outside of anything fullscreen first, and don't be alarmed if a locked-down work laptop's security software wants a second look at it.
A Couple More 2026 Caveats
- Execution policy: depending on your system's PowerShell execution policy, running a .ps1 may need
Set-ExecutionPolicy RemoteSigned(per user) or launching with-ExecutionPolicy Bypass. That's a deliberate safety rail, not a bug. - Just like the original post's promise about the .exe: this script opens no network ports, reads and writes nothing to disk beyond the script file itself, and touches nothing in the registry. It's one API call, nothing more.
Bottom Line
What used to require Visual Studio, a compiled .exe, and a file-hosting link now takes one line in a terminal window that's already installed on every Windows PC. The API call is the same one from 2010; the delivery mechanism is what finally caught up.
If you liked this kind of "make the computer do the boring thing" trick, that's basically the whole premise of test automation. My Udemy course walks through Playwright with TypeScript for end-to-end testing, API testing, AI-assisted testing, and MCP — from zero to a real automation suite.
Playwright TypeScript Automation Testing: E2E, API, AI, MCP →
Disclaimer: the code in this post is free, provided as-is, with no guarantees or warranties of any kind — express or implied — including any warranty of fitness for a particular purpose or of working identically across every Windows build, driver, or hardware configuration. You run it at your own risk; the author is not responsible for any damage, data loss, or other consequences arising from its use.




