无法捕获睡眠/挂起消息(winXP)

6
我的应用程序需要阻止睡眠/休眠模式。我已经有相应的代码,但在成功捕获WM_POWERBROADCAST消息后,既无法成功捕获PBT_APMQUERYSUSPEND消息,也无法成功捕获PBT_APMQUERYSTANDBY消息。有趣的是,我的应用程序可以捕获PBT_APMRESUMECRITICAL和PBT_APMRESUMEAUTOMATIC消息。
最重要的问题是:是否有任何原因导致我的应用程序无法捕获待机/挂起消息,但成功捕获恢复消息?
这个Q&A [stackoverflow.com]对此有帮助,但是消息似乎没有传递到我的应用程序中。
我的代码(为简洁起见已删除事件记录代码):
        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);
    }
5个回答

3
现在它可以在XP和Vista上工作了。我创建了一个带有相关代码的存根winform应用程序(显然可以进行清理,但它传达了重点)。
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


    }
}

1

0

你是在运行Vista或Windows Server 2008吗?这个页面

由于Windows Vista和Windows Server 2008的电源管理模型发生了变化,PBT-APMQUERYSUSPEND事件不再传递给应用程序。相反,BT_APMSUSPEND事件被传递...

这可能就是为什么你没有看到它的原因吗?


这是针对WinXP的(还没有涉及到Vista/2008)。看起来应该“只是工作”,但它并没有。我不知道为什么我可以捕获“恢复”消息,但却无法捕获“睡眠”消息。有什么想法吗? - Garrett

0

我在我的开发机和另一台不同的测试机(也是winXP)上尝试了同样的代码。在我的机器上,它继续失败,意味着机器进入睡眠状态。但是在另一台机器上却可以运行!起初我以为这是调试与发布模式之间的问题,但事实并非如此。

似乎我的开发机器有些不同,虽然我不知道具体是什么原因。

谜团解决了...有点儿吧。


0
在Vista中,调用SetThreadExecutionState通知WPM系统不处于空闲状态。
在Windows XP/2000中: 应用程序可以返回BROADCAST_QUERY_DENY来拒绝PBT_APMQUERYSUSPEND或PBT_APMQUERYSUSPENDFAILED请求。
MSDN: Windows XP及更早版本:系统广播PBT_APMQUERYSUSPEND事件以请求暂停系统操作的许可。系统期望每个应用程序和驱动程序确定是否应发生请求的事件,并在其发生时返回TRUE,否则返回BROADCAST_QUERY_DENY。应用程序不应拒绝此请求。如果应用程序拒绝此请求,则系统会广播PBT_APMQUERYSUSPENDFAILED事件。此事件通知应用程序和驱动程序继续按照惯例运行。
此外,我认为Win2K不支持PBT_APMQUERYSTANDBY或PBT_APMSTANDBY。您是否尝试全局记录Windows关闭时的广播,以查看它们是否已发送?

感谢评论!我还没有编写Vista代码,只有XP(不担心win2k,因为它不在我们支持的操作系统列表中)。目前的谜团是为什么代码在我的机器上无法工作(但在测试机器上可以),但我将不得不自己探索这个问题。=) - Garrett
很高兴你解决了它。可惜没有DWOMM认证!http://www.codinghorror.com/blog/images/works-on-my-machine-starburst.png :oÞ - miPwn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接