阻止系統自動睡眠的小軟件,附C#制作過程


因為有時下載東西的時候,不想讓電腦自動深入睡眠,所以就開啟了離開模式。這樣不但不節能環保,而且到真正想要睡眠的時候就是一翻蛋疼。

改過自新,關閉了離開模式,同時無操作30分鍾后也會進入睡眠模式。但是在下載的時候怎么辦呢?反正也是閑着,就寫了這東西:

增加了合上蓋子時保持喚醒功能,截圖懶得換了。

QQ20130831205319_thumbimage_thumbimage_thumb1image_thumb2

 

第一張是主界面,最小化時會隱藏窗口。后面的是托盤的菜單,托盤會根據不同的設置選擇不同的圖標。

其中,開啟離開模式不需要修改注冊表,自然的,也就在軟件開着的時候有效。監控模式則是每隔半分鍾,就將本次設置告知系統。為啥需要這個選項呢?往后看看實現就知道了。(嗯,發現其實不用這個選項的)

程序在最后面。

 


下面是實現(其實寫這個程序的目的之一就是為了學習C#……):

(所謂的)核心代碼是調用這個API ( MSDN的介紹) :

EXECUTION_STATE WINAPI SetThreadExecutionState(
  _In_  EXECUTION_STATE esFlags
);

這個API作用是允許程序通知系統在使用某些資源,以阻止系統進入睡眠或關閉顯示器。

參數esFlags是以下選項的組合:ES_AWAYMODE_REQUIREDES_CONTINUOUSES_DISPLAY_REQUIREDES_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的圖標:在項目的屬性里,選擇應用程序選項卡,就可以設置圖標了。


睡眠終結者: http://files.cnblogs.com/h46incon/SleepPreventer.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM