views:

1142

answers:

5

My app needs to block sleep/hibernate mode. I have the code in place, but after successfully catching the WM_POWERBROADCAST message, neither PBT_APMQUERYSUSPEND nor PBT_APMQUERYSTANDBY are being caught successfully. Interestingly, the PBT_APMRESUMECRITICAL and PBT_APMRESUMEAUTOMATIC messages are being caught by my app.

Bottom line question: is there any reason why my app would fail to catch the standby/suspend messages, but succeed in catching the resume messages?

This Q&A [stackoverflow.com] helped, btw, but again, the messages don't seem to be making it to my app.

My code (w/ event logging code removed for brevity):

     protected override void WndProc(ref System.Windows.Forms.Message m)
 {
  // Power status event triggered
  if (m.Msg == (int)NativeMethods.WindowMessage.WM_POWERBROADCAST)
  {
   // Machine is trying to enter suspended state
   if (m.WParam.ToInt32() == (int)NativeMethods.WindowMessage.PBT_APMQUERYSUSPEND ||
    m.WParam.ToInt32() == (int)NativeMethods.WindowMessage.PBT_APMQUERYSTANDBY)
   {
    // Have perms to deny this message?
    if((m.LParam.ToInt32() & 0x1) != 0)
    {
     // If so, deny broadcast message
     m.Result = new IntPtr((int)NativeMethods.WindowMessage.BROADCAST_QUERY_DENY);
    }
   }
   return; // ?!
  }

  base.WndProc(ref m);
 }
A: 

Are you running on Vista or Windows Server 2008? This page says

Due to changes in the Power Management Model for Windows Vista and Windows Server 2008, the PBT-APMQUERYSUSPEND event is no longer delivered to applications. Instead the BT_APMSUSPEND event is delivered...

Could that be why you're not seeing it?

JeffH
This is for WinXP (haven't gotten to Vista/2008 yet).Seems like it should "just work," but it doesn't. I've no idea why I'd capture the "resume" messages but not the "sleep" messages.Ideas, anyone?
Garrett
A: 

I tried this same code in a test app on my (dev) machine & on a different (test) machine (also winXP). On my machine, it continues to fail, meaning the machine goes into sleep. But on the other machine, it works! At first I thought this was a debug vs release mode issue, but that's not the case.

It seems that something is different about my dev machine, though I have no idea what it could be.

Mystery solved... sorta.

Garrett
A: 

In Vista call SetThreadExecutionState to notify WPM that system is not idle.

In Windows XP/2000:
An application can return BROADCAST_QUERY_DENY to deny a PBT_APMQUERYSUSPEND or PBT_APMQUERYSUSPENDFAILED request.

MSDN: Windows XP and earlier: The system broadcasts a PBT_APMQUERYSUSPEND event to request permission to suspend system operation. The system expects each application and driver to determine whether the requested event should occur and to return TRUE if it occurs, or return BROADCAST_QUERY_DENY otherwise. Applications should not deny this request. If an application denies this request, the system broadcasts a PBT_APMQUERYSUSPENDFAILED event. This event notifies applications and drivers to continue operation as usual.

Also I don't think either PBT_APMQUERYSTANDBY or PBT_APMSTANDBY are supported in Win2K. Have you tried gloablly logging broadcast when Windows shuts down to see if they are being sent?

MaSuGaNa
Thanks for the comments! I haven't written the Vista code yet, just XP (not worried about win2k since that's not on our list of supported OSes). The mystery at this point is why the code doesn't work on my machine (but DOES work on the test machine), but I'll have to explore that one on my own. =)
Garrett
Glad you fixed it. Shame there isn't a DWOMM certification! http://www.codinghorror.com/blog/images/works-on-my-machine-starburst.png :oÞ
MaSuGaNa
+1  A: 

It works now, for both XP and Vista. I created a stub winform app with the relevant code (could be cleaned up, obviously, but it conveys the point).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace standbyTest
{
    public partial class Form1 : Form
    {

     [DllImport("Kernel32.DLL", CharSet = CharSet.Auto, SetLastError = true)]
     protected static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE state);

     [Flags]
     public enum EXECUTION_STATE : uint
     {
      ES_CONTINUOUS = 0x80000000,
      ES_DISPLAY_REQUIRED = 2,
      ES_SYSTEM_REQUIRED = 1,
      ES_AWAYMODE_REQUIRED = 0x00000040
     }

     public Form1()
     {
      if(Environment.OSVersion.Version.Major > 5)
      {
       // vista and above: block suspend mode
       SetThreadExecutionState(EXECUTION_STATE.ES_AWAYMODE_REQUIRED | EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
      }

      InitializeComponent();

      //MessageBox.Show(string.Format("version: {0}", Environment.OSVersion.Version.Major.ToString() ));

     }

     protected override void OnClosed(EventArgs e)
     {
      base.OnClosed(e);

      if(Environment.OSVersion.Version.Major > 5)
      {
       // Re-allow suspend mode
       SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
      }
     }


     protected override void WndProc(ref System.Windows.Forms.Message m)
     {
      // Power status event triggered
      if(m.Msg == (int)WindowMessage.WM_POWERBROADCAST)
      {
       // Machine is trying to enter suspended state
       if(m.WParam.ToInt32() == (int)WindowMessage.PBT_APMQUERYSUSPEND ||
         m.WParam.ToInt32() == (int)WindowMessage.PBT_APMQUERYSTANDBY)
       {
        // Have perms to deny this message?
        if((m.LParam.ToInt32() & 0x1) != 0)
        {
         // If so, deny broadcast message
         m.Result = new IntPtr((int)WindowMessage.BROADCAST_QUERY_DENY);
        }
       }
       return;
      }

      base.WndProc(ref m);
     }
    }



    internal enum WindowMessage
    {

     /// <summary>
     /// Notify that machine power state is changing
     /// </summary>
     WM_POWERBROADCAST = 0x218,
     /// <summary>
     /// Message indicating that machine is trying to enter suspended state
     /// </summary>
     PBT_APMQUERYSUSPEND = 0x0,
     PBT_APMQUERYSTANDBY = 0x0001,

     /// <summary>
     /// Message to deny broadcast query
     /// </summary>
     BROADCAST_QUERY_DENY = 0x424D5144


    }
}
Garrett
+1  A: 

Try to subscribe the PowerModeChanged event:

http://stackoverflow.com/questions/1562474/how-do-i-check-when-the-computer-is-being-put-to-sleep-or-wakes-up

UGEEN