Making a single monitor go to sleep in a Windows multi-monitor setup
I have always had two or three monitors on my desktop, all connected to my main computer. In addition, I have a large wall-mounted LCD TV, also connected to the main computer, on which I display presentations and use Windows Media Center (or occasionally an HTML5/Flash/Silverlight player, if I have no other option) to view films and other video.
When I watch films at night, I turn off the desktop monitors, because I want it to be dark in the room. This has always worked well, until recently when I upgraded the main desktop monitor. Now there is a problem: when I turn on the monitor again, all windows on that desktop are moved to a different monitor (even the minimized ones, which are the most numerous). Hence, I have to move manually all these windows back to the main monitor each time I turn it on again.
Naturally, this is highly annoying, and, unfortunately, there seems to be no good solution or workaround offered by Windows. For instance, although it is easy programmatically to make all monitors go to sleep (by sending a WM_SYSCOMMAND
message with a wParam
value of SC_MONITORPOWER
and a lParam
value of 2
), this has the unfortunate effect of also turning off the wall-mounted LCD TV, which has a clearly detrimental effect on the film-watching experience.
Fortunately, two facts saved the day:
- The new monitor happens to come with a very easy-to-use SDK (basically a DLL and some documentation).
- I happen to be a Win32 programmer.
Hence, I wrote a tiny Windows CLI utility to put the main monitor to sleep individually:
program monman;
{$APPTYPE CONSOLE}
const
DellMon = 'DellMonitorSdkDll.dll';
const
MONITOR_ERR_TIMEOUT = 1;
MONITOR_ERR_PARAMS = 2;
MONITOR_ERR_CONNECT = 3;
MONITOR_ERR_COMMS = 4;
MONITOR_FAILURE = -1;
MONITOR_SUCCESS = 0;
POWER_STATE_OFF = 0;
POWER_STATE_ON = 1;
POWER_STATE_STANDBY = 2;
type
TMonitorCode = integer;
function GetAvailableMonitors(var Count: byte): TMonitorCode; cdecl; external DellMon;
function ConnectMonitor(Monitor: byte): TMonitorCode; cdecl; external DellMon;
function DisconnectMonitor: TMonitorCode; cdecl; external DellMon;
function SetPowerState(PowerState: byte): TMonitorCode; cdecl; external DellMon;
function MonCheck(const FuncName: string; Code: TMonitorCode): boolean;
begin
result := Code = MONITOR_SUCCESS;
if not result then
writeln('Error: ', FuncName, ' failed with code ', Code, '.');
end;
procedure SetDellMonitorPowerState(PowerState: byte);
var
cmon: byte;
begin
if not MonCheck('GetAvailableMonitors', GetAvailableMonitors(cmon)) then Exit;
if cmon = 0 then
begin
writeln('Error: no compatible monitor found.');
Exit;
end;
if not MonCheck('ConnectMonitor', ConnectMonitor(0)) then Exit;
try
MonCheck('SetPowerState', SetPowerState(PowerState));
finally
MonCheck('DisconnectMonitor', DisconnectMonitor);
end;
end;
begin
if ParamCount = 1 then
if ParamStr(1) = 'on' then
SetDellMonitorPowerState(POWER_STATE_ON)
else if ParamStr(1) = 'off' then
SetDellMonitorPowerState(POWER_STATE_STANDBY)
else if ParamStr(1) = '/?' then
begin
writeln('monman: controls Dell UP2716D and UP2516D monitors.');
writeln('Usage:');
writeln(' monman on');
writeln(' Turns on the monitor.');
writeln(' monman off');
writeln(' Turns off the monitor (standby mode).');
writeln(' monman /?');
writeln(' Displays this help message.');
end
else
writeln('Error: Invalid parameter. Type monman /? for help.');
end.
Putting the executable on the PATH, this works like a charm: