-
一直把这里的密码忘了,今天在一个文本里发现我记录的一些网站的密码, 试了几个终于登陆进来了,本来打算
www.sharping.net和这里同步更新的, 因为忘记密码也懒得去找回, 现在可以更新了(废话).
一般的嵌入式项目通常采用sometimes offline的设计思想, 这样的设计必将有数据同步的功能, 然而数据同步时如果本地没有数据库,加之项目数据的复杂性和无线通讯的带宽等方面的制约, 常常使得同步时间非常漫长, 数据同步这样的操作往往又不能采用单独的线程在后台完成, 恰巧我正在开发的项目就是具备了这些特点.
通过我们版本发布后对用户使用的调查发现, 同步失败的原因有一半以上是由于PDA在常时间闲置时会自动关闭电源, 以导致网络连接断开, 而由于用户对PDA的使用并不熟悉, 我们没理由要求用户到控制面板中去进行电源设置. 程序中解决掉电问题成为必然. 到微软查找了相关的资料找到了一些解决办法. 要防止PDA闲置时电源自动关闭,可以从两种角度去思考, 常规做法是更改WinCE配置, 也就是修改电源闲置关闭时间的注册表键值, 然后激发一个事件重新加载这些配置, 这样做存在一个恢复的问题, 当我们的工作(同步)完成后需要恢复用户原来的配置, 那么我们换一角度去思考, 既然WinCE允许设置自动关闭时间, 那么WinCE系统中必然存在一个线程在指定时间后挂起系统(关闭电源), 这个线程一定有一个定时器, 那么我们是否可以不断的刷新这个定时器,让永远到达不到用户设置的自动关闭时间呢? 事实证明这个思路是可行的.
查阅相关资料可以知道在WinCE核心API库(coredll.dll)中存在一个函数SystemIdleTimerReset, 正是这个函数实现了系统自动挂起的定时功能.
系统在设置了自动关闭时间会调用这个函数开始记时, 这样我们只要在系统尚未达到关闭时间之前调用他就OK, 通过控制面板查看电源管理中的自动关闭设置发现, 最短关闭时间为一分钟.那么就是说只要我们每59秒调用一次SystemIdleTimerReset即可实现电源永不关闭. 有了思路就很简单了:
2 private static extern void SystemIdleTimerReset();
3
4 private static int nDisableSleepCalls = 0 ;
5 private static System.Threading.Timer preventSleepTimer = null ;
6
7 private static void PokeDeviceToKeepAwake( object extra)
8 {
9 try
10 {
11 SystemIdleTimerReset();
12 }
13 catch (Exception e)
14 {
15 // TODO
16 }
17 }
18
19 /**/ /// <summary>
20 /// 禁止设备自动关闭电源
21 /// </summary>
22 public static void DisableDeviceSleep()
23 {
24 nDisableSleepCalls++;
25 if (nDisableSleepCalls == 1)
26 {
27 //Debug.Assert(preventSleepTimer == null);
28 // 没隔30秒刷新一次计时器
29 preventSleepTimer = new System.Threading.Timer(new System.Threading.TimerCallback(PokeDeviceToKeepAwake),
30 null, 0, 30 * 1000);
31 }
32 }
33 /**/ /// <summary>
34 /// 允许设备自动关闭电源
35 /// </summary>
36 public static void EnableDeviceSleep()
37 {
38 nDisableSleepCalls--;
39 if (nDisableSleepCalls == 0)
40 {
41 //Debug.Assert(preventSleepTimer != null);
42 if (preventSleepTimer != null)
43 {
44 preventSleepTimer.Dispose();
45 preventSleepTimer = null;
46 }
47 }
48 }
49
这样我们只要在同步前调用DisableDeviceSleep方法,在同步后调用EnableDeviceSleep就OK了, 在也不用担心用户PDA电源自动关闭引起同步失败了.
可是事情远比我们想象的要复杂, 大家都PDA的背光也会自动关闭, 你可以想象一用户在同步时候的情形, 他在等待漫长的同步过程时背光突然关闭了, 可是我们让他电源不断, 这时候他以为电源关闭了, 结果按下了电源键盘, 结果不用我说了吧.
背光我们同样要处理, 是否也是有个线程作为背光定时器内, 答案是肯定的, 不过为了强调我们研究的技术性, 背光我用修改WinCE注册表方式搞定. 通过查阅资料我找到了这四个注册表键:
[HKEY_CURRENT_USER\ControlPanel\Backlight\BatteryTimeout] 使用电池时PDA自动关闭背光时间
[HKEY_CURRENT_USER\ControlPanel\Backlight\BatteryTimeout] 使用外接电源时PDA自动关闭背光时间
[HKEY_CURRENT_USER\ControlPanel\Backlight\BatteryTimeoutUnchecked] 禁止使用电池自动关闭时为选中的时间
[HKEY_CURRENT_USER\ControlPanel\Backlight\ACTimeoutUnchecked] 禁止使用外接电源自动关闭时为选中的时间
我们要做就是修改这些键值, 然后激发一个能让这些新值生效的事件。 思路很简单,直接看代码了:
/// 指示背光是否被设置
/// </summary>
private static int nBatteryTimeoutCalls = 0 ;
private static int nACTimeoutCalls = 0 ;
/**/ /// <summary>
/// 禁止背光自动关闭
/// </summary>
public static bool DisableBacklightOff()
{
OpenNETCF.Win32.RegistryKey key = null;
bool bIsSet = false;
try
{
key = OpenNETCF.Win32.Registry.CurrentUser.OpenSubKey(@"ControlPanel\Backlight\", true);
if(key != null)
{
string oldValue1 = String.Empty;
string oldValue2 = String.Empty;
oldValue1 = key.GetValue("BatteryTimeout").ToString();
oldValue2 = key.GetValue("ACTimeout").ToString();
if(oldValue1.Trim() != "0")
{
nBatteryTimeoutCalls++;
if(nBatteryTimeoutCalls == 1)
{
key.SetValue("BatteryTimeout", 0);
key.SetValue("BatteryTimeoutUnchecked", int.Parse(oldValue1));
bIsSet = true;
}
else
{
nBatteryTimeoutCalls--;
}
}
if(oldValue2.Trim() != "0")
{
nACTimeoutCalls++;
if(nACTimeoutCalls == 1)
{
key.SetValue("ACTimeout", 0);
key.SetValue("ACTimeoutUnchecked", int.Parse(oldValue2));
bIsSet = true;
}
else
{
nACTimeoutCalls--;
}
}
if(bIsSet)
{
IntPtr hBackLightEvent = CreateEvent(IntPtr.Zero, false, true, "BackLightChangeEvent");
if (hBackLightEvent != IntPtr.Zero)
{
EventModify(hBackLightEvent,3);
CloseHandle(hBackLightEvent);
}
}
}
return true;
}
catch
{
return false;
}
finally
{
if(key != null)
{
key.Dispose();
key = null;
}
}
}
/**/ /// <summary>
/// 允许背光自动关闭
/// </summary>
public static bool EnableBacklightOff()
{
OpenNETCF.Win32.RegistryKey key = null;
bool bIsSet = false;
try
{
key = OpenNETCF.Win32.Registry.CurrentUser.OpenSubKey(@"ControlPanel\Backlight\", true);
if(key != null)
{
string oldValue1 = String.Empty;
string oldValue2 = String.Empty;
oldValue1 = key.GetValue("BatteryTimeoutUnchecked").ToString();
oldValue2 = key.GetValue("ACTimeoutUnchecked").ToString();
if(oldValue1.Trim() != "0")
{
nBatteryTimeoutCalls--;
if(nBatteryTimeoutCalls == 0)
{
key.SetValue("BatteryTimeoutUnchecked", 0);
key.SetValue("BatteryTimeout", int.Parse(oldValue1));
bIsSet = true;
}
else
{
nBatteryTimeoutCalls++;
}
}
if(oldValue2.Trim() != "0")
{
nACTimeoutCalls--;
if(nACTimeoutCalls == 0)
{
key.SetValue("ACTimeoutUnchecked", 0);
key.SetValue("ACTimeout", int.Parse(oldValue2));
bIsSet = true;
}
else
{
nACTimeoutCalls++;
}
}
if(bIsSet)
{
IntPtr hBackLightEvent = CreateEvent(IntPtr.Zero, false, true, "BackLightChangeEvent");
if (hBackLightEvent != IntPtr.Zero)
{
EventModify(hBackLightEvent,3);
CloseHandle(hBackLightEvent);
}
}
}
return true;
}
catch
{
return false;
}
finally
{
if(key != null)
{
key.Dispose();
key = null;
}
}
}
OK! 问题解决.