Page 1 of 2

Putting a PC to sleep from a batch

Posted: 22 Feb 2016 17:32
by sambul35
Command shutdown doesn't offer sleep option. If Hibernation is disabled, shutdown /h may not go to pure sleep mode either. On the web there're suggestions to use the following command for sleep if Hibernation is disabled:

Code: Select all

%windir%\system32\rundll32.exe PowrProf.dll, SetSuspendState 0,1,0


However, several authors pointed that using Rundll32.exe to call random functions is inappropriate and may harm Windows install. Besides that, any parameters after SetSuspendState are said to be ignored by rundll32.exe, and the only affecting parameter is whether Hibernation is set On or Off. Others mentioned stand alone programs to call from a batch to enter Sleep mode, including Powershell and VBS samples.

Can someone provide a batch code sample putting PC to sleep without invoking rundll32.exe and other outside programs or programming languages?

Re: Putting a PC to sleep from a batch

Posted: 22 Feb 2016 23:15
by mirrormirror
Been using the following command on a win7 machine for a couple years with no issues:

%windir%\System32\rundll32.exe powrprof.dll,SetSuspendState Standby

hibernation is also disabled on the machine. You can run it directly from a shortcut or if you put it in a batch file, you can have it execute other commands before/after sleep mode.

If you have problems going into sleep mode sometimes it is related to poor power (as in electricity) quality. It can be your power supply, UPS or just poor quality from your outlet. Usually, a good UPS will compensate for this.

Re: Putting a PC to sleep from a batch

Posted: 23 Feb 2016 07:38
by penpen
This "Suspend.bat" may help you/mirrormirror:

Code: Select all

@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"; };"
You may replace the parameters according to the MSDN documentation.


mirrormirror wrote:Been using the following command on a win7 machine for a couple years with no issues:

%windir%\System32\rundll32.exe powrprof.dll,SetSuspendState Standby
The functions you want to call using rundll32 must meet specific requirements to work with no errors.
If you want to use rundll32 to call a function (encapsulated in an dll) that don't follow these rules then this call results in a corrupted stack:
This may not harm your computer if this corrupted stack is not used anymore;
for example if the application using this call is closed immediately - which in most cases is not possible.

It is a fact, that SetSuspendState (see MSDN documentation linked above) in powrprof.dll does not follow the above rules:
It is no "void function" and instead returns a boolean value.
So the stack now has an additional element (this boolean value) => corrupted stack.

If you call rundll32 from within a batch, then the stack automatically is used to proceed the batch script,
even if this was the last command.
=> This is unsafe and could result in unpredictable system behaviour.

Using a shortcut may be safe or not:
But i'm not familiar with the execution details of shortcuts.

This is meant when saying:
sambul35 wrote:However, several authors pointed that using Rundll32.exe to call random functions is inappropriate and may harm Windows install.



penpen

Re: Putting a PC to sleep from a batch

Posted: 23 Feb 2016 07:55
by sambul35
Is this corruption of stack permanent on disk or only in RAM, and restored to normal stack upon reboot?

Is there a way to get PS error code for what caused "Couldn't set suspend state: Denied" outcome?

What programming language this PS snippet is written on? Are there any common rules, how to inject into a batch a code using a different programming language?

Re: Putting a PC to sleep from a batch

Posted: 23 Feb 2016 10:58
by penpen
sambul35 wrote:Is this corruption of stack permanent on disk or only in RAM, and restored to normal stack upon reboot?
First to say that the stack is located somewhre in the IA32 memory address space:
It may be located on any disk (pagefile), or in RAM.
But it is more probable that the call stack of a program running on an average system is located in RAM.

You could build as many call stacks you want:
Typically the system (OS), every process and every task has their own call stack.

What happens with the call stack depends on what you call "reboot".
The corruption "survives" the ACPI modes G0, G1, S0-S4, and C0-C7 (containing S3=Sleep=Suspend to RAM and S4=Hibernate=Suspend to disk).
Returning from these states doesn't "restore" the stack (it's stays corrupted).

Only if the system (re)boots from ACPI modes S5 (=soft off), S6 (=PSU disconnected from MB) and G3 (=mechanical off) the stack will be rebuild.


sambul35 wrote:Is there a way to get PS error code for what caused "Couldn't set suspend state: Denied" outcome?
No, there is no way to get this error code, because no error happens (and in addition no exception is thrown).

Possible scenarios:
- The system may be configured by admin to ignore this functionality for special/all modes.
- A hardware may not support ACPI and windows/Linux/... may ignore the users wish.
- The OS security manager may deny the selected ACPI mode (if an application with enough rights advises the security manager to react this way).
- ...

See the (also linked above) "MSDN documentation":
https://msdn.microsoft.com/en-us/library/system.windows.forms.application.setsuspendstate(v=vs.110).aspx


sambul35 wrote:What programming language this PS snippet is written on?
I'm not sure how you mean this:
This is a typical batch file containing a call to Powershell to execute the given powershell command.
The external assembly of the called commandlet probably is programmed in C++ or C#.

I only wanted to avoid using a second powershell file for example this "Suspend.ps1":

Code: Select all

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".
   write-host "See: https://msdn.microsoft.com/en-us/library/system.windows.forms.application.setsuspendstate(v=vs.110).aspx"
}


sambul35 wrote:Are there any common rules, how to inject into a batch a code using a different programming language?
No, you have to try to write a hybrid that is accepted by batch and the programming language you are using.
For more information see:
- http://www.dostips.com/forum/viewtopic.php?f=3&t=5543
- http://www.dostips.com/forum/viewtopic.php?f=3&t=6931


penpen

Re: Putting a PC to sleep from a batch

Posted: 23 Feb 2016 17:49
by sambul35
Thanks for the detail reply. I asked about "programming language" since your code MS docs link refers to several such languages somehow relevant to Powershell. Also, the PS commands syntax is different from regular batch syntax, hence the question whether PS has its own programming language or uses some popular language as a base?

Since you mentioned several "ximera" examples of combining several languages into a batch, may be you seen any examples of setting USB camera properties like brigtness & contrast from a batch via DirectShow API like this one? What language such hybrid would have to be written on?

Re: Putting a PC to sleep from a batch

Posted: 23 Feb 2016 17:51
by mirrormirror
Thanks penpen for the info - (I never investigated it before) and the batch file.

Here are two utilities I've used in the past for this purpose (in case you need them on a machine that does not have powershell).
https://www.grc.com/wizmo/wizmo.htm
http://www.nirsoft.net/utils/nircmd.html
They each do multiple things but include suspend/sleep functionality.

Re: Putting a PC to sleep from a batch

Posted: 24 Feb 2016 10:23
by penpen
sambul35 wrote:I asked about "programming language" since your code MS docs link refers to several such languages somehow relevant to Powershell. Also, the PS commands syntax is different from regular batch syntax, hence the question whether PS has its own programming language or uses some popular language as a base?
Ah, i think i got the point:
Powershell is indeed a(n own) programming language; the function (and namespace) above is defined by the ".NET framework" and the page lists some interfaces for different languages (C#, C++, VB, J#, and JScript).

The full .NET framework functionality is available from Powershell by using CmdLets:
Just add the needed assembly as listed in the above MSDN page.


sambul35 wrote:Since you mentioned several "ximera" examples of combining several languages into a batch, may be you seen any examples of setting USB camera properties like brigtness & contrast from a batch via DirectShow API like this one? What language such hybrid would have to be written on?
I would prefer to write such things using C++ and Visual Studio.

The .NET framework is planned to cover all C++ functionality (somehow in far future)...
... but up to then the best way to access "Non-.Net Framework-c++-functions" is to use explicit platform invoke with C#.

You could create an interface of an unmanaged dynamic-link library using "DllImport" (PInvoke) - example:
http://www.dostips.com/forum/viewtopic.php?p=34649#p34649.

This is a good source of working interfaces (of that type; but "your" function is not listed there):
http://www.pinvoke.net/

Using the MS type definition and the MS Fundamental Types of the C++ Language,
your interface should be similar to this:

Code: Select all

using System;
using System.Runtime.InteropServices;

using HRESULT = System.Int32;
using LONG    = System.Int32;


namespace PInvokeDirectShow {
public class IVideoProcAmp {
   [DllImport ("???.dll", CharSet=CharSet.Auto, ExactSpelling=true, SetLastError=true)]
   private static extern HRESULT put_Brightness(
      LONG Value,
      LONG Flags
   );
}
}
I don't know the name of the dll (not listed on the referenced MSDN description), so i used "???.dll" you have to do some research on that.
In addition you may exchange the "CharSet" and "ExactSpelling" with more proper values (depends on how this dll was compiled).

You could then access the C# source with Powershell, so you could use that in a batch file (but with external powershell [.ps1] file):
https://blogs.technet.microsoft.com/ste ... l-scripts/


@mirrormirror:
Thanks for the links, i think i will test these tools.


penpen

Re: Putting a PC to sleep from a batch

Posted: 24 Feb 2016 12:19
by sambul35
Did you mean DirectShow or the Cam driver related ???.dll ?

As to using 3rd party programs to put a PC to sleep & suspend mode from a batch, there're a few more I know off:

Sleep by Gammadyne
PsTools by Sysinternals

But your above PS code snippet works well for me. Would you share a similar one for User Logoff? :)

Re: Putting a PC to sleep from a batch

Posted: 25 Feb 2016 13:35
by penpen
sambul35 wrote:Did you mean DirectShow or the Cam driver related ???.dll ?
The DirectShow related "???.dll".
It has to be the name of the dll, that exports the function "put_Brightness".
You probably have to do that for most (if not all) functions, you want to use in your C# program:
The above is just a "demo" for that special function.

sambul35 wrote:As to using 3rd party programs to put a PC to sleep & suspend mode from a batch, there're a few more I know off:
Thanks^^.

sambul35 wrote:But your above PS code snippet works well for me. Would you share a similar one for User Logoff? :)
If you want to log off yourself, then this "logout.bat" should help you:

Code: Select all

shutdown /l
(I'm unsure since when this executable is part of Windows, so you have to try if it works for you.)


penpen

Re: Putting a PC to sleep from a batch

Posted: 06 Mar 2016 17:23
by sambul35
penpen wrote:This "Suspend.bat" may help you/mirrormirror:

Code: Select all

@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"; };"
You may replace the parameters according to the MSDN documentation.


Is it possible to check from the batch, if there was any user activity within the last 15 min, such as mouse or keyboard input events, and if YES, exit without putting PC to sleep? The batch can be run again later by Windows Scheduler every 15 min to put the PC to sleep if there was no recent user activity. Such batch would be useful to stop select Windows services after certain time of day preventing the PC from ever going to sleep.

Re: Putting a PC to sleep from a batch

Posted: 08 Mar 2016 08:02
by sambul35
Here's yet another way to put PC to sleep from a batch - by starting the batch with Windows Scheduler. Lets say a process runs on your PC requiring High Performance Power Plan remain active, where HDD never sleeps, and the PC Monitor stays asleep, so the PC runs "blind". If you need to put the PC to sleep after a certain time of day, setup a Scheduled Task in Windows Scheduler that will start the batch with admin privileges enabling OS to invoke sleep mode, like this snippet:

Code: Select all

@ echo off
:: Scheduled task batch to stop YourServiceName service, change Power Plan to Balanced defined in
:: Windows Power Options, thus enabling OS to activate sleep mode if no user activity

set "servn=YourServiceName"
set "mes1=%servn% is up"
set "mes2=%servn% is down"
set "mes3=Stopping service..."
set "mes4=Current Power Plan"
set "mes5=PC will sleep if no user activity"
set "mes6=Exiting program"
echo/
tasklist /nh /fi "imagename eq %servn%.exe" | find /i "%servn%" && (set serv=1 & echo %mes1%) || (set serv=0 & echo %mes2%)
if %serv% equ 1 net stop %servn% 1> nul && (echo/ & echo %mes3% %mes2%)
for /f "tokens=4-7 skip=1" %%a in ('powercfg -l') do (
   if "%%b"=="(Balanced)" (set pquid=%%a & set "plann=%%b")
   if "%%c"=="*" (
      set "planc=%%b"
   ) else if "%%d"=="*" (
      set "planc=%%b %%c"
      )
)
if "%planc%"=="%plann%" (
   echo/ & echo %mes4% "%planc:~1,-1%"
) else (
   powercfg -s %pquid%
   echo/ & echo %mes4% "%planc:~1,-1%" changed to "%plann:~1,-1%". %mes5%
)
echo/ & echo %mes6%
timeout /t 10 /nobreak > nul
exit /b

Re: Putting a PC to sleep from a batch

Posted: 08 Mar 2016 15:41
by penpen
sambul35 wrote:Is it possible to check from the batch, if there was any user activity within the last 15 min, such as mouse or keyboard input events, and if YES, exit without putting PC to sleep?
Yes, you could access the c++ function "GetLastInputInfo" from c#.
I would create an executable returning the time (in milliseconds) since the last user activity (keyboard or mouse) using:

Code: Select all

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

   set "csc="

   pushd "%SystemRoot%\Microsoft.NET\Framework"
   for /f "tokens=* delims=" %%i in ('dir /b /o:n "v*"') do (
      dir /a-d /b "%%~fi\csc.exe" >nul 2>&1 && set "csc="%%~fi\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 (
rem      %csc% /?
rem      %csc% /nologo /optimize /warnaserror /nowin32manifest /unsafe /debug- /target:exe /out:"%%~b.exe" "%~f0"
rem      %csc% /nologo /warnaserror /r:System.dll /r:System.Windows.Forms.dll /r:System.Drawing.dll /target:exe /out:"%~f1.exe" "%~f1.cs"
      %csc% /nologo /r:System.dll /r:System.Management.dll /target:exe /out:"%%~b.exe" "%~f0"
   )
   exit /B
*/

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Management;

using BOOL = System.Boolean;
using UINT = System.UInt32;
using DWORD = System.UInt32;


public class GetLastInputTime {
   [StructLayout(LayoutKind.Sequential)]
   struct LASTINPUTINFO {
      public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
      public UINT cbSize;   
      public DWORD dwTime;
   }

   [DllImport ("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true, SetLastError=true)]
   static extern BOOL GetLastInputInfo(
      ref LASTINPUTINFO plii
   );

   public static void Main (string [] args) {
      LASTINPUTINFO lii = new LASTINPUTINFO();
      lii.cbSize = (UINT)Marshal.SizeOf(lii);

      if (!GetLastInputInfo(ref lii)) {
         Console.Error.WriteLine ("Failed GetLastInputInfo: {0}", Marshal.GetLastWin32Error());
      } else {
         Console.WriteLine ("{0}", (Environment.TickCount - lii.dwTime));
      }
   }
}
But you could also use the technique i've linked above to run all from a powershell script.


penpen

Re: Putting a PC to sleep from a batch

Posted: 08 Mar 2016 16:20
by sambul35
@penpen

Thanks for the code and interesting solution. :)

Re: Putting a PC to sleep from a batch

Posted: 10 Apr 2016 07:15
by sambul35
A relevant question comes up. Is it possible to modify this batch to use it for the following purposes:

- 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.

The problem seems to be determining in the batch, whether it was called at PC wakeup or not (i.e. a recent wakeup event occurred). Is it possible with a similar to User Input C++ function, or would someone suggest a simpler approach to alter the batch code flow execution based on that criteria? I do realize, it can be done by using 2 separate batches, but may be the same is possible using just one batch?

One solution I can think of is checking current time. If its around Sleep scheduled task time, then its likely not a wakeup event that called the batch, but rather the Sleep task. However, this approach is not accurate, since manual wakeup can occur shortly after PC going to sleep, and in fact often a user is late to catch the moment to stop PC entering sleep mode, so needs to wake it up.