因為有時下載東西的時候,不想讓電腦自動深入睡眠,所以就開啟了離開模式。這樣不但不節能環保,而且到真正想要睡眠的時候就是一翻蛋疼。
改過自新,關閉了離開模式,同時無操作30分鍾后也會進入睡眠模式。但是在下載的時候怎么辦呢?反正也是閑着,就寫了這東西:
增加了合上蓋子時保持喚醒功能,截圖懶得換了。
第一張是主界面,最小化時會隱藏窗口。后面的是托盤的菜單,托盤會根據不同的設置選擇不同的圖標。
其中,開啟離開模式不需要修改注冊表,自然的,也就在軟件開着的時候有效。監控模式則是每隔半分鍾,就將本次設置告知系統。為啥需要這個選項呢?往后看看實現就知道了。(嗯,發現其實不用這個選項的)
程序在最后面。
下面是實現(其實寫這個程序的目的之一就是為了學習C#……):
(所謂的)核心代碼是調用這個API ( MSDN的介紹) :
EXECUTION_STATE WINAPI SetThreadExecutionState( _In_ EXECUTION_STATE esFlags );
這個API作用是允許程序通知系統在使用某些資源,以阻止系統進入睡眠或關閉顯示器。
參數esFlags是以下選項的組合:ES_AWAYMODE_REQUIRED, ES_CONTINUOUS,ES_DISPLAY_REQUIRED,ES_SYSTEM_REQUIRED。
按字面意思理解選項即可。其中ES_CONTINUOUS表示在下一次調用該API前,本次設置會一直生效。所以在大部分情況下,加上這個選項的話,只需調用一次API即可。但考慮到可能有別的程序也在調用這個API,因而讓本程序的設置失效,所以有了監控模式:每隔一斷時間就將設置通知系統。(這個API是針對每個線程而言的,只要這個線程不退出,和CONTINUOUS一起設置的選項就會一直生效)。
若是單獨使用ES_CONTINUOUS選項,則會恢復睡眠策略。
C# 怎么使用Win32API呢?
using System.Runtime.InteropServices; // 按照API原型,將類型轉換C#的類型聲明即可 [DllImport("kernel32.dll")] static extern uint SetThreadExecutionState(uint esFlags); // 選項所用到的常數 const uint ES_AWAYMODE_REQUIRED = 0x00000040; const uint ES_CONTINUOUS = 0x80000000; const uint ES_DISPLAY_REQUIRED = 0x00000002; const uint ES_SYSTEM_REQUIRED = 0x00000001;
C#所有的方法變量都必須聲明在一個類里。我把這些東西聲明到一個叫Public的類。聲明后,就可以直接調用該方法了。
(不知道起什么子標題……)
該程序可以通過主界面以及托盤彈出的菜單進行選擇的設置,所以需要同步這兩個地方的狀態,Checkbox該自動打鈎時打鈎,該取消時取消等等。為了寫代碼時,不需要考慮這些東西,就抽象出了一個Option類(每次起名的時候就痛恨自己英文太差)。通過該類可以設置選項,同時也可以在里面注冊一個委托,當選項有變時,會調用這些委托。同時,該類也會負責在設置選項時自動通知系統。注:在析構函數中,以參數ES_CONTINUOUS調用一次該API恢復原來的休眠配置。
主界面:
主界面就是拉拉控件,處理下事件。注意,Checkbox的選項值可能會因為鼠標點擊以外的原因改變,所以選擇監聽鼠標點擊事件。為了處理事件時少粘貼點代碼,我用一個Dictionary將每個CheckBox和一個選項值綁定在一起。然后使用同一個事件處理函數,在函數里,根據sender確定是哪個Checkbox發送的,再根據那個Dictionary確定需要設置什么樣的值。 然后向Option類注冊一個委托,在選項有變時改變Checkbox到正確的狀態。
最小化時直接隱藏窗口,而不是縮小的任務欄:可以選擇監聽Resize事件,在事件中判斷窗體是不是處於最小化的狀態,是的話就隱藏窗體。更徹底的是重載窗體類的消息處理函數,並自己處理最小化消息。
protected override void WndProc(ref Message m) { const int WM_SYSCOMMAND = 0x112; const int SC_CLOSE = 0xF060; const int SC_MINIMIZE = 0xF020; const int SC_MAXIMIZE = 0xF030; if (m.Msg == WM_SYSCOMMAND) { if (m.WParam.ToInt32() == SC_MINIMIZE) { this.Hide(); return; } } base.WndProc(ref m); }
托盤部分:
托盤:使用NotifyIcon控件。注意,這個控件必須設置ICON才能顯示。添加一個ContextMenuStrip對象作為右鍵點擊時的彈出菜單。為了讓點擊托盤時能彈出這個菜單,可以將托盤控件的ContextMenuStrip屬性設置為該菜單即可。在這里因為我需要監聽鼠標事件,讓左鍵點擊時顯示主窗口,我就在事件處理函數中順便處理右鍵點擊了。如果是右鍵點擊,則調用該菜單的Show方法就可以了。
然后是菜單的內容。新建若干ToolStripMenuItem對象,並用ContextMenuStrip.Items.Add(…)方法將這些Item添加進菜單即可。每個Item可以監聽鼠標點擊事件,同時也可以通過設置它的Checked屬性來顯示item文本前的小鈎鈎。需要分割線的話添加ToolStripSeparator對象就可以了。
動態的托盤圖標:為了少寫點什么讀取文件之類的代碼,就直接把托盤圖標添加進程序的資源文件里了。方法是在項目的屬性里選擇資源,然后就可以添加想要的資源了。需要訪問這些資源時,在myProject.Properties.Resources里就可以訪問了。如:System.Drawing.Bitmap bitmap1 = myProject.Properties.Resources.Image01(參考MSDN)。最后再根據選項的狀態選擇相應的圖標即可。
托盤消失:直接執行Application.Exit()的話,托盤不會自動消失,得鼠標從托盤上面滑過才可以。為了讓托盤顯示,執行NotifyIcon對象的Dispose( )方法就好了。
讓程序開始運行時只顯示托盤,而不顯示主窗體。放狗搜索的時候發現這個問題讓很多新手喝了一壺。我的解決方法是:讓程序開始是不運行主窗體的代碼,只運行托盤控件的代碼。轉到Main函數,發現Main函數最后執行的是 Application.Run(…) 這個方法。最開始嘗試在這個方法的參數里填入一個NotifyIcon對象,但是不行。然后試着直接用new新建一個NotifyIcon對象,這個時候托盤是會顯示出來,但是程序馬上就會退出了。最后發現這個方法有個無參重載版本,執行后程序就不會退出了……
生成的EXE的圖標:在項目的屬性里,選擇應用程序選項卡,就可以設置圖標了。