Putting a PC to sleep from a batch

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Putting a PC to sleep from a batch

#16 Post by penpen » 10 Apr 2016 16:42

sambul35 wrote:- at a certain time its run from Task Scheduler to stop a given Service and switch Windows Power Plan to Balanced thus allowing OS to put PC to Sleep. This is current mode, where the batch is factually used to put the PC to sleep;
- at manual or automatic PC wakeup at any time, the same batch is run again by Task Scheduler to start the same Service and respectively switch Power Plan to High Performance.
If you are using a batch to put the pc to sleep,
then there should be no need to call this batch on wakeup:
It is still running.

Extending the above "Suspend.bat" (just sketched):

Code: Select all

:: before putting the pc to sleep
:: for example: switch Windows Power Plan to "Balanced"

:: put the pc to sleep
echo(Put the pc to sleep at "%time%".
@PowerShell -Command "Add-Type -Assembly System.Windows.Forms; if (-NOT [System.Windows.Forms.Application]::SetSuspendState([System.Windows.Forms.PowerState]::Suspend, $true, $false)) { write-host "Couldn`'t set suspend state: Denied.`r`nSee: https://msdn.microsoft.com/en-us/library/system.windows.forms.application.setsuspendstate`(v=vs.110`).aspx"; };"

:: after pc wakeup
echo(The pc woke up at "%time%".
pause
:: for example: switch Windows Power Plan to "High Performance"


penpen

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Putting a PC to sleep from a batch

#17 Post by sambul35 » 10 Apr 2016 17:40

This is interesting concept, I didn't know about. Apparently, the batch can auto continue after wakeup where it stopped at going to sleep. Will definitely check it out. I wonder, if its possible to use this technique in other situations, without immediately putting PC to sleep. I.e., can the batch do something and then keep running inactive until Windows puts the PC to sleep, and then continue code execution upon PC wakeup?

I wanted to give Windows a chance to put PC to sleep, as despite a certain service must be stopped at a given time, other activities on that PC may continue. That's why I used the above batch with Task Scheduler. The problem is, when the PC wakes up, the stopped service doesn't start automatically. Task Scheduler doesn't have built-in means to start a service, so it must be done by something like a batch. Of course it can be a small separate batch, but I was wondering if a more elegant simple solution exists allowing to use the same batch to stop and start a service over sleep-wakeup cycle. :wink:

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Putting a PC to sleep from a batch

#18 Post by penpen » 10 Apr 2016 18:53

sambul35 wrote:This is interesting concept, I didn't know about. Apparently, the batch can auto continue after wakeup where it stopped at going to sleep.
It is the default behaviour of a batch file:

Code: Select all

:: sample batch file
command1
command2
command3
...
The command line interpreter (CLI) processes batch files one command after the other.
The CLI only proceeed with the next command, after it has finished processing the actual one.

So if command2 should set the pc to sleep, and the CLI processes it, then the result is,
that the pc is sleeping, and the CLI's next command is command3.
But while sleeping the CLI naturally is not able to proceed.
After waking the pc up, the batch file has no choice:
The CLI proceeds with the next command (command3).


sambul35 wrote:can the batch do something and then keep running inactive until Windows puts the PC to sleep, and then continue code execution upon PC wakeup?
I'm not sure how you mean that.
If the pc is set to sleep from outside the batch file, then you have write a liitle (c++/c#/...) application to wait until it catches this special event (<-- not as easy as it sounds).
But this batch file cannot do other tasks while waiting:
You may use the "start" command to create child processes, but depending on their task you may have to synchronise them on the event "set pc to sleep" (or better: "on wakeup" - which is nearly the same from any applications viewpoint).


sambul35 wrote:I wanted to give Windows a chance to put PC to sleep, as despite a certain service must be stopped at a given time, other activities on that PC may continue. That's why I used the above batch with Task Scheduler. The problem is, when the PC wakes up, the stopped service doesn't start automatically. Task Scheduler doesn't have built-in means to start a service, so it must be done by something like a batch. Of course it can be a small separate batch, but I was wondering if a more elegant simple solution exists allowing to use the same batch to stop and start a service over sleep-wakeup cycle. :wink:
I think i have sketched this task with the extended "Suspend.bat" above, or i may misunderstand you.

In another sketch (I'm on my mobile phone, and here it is very late now, so actually i'm a bit lazy - gn8 until tomorrow :) ):

Code: Select all

:: replace this label comment with the batch code to try to stop certain service
:: replace this label comment with the batch code with the error handling, if the service cannot be stopped (maybe "exit /B 1")
:: replace this label comment with the batch code to put PC to sleep (the CLI only reaches this part if the certain service stopped)
:: replace this label comment with the batch code to restart the certain service (again) (the CLI reaches this part only after pc is woken up)
The first (and second? i'm unsure) label comment is replaced with your linked batch (i think).
The third label comment is replaced with the original "Suspend.bat" content.
The fourth line should be similar to your linked batch, but should switch the Windows Power Plan to High Performance.


penpen

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Putting a PC to sleep from a batch

#19 Post by sambul35 » 10 Apr 2016 19:52

The tricky part with your last approach is to figure out what service stop would signify no activity on the PC thus allowing to put it to sleep from the batch. It can't be the same service the batch is designed to stop, because as mentioned above, even if that service is stopped, other PC activities (like typing a document etc.) may continue. :?

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Putting a PC to sleep from a batch

#20 Post by penpen » 11 Apr 2016 06:00

Maybe i misunderstood what you've written (in the past or now).
I thought you only needed to sucessfully execute your above batch (switching to Balanced/... mode) before put the pc to sleep, and restore the setting after wakeup.

I didn't expect, that you didn't have an imagination on what calling "no activity (on the PC)".
There is no common definition of that term, and therefore no service could detect if it is save (for you) to put the pc to sleep for a simple reason:
Windows (and me) cannot know what you might call "no activity on the PC".
Windows could detect if/what processes/tasks are running, but it cannot recognize the semantic of any process/task (especially if it is a user dependent semantic).

Windows has a build in support for applications to prevent the system going to sleep at all:
SetThreadExecutionState
If you then try to put the pc to sleep using the "Suspend.bat" nothing happens.

You could check for user inputs (mouse, keyboard, ... see above) and define that having no such input for 5 minutes means "no activity on PC", and the call Suspend.bat.
But that might be wrog in your case if you (for example) are watching a movie on your pc.

So you have to create a list (software or circumstances like network activity/user input/whatever you like) on your own,
and then check if any of these programs is running (maybe using tasklist) / if the circumstances are met and then call "Suspend.bat".


penpen

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Putting a PC to sleep from a batch

#21 Post by sambul35 » 11 Apr 2016 07:43

Thanks for the detail analysis of options available. It appears to be more complex undertaking than originally expected. :D Windows Power Management code definitely has a view on what constitutes "no activity" on the PC thus allowing to put it to sleep.

One option is to turn the PC to sleep from the batch. If an activity like watching a movie would prevent it, then the attempt must be repeated after a certain wait time, and will only be possible if the PC is still awake. This also means that after awakening the batch will continue attempts to put the PC to sleep. :mrgreen:

So I do have imagination, but prefer the simplest solutions possible, though they may not be the exhaustive ones. In this case I'd probably go with time driven approach, and see if its sufficient to address the issue.

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Putting a PC to sleep from a batch

#22 Post by penpen » 11 Apr 2016 19:13

Your last post confuses me.

sambul35 wrote:Windows Power Management code definitely has a view on what constitutes "no activity" on the PC thus allowing to put it to sleep.
No.
Windows just checks the "user input" and the system idle timer may expire somewhen if no input is detected:
You could costumize this timer duration under the Power Plan settings.

sambul35 wrote:Thanks for the detail analysis of options available.
This is the most confusing part.
My last post doesn't list options available, it should be read as (i misused a quote block for that):
The term "no activity" is not defined, so i asked you to define "no activity" by yourself.
(+ explaination why "no activity" cannot be defined in common)
(+ some partial solution how to test your definition parts, once you have defined it).


Sidenote (because i'm unsure):
If your definition of "no activity" is "no activity := no user input", and you don't need to set anything before going to sleep or restore anything after "woken", then i would recommend you just to edit the value of the system idle timer; Win 8.1. sample image:
http://www.apricorn.com/media/faq_images/windows_setting_1.PNG
In that case you just don't need any batch.


I expected that you to do something like this (kind of Roadmap of what i once thought you wanted to program - i hope i have not misinterpreted your above posts on this):
1) Create a definition similar to the following:
Sample Definition: ("no activity"; for hypothetical me "no activity" means)
"no activity" := (
"Software XYZ is not running" AND
"no user input" AND
"no network activity on port 123"
)

Write a function that checks these parts:
- "Software XYZ is not running" with "tasklist.exe"
- "no user input" my above c# application
- "no network activity on port 123" (actually not implemented at all, because i don't know if you would ever need it)

Then you should encapsulate it in the batch "function" ":noActivity" which sets the errorlevel to 1 if "no activity" is found, else 0.

2) Encapsulate all what you want to do before "going to sleep" in the batch function ":before".
3) Encapsulate all what you want to do after "waking" in the batch function ":after".
4) Then the batch file (i thought you were asking for) is this:

Code: Select all

@echo off
call :noActivity
if errorlevel 1 (
   call :before
   call "Suspend.bat"
   call :after
)
goto :eof

:noActivity
:: ... (your implementation)

:before
:: ... (your implementation)

:after
:: ... (your implementation)

5) Use the task scheduler to start this batch in intervals of your choice.


Another Sidenote:
You don't need to modify the Systems Power Plan to set the system to sleep:
The "Suspend.bat" works no matter which power plan is active.
So i thought this batch is the "certain service" you want to stop, before you suspend the pc (+restore it after "rewoken").


penpen

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Putting a PC to sleep from a batch

#23 Post by sambul35 » 23 Apr 2016 08:12

Sorry for some delay in answering. I set "Notify" and this thread to "Watched", but for some reason don't get any email notifications of new posts. Are they available on this forum? To everyone? :wink:

As per above suggestions, I expended my earlier sample batch to be called by Task Scheduler at wake up / log on, and/or set times to start chosen Windows service(s), or stop the service(s) & set Power Plan to Balanced depending on day time to allow Windows putting the PC to sleep if no user activity like watching movies etc.

Code: Select all

@ echo off
:: Scheduled task batch to start & stop YourServiceName service at given day time periods
:: When needed, changes Power Plan to Balanced defined in Windows Power Options,
:: thus enabling OS to activate sleep mode if no user activity

setlocal EnableExtensions EnableDelayedExpansion
set "servn=YourServiceName" & set "curTime=%Time: =0%"
set "mes1=%servn% is up" & set "mes2=%servn% is down"
set "mes3=Stopping service... %mes2%" & set "mes4=Starting service... %mes1%"
set "mes5=Current Power Plan" & set "mes6=PC will sleep if no user activity."
set "mes7=Exiting program"
echo/

if %curTime% geq 08:00 if %curTime% leq 17:00 (
   call :service "stop" "%mes3%"
   call :power "%mes5%" "%mes6%"
) else (
   call :service "start" "%mes4%"
)
echo/ & echo %mes7%
timeout /t 5 /nobreak > nul
exit /b

:service
tasklist /nh /fi "imagename eq %servn%.exe" | find /i "%servn%" && (set serv=1 & echo %mes1%) || (set serv=0 & echo %mes2%)
if %serv% equ 1 (
   if %~1==stop net %~1 %servn% 1> nul && (echo/ & echo %~2)
) else (
   if %~1==start net %~1 %servn% 1> nul && (echo/ & echo %~2)
   )
(goto) 2>nul

:power
for /f "tokens=4-7 skip=1" %%a in ('powercfg -l') do (
   if "%%b"=="(Balanced)" (set planid=%%a & set "plann=%%b")
   if "%%c"=="*" (
      set "planc=%%b"
   ) else if "%%d"=="*" (
      set "planc=%%b %%c"
   )
)
if "!planc!"=="!plann!" (
   echo/ & echo %~1 "!planc:~1,-1!"
) else (
   powercfg -s !planid!
   echo/ & echo %~1 "!planc:~1,-1!" changed to "!plann:~1,-1!". %~2
)
(goto) 2>nul


Another simple option would be running a similar batch with parameter(s), since Task Scheduler allows to start a program with arguments. For example, it can be scheduled to start at a certain time with argument %1=1, and at wakeup event with %1=2. The batch can be modified to execute required code depending on the parameter value. This option doesn't require time verification. Its also possible to combine both approaches for greater flexibility and action precision.

Sponge Belly
Posts: 216
Joined: 01 Oct 2012 13:32
Location: Ireland
Contact:

Re: Putting a PC to sleep from a batch

#24 Post by Sponge Belly » 04 Jan 2017 15:10

Hi All! :)

I borrowed Penpen’s C# Hybrid, transplantedd some code I found in this CodeHill article, and ended up with suspend.cmd:

Code: Select all

//
// >nul 2>nul & @goto main
/*
:main
@echo off & setlocal enableextensions disabledelayedexpansion

set "csc="
pushd "%SystemRoot%\Microsoft.NET\Framework"
for /f "tokens=* delims=" %%A in ('dir /b /o:n "v*"') do (
    dir /a-d /b "%%~dpfA\csc.exe" >nul 2>&1 && set "csc="%%~dpfA\csc.exe""
)
popd

if defined csc (
    echo most recent C#.NET compiler located in:
    echo %csc%.
) else (
    echo C#.NET compiler not found.
    goto :eof
)

for %%A in ("%~dpn0") do for %%B in ("%%~dpnA") do (
    %csc% /nologo /r:System.dll /r:System.Management.dll /target:exe ^
    /out:"%%~B.exe" "%~dpf0"
)

endlocal & goto :eof
*/
// C#
using System.Windows.Forms;

public partial class mainForm : Form {
    static void Main() {
        Application.SetSuspendState(PowerState.Suspend, false, false);
    }
}


Which created suspend.exe (3584 bytes). To my surprise, my computer went into standby mode when executed! :shock:

As you can see, it’s not very fancy. The return value is not tested for failure, and the 2nd and 3rd parameters passed to the SetSuspendState method are hard-wired to false.

Any improvements appreciated.

- SB

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Putting a PC to sleep from a batch

#25 Post by penpen » 04 Jan 2017 18:26

Sponge Belly wrote:To my surprise, my computer went into standby mode when executed! :shock:
You shouldn't be surprised :D .

You are using a rather similar code like my first suggestion (inner code only in multiple lines):

Code: Select all

if (-NOT [System.Windows.Forms.Application]::SetSuspendState([System.Windows.Forms.PowerState]::Suspend, $true, $false)) {
   write-host "Couldn`'t set suspend state: Denied.`r`nSee: https://msdn.microsoft.com/en-us/library/system.windows.forms.application.setsuspendstate`(v=vs.110`).aspx";
};"
Although this is Powershell code and you are using C# sources, we are accessing the same .NET interface, so either both should work (or none).
(With the exception, that i used the "true" value for "force", so your application is more polite.)


penpen

Sponge Belly
Posts: 216
Joined: 01 Oct 2012 13:32
Location: Ireland
Contact:

Re: Putting a PC to sleep from a batch

#26 Post by Sponge Belly » 05 Jan 2017 17:12

Hi Penpen,

I’d like to make suspend.exe even more polite! :)

Please tell me the C# code for returning error level 0 if the program ran successfully, and error level 1 if there was a problem.

Thanks!

- SB

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Putting a PC to sleep from a batch

#27 Post by penpen » 06 Jan 2017 08:11

If you use "static int Main()" instead of "static void Main()", then you could return any value (for example using a conditional expression):

Code: Select all

using System;
using System.Windows.Forms;

public partial class mainForm : Form {
   static int Main() {
      return (Application.SetSuspendState(PowerState.Suspend, true, false)) ? 0 : 1;
   }
}


penpen

Sponge Belly
Posts: 216
Joined: 01 Oct 2012 13:32
Location: Ireland
Contact:

Re: Putting a PC to sleep from a batch

#28 Post by Sponge Belly » 06 Jan 2017 12:57

Hi Penpen,

That’s perfect! Thanks again. :)

- SB

Post Reply