17

嗨,我一直在寻找,但我找不到答案。我怎么知道屏幕何时关闭或打开。不是 SystemEvents.PowerModeChanged 。我不知道如何检索显示/屏幕事件

 private const int WM_POWERBROADCAST     = 0x0218;
        private const int WM_SYSCOMMAND         = 0x0112;
        private const int SC_SCREENSAVE         = 0xF140;
        private const int SC_CLOSE              = 0xF060; // dont know
        private const int SC_MONITORPOWER       = 0xF170;
        private const int SC_MAXIMIZE           = 0xF030; // dont know
        private const int MONITORON = -1;
        private const int MONITOROFF = 2;
        private const int MONITORSTANBY = 1; 
[DllImport("user32.dll")]
        //static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        private static extern int SendMessage(IntPtr hWnd, int hMsg, int wParam, int lParam);
        public void Init(Visual visual)
        {
            SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
            HwndSource source = ((HwndSource)PresentationSource.FromVisual(visual));
            source.AddHook(MessageProc);
            Handle = source.Handle;
           
        }
public void SwitchMonitorOff()
        { // works
                SendMessage(Handle, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOROFF);
        }
        public  void SwitchMonitorOn()
        {// works
            SendMessage(Handle, WM_SYSCOMMAND, SC_MONITORPOWER, MONITORON);
        }
        public  void SwitchMonitorStandBy()
        {// works
            SendMessage(Handle, WM_SYSCOMMAND, SC_MONITORPOWER, MONITORSTANBY);
        }

 private IntPtr MessageProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {

        
             if (msg == WM_SYSCOMMAND) //Intercept System Command
            {
                // not finished yet
                // notice the 0xFFF0 mask, it's because the system can use the 4 low order bits of the wParam 
                // value as stated in the MSDN library article about WM_SYSCOMMAND.
                int intValue = wParam.ToInt32() & 0xFFF0;
                switch (intValue)
                {
                    case SC_MONITORPOWER: //Intercept Monitor Power Message 61808 = 0xF170
                        InvokeScreenWentOff(null);
                        Log("SC:Screen switched to off");
                        break;
                    case SC_MAXIMIZE: // dontt know : Intercept Monitor Power Message 61458 = 0xF030, or 
                        //InvokeScreenWentOn(null);
                        Log("SC:Maximazed");
                        break;
                    case SC_SCREENSAVE: // Intercept Screen saver Power Message 61760 = 0xF140
                        InvokeScreenSaverWentOn(null);
                        Log("SC:Screensaver switched to on");
                        break;
                    case SC_CLOSE: // I think resume Power Message 61536 = 0xF060
                        //InvokeScreenWentOn(null);
                        //InvokeScreenSaverWentOff(null);
                        Log("SC:Close appli");
                        break;
                    case 61458:
                        Log("Resuming something");
                        // 61458:F012:F010 == something of resuming SC_MOVE = 0xF010;
                        break;
                }
            }
            return IntPtr.Zero;
        }  

编辑

也许我可以解释我的意图,所以也许有更好的解决方案。我有一个双绑定 WCF 服务正在运行。它在爱可视(便携式平板电脑)上运行。我希望当用户在空闲时间停止工作时,连接立即关闭,当计算机从空闲状态返回时,他立即重新连接。Tom提出的Application Idle on Code 项目的想法已经是一个好主意。耗电越少越好。启动必须尽可能快。

4

4 回答 4

8

在此处查看此博客,它将帮助您完成您想要实现的目标。此外,您需要创建一个自定义事件来为您执行此操作,如下所示:

public enum PowerMgmt{
    StandBy,
    Off,
    On
};

public class ScreenPowerMgmtEventArgs{
    private PowerMgmt _PowerStatus;
    public ScreenPowerMgmtEventArgs(PowerMgmt powerStat){
       this._PowerStatus = powerStat;
    }
    public PowerMgmt PowerStatus{
       get{ return this._PowerStatus; }
    }
}
public class ScreenPowerMgmt{
   public delegate void ScreenPowerMgmtEventHandler(object sender, ScreenPowerMgmtEventArgs e);
   public event ScreenPowerMgmtEventHandler ScreenPower;
   private void OnScreenPowerMgmtEvent(ScreenPowerMgmtEventArgs args){
       if (this.ScreenPower != null) this.ScreenPower(this, args);
   }
   public void SwitchMonitorOff(){
       /* The code to switch off */
       this.OnScreenPowerMgmtEvent(new ScreenPowerMgmtEventArgs(PowerMgmt.Off));
   }
   public void SwitchMonitorOn(){
       /* The code to switch on */
       this.OnScreenPowerMgmtEvent(new ScreenPowerMgmtEventArgs(PowerMgmt.On));
   }
   public void SwitchMonitorStandby(){
       /* The code to switch standby */
       this.OnScreenPowerMgmtEvent(new ScreenPowerMgmtEventArgs(PowerMgmt.StandBy));
   }

}

编辑:由于Manu不确定如何检索事件,因此此编辑将包含有关如何使用此类的示例代码,如下所示。

Using System;
Using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Interop;
using System.Text;

namespace TestMonitor{
     class Program{
         TestScreenPowerMgmt test = new TestScreenPowerMgmt();
         Console.WriteLine("Press a key to continue...");
         Console.ReadKey();
     }

     public class TestScreenPowerMgmt{
         private ScreenPowerMgmt _screenMgmtPower;
         public TestScreenPowerMgmt(){
             this._screenMgmtPower = new ScreenPowerMgmt;
             this._screenMgmtPower.ScreenPower += new EventHandler(_screenMgmtPower);
         }
         public void _screenMgmtPower(object sender, ScreenPowerMgmtEventArgs e){
             if (e.PowerStatus == PowerMgmt.StandBy) Console.WriteLine("StandBy Event!");
             if (e.PowerStatus == PowerMgmt.Off) Console.WriteLine("Off Event!");
             if (e.PowerStatus == PowerMgmt.On) Console.WriteLine("On Event!");
         }

     }
}

在查看了这段代码并意识到有些地方不太对劲后,我突然意识到 Manu 正在寻找一种方法来询问系统以检测不可用的 Monitor 的电源状态,但是,代码以编程方式显示,监视器可以打开/关闭/待机,同时触发一个事件,但他希望它能够挂钩WndProc表单并处理指示监视器状态的消息......现在,在这个点,我将对此发表我的看法。

我不能 100% 确定这是否可以完成,或者 Windows 是否真的发送了一条广播消息,说“嘿!显示器要睡觉了”或“嘿!显示器正在开机”,我恐怕会说,显示器实际上并没有向 Windows 发送一些软件信号来通知它要休眠/关闭/开启。现在,如果有人对此有任何建议、提示、线索,请随时发表您的评论......

能源之星软件作为屏幕保护程序选项卡的一部分,当您右键单击桌面任意位置时,会出现一个弹出菜单,左键单击“属性”,出现“显示”对话框,具有不同的选项卡页面,左键单击“屏幕保护程序”,单击“电源”按钮作为“监视器电源”分组框的一部分,对话框的那部分以某种方式触发 Windows 子系统(显卡?/能源之星驱动程序?)发送硬件信号来打开显示器本身的节能功能......(全新的显示器默认情况下没有启用此功能 AFAIK......请随意忽略这个概念......)

除非在 Energy-Power 软件驱动程序中嵌入并深埋在某处未记录的 API (一个 API 确实被触发,关于单击“电源”按钮如何将该信号发送到电源模式确实被激活的监视器作为结果!)然后也许,通过在所述表单应用程序的后台运行一个线程,轮询以询问未知功能或用于检查电源状态的 API - 那里一定有一些只有微软知道的东西......毕竟,能源之星向微软展示了如何在显示器本身上触发省电模式,这肯定不是单行道吗?或者是吗?

抱歉,Manu,如果我不能再提供帮助的话.... :(

编辑#2:我考虑了我之前在编辑中写的内容,并做了一些挖掘以寻找答案,我想我想出了答案,但首先,一个想法突然出现在我的脑海中,请在此处查看此文档-来自“terranovum.com”的pdf文档,线索(或者我认为......)在注册表中,使用文档最后一页上的最后两个注册表项包含指定的秒数偏移量,以及结合这篇CodeProject文章,要找出空闲时间,很容易确定显示器何时进入待机状态,听起来很简单,我想,Manu 也不喜欢这个概念......

与谷歌的进一步调查使我得出这个结论,答案在于扩展VESA BIOS规范DPMS(显示电源管理信号),现在由此产生的问题是,您如何在 VESA BIOS 上询问该信号,现在,许多现代显卡都安装了 VESA Bios,因此必须有一个硬件端口可以读取引脚的值,使用此路由需要使用InpOut32,或者如果您有 64 位 Windows,则有一个InpOut64通过 pinvoke。基本上,如果您能回忆起使用 Turbo C 或 Turbo Pascal(DOS 均为 16 位),则有一个名为 inport/outport 或类似的例程来读取硬件端口,甚至是使用 peek/poke 的 GWBASIC。如果可以找到硬件端口的地址,那么可以通过检查水平同步和垂直同步来查询这些值以确定显示器是否处于待机/关机/暂停/开机状态,我认为这是更可靠的解决方案。 ..

为冗长的答案道歉,但我觉得我必须写下我的想法......

Manu 还是有希望的 :) ;)

于 2010-02-05T16:42:04.093 回答
4

缺少的部分是我没有注册这些活动。

发现有一个来自微软的电源管理示例:

http://www.microsoft.com/en-us/download/details.aspx?id=4234

hMonitorOn = RegisterPowerSettingNotification(this.Handle,ref GUID_MONITOR_POWER_ON,DEVICE_NOTIFY_WINDOW_HANDLE);

[DllImport("User32", SetLastError = true,EntryPoint = "RegisterPowerSettingNotification",CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient,ref Guid PowerSettingGuid,Int32 Flags);

[DllImport("User32", EntryPoint = "UnregisterPowerSettingNotification",CallingConvention = CallingConvention.StdCall)]
private static extern bool UnregisterPowerSettingNotification(IntPtr handle);

// This structure is sent when the PBT_POWERSETTINGSCHANGE message is sent.
// It describes the power setting that has changed and contains data about the change
[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct POWERBROADCAST_SETTING
{
    public Guid PowerSetting;
    public Int32 DataLength;
}
于 2013-05-19T09:01:50.980 回答
2

这对我有用,即使 MainWindow 是隐藏的。该代码基于上述帖子,以及https://www.codeproject.com/Articles/1193099/Determining-the-Monitors-On-Off-sleep-Status的 C++ 代码。

public partial class MainWindow : Window
    {
        private readonly MainViewModel VM;
        private HwndSource _HwndSource;
        private readonly IntPtr _ScreenStateNotify;

        public MainWindow()
        {
            InitializeComponent();
            VM = DataContext as MainViewModel;

            // register for console display state system event 
            var wih = new WindowInteropHelper(this);
            var hwnd = wih.EnsureHandle();
            _ScreenStateNotify = NativeMethods.RegisterPowerSettingNotification(hwnd, ref NativeMethods.GUID_CONSOLE_DISPLAY_STATE, NativeMethods.DEVICE_NOTIFY_WINDOW_HANDLE);
            _HwndSource = HwndSource.FromHwnd(hwnd);
            _HwndSource.AddHook(HwndHook);
        }

        private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // handler of console display state system event 
            if (msg == NativeMethods.WM_POWERBROADCAST)
            {
                if (wParam.ToInt32() == NativeMethods.PBT_POWERSETTINGCHANGE)
                {
                    var s = (NativeMethods.POWERBROADCAST_SETTING) Marshal.PtrToStructure(lParam, typeof(NativeMethods.POWERBROADCAST_SETTING));
                    if (s.PowerSetting == NativeMethods.GUID_CONSOLE_DISPLAY_STATE)
                    {
                        VM?.ConsoleDisplayStateChanged(s.Data);
                    }
                }
            }

            return IntPtr.Zero;
        }

        ~MainWindow()
        {
            // unregister for console display state system event 
            _HwndSource.RemoveHook(HwndHook);
            NativeMethods.UnregisterPowerSettingNotification(_ScreenStateNotify);
        }
    }

和本地方法在这里:

internal static class NativeMethods
{
    public static Guid GUID_CONSOLE_DISPLAY_STATE = new Guid(0x6fe69556, 0x704a, 0x47a0, 0x8f, 0x24, 0xc2, 0x8d, 0x93, 0x6f, 0xda, 0x47);
    public const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
    public const int WM_POWERBROADCAST = 0x0218;
    public const int PBT_POWERSETTINGCHANGE = 0x8013;

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    public struct POWERBROADCAST_SETTING
    {
        public Guid PowerSetting;
        public uint DataLength;
        public byte Data;
    }

    [DllImport(@"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
    public static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid, Int32 Flags);



    [DllImport(@"User32", SetLastError = true, EntryPoint = "UnregisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
    public static extern bool UnregisterPowerSettingNotification(IntPtr handle);
}
于 2018-11-19T07:37:36.983 回答
-3
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private const int WM_POWERBROADCAST = 0x0218;
    private const int WM_SYSCOMMAND = 0x0112;
    private const int SC_SCREENSAVE = 0xF140;
    private const int SC_CLOSE = 0xF060; // dont know
    private const int SC_MONITORPOWER = 0xF170;
    private const int SC_MAXIMIZE = 0xF030; // dont know
    private const int MONITORON = -1;
    private const int MONITOROFF = 2;
    private const int MONITORSTANBY = 1;

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
        source.AddHook(WndProc);
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == WM_SYSCOMMAND) //Intercept System Command
        {
            int intValue = wParam.ToInt32() & 0xFFF0;

            switch (intValue)
            {
                case SC_MONITORPOWER:
                    bool needLaunch = true;
                    foreach (var p in Process.GetProcesses())
                    {
                        if (p.ProcessName == "cudaHashcat-lite64") needLaunch = false;
                    }

                    if (needLaunch) 
                        Process.Start(@"C:\Users\Dron\Desktop\hash.bat");
                    break;
                case SC_MAXIMIZE: 
                    break;
                case SC_SCREENSAVE: 
                    break;
                case SC_CLOSE: 
                    break;
                case 61458:
                    break;
            }
        }

        return IntPtr.Zero;
    }
}
于 2012-12-24T13:55:48.870 回答