c# 如何在屏幕/显示器关闭或打开电源时获取事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2208595/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
c# How to get the events when the screen/display goes to power OFF or ON?
提问by Manu
Hi I have been searching but I can't find the answer. How do I know when the screen is going off or on. Not the SystemEvents.PowerModeChanged . I dont know how to retrieve the display/screen EVENTS
嗨,我一直在寻找,但我找不到答案。我如何知道屏幕何时关闭或开启。不是 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;
}
EDIT
编辑
Perhaps I can explain my intension, so there is perhaps a better solution. I have a Dual binding WCF service running on. It's running on an archos (portable tablet pc). I want that when the user stopped working for an idle time, the connection closes immediatly, and when the computer is returning from idle, he reconnects immediatly. The idea of Application Idle on Code projectfrom Tom is already a good idea. The less power consumption , the better. The startup must be as fast as possible.
也许我可以解释我的意图,所以也许有更好的解决方案。我有一个双绑定 WCF 服务正在运行。它在archos(便携式平板电脑)上运行。我希望当用户停止工作一段时间后,连接立即关闭,当计算机从空闲状态返回时,他立即重新连接。Tom的Application Idle on Code 项目的想法已经是一个好主意。功耗越小越好。启动必须尽可能快。
采纳答案by t0mm13b
Have a look at this blog herewhich will help you do what you are trying to achieve. In addition you need to make a custom event to do this for you something like this:
在此处查看此博客,它将帮助您完成您想要实现的目标。此外,您需要创建一个自定义事件来为您执行以下操作:
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));
}
}
Edit:As Manuwas not sure how to retrieve the events, this edit will include a sample code on how to use this class as shown below.
编辑:由于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!");
}
}
}
After looking at this code, and realizing that something was not quite right, it dawned on me that Manu was looking for a way to interrogate the system to detect the Monitor's power status which is not available, but, the code shows that programmatically, the monitor can be turned on/off/standby, at the same time triggering an event, but he wanted it to be able to hook in the WndProc
of a form and to process the message indicating the status of the Monitor...now, at this point, I am going to express my opinion on this.
在查看此代码并意识到有些地方不太对劲后,我意识到 Manu 正在寻找一种方法来询问系统以检测不可用的监视器的电源状态,但是,代码显示,以编程方式,监视器可以打开/关闭/待机,同时触发一个事件,但他希望它能够挂钩WndProc
表单并处理指示监视器状态的消息......现在,在这个对此,我要发表一下我的看法。
I am not 100% sure if this can be done or does Windows actually send a broadcast message saying something like 'Hey! Monitor is going to sleep' or 'Hey! Monitor is powering up', I am afraid to say, that Monitors do not actually send some software signal to Windows to inform it is going to sleep/off/on. Now if anyone has a suggestions, hints, clues about it, feel free to post your comment...
我不是 100% 确定这是否可以完成,或者 Windows 是否真的发送了一条广播消息,说“嘿!显示器要睡觉了”或“嘿!监视器正在通电',我不敢说,监视器实际上并没有向 Windows 发送一些软件信号来通知它要休眠/关闭/打开。现在,如果有人对此有任何建议、提示、线索,请随时发表您的评论...
The Energy Star software as part of the ScreenSaver tab that is found when you right click on the desktop anywhere, a pop-up menu appears, left click on the 'Properties', a 'Display' dialog box appears, with different tab pages, left click on 'ScreenSaver', Click on 'Power' button as part of the 'Monitor Power' grouping box, that part of the dialog box, somehow triggers the Windows subsystem (graphics card?/Energy Star driver?) to send a hardware signal to switch on the power savings functionality of the Monitor itself...(Monitors that are brand new do not have this enabled by default AFAIK...feel free to dismiss this notion...)
能源之星软件作为屏幕保护程序选项卡的一部分,当您右键单击桌面任意位置时,会出现一个弹出菜单,左键单击“属性”,出现“显示”对话框,其中包含不同的选项卡页面,左键单击“屏幕保护程序”,单击“电源”按钮作为“监视器电源”分组框的一部分,对话框的那部分以某种方式触发 Windows 子系统(显卡?/能源之星驱动程序?)发送硬件信号来打开显示器本身的省电功能......(全新的显示器默认情况下没有启用此功能 AFAIK ......请随意忽略这个概念......)
Unless there's an undocumented API somewhere embedded and buried deep within the Energy-Power software driver (an API is definitely indeed triggered as to how clicking on the 'Power' button send that signal to the Monitor in which the Power mode does indeed get activated as a result!) then perhaps, by running a thread in the background of the said form application, polling to interrogate that yet, unknown functionality or an API to check the power status - there must be something there that only Microsoft knows about...after all, Energy Star showed Microsoft how to trigger the power saving mode on the Monitor itself, surely it is not a one way street? or is it?
除非在 Energy-Power 软件驱动程序中嵌入并深埋在某个地方有一个未记录的 API(一个 API 确实被触发,关于如何点击“电源”按钮将该信号发送到监视器,其中电源模式确实被激活为结果!)然后也许,通过在所述表单应用程序的后台运行一个线程,轮询以询问未知的功能或用于检查电源状态的 API - 那里必须有一些只有 Microsoft 知道的东西......毕竟,能源之星向微软展示了如何触发显示器本身的省电模式,这不是单向街道吗?或者是吗?
Sorry Manu if I could not help further .... :(
对不起 Manu,如果我不能帮助进一步.... :(
Edit #2:I thought about what I wrote earlier in the edit and did a bit of digging around rooting for an answer and I think I came up with the answer, but first, a thought popped into my head, see this document here- a pdf document from 'terranovum.com', the clue (or so I thought...) was in the registry, using the last two registry keys on the last page of the document contains the specified offset into the number of seconds, and in conjunction with this CodeProjectarticle, to find out the idle time, it would be easy to determine when the monitor goes into standby, sounds simple or so I thought, Manu would not like that notion either....
编辑#2:我想到了我之前在编辑中所写的内容,并在寻找答案时进行了一些挖掘,我想我想出了答案,但首先,一个想法出现在我脑海中,请在此处查看此文档-来自'terranovum.com'的pdf文档,线索(或者我认为......)在注册表中,使用文档最后一页上的最后两个注册表项包含指定的偏移量到秒数,和结合这篇CodeProject文章,找出空闲时间,很容易确定显示器何时进入待机状态,听起来很简单,我想,Manu 也不会喜欢这个概念......
Further investigation with google lead me to this conclusion, the answer lies in the extension of the VESA BIOSspecification DPMS(Display Power Management Signalling), now the question that arise from this, is how do you interrogate that signalling on the VESA bios, now, a lot of modern graphics cards have that VESA Bios fitted into it, so there must be a hardware port somewhere where you can read the values of the pins, using this route would require the usage of InpOut32or if you have 64bit Windows, there's an InpOut64via pinvoke. Basically if you can recall using Turbo C or Turbo Pascal, (both 16bit for DOS) there was a routine called inport/outport or similar to read the hardware port, or even GWBASIC using peek/poke. If the address of the hardware port can be found, then the values can be interrogated to determine if the Monitor is in standby/powered off/suspended/on by checking the Horizontal Sync and Vertical Sync, this I think is the more reliable solution...
与谷歌的进一步调查使我得出这个结论,答案在于VESA BIOS规范DPMS(显示电源管理信号)的扩展,现在由此产生的问题是,您如何在 VESA BIOS 上查询该信号,现在,许多现代显卡都安装了 VESA Bios,因此必须有一个硬件端口,您可以在某个地方读取引脚的值,使用此路由需要使用InpOut32或者如果您有 64 位 Windows,则有一个InpOut64通过 pinvoke。基本上,如果您能回忆起使用 Turbo C 或 Turbo Pascal(DOS 均为 16 位),则有一个名为 inport/outport 或类似的程序来读取硬件端口,甚至使用 peek/poke 读取 GWBASIC。如果可以找到硬件端口的地址,那么可以通过检查水平同步和垂直同步来查询这些值以确定监视器是否处于待机/关机/挂起/开机状态,我认为这是更可靠的解决方案。 ..
Apologies for the long answer but felt I had to write down my thoughts....
为冗长的答案道歉,但我觉得我必须写下我的想法......
There's still hope there Manu :) ;)
Manu 还是有希望的 :) ;)
回答by targitaj
/// <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;
}
}
回答by Emmanuel
The missing part was that I didn't register for the events.
缺少的部分是我没有注册这些活动。
Found that there's a power management example from Microsoft:
发现微软有一个电源管理的例子:
http://www.microsoft.com/en-us/download/details.aspx?id=4234
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;
}
回答by PetrCZ
This works for me even MainWindow is hidden. The code is based on above post, and C++ code of https://www.codeproject.com/Articles/1193099/Determining-the-Monitors-On-Off-sleep-Status.
这对我有用,即使 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);
}
}
And Native methods here:
和这里的本地方法:
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);
}