在使用 Standard Shell 的情况下,出于节约用电和保护显示器的目的,我们需要在 User Idle 时进入显示器节电模式 (ScreenOff),在 User Active 时退出显示器节电模式 (ScreenOn)。
该项功能要依靠 Power Manager 和 GWES 来实现,具体为:
- Power Manager 维护显示器电源状态,通过设备驱动控制显示器进入/退出节电模式。
- GWES 监视用户输入事件 (User Active) 并通知 Power Manager。
由于不少 BSP 未提供任何设备驱动来管理显示器,故我们需自行实现显示器电源管理功能。
在 $(_WINCEROOT)/PUBLIC/COMMON/OAK/FILES/common.reg 中存在:
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Power]
"DisableGwesPowerOff"=dword:0
"ScreenPowerOff"=DWORD:300
根据 "DisableGwesPowerOff" 的值,存在两种实现方式:
- 当 "DisableGwesPowerOff"=dword:0,GWES 会发送 WM_POWERBROADCAST 消息给任务栏,具体可在 $(_WINCEROOT)/PUBLIC/SHELL/OAK/HPC/EXPLORER/TASKBAR/taskbar.cpp 中找到如下代码段:
{
const WCHAR g_MessengerWindowClass[] = L"MSGSBlObj";
HWND hwndMessenger;
hwndMessenger = FindWindow(g_MessengerWindowClass, NULL);
if (hwndMessenger)
{
// Notify Messenger of idle state
PostMessage(
FindWindow(g_MessengerWindowClass, NULL),
msg,
wParam,
lParam
);
}
}
// Place holder for real screen saver implementation
switch(wParam)
{
case PBT_APMUSERIDLE:
// ScreenSaver start
RETAILMSG(TRUE, (L"Screen Saver Started. "));
break;
// Screen saver app should be looking for this message
case PBT_APMUSERACTIVE:
// ScreenSaver end
RETAILMSG(TRUE, (L"Screen Saver Ended. "));
break;
}
break;
该段代码在 User Idle 时,调试输出“屏保已启动”;在 User Active 时,调试输出“屏保已结束”。考虑到多数 BSP 支持 GETPOWERMANAGEMENT / SETPOWERMANAGEMENT escape function,调用 ExtEscape 函数就可进入/退出节电显示器模式,修改后的代码如下:
// ScreenOff
SetScreenPower(FALSE);
break;
case PBT_APMUSERACTIVE:
// ScreenOn
SetScreenPower(TRUE);
break;
其中,函数 SetScreenPower 的实现代码如下:
{
HDC hDC(GetDC(NULL));
if (!hDC) return GetLastError();
_ATLTRY
{
DWORD dwQuery(GETPOWERMANAGEMENT);
if (ExtEscape(hDC, QUERYESCSUPPORT, sizeof(DWORD), (LPCSTR)&dwQuery, 0, NULL) <= 0)
AtlThrowLastWin32();
VIDEO_POWER_MANAGEMENT power;
power.Length = sizeof(power);
if (ExtEscape(hDC, GETPOWERMANAGEMENT, 0, 0, sizeof(power), (LPSTR)&power) != sizeof(power))
AtlThrowLastWin32();
power.PowerState = bScreenOn ? VideoPowerOn : VideoPowerOff;
if (ExtEscape(hDC, SETPOWERMANAGEMENT, sizeof(power), (LPSTR)&power, 0, NULL) <= 0)
AtlThrowLastWin32();
}
_ATLCATCH(e)
{
ATLVERIFY(ReleaseDC(NULL, hDC));
return HRESULT_CODE((HRESULT)e);
}
ATLVERIFY(ReleaseDC(NULL, hDC);
return NO_ERROR;
}
- 当 "DisableGwesPowerOff"=dword:1,这时 GWES 不会发送 WM_POWERBROADCAST 消息给任务栏了。那么我们需要通过调用 RequestPowerNotifications 函数要求 Power Manager 将电源通告写入指定的消息队列。
{
// 消息队列参数
MSGQUEUEOPTIONS qo;
qo.dwSize = sizeof(MSGQUEUEOPTIONS);
qo.dwFlags = MSGQUEUE_NOPRECOMMIT;
qo.dwMaxMessages = 0;
qo.cbMaxMessage = 256;
qo.bReadAccess = TRUE;
// 创建消息队列
CSafeHandle<HANDLE, CloseMsgQueue> hPowerNMsgQ(CreateMsgQueue(NULL, &qo));
// 请求电源通告
CSafeHandle<HANDLE, (BOOL(__stdcall*)(HANDLE))StopPowerNotifications> hNotify(RequestPowerNotifications(hPowerNMsgQ, PBT_TRANSITION));
while (TRUE)
{
// 判断消息队列是否有数据写入
if (WaitForSingleObject(hPowerNMsgQ, INFINITE) == WAIT_FAILED)
return GetLastError();
// 从消息队列读取数据
BYTE buffer[256];
DWORD cRead, dwFlags;
if (!ReadMsgQueue(hPowerNMsgQ, buffer, sizeof(buffer), &cRead, INFINITE, &dwFlags))
return GetLastError();
// 根据 Power Manager 维护的显示器电源状态,进入/退出节电模式
PPOWER_BROADCAST pPowerBroadcast((PPOWER_BROADCAST)buffer);
if (!_tcsicmp(pPowerBroadcast->SystemPowerState, TEXT("On")))
SetScreenPower(TRUE);
else if (!_tcsicmp(pPowerBroadcast->SystemPowerState, TEXT("SystemIdle")))
SetScreenPower(FALSE);
}
return NO_ERROR;
}
本文介绍如何在StandardShell下实现显示器的节电模式切换。利用PowerManager和GWES组件,结合不同BSP特性,通过代码实现显示器在用户闲置和活跃时自动进入和退出节电模式。
1758





