|
在 阻止系統(tǒng)自動睡眠的小軟件,附C#制作過程 ,弄了一個防止系統(tǒng)睡眠的工具。然后馬上發(fā)現(xiàn),新的需求來了:為了保護環(huán)境(省錢),在系統(tǒng)設置中,合上蓋子時會自動睡眠。那因下載之類的原因,需要臨時禁止睡眠的話,又懶得去改設置,而且下次還得改回來。所以沒事也是折騰,就研究了怎么用軟件實現(xiàn)了。
最開始的思路就是進行Hook,以截斷睡眠消息。但是木有找到方法。 然后發(fā)現(xiàn)當系統(tǒng)進行睡眠時,會廣播一個消息,然后每個軟件會有兩秒鐘(xp和03可以長達20秒)的時間進行善后(PBT_APMSUSPEND event)。雖然可以喚醒睡眠的電腦(System Wake-up Events),但是還沒找到方法取消這次睡眠。 最后,我的解決方法時,臨時修改電源設置,即將合上蓋子的動作設置為啥事不干,然后在需要的時候恢復原來的設置。
Windows下電源管理,及配置工具powercfg Windows下電源管理方案是這樣的。最大的維度是電源配置方案,每套方案包含著一組電源設置??梢愿漠斍凹せ畹姆桨福部梢孕薷拿總€電源設置的值。 使用系統(tǒng)自帶工具powercfg進行電源配置的查看及更改:其中GUID值會在后面用到。
注意到這里:
電源方案GUID可能會因激活的方案不同而不同,而子組GUID和電源設置GUID在每個方案下都是一樣的。后面用這兩個ID進行設置就好。對了,每個設置都有直流和交流兩項,分別表示使用筆記本電源和外置電源的設置。 至此,省事的話差不多可以收工了:使用powercfg這個工具對電源方案進行設置就好了。 但是,為了折騰,我還是選擇了使用API對電源方案進行配置。 祭出要用到的API。
大致流程很簡單,首先獲取當前的設置,保存下來。然后對系統(tǒng)進行設置,使其合上蓋子時不采取任何操作。最后在需要的時候?qū)⒃瓉淼脑O置寫回。需要注意的一點是,在對當前激活的方案的設置進行修改時,需要調(diào)用 PowerSetActiveScheme 一次才能生效。
下面的問題,就變成了如何在C#里使用API了。 WinAPI基本只提供了C的接口,很多在C#中都沒有封裝,所以需要自己對相應的函數(shù)進行聲明。一個簡單的例子是下面這樣。
其中,最蛋疼的一點就是得自己進行參數(shù)的類型轉(zhuǎn)換。最最蛋疼的一點是,使用有些API得往參數(shù)里傳二級指針的時候根本就不知道該怎么辦。
基本數(shù)據(jù)類型參考這個表格就好了(網(wǎng)上抄的,而且需要注意的是,真的是僅供參考): 對于指針,參考這個博文(他也是轉(zhuǎn)的,沒去找原始出處了): C#調(diào)用Win32 API如何處理指針類型的參數(shù)
下面來兩個用到的具體例子。
在C#里聲明的時候長這樣了:
世界還是很簡單的,直到碰上了一個二級指針:
這東西目的是把一個指向GUID* 的變量p_GUID,的地址傳進去,然后他會new一個GUID作為結(jié)果,再然后會把p_GUID的值設為這個結(jié)果的地址。使用完畢之后,需調(diào)用LocalFree釋放這段內(nèi)存。 這下不能用ref 來省事了,所以就老老實實傳個IntPtr進去吧:
調(diào)用之后,p_ActivePolicyGuid就是一個指向GUID變量的指針了。由于使用了ref修飾,所以他本身是個一級指針。要怎么樣對他指向的內(nèi)容進行解釋呢?C#里有個Marshal:
世界稍微有點復雜,但還是能接受的。 直到…… 一個一個手工轉(zhuǎn)這也太不是個事了。 無意間看到這個網(wǎng)站,相見恨晚: http://www./ 別的碼農(nóng)們干完上面的活后,把成果分享在這上面,造福后人。呃,這東西在VS上還弄了個插件…… 只要輕按Insert……不過對API的實際用法不一樣,也會導致聲明的類型有所不同,自己了解一下轉(zhuǎn)換方法總是有好處的。
當運行軟件后,用戶又去系統(tǒng)里對電源設置進行更改,比如又把合上蓋子的動作改成睡眠的話,那就不好了。更可能發(fā)生的情況是,系統(tǒng)更改了當前激活的電源方案,比如從“節(jié)能”改成“高性能”,那合上蓋子的動作就很有可能改變了。所以我們需要對這個動作進行監(jiān)控。
這有個API(就是上面截圖里的那個)可以在修改制定選項時進行通知:
需要往第一個參數(shù)里傳入一個句柄。這個句柄可以有兩種類型,一是窗口句柄,另一種比較復雜,涉及到服務,覺得很麻煩,還不知道有沒比較簡便的方法。 這個時候就比較坑爹了,因為剛開始寫這個軟件的時候,主線程里只跑了一個NotifyIcon控件,這東西的handle是私有的,而且就算通過下面的hack拿到句柄,并注冊成功后,這個線程也收不到消息。hack代碼如下(抄這的: 來個更BT的NotifyIcon支持BalloonTip,還沒搞懂):
所以最后還是乖乖地弄了一個Form控件。這有一個問題:一個線程已經(jīng)有消息隊列了,我能不能在需要注冊窗體handle的地方,注冊線程的handle?
注冊之后怎么用呢? 一是重載窗體的消息處理函數(shù):
二是使用消息過濾: IMessageFilter 實現(xiàn)了這個接口后,就可以使用 Application.AddMessageFilter 方法添加消息過濾了。
|
|
|
來自: NaturalWill > 《C#》