在使用 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 中找到如下代码段:
case
WM_POWERBROADCAST:
{
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 ;
{
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 函数就可进入/退出节电显示器模式,修改后的代码如下:
case
PBT_APMUSERIDLE:
// ScreenOff
SetScreenPower(FALSE);
break ;
case PBT_APMUSERACTIVE:
// ScreenOn
SetScreenPower(TRUE);
break ;
// ScreenOff
SetScreenPower(FALSE);
break ;
case PBT_APMUSERACTIVE:
// ScreenOn
SetScreenPower(TRUE);
break ;
其中,函数 SetScreenPower 的实现代码如下:
DWORD SetScreenPower(BOOL bScreenOn)
{
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);
}
{
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 将电源通告写入指定的消息队列。
int
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine,
int
nCmdShow)
{
// 消息队列参数
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;
}
{
// 消息队列参数
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;
}