Windows CE的電源管理之一
Windows CE的基本電源管理功能
在所有版本的Windows CE操作系統中,圖形、視窗和事件子系統(GWES)在電源管理方面都發揮了關鍵作用。這是因為早期版本的電源管理功能是由用戶的活動所驅動的,而GWES負責處理所有用戶的輸入,如鍵盤、鼠標和觸摸屏。GWES設置定時器監控用戶的活動,當一段時間內用戶沒有任何輸入時,便使系統進入休眠狀態。通過注冊表可以設置這幾個定時器的超時值,它們可以分別被用于電池供電或外部電源供電時。當然,通過注冊表也可以禁用GWES的電源管理功能,它在Windows CE.NET以后的版本中是默認被禁用的,這有利于電源管理器的集中管理。
圖1 Windows CE基本的電源轉換流程
No Power | 既沒有電池也沒有外部電源供電. |
On | 所有設備上電的常規運行狀態. |
Suspend | 休眠狀態,這時大部分設備關閉,僅RAM(自刷新)和外部時鐘運行. |
Idle | 空閑狀態,這時可停止CPU的運行. |
Critical off | 電池電壓過低的狀態. |
Power-on reset | 系統清空RAM并初始化文件系統. |
Cold boot | First application of power, for example, when a backup battery is installed. |
Warm boot | 軟啟(熱啟動),清空RAM并返回運行(On)狀態. |
On-to-Idle | 從全速運行狀態到空閑狀態的轉換. |
Idle-to-On | 處理器從低功耗狀態回到全速運行狀態. |
On-to-Suspend | 由于某些事件的觸發,處理器轉換到停止運行狀態。并調用設備驅動函數 XXX_PowerDown. |
Suspend-to-On | 由特定的喚醒事件觸發,處理器從停止狀態返回到全速運行態。并調用設備驅動函數XXX_PowerUp. |
On-to-Critical off | 當電池電壓過低時轉換到Critical off狀態. |
上圖是Windows CE系統基本的電源狀態轉換策略,對應有5種系統電源狀態(等級):No Power, On, Suspend, Idle, Critical off。相關描述和轉換方式參見上表。
基本的電源管理功能所采用的節能方法是使系統適時的進入休眠狀態,當下面的一種事件發生時,系統將進入休眠狀態(
SUSPEND
):
l
用戶按下
On/Off
按鈕;
l
監控用戶活動的定時器超時;
l
應用程序調用
API
,如
GwesPowerOffSystem
或
SetSystemPowerState
。
當下面的一種事件發生時,系統將退出休眠狀態:
l
用戶再次按下
On/Off
按鈕;
l
發生某個警告事件,如某個日期或時間定時器的到時提醒;
l
發生某個喚醒事件,由外設如串口設備或者網卡觸發中斷來喚醒系統。
雖然通過用戶操作、應用程序或者外設都可以使系統進入或者退出休眠狀態,但基本的電源管理功能所能控制的粒度過大,對應于 CPU 只有三種狀態: On , Idle 和 Suspend ,對應于所有外設只有兩種狀態: On 和 Suspend 。而且,當系統進出休眠狀態時,應用程序都得不到任何通知。
Windows CE 的高級電源管理功能
加入了電源管理組件的 Windows CE 具有高級的電源管理功能,它允許每個外設具有自己的電源狀態,有別于一般的系統電源狀態( System Power State ),被稱作設備電源狀態( Device Power State )。現在應用程序有能力設置個別外設的電源狀態,比如一個文件傳輸程序,在保持串口或者藍牙端口正常通訊時,可以關閉顯示屏幕和背光。這就為實現更高級別的動態電源管理提供了可能。
我們可以通過注冊表任意設定一組系統電源狀態,使其對應于我們設計的狀態模型。對于設備電源狀態則沒有這么大的靈活度,它具有 5 個設備狀態:
D0 : Full on ; D1 : Low on ; D2 : Standby ; D3 : Sleep ; D4 : Off
當定義好系統電源狀態,并為每個外設分配了設備電源狀態后,通過注冊表,我們可以將兩者進行映射。在某個系統電源狀態下,比如一個電池供電的系統,當電池電量已經少于 50% 時,顯示屏幕和背光可能處于 D1 狀態,而網絡設備可以設置為 D3 狀態。也就是說,在同一時刻,不同的外設可能處于不同的設備電源狀態中。這樣的靈活性意味著每個設備可以最小程度的消耗電池資源。
如圖
2
所示,電源管理器實現為一個名為
Pm.dll
的動態鏈接庫,電源管理接口分為應用程序和驅動程序兩部分。驅動程序通過
DeviceIoControl
服務例程來處理電源管理器發來的設備電源狀態改變請求,另外電源管理器通過消息隊列通知應用程序電源相關的事件。
為了獲得高級電源控制的功能,必須通過Platform Builder將電源管理組件編譯到內核鏡像中。實現的源代碼參見{WINCEROOT}\Private\Winceos\Coreos\Device\PMIF\pmif.c,這段代碼只是一個封裝,它會調用{WINCEROOT}\Public\Common\Oak\Drivers\PM中的組件。參考這個實現OEM可以根據需要設計自己的電源管理策略。
下一篇將分三個部分來解讀WinCE的電源管理,首先從系統平臺開發的角度,這部分一般由OEM廠商負責實現,然后介紹電源管理的接口,分別從設備驅動和應用程序的角度介紹如何應用電源管理。
Windows CE的電源管理之二
OEM Adaptation Layer ( OAL )是一層與硬件平臺相關的代碼,它在電源狀態轉換中扮演著重要的角色。首先必須實現的是以下幾個與硬件相關的函數:
OEMInit :初次上電時(或在冷啟后)被調用,一般在這個函數中處理一些重要的初始化工作,如初始化系統內存,建立調試環境,設置系統中斷等;
OEMIdle :沒有線程可調度運行時被內核調用,這時可將 CPU 置于低功耗狀態,并保證其可以快速返回運行狀態;
OEMPowerOff :系統進入休眠( Suspend )狀態前被調用,它即是系統進入休眠狀態前被調用的最后一個函數,也是在系統被喚醒后所執行的第一個函數(中斷處理程序以外);
Interrupt Handler :一些用來喚醒系統的中斷處理程序。當中斷發生時,內核首先調用中斷處理程序,其中一些中斷是可以將系統從睡眠狀態中喚醒的,但中斷時間內能處理的事情非常少,在中斷處理程序里大部分 API 也是不能被調用的。
高級的電源管理功能允許任何設備驅動將其中斷作為系統的喚醒源,在版本 Windows CE.NET 4.1 以前, OAL 掌管著所有的中斷處理,這就意味著在生成內核鏡像以后,我們就無法在系統中添加新的中斷處理程序了。現在即使這個設備是在運行時才安裝到系統中來的,比如 PCMCIA 或者 CF 接口的無線網卡,它可以通過調用 API ( LoadIntChainHandler 和 FreeIntChainHandler )實現中斷處理程序的安裝和卸載。在系統中添加了新的中斷處理程序后,它就可以通過內核 IOCTL 代碼( IOCTL_HAL_ENABLE_WAKE )實現其作為系統的喚醒源。
位于操作系統內核層的電源管理策略,也要為設備驅動及上層應用程序提供接口,通過 PowerPolicyNotify 向電源管理組件發送事件請求,設備及應用程序就可以參與到系統的電源管理策略中來。
2. 設備驅動的電源管理
我們可以為設備驅動添加電源管理功能,首先就是要在驅動程序中添加電源管理的 IOCTL 代碼,然后通知電源管理器該驅動是支持電源管理的,最后在驅動中編碼實現該設備支持的幾種電源狀態。
2.1 建立支持電源管理的設備驅動
為了建立一個能夠對設備進行電源管理的驅動程序,我們必須首先建立一個支持
non-COM-related
設備接口的驅動程序。
non-COM-related
設備接口標明這個設備是支持電源管理的。可以用以下方式建立這種接口:
l
可以在注冊表中,用激活設備所用的
IClass
值定義接口;
l
可以在驅動程序的
Init
函數中,設置注冊表中的
IClass
值;
l
可以使用
ActivateDeviceEx
的參數
REGINI
設置
IClass
值;
l
可以在驅動程序中顯示地調用
AdvertiseInterface
函數。
電源管理器通過
IOCTL
代碼來和驅動通信。通常情況下,當一個驅動程序聲明為支持電源管理時,驅動只需要在
DeviceIoControl
中實現電源的管理即可。下面是電源管理器用來與驅動通信的
IOCTL
代碼:
l
IOCTL_POWER_CAPABILITIES
:代表電源管理器請求設備驅動返回設備支持的電源狀態及相關特征;
l
IOCTL_POWER_SET
:請求驅動更新設備的電源狀態;
l
IOCTL_POWER_QUERY
:電源管理器詢問設備是否準備好進行狀態切換;
l
IOCTL_POWER_GET
:請求驅動返回當前設備的電源狀態;
l
IOCTL_REGISTER_POWER_RELATIONSHIP
:通知父設備注冊所有它所控制的設備。
其中 IOCTL_POWER_CAPABILITIES 和 IOCTL_POWER_SET 是支持電源管理的設備驅動必須實現的。
2.2 IOCTL 代碼的實現
在設備自舉的時候,設備驅動必須使設備處于
D0
狀態,當電源管理器通過
IOCTL_POWER_CAPABILITIES
向設備發出查詢時,設備驅動應該盡可能詳細的報告該設備的電源管理能力,以便將自己納入到系統的電源管理策略中去。在自舉完成后,電源管理器可以根據管理策略,調用
IOCTL_POWER_SET
調整設備的電源狀態。在設備自我管理電源的情況下,設備應該通過
DevicePowerNotify
函數請求系統改變它們的電源狀態。在實現對
IOCTL_POWER_SET
支持時,設備驅動開發應該注意以下幾點:
l
設備并不一定具備所有五種設備電源狀態,但至少可以工作在
D0
狀態;
l
電源管理器可能會要求設備進入任何設備電源狀態,并不僅僅是設備聲明支持的幾個。
l
如果一個設備被要求進入一個它并不支持的電源狀態,它就會進入另一個它支持的更高功耗的狀態。例如,一個設備并不支持
D2
,它會被要求進入
D1
。
l
電源管理器可能會通過發出
IOCTL_POWER_SET
,使設備再次進入它已經處于的當前狀態。在這種情況下,設備驅動程序簡單的返回成功即可。
l
設備的電源狀態不一定與系統的電源狀態同步,因為它可能受到應用程序需求的限制。
2.3 休眠和喚醒的處理
支持電源管理的流設備驅動通過 XXX_PowerDown 和 XXX_PowerUp 接收系統休眠和喚醒的通知,這些通知在內核調用 OEMPowerOff 之前發出,并處于中斷上下文中。
設備驅動的開發者必須清楚,在系統休眠期間,設備應該處于何種狀態。并不是所有的設備在這個時候都應該強制被關閉,比如在音頻設備可以不依賴處理器來播放音樂時,在系統休眠期間它的狀態就應該交給電源管理器和應用程序來處理。而如果這個音頻設備的播放需要處理器頻繁的工作,在系統進入休眠狀態時,驅動程序應該果斷的關閉設備的電源,即使該設備由于應用程序的請求不處于 D4 狀態。
另外,設備的
D3
狀態值得特殊考慮,因為這個狀態不僅僅與設備的功耗級別相關,處于
D3
狀態的設備還被允許將系統從休眠狀態中喚醒。所以當我們開發支持電源管理的設備驅動時需要注意以下幾點:
l
支持喚醒系統的設備不應該主動通過
DevicePowerNotify
請求進入
D3
狀態,因為一般情況下,只有當系統打算進入休眠狀態時,電源管理器才將設備作為喚醒源啟用。從代碼的角度也應該避免這一點,因為驅動程序無法區分來自
IOCTL_POWER_SET
中對
D3
狀態的請求是由它自己還是電源管理器發起的;
l
如果有必要,能喚醒系統的設備可以定義一樣的
D2
和
D3
狀態,而只有
D3
具有啟動喚醒的功能;
l
支持
D3
的設備不一定具有將系統從休眠狀態喚醒的功能,不能將系統喚醒的設備,但它又具有低功耗模式,是可以自己主動請求進入
D3
狀態的;
l
如果不能喚醒系統的設備處于
D3
狀態,在系統進入休眠狀態時,它應該在
XXX_PowerDown
中將狀態轉換為
D4
,并且在
XXX_PowerUp
中恢復到
D3
狀態;如果無法這么做,它就不應該支持
D3
狀態,而是在請求進入
D3
時直接進入
D4
狀態;
做到了以上幾點,在系統進入休眠狀態時, OEM 和應用程序開發人員就可以請求將設備進入 D3 狀態,而不必關心這個設備是否支持喚醒系統。
3. 應用程序的電源管理
電源管理組件提供了一組接口供應用程序參與到電源管理的活動中,應用程序可以通過 RequestPowerNotifications 函數請求電源管理器向其發送電源相關的通知,也可以通過 SetPowerRequirement 通知電源管理器將設備設置在特殊的電源狀態下。這樣,指定設備的電源狀態就不會隨系統電源狀態的改變而改變。
3.1 電源通知機制
電源相關的通知通過消息隊列傳遞給應用程序,通常應用程序新建一個消息隊列,并通過
RequestPowerNotifications
將這個消息隊列的句柄傳遞給電源管理器,同時創建一個線程偵聽來自這個隊列的消息。電源管理器定義了如下幾種通知:
l
PBT_RESUME
:當系統從休眠狀態被喚醒是產生;
l
PBT_POWERSTATUSCHANGE
:當系統接入或者斷開外部電源時產生;
l
PBT_TRANSITION
:當電源管理器執行系統電源狀態轉換時發生;
l
PBT_POWERINFOCHANGE
:當電池信息更新時發生。
3.2 電源請求機制
電源請求機制為應用程序提供了強大的能力控制電源管理器調整設備的電源等級,與其他所有的電源設置相比,它具有很高的優先級。舉例來說,假設有一個條形碼閱讀器連接在 COM1 端口,并且 COM1 只有在最高電源等級( D0 )時才能驅動這個條形碼閱讀器。為了使其正常工作,應用程序將調用 SetPowerRequirement 把 COM1 指定 D0 狀態。假設之后串口驅動自身決定降低一個電源等級,驅動調用 DevicePowerNotify 通知電源管理器它期望的設備電源狀態,驅動程序的這個請求將不起作用,直到應用程序調用 ReleasePowerRequirement 為止。繼續這個例子,假設這時的系統電源狀態轉換為低能耗等級,雖然與之相關的 COM1 電源等級為 D3 ,由于應用程序的電源請求, COM1 將繼續維持在 D0 狀態。
在調用 SetPowerRequirement 函數時,指定 POWER_FORCE 標志將強制設備不進入休眠狀態,即使這時系統已處于休眠狀態。
3.3 設置系統電源狀態
在某些應用的場合下,應用程序可能需要改變系統的電源狀態。 OEM 通過注冊表定義了系統支持的電源狀態, 應用程序可以通過 GetSystemPowerState 返回當前系統電源狀態的名稱,也可以通過 SetSystemPowerState 改變系統的電源狀態。
Windows CE的電源管理之三
本篇將以Windows Mobile為例介紹Windows CE電源管理的實現,大體上,Windows Mobile分為Pocket PC和Smartphone兩種版本。這兩者之間的主要區別在于觸摸屏和電源模型,Smartphone采用的是“Always On”模型。為了說清楚它們的區別,我們就先從系統電源狀態說起吧(這里有些系統電源狀態是從WM5開始才有的)。
1. Windows Mobile的系統電源狀態
- On:用戶與系統交互時的狀態;
- BacklightOff:在一段時間內(默認15秒),如果一直沒有用戶操作(比如按下某個鍵或者觸摸屏幕),就關閉背光,這時其他的設備都沒變化。這個timeout值可以通過控制面板進行設置;
- UserIdle:這個狀態只在Smartphone中被使用。經過一段稍長的時間,如果一直沒有用戶操作,就關閉背光和LCD。這個timeout值可以通過控制面板進行設置;
- ScreenOff:一般由某些程序指定,才進入這個狀態。比如音樂播放器程序,當你聽音樂時按下某個鍵可以將屏幕關閉。PocketPC和Smartphone都使用這個狀態,它與UserIdle的不同在于,ScreenOff意味著“用戶主動關閉了顯示,只有當他按下電源鍵時才重新顯示”,而UserIdle意味著“用戶有段時間沒操作了,那么我們可以關閉屏幕來省電”,所以在UserIdle時,隨便按下Smartphone的哪個鍵都會啟動顯示;
- Suspend:這是PocketPC的睡眠模式,幾乎所有設備都被關閉,直到某個硬件設備觸發中斷才將系統喚醒,這個timeout值可以通過控制面板進行設置(默認為3分鐘);
- Resuming:這是PocketPC被喚醒后的狀態,這時屏幕是關閉的,并啟動一個15秒的計時器,在這段時間內決定接下來進入哪個狀態,如果計時器超時則重新回到睡眠狀態;
- Unattended:這個狀態只在PocketPC中被使用,用戶對其不會有所察覺。有些程序,如ActiveSync每5分鐘會喚醒系統進行同步,同步完成后再讓系統繼續睡眠,這段時間不希望打擾用戶,即程序在后臺執行。
可以通過注冊表查看系統電源狀態對應的具體設備的電源狀態,[HLM\System\CurrentControlSet\Control\Power\State]。
現在我們知道,Smartphone沒有真正的睡眠模式,即使它會在一段時間后關閉背光和屏幕,但它并沒有睡著,只是休息一下眼睛罷了,它的大腦和四肢仍在正常工作。PocketPC所采用的模型比Smartphone要復雜的多,你可以按下電源鍵讓系統睡眠,在必要時,也可以喚醒系統做一些工作然后再繼續睡眠。如果你在Smartphone上運行一個桌面精靈之類的程序,她為了引起你的注意,長時間的蹦啊跳啊,不管白天還是黑夜,可想而知,你的待機時間將......
你可能會覺得PocketPC的“Sleep”模型比Smartphone的“Always On”模型要省電,其實恰恰相反。因為在系統睡眠的過程中,它需要通知所有的設備驅動,為了讓它們保存一些重要的信息并關閉相應的硬件設備,在系統被喚醒時也需要通知它們恢復先前的工作。這個過程不僅耗時還可能會耗更多的電,因為一些設備在頻繁的狀態轉換過程中會消耗比較多的能量。這也就是為什么當你收到一條短信時,睡眠狀態的PocketPC要花3到6秒的時間來處理,而Smartphone只需要幾個微秒:)
2. Windows Mobile的電源管理策略
我們可以用系統電源狀態機來簡單的描述Windows Mobile的電源管理策略,以PocketPC為例,系統電源狀態機如下圖所示:
系統內部的電源管理器負責協調電源狀態的轉換,電源狀態的轉換主要由一下幾種方式觸發:
- 計時器超時:SuspendTimeout和ResumingSuspendTimeout,分別對應于第一節介紹Suspend和Resuming狀態時所提到的計時器。細說起來,它們每個又有兩個值,分別對應著電源供電時和電池供電時的超時值,也就是注冊表[HLM\System\CurrentControlSet\Control\Power\Timeout]中的ACSuspendTimeout、BattSuspendTimeout、ACResumingSuspendTimeout、BattResumingSuspendTimeout;
- 系統調用:驅動程序或應用程序通過相應的API,請求進入某種電源狀態。這類API在前面的文章中已經有所介紹,如SetSystemPowerState、SetPowerRequirement、DevicePowerNotify等;
- 平臺相關的系統調用:通過PowerPolicyNotify通知電源管理器發生了某個事件,它的實現比較靈活,驅動程序或應用程序可以通過相應的參數與電源管理器進行交互,比如PPN_POWERCHANGE、PPN_SUSPENDKEYPRESSED、PPN_UNATTENDEDMODE等,參見"pmpolicy.h";
- 直接訪問內核對象:事件(Event)作為Windows CE系統的內核對象,可以通過事件名稱在進程間共享,因此我們可以訪問電源管理器中的兩個事件,它們的名字分別是_T("PowerManager/ReloadActivityTimeouts")、_T("PowerManager/SystemIdleTimerReset")。如果你的程序需要動態修改那幾個計時器的時間長度,可以通過第一個事件通知電源管理器重新讀取注冊表中計時器的值,而第二個事件與SystemIdleTimerReset功能一樣,可以阻止系統進入睡眠狀態。
3. Windows Mobile電源管理相關API的應用
最后,通過幾個應用場景簡單介紹一下常用的電源管理相關的API的使用:
- 如果你在設計的是媒體播放器程序,不希望在播放電影時,系統自動轉入Suspend狀態,這時可以每隔30秒調用一次SystemIdleTimerReset,它會幫你重置那個計時器;如果你還想同時保持背光,那么可以調用SetPowerRequirement(TEXT("BKL1:"), D0, POWER_NAME, NULL, 0);如果你提供一個按鈕允許用戶關閉屏幕,那么調用SetSystemPowerState(NULL, POWER_STATE_IDLE, 0);
- 如果你在設計的是天氣預報程序,需要每天早上6點在線更新天氣信息,這時可以調用CeRunAppAtTime,系統到時會被RTC中斷喚醒,還記得前面提到的那個15秒的計時器嗎,這時你的程序應該在15秒內請求進入Unattended狀態,否則系統將重新回到睡眠狀態。在處理更新的過程中,還是應該每隔30秒調用一次SystemIdleTimerReset,在處理完更新后,應該再次調用CeRunAppAtTime,并放棄Unattended狀態。請注意,在電源管理器的實現代碼中,用了一個引用計數的變量(gdwUnattendedModeRequests)統計所有對Unattended狀態的請求,所以PowerPolicyNotify(PPN_UNATTENDEDMODE, TRUE);和PowerPolicyNotify(PPN_UNATTENDEDMODE, FALSE);要成對出現,否則系統將無法回到睡眠狀態。
-
如果你要開發一個監控電池狀態的程序,首先應該創建一個接收狀態通知的線程,在這個線程里調用RequestPowerNotifications,這個函數的第一個參數是一個消息隊列的句柄,所以必須先創建一個消息隊列(CreateMsgQueue),第二個參數是你希望得到的通知類型,這里要用到的是PBT_POWERSTATUSCHANGE|PBT_POWERINFOCHANGE,然后線程就可以等待通知了(WaitForSingleObject),一旦有通知到來,線程通過ReadMsgQueue讀取消息的內容,再做些更新UI的工作。
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->// ***************************************************************************
// Function Name: PowerNotificationThread
//
// Purpose: listens for power change notifications
//
DWORD PowerNotificationThread(LPVOID pVoid)
{
// size of a POWER_BROADCAST message
DWORD cbPowerMsgSize = sizeof POWER_BROADCAST + (MAX_PATH * sizeof TCHAR);
// Initialize our MSGQUEUEOPTIONS structure
MSGQUEUEOPTIONS mqo;
mqo.dwSize = sizeof (MSGQUEUEOPTIONS);
mqo.dwFlags = MSGQUEUE_NOPRECOMMIT;
mqo.dwMaxMessages = 4 ;
mqo.cbMaxMessage = cbPowerMsgSize;
mqo.bReadAccess = TRUE;
// Create a message queue to receive power notifications
HANDLE hPowerMsgQ = CreateMsgQueue(NULL, & mqo);
if (NULL == hPowerMsgQ)
{
RETAILMSG( 1 , (L " CreateMsgQueue failed: %x\n " , GetLastError()));
goto Error;
}
// Request power notifications
HANDLE hPowerNotifications = RequestPowerNotifications(hPowerMsgQ, PBT_POWERSTATUSCHANGE | PBT_POWERINFOCHANGE);
if (NULL == hPowerNotifications)
{
RETAILMSG( 1 , (L " RequestPowerNotifications failed: %x\n " , GetLastError()));
goto Error;
}
HANDLE rgHandles[ 2 ] = { 0 } ;
rgHandles[ 0 ] = hPowerMsgQ;
rgHandles[ 1 ] = g_hEventShutDown;
// Wait for a power notification or for the app to exit
while (WaitForMultipleObjects( 2 , rgHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
{
DWORD cbRead;
DWORD dwFlags;
POWER_BROADCAST * ppb = (POWER_BROADCAST * ) new BYTE[cbPowerMsgSize];
// loop through in case there is more than 1 msg
while (ReadMsgQueue(hPowerMsgQ, ppb, cbPowerMsgSize, & cbRead,
0 , & dwFlags))
{
switch (ppb -> Message)
{
case PBT_POWERINFOCHANGE:
{
RETAILMSG( 1 ,(L " Power Notification Message: PBT_POWERINFOCHANGE\n " ));
// PBT_POWERINFOCHANGE message embeds a
// POWER_BROADCAST_POWER_INFO structure into the
// SystemPowerState field
PPOWER_BROADCAST_POWER_INFO ppbpi =
(PPOWER_BROADCAST_POWER_INFO) ppb -> SystemPowerState;
if (ppbpi)
{
RETAILMSG( 1 ,(L " Length: %d " , ppb -> Length));
RETAILMSG( 1 ,(L " BatteryLifeTime = %d\n " ,ppbpi -> dwBatteryLifeTime));
RETAILMSG( 1 ,(L " BatterFullLifeTime = %d\n " ,
ppbpi -> dwBatteryFullLifeTime));
RETAILMSG( 1 ,(L " BackupBatteryLifeTime = %d\n " ,
ppbpi -> dwBackupBatteryLifeTime));
RETAILMSG( 1 ,(L " BackupBatteryFullLifeTime = %d\n " ,
ppbpi -> dwBackupBatteryFullLifeTime));
RETAILMSG( 1 ,(L " ACLineStatus = %d\n " ,ppbpi -> bACLineStatus));
RETAILMSG( 1 ,(L " BatteryFlag = %d\n " ,ppbpi -> bBatteryFlag));
RETAILMSG( 1 ,(L " BatteryLifePercent = %d\n " ,
ppbpi -> bBatteryLifePercent));
RETAILMSG( 1 ,(L " BackupBatteryFlag = %d\n " ,
ppbpi -> bBackupBatteryFlag));
http://www.cnblogs.com/Images/Outli
發表評論
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

評論