1

I have a very long running syncronization task that cannot be interrupted by the screen saver or aggressive power saving modes. I want to make a single api call to stop power save mode and then restore it once the task is done.

The following code is peaced together from various other posts but it has no effect on XP's power management settings. What am I doing wrong? TIA!

Private Declare Function SetThreadExecutionState Lib "kernel32" (ByVal esFlags As Long) As Long

Public Enum EXECUTION_STATE As Integer

    ES_CONTINUOUS = &H80000000
    ES_DISPLAY_REQUIRED = &H2
    ES_SYSTEM_REQUIRED = &H1
    ES_AWAYMODE_REQUIRED = &H40

End Enum

Public Shared Sub PowerSaveOff()
    SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED Or EXECUTION_STATE.ES_CONTINUOUS)
End Sub

Public Shared Sub PowerSaveOn()
    SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS)
End Sub

Here are the systems screensaver and powermode settings: alt text http://img87.imageshack.us/img87/1600/25251376.jpg alt text http://img403.imageshack.us/img403/8145/73347627.jpg

Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
Tim Santeford
  • 27,385
  • 16
  • 74
  • 101
  • From http://msdn.microsoft.com/en-us/library/aa373208(VS.85).aspx, "This function does not stop the screen saver from executing." – AMissico Mar 19 '10 at 17:57
  • I think you need to read the "Remarks" section of http://msdn.microsoft.com/en-us/library/aa373208(VS.85).aspx – AMissico Mar 19 '10 at 18:01
  • 1
    `ByVal esFlags As Long` => `ByVal esFlags As EXECUTION_STATE` would help with IntelliSense. – AMissico Mar 19 '10 at 18:03
  • @AMissico - That same page mentions "Multimedia applications, such as video players and presentation applications, must use ES_DISPLAY_REQUIRED when they display video for long periods of time without user input." This is the exact behavior I'm tring to simulate. – Tim Santeford Mar 19 '10 at 18:05
  • "System Sleep Criteria" at http://msdn.microsoft.com/en-us/library/aa373233(VS.85).aspx – AMissico Mar 19 '10 at 18:05
  • @Tim Santeford: Yes, I noticed that. Have you tried ES_AWAYMODE_REQUIRED? Also, in "System Sleep Criteria" it mentions that you must call SetThreadExecutionState at intervals to reset the "idle timer". You just can't call it once. – AMissico Mar 19 '10 at 18:08
  • That doc says: In "Windows Server 2003 and Windows XP/2000: ES_AWAYMODE_REQUIRED is not supported." – Tim Santeford Mar 19 '10 at 18:36
  • @AMissico I added ES_SYSTEM_REQUIRED and this seems to have stopped hibernation... – Tim Santeford Mar 19 '10 at 18:38
  • @Tim Santeford: I was just going to recommend that. You still using ES_DISPLAY_REQUIRED correct? – AMissico Mar 19 '10 at 18:41
  • It seems you need to call SystemParametersInfo with SPI_SETBLOCKSENDINPUTRESETS then call SendInput periodically. – AMissico Mar 19 '10 at 18:46
  • I'm calling SetThreadExecutionState(ES_DISPLAY_REQUIRED Or ES_CONTINUOUS Or ES_SYSTEM_REQUIRED) one time and it is preventing hibernation indefinitely! The screen saver is still coming on but I can live with that if I must. – Tim Santeford Mar 19 '10 at 18:56
  • Awesome. I am looking into the disabling the screen saver. Either using SPI_SETBLOCKSENDINPUTRESETS or WM_SYSCOMMAND. – AMissico Mar 19 '10 at 19:05
  • http://stackoverflow.com/questions/2175384/screensaver-hurts-cuda-performance – AMissico Mar 19 '10 at 19:06
  • @AMissico ok im going to give the sendkey trick a shot. I was trying to avoid multi-threading but I guess Ill have to do this way. Really appreciate your help so far. Post a quick answer involving ES_SYSTEM_REQUIRED and I will accept it. – Tim Santeford Mar 19 '10 at 19:15
  • @Tim Santeford: Not "sendkey" the statement, but `SendInput` API function. I am not sure, but I think it is different. I may have a good solution using WM_SYSCOMMAND. – AMissico Mar 19 '10 at 19:22
  • @Tim Santeford: Using WndProc/WM_SYSCOMMAND/SC_SCREENSAVE will have no affect on Vista+ with password protection turned on. – AMissico Mar 19 '10 at 19:47
  • @AMissico thanks for trying that. I think using SendInput will serve my purposes. I'm going to try it out and post the results back here on Monday. – Tim Santeford Mar 19 '10 at 19:52

3 Answers3

2

I added EXECUTION_STATE.ES_SYSTEM_REQUIRED, which "Forces the system to be in the working state by resetting the system idle timer," and prevents the system from entering a power saving state. I also changed the API calling convention to use EXECUTION_STATE, wrapped everything in a simple utility class with some documentation.

''' <summary>
''' Simple power manager class that enables applications to inform the system
''' that it is in use, thereby preventing the system from entering the sleeping
''' power state or turning off the display while the application is running.
''' </summary>
Public Class PowerManager

#Region " Private Sub New "
    Private Sub New()
        'keep compiler from creating default constructor to create utility class
    End Sub
#End Region

    ''' <summary>
    ''' Enables applications to inform the system that it is in use, thereby preventing the system from entering the sleeping power state or turning off the display while the application is running.
    ''' </summary>
    ''' <param name="esFlags">The thread's execution requirements. This parameter can be one or more of the EXECUTION_STATE values.</param>
    ''' <returns>
    ''' <para>If the function succeeds, the return value is the previous thread execution state, as a EXECUTION_STATE value.</para>
    ''' <para>If the function fails, the return value is NULL.</para>
    '''</returns>
    ''' <remarks>
    ''' <para>This function does not stop the screen saver from executing.</para>
    ''' <para>http://msdn.microsoft.com/en-us/library/aa373208.aspx</para>
    ''' </remarks>
    Private Declare Function SetThreadExecutionState Lib "kernel32" (ByVal esFlags As EXECUTION_STATE) As EXECUTION_STATE

    Public Enum EXECUTION_STATE As Integer

        ''' <summary>
        ''' Informs the system that the state being set should remain in effect until the next call that uses ES_CONTINUOUS and one of the other state flags is cleared.
        ''' </summary>
        ES_CONTINUOUS = &H80000000

        ''' <summary>
        ''' Forces the display to be on by resetting the display idle timer.
        ''' </summary>
        ES_DISPLAY_REQUIRED = &H2

        ''' <summary>
        ''' Forces the system to be in the working state by resetting the system idle timer.
        ''' </summary>
        ES_SYSTEM_REQUIRED = &H1

    End Enum

    Public Shared Function PowerSaveOff() As EXECUTION_STATE
        Return SetThreadExecutionState(EXECUTION_STATE.ES_SYSTEM_REQUIRED Or EXECUTION_STATE.ES_DISPLAY_REQUIRED Or EXECUTION_STATE.ES_CONTINUOUS)
    End Function

    Public Shared Function PowerSaveOn() As EXECUTION_STATE
        Return SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS)
    End Function

End Class

Public Class Form1

    Private _cancel As Boolean

    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click

        'set system standby to one minute

        _cancel = False

        PowerManager.PowerSaveOff()

        Do Until _cancel
            My.Application.DoEvents()
        Loop

        PowerManager.PowerSaveOn()

        'do not forget to restore your power settings

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        _cancel = True
    End Sub

End Class
AMissico
  • 21,470
  • 7
  • 78
  • 106
1

This works for me:

Private Const SPI_SETSCREENSAVEACTIVE = 17
Private Const SPI_SETSCREENSAVETIMEOUT = 15
Private Const SPIF_SENDWININICHANGE = &H2
Private Const SPIF_UPDATEINIFILE = &H1
Private Declare Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long, ByVal lpvParam As Long, ByVal fuWinIni As Long) As Long

Private Sub Form_Load()
Call SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, 0, SPIF_UPDATEINIFILE + SPIF_SENDWININICHANGE)
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Call SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 1, 0, SPIF_UPDATEINIFILE + SPIF_SENDWININICHANGE)
End Sub
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
tmp
  • 11
  • 1
-1
Microsoft.Win32.RegistryKey rkScreenSaver =
        Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@" Control Panel\Desktop", true );
if ( (string)rkScreenSaver.GetValue( "ScreenSaveActive" ) == "1" )
{
   rkScreenSaver.SetValue( "ScreenSaveActive", "0" );
   rkScreenSaver.Close( );
}

This should work for you.

Alternatively why not extend the time for the screen saver to 10 minutes?

  • Is was trying to avoid changing the users screen saver settings but I might have to do that. Even with screen saver settings disabled I still have the hibernation issue which is more problematic for me. – Tim Santeford Mar 19 '10 at 18:10
  • I'll keep looking to see if I find a solution to the hibernation part. – Michael Boggess Mar 19 '10 at 18:13
  • Manipulating the registry and user settings is not an option. – AMissico Mar 19 '10 at 18:17
  • I'll keep that in mind AMissico. By the way, this thread has already appeared in Google search results! – Michael Boggess Mar 19 '10 at 18:21
  • Yes Googlebot has some serious love for SO. – Tim Santeford Mar 19 '10 at 18:23
  • @mjboggess: Yes, I noticed that a couple of weeks ago. I don't like it because it is hard to filter out "junk" (the current discussion) with "information". – AMissico Mar 19 '10 at 18:23
  • 1
    @mjboggess: -1, Directly manipulating the registry is always a "last resort" solution. In many cases there is a Windows API call to do what is needed. If you do have to manipulate the registry, it is usually a work around. – AMissico Mar 19 '10 at 18:48