Windows定时关机程序的开发随笔与经验杂谈(TimeSwitch)

来源:互联网 发布:五子棋ai活三活四算法 编辑:程序博客网 时间:2024/04/30 08:13

今天上课学了个叫DateTimePicker的控件,突然想到以前用C#写过的一个设定自动关机的程序,遗憾的是那个程序一直没能拥有通过“设定关机日期/时间”初始化倒计时程序的功能,那么今天是不是就可以实现了呢?我想应该没什么问题吧,于是中午放学后我就泡在了电脑前,打开VC++6.0开始写这个程序。

 

【一期开发】

 

以下是我碰到的一些问题:

1. 我使用两个DateTimePicker控件,一个用来读用户设定的日期,另一个读时间,第一个问题就是如何才能将他们组合起来使用?我的做法是初始化apttime(指定时间)时,分别采用两个DateTimePicker控件的有效数值部分。即读日期的那个控件取它的年/月/日,读时间的控件取它的时/分/秒,这样有了年/月/日/时/分/秒就可以初始化新的CTime对象apttime了。

2. 两个CTime对象可以直接相减,但减完的结果是CTimeSpan的对象,如果想得到两个CTime对象之间相差的秒数,可以使用CTimeSpan成员方法GetTotalSecond();另外关于time_t与CTime之间的关系,查看time.h可知time_t实际上就是long,事实上它保存的是当前时间与1970年1月1日0时0分0秒之间的秒数差。

CTime对象转换为time_t的方法:

- 调用CTime成员函数GetTime: time_t GetTime() const;  详查MSDN..

time_t转换为CTime对象的方法:

- 调用CTime的一种构造函数: CTime(time_t time);  详查MSDN..

3. 要在对话框上设置两组RadioButton(单选按钮),但软件调试时发现所有的单选按钮变成了一个大组(即使是将他们用GroupBox隔开),这背离了我们的本意,解决这个问题最好的方法就是将每组RadioButton的第一个按钮的Group属性选中;

4. 为了方便调节文本框中的数值,可以为每个文本框添加Spin(旋转按钮)控件,但是简单的添加Spin控件并不能达到我们的目的,正确的做法是在对话框编辑页面按Ctrl+D快捷键,调出Tab Order功能,依次点击页面上的控件使标号改变,只需要将Spin控件的Tab顺序调整到其对应的文本框控件之后即可,例如一个文本框控件的Tab顺序号为6,那么他会跟Tab顺序号为7的Spin控件绑定!当然在Spin控件的属性中,还需要选中Auto buddy属性, 为了使界面美观,还可以更该Alignment属性为Right,这样Spin控件就会吸附在EditBox控件右边,就好像是一个控件!当然,如果希望点击Spin控件上的按钮实现文本框中的数字+1或-1,那么最简单的方法就是选中Spin控件属性中的Set buddy integer。最后还要注意,Spin默认的数值范围是0~100,点击下(右)按钮则增加伙伴控件中的数值,点击上(左)按钮则减少它,这与我们通常的习惯正好相反,解决的方法就是通过CSpinButtonCtrl::SetRange()函数设置正确的数值范围即可!

解决了上述问题,再配合MSDN,很快就能够完成此程序的第一个版本:我是通过system()函数调用XP系统自带的shutdown.exe解决倒计时关机问题的,说白了刚才做的工作只是一个外壳,方便用户使用shutdown命令的Shell而已,它能通过友好的用户界面将使用者的意图转换为准确合理的参数,提供给shutdown程序接收。

当然使用system()函数也存在着一些弊端,比如调用命令的时候会出现黑色的命令提示符窗口;另外这种方案本身也具有很大的局限性,由于XP与Vista系统下shutdown命令的用法与格式有较大不同,因此目前这个版本的软件只能在XP系统中使用!

 

TimeSwitch_V0.1_BETA

 TimeSwitch_V0.1_BETA

 

【二期开发】

对于V0.1版本中存在的诸多缺点,此次开发的新版本将加以改进!首先,我打算让软件脱离对系统自带的关机程序shutdown.exe的依赖,其次软件的外观以及关于界面的信息也需要做一些调整,完成后将直接发布V1.0Final版。

说做就做,我动手查了一下Windows用于关机的函数,发现有这么几个函数:

AbortSystemShutdown

ExitWindows

ExitWindowsEx

InitiateSystemShutdown

LockWorkStation

我最开始的尝试是InitiateSystemShutdown,因为感觉它就是shutdown.exe调用的函数,随后我也印证了自己的想法,用这个函数成功设计出了V1.0Final,初步达到了目的(摆脱了黑色的命令提示符窗口,并且也不再依赖于系统自带的shutdown.exe),虽然说只是动手改了改代码,但是还是遇到了问题:

主要是在调用InitiateSystemShutdown()函数时总是返回FALSE,于是我就调用GetLastError()查询错误代码,根据MSDN所示,这个错误是“Access is denied.(拒绝访问)”,会不会是因为权限不够呢?即使是我以管理员身份登录,系统也会自动为进程设置默认的安全级别?带着这些疑问我上网搜寻答案,结果正如我所料,确实是我的代码中缺少了为进程提升权限的代码!

参考过别人的代码,我在自己的CTimeSwitchDlg中添加了成员函数SetPriviles(),并在应用程序初始化时(CTimeSwitchApp::InitInstance()函数中)调用它以提升当前进程的权限,问题迎刃而解。

 

 

TimeSwitch_V1.0_FINAL

TimeSwitch_V1.0_FINAL

 

【三期开发】

完成了上两次的开发,这次想把那个定时的功能搬到软件内部完成,而不是调用Windows的定时关机功能,初步的打算就是写段比较当前时间与预设时间的代码,当时间差小于或等于零的时候调用ExitWindowsEX函数关闭计算机。于是我写的测试代码如下:

 

 

这里看到的ShutdownTimer函数,是在“启动”按钮的消息响应函数里完成倒数秒数计算后调用的,它循环检测当前时间并且判断是否有必要更新界面上的提示,最后当时间差小于或等于零时退出循环执行关机操作。
这样一段代码如果没有"Sleep(200);"这条语句的话,执行时CPU占用率将达到50%左右,添加这条语句后CPU的占用率将明显下降,据测试延时200ms时的CPU占用率将下降到5%以下,并且长期维持在2%左右。

但是这样做还有一个问题,那就是对于应用程序而言,如果这个函数(“启动”按钮的消息响应函数)迟迟不能退出,那么它将影响到后续消息的处理,表现形式就是用户进行鼠标键盘操作时应用程序失去响应。

解决这个问题最直接的方法就是创建一个线程,让这个线程调用ShutdownTimer函数并在后台实时更新提示信息。

但事实上我们还有更好的方法,我们完全可以设定一个系统定时器,用类似于“中断”的方式定时处理时间上的变化,由此我们引入了SetTimer()函数。关于它的用法可以在百度查到很详细的说明,我选用的是CWnd::SetTimer()这个函数,原因是它的参数设置简单,如果将它的默认处理函数设置为NULL,那么SetTimer函数将向消息队列中发送一个WM_TIMER消息,因此只需为调用这个SetTimer函数的窗口添加OnTimer消息处理函数即可!

完成后的代码如下:

 

 

TimeSwitch_V2.0_FINAL

TimeSwitch_V2.0_FINAL

 

 

如需索取全部源代码请联系本人,欢迎批评指教!

原创粉丝点击