2010-02-05 48 views
12

嗨,我一直在尋找,但我找不到答案。我如何知道屏幕何時開啓或關閉。不是SystemEvents.PowerModeChanged。 我不知道如何來檢索顯示器/屏事件c#當屏幕/顯示屏關閉或打開時,如何獲取事件?

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 project的想法已經是一個好主意。功耗越低越好。啓動必須儘可能快。

回答

7

看看這個博客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)); 
    } 

} 

編輯:作爲馬努不知道如何檢索事件,該編輯將包括樣本代碼如何使用這個類如下所示。

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!"); 
     } 

    } 
} 

看着這個代碼,並意識到事情不太對之後,我明白了馬努一直在尋找一種方式來詢問系統檢測到顯示器的電源狀態是不可用的,但是,代碼以編程方式顯示了監視器可以打開/關閉/待機,同時觸發一個事件,但他希望它能夠掛鉤表單的WndProc並處理指示監視......現在,在這一點上,我要對此表達我的看法。

我不是100%確定是否可以這樣做,或者Windows實際上是否發送了一條廣播消息,說'嗨!監視器要睡覺'或'嘿!顯示器正在上電',我恐怕說,監視器實際上並沒有向Windows發送一些軟件信號來通知它將進入睡眠/關閉/開啓狀態。現在,如果任何人有一個建議,提示,關於它的線索,隨時發表您的評論...

能源之星軟件作爲ScreenSaver選項卡的一部分,當你右鍵單擊桌面任何地方,流行點擊'屬性',出現'顯示'對話框,帶有不同的標籤頁,左鍵點擊'屏幕保護程序',點擊'電源'按鈕作爲'顯示器電源'分組框的一部分,對話框的那一部分以某種方式觸發了Windows子系統(圖形卡?/能源之星驅動程序?)發送硬件信號來開啓監視器本身的省電功能......(全新的監視器沒有這可以通過默認的AFAIK啓用...隨時忽略這個概念...)

除非有一個非對象d API嵌入並埋藏在Energy-Power軟件驅動程序的深處(一個API確實觸發瞭如何點擊'Power'按鈕將該信號發送到監視器,其中Power模式確實被激活的結果! ),那麼也許可以通過在所述表單應用程序的後臺運行一個線程,通過輪詢來詢問,然後用未知的功能或API來檢查電源狀態 - 必須有一些只有微軟知道的東西......畢竟,能源之星向微軟展示瞭如何在監控器上觸發省電模式,它肯定不是單向的街道?還是它?

對不起馬努,如果我不能進一步幫助.... :(

編輯#2:我想過我剛纔在編輯撰寫並做了一些四處生根的答案和我想我想出了答案,但首先,我的腦海裏浮現出一個念頭,看到這個文檔here - 來自'terranovum.com'的pdf文檔,線索(或者我認爲...)在註冊表中,使用文檔最後一頁上的最後兩個註冊表項包含秒數的指定偏移量,並結合此文章找出空閒時間,可以很容易地確定何時顯示器進入待機狀態,聽起來很簡單,所以我想,馬努不會喜歡這個想法......

谷歌進一步調查使我得出這個結論,答案在於VESA BIOS規範DPMS(顯示器功率管理信令)的擴展,現在由此產生的問題,你是如何詢問VESA BIOS上的信號,現在很多現代圖形卡都安裝了VESA Bios,所以必須有一個硬件端口,在這裏你可以讀取引腳的值,使用這條路線將需要使用InpOut32或者如果您有64位Windows,則通過pinvoke有一個InpOut64。基本上,如果您可以回想起使用Turbo C或Turbo Pascal(對於DOS都是16位),則有一個稱爲inport/outport或類似的例程來讀取硬件端口,甚至使用peek/poke讀取GWBASIC。如果可以找到硬件端口的地址,那麼通過檢查水平同步和垂直同步,可以詢問這些值以確定監視器是處於待機/關機/暫停/開啓狀態,這是我認爲是更可靠的解決方案。 ..

道歉長的答案,但覺得我寫下我的想法....

目前仍然希望有馬努:);)

+1

不是真的,你怎麼找回事件。如何知道它是通電還是斷電? - 10分鐘後,我的屏幕變暗。 - 15分鐘後我的屏幕熄滅。 - 我移動鼠標,屏幕變爲ON。 如何獲取此事件? (不是會話事件) – Manu 2010-02-06 06:31:43

+0

@Manu:讓我編輯這個答案,包括如何檢索事件的樣本.... – t0mm13b 2010-02-06 18:13:54

+0

哇,interestin。我沒想到它變得如此複雜。我不是專家,當然不是註冊管理機構。所以我有點害怕觸及那個。 「應用程序空閒」已經是一個好主意。 – Manu 2010-02-07 17:14:48

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

請在您的代碼中添加一些註釋(爲什麼此代碼可解決所提出的問題)。沒有解釋,這不是一個答案。 – Artemix 2012-12-24 14:17:24

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

您提供的鏈接現在已被破壞,並且您的示例代碼缺少第一行的值。 僅適用於將來可能遇到此問題的任何人:對於GUID_MONITOR_POWER_ON值(02731015-4510-4526-99e6-e5a17ebd1aea),我使用了[此鏈接](https://msdn.microsoft.com/en-us/ library/windows/desktop/hh448380(v = vs.85).aspx)作爲參考,0代表DEVICE_NOTIFY_WINDOW_HANDLE的值。 – 2017-04-23 13:19:21

相關問題