Turn off Win10 display(s) via OpenSSH

1

1

There are a lot of solutions to turn off display locally on a Windows10 machine. They all are some form of the following SendMessage line. There's also little executables like nircmd that do this. However there seems to be some limitation when running any of those via OpenSSH. Since my OpenSSH is configured to use the exact same credentials as my local user i can't really figure out why nothing happens. The ps script versions return a simple 1, and solutions like nircmd just return nothing at all. Not even an error. Any insight what might happen here and how to make it work?

Sample script:

powershell (Add-Type '[DllImport(\"user32.dll\")]^public static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);' -Name a -Pas)::SendMessage(-1,0x0112,0xF170,2)

Source

edit: maybe i should add that other nircmd options like mute audio work without any problems.

narfelchen

Posted 2018-12-10T13:10:40.427

Reputation: 23

Some more utilities to try: Turn Off Monitor or Display Power Off Utility or ScreenOff.

– harrymc – 2018-12-10T14:03:41.950

Thx, but those all have the same effect. They work locally on the machine, but not executed via ssh. That leads me to believe that somehow the ssh can't see/enumerate/detect displays to shut off. But without any log or error message i'm out of ideas. – narfelchen – 2018-12-10T17:33:32.110

Are you doing Windows-to-Windows? – harrymc – 2018-12-10T19:36:08.967

Since you are using OpenSSH, this means you are going form Linux to Windows, as it really would not be a go to for Windows to Windows, just use normal Windows PSRemoting. Now, all that being said, why are you trying to turn off a Windows display from a Linux box remotely. Just curious, or are you on a local Windows using an SSH session, again, not sure why you'd want to do that. – postanote – 2018-12-10T20:30:49.563

It's a simple convenience thing. I want to be able to switch the displays on my desktop on and off from a home automation system running on a raspberry pi. Of course there are solutions like automatic switch off in idle time for the displays but that's not on demand and i just like to switch between them at my convenience. Not the end of the world, just became a curiosity thing since everything else works (e.g. switching audio devices). – narfelchen – 2018-12-11T06:02:12.570

A session can contain multiple Window stations and a Window station can contain multiple Desktops. I assume that your SSH session creates a new session but does not create a Desktop thus there is no receiver to pick up your message. I can't find a definitive answer to this so this might well be wrong but it might give you something to look at. – Lieven Keersmaekers – 2018-12-11T06:20:54.837

If it wasn't for all the audio stuff working i would think that's it. But somehow the audio system appears to be abstracted to the ssh session. Also i was under the impression that the session was basically the same thing because its running in the local user context that is logged in. But then again, i can't find any info where to look what the ssh user can and cannot do. Every search returns me to people looking for answers on linux. Which makes of course makes sense, but still. Every document on the web seems to explain installation and then stop. – narfelchen – 2018-12-11T16:53:05.373

Answers

1

This is because the interactive Windows session is not the current session, so it's not running in a context where the physical display is actively connected. Sound and other features are, but the display is tied to a session. You can either login interactively, or use something like psexec to execute the powershell process in the context of the user. If you're okay with using psexec then you can use this:

 FOR /F "usebackq tokens=4" %s IN (`tasklist /nh /fo table /fi "imagename eq explorer.exe"`) DO psexec -accepteula -nobanner -d -i %s -w "%windir%" powershell (Add-Type '[DllImport(\"user32.dll\")]^public static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);' -Name a -Pas)::SendMessage(-1,0x0112,0xF170,2)

You will need psexec for this, though.

If there are more than one interactive sessions, it will be executed more than once.

If running from batch you'll need to replace both %s with %%s.

This is the part that collects the interactive session:

FOR /F "usebackq tokens=4" %s IN (`tasklist /nh /fo table /fi "imagename eq explorer.exe"`) DO echo %s

The following can be copied into a batch file and run from a terminal emulator.

FOR /F "usebackq tokens=4" %%s IN (`tasklist /nh /fo table /fi "imagename eq explorer.exe"`) DO (
    psexec -accepteula -nobanner -d -i %%s -w "%windir%" powershell -NoProfile -NoLogo -Command "(Add-Type '[DllImport(\"user32.dll\")]public static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);' -Name a -Pas)::SendMessage(-1,0x0112,0xF170,2)"
)

The only difference is that each %s was changes to %%s.

Be aware that running it interactively on the computer that you're trying to disable the displays on WILL LIKELY FAIL. This is because your interactive session is still active.

To wake your screens you can use the following (tested and works on several of my devices):

:: gather session handle
FOR /F "usebackq tokens=4" %%s IN (`tasklist /nh /fo table /fi "imagename eq explorer.exe"`) DO SET hsession=%%s

:: wake display
psexec -accepteula -nobanner -d -i %hsession% -w "%windir%" powershell -NoProfile -NoLogo -Command "(Add-Type '[DllImport(\"user32.dll\")]public static extern int SendMessage(int hWnd, int hMsg, int wParam, int lParam);' -Name a -Pas)::SendMessage(-1,0x0112,0xF170,-1)"
CALL :wait 2

:: reactivate session
psexec -accepteula -nobanner -d -i %hsession% -w "%windir%" powershell -NoProfile -NoLogo -Command "$x=Add-Type '[DllImport(\"kernel32.dll\")]public static extern void SetThreadExecutionState(uint esFlags);' -name System -namespace Win32 -passThru;$x::SetThreadExecutionState([uint32]\"0x03\");Sleep 5;$x::SetThreadExecutionState([uint32]\"0x40\");"
CALL :wait 2
GOTO:EOF

:wait
SET dowait=%~1
IF "%dowait%"=="" SET dowait=10
ping -n %dowait% 127.0.0.1 >NUL
GOTO:EOF

shawn

Posted 2018-12-10T13:10:40.427

Reputation: 612

Thank you, it took me a while to understand this, but it works flawlessly. If I understand correctly the whole line roughly translates to "check a list of running processes for the pid of explorer running on the desktop, use this context to remotely execute the powershell command". – narfelchen – 2019-01-04T11:00:53.903

Close, the PID is the process ID, but this is collecting the session ID. The session is the interactive environment that hosts the GUI for a single user, not just a specific application. Filtering using explorer.exe is the easiest way to obtain all the interactive sessions on a device. – shawn – 2019-01-05T20:57:43.527

I tried to do the inverse (monitors on) by changing the last parameter of SendMessage. That didnt work so i went with "move the mouse" which works, but is not in the user context i thought i would get with the above. Any idea what happens there? FOR /F "usebackq tokens=4" %%s IN (tasklist /nh /fo table /fi "imagename eq explorer.exe") DO psexec -accepteula -nobanner -d -i %%s -w "%windir%" powershell (Add-Type '[DllImport(\"user32.dll\")]^public static extern void mouse_event(uint dwFlags, int dx, int dy, uint dwData, int dwExtraInfo);' -Name user32 -PassThru)::mouse_event(1,40,0,0,0) – narfelchen – 2019-03-26T21:45:54.687

I need this too but I cant get your lines to work. Camn you please add a working .bat file that can be executed over ssh? thanks I also get this when I try to execute cmd /k screenoff2.bat
s was unexpected at this time.
– yarun can – 2019-11-02T18:44:50.787

@yaruncan I added batch files. The error you're seeing is due to how batch treats %. In batch they have to be doubled in almost all contexts. – shawn – 2019-11-10T02:27:05.670