Can't catch sleep/suspend messages (winXP)
Asked Answered
J

5

6

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);
    }
Jacobean answered 7/4, 2009 at 19:0 Comment(0)
J
3

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


    }
}
Jacobean answered 8/4, 2009 at 21:59 Comment(0)
F
1

Try to subscribe the PowerModeChanged event:

How do I check when the computer is being put to sleep or wakes up?

Fineable answered 11/10, 2010 at 19:31 Comment(0)
P
0

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?

Perpetuate answered 7/4, 2009 at 19:59 Comment(1)
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?Jacobean
J
0

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.

Jacobean answered 8/4, 2009 at 15:20 Comment(0)
U
0

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?

Ultrafilter answered 8/4, 2009 at 16:0 Comment(2)
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. =)Jacobean
Glad you fixed it. Shame there isn't a DWOMM certification! codinghorror.com/blog/images/works-on-my-machine-starburst.png :oÞUltrafilter

© 2022 - 2024 — McMap. All rights reserved.