c#当屏幕/显示器关闭或打开电源时如何获取事件?

时间:2010-02-05 16:20:41

标签: c# events screen monitor

您好我一直在搜索,但我找不到答案。我怎么知道屏幕何时关闭或打开。不是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服务。它在archos(便携式平板电脑)上运行。我希望当用户停止工作一段空闲时间时,连接立即关闭,当计算机从空闲状态返回时,他立即重新连接。来自汤姆的Application Idle on Code project的想法已经是一个好主意。功耗越小越好。启动必须尽可能快。

4 个答案:

答案 0 :(得分:8)

查看此博客here,它将帮助您完成您想要实现的目标。此外,你需要制作一个自定义事件来为你做这样的事情:

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并处理指示状态的消息监控......现在,在这一点上,我将对此发表意见。

我不是百分百确定是否可以这样做,或者Windows实际上发送的广播消息是“嘿!监视器将要睡觉'或'嘿!监视器正在加电',我担心,监视器实际上并没有向Windows发送一些软件信号,以告知它将进入休眠/关闭/打开状态。现在,如果有人有任何建议,提示,线索,请随时发表评论...

作为ScreenSaver标签的一部分,当您在任何地方右键单击桌面时,会出现一个弹出菜单,左键单击“属性”,出现“显示”对话框,可以看到能源之星软件标签页,左键单击'ScreenSaver',点击'Power'按钮作为'Monitor Power'分组框的一部分,对话框的那一部分,以某种方式触发Windows子系统(显卡?/能源之星驱动程序?)到发送硬件信号以打开显示器本身的省电功能......(全新的显示器默认情况下没有启用AFAIK ...随意忽略这个概念......)

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

对不起Manu,如果我无法进一步帮助.... :(

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

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

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

还有希望Manu :);)

答案 1 :(得分:3)

缺少的部分是我没有注册参加活动。

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

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;
}

答案 2 :(得分:0)

即使MainWindow隐藏了,这也对我有用。该代码基于上述内容和drop_duplicates的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);
}

答案 3 :(得分:-1)

/// <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;
    }
}