一:需求描述
最近经常在需要让计算机在特定时间关闭或者重启,所以写了这个程序。当然满足这一需求的方法很多,比如用计划任务等等,但是既然在学VC,就自己动手做一个吧。
二、解题思路
定时关机无非是设定一个关机时间,然后跟当前系统时间比较,取得这个差,然后设置一个定时器,到时间就执行相应的操作,比如shutdown、reboot、logoff等等。
三、详细过程
程序最终的画面如下:
第一步:利用Wizard生成一个对话框框架。然后按上图所示放置控件。其中“倒计时”的ID设为IDC_STATIC_NAME,“00:00:00”的ID设为IDC_STATIC_TIME(这两个在程序里要用),另外为日期、时间、ComboBox添加控件变量分别为m_Data1,m_Data2,m_ComboBox.ComboBox的样式为Drop list,即不接收输入。好了,下面就添加代码。
第二步:添加代码:
我要在按下“设定”按钮时,,把“设定”改为“重新设定”,让能设定的选项均不能再修改,除非再按一次这个按钮。所以要用一个bool型的变量记录按钮的状态,所以在对话框的头文件添加变量IsChanged,类型为bool.另外再添加3个变量m_Hour,m_Min,m_Sec,类型为int,一个long型的m_remain。好了,变量先添加这些,接下来要重载OnOK、OnCancel函数,直接用向导添加就行了;为按钮添加默认的点击处理函数;添加最小化到系统托盘的函数toTray(),消息映射 afx_msg LRESULT onShowTask(WPARAM wParam,LPARAM lParam);以及afx_msg void OnTimer(UINT_PTR nIDEvent);记得还要在对话框cpp文件中添加相应的内容,此处不说了。在对话框cpp中添加#define WM_SHOWTASK WM_USER+1为自定义的消息。好了,现在就为函数写代码吧。
首先是在OnInitDialog()中添加
m_ComboBox.InsertString(0,_T("注销"));
m_ComboBox.InsertString(1,_T("关闭计算机"));
m_ComboBox.InsertString(2,_T("重新启动"));
m_ComboBox.SetCurSel(1);
// TODO: 在此添加额外的初始化代码
IsChanged =true;
然后是OnOK函数中添加一行代码OnBnClickedButton1();
OnCancel函数中添加如下代码:
NOTIFYICONDATA nid1;
nid1.hWnd = this->m_hWnd;
nid1.uID = IDR_MAINFRAME;
Shell_NotifyIcon(NIM_DELETE,&nid1);
CDialog::OnCancel();
目的是把系统托盘中的图标移走。
OnTimer函数中添加如下代码
m_Hour = m_remain/3600;
m_Min = (m_remain%3600)/60;
m_Sec = (m_remain%3600)%60;
CString m_time;
if(m_Hour>=10)
m_time.Format("%d:",m_Hour);
else
m_time.Format("0%d:",m_Hour);
if(m_Min>=10)
m_time.Format(m_time+"%d:",m_Min);
else
m_time.Format(m_time+"0%d:",m_Min);
if(m_Sec>=10)
m_time.Format(m_time+"%d",m_Sec);
else
m_time.Format(m_time+"0%d",m_Sec);
SetDlgItemText(IDC_STATIC_TIME,m_time);
if(m_remain<=60)
{
if(!IsTop)//只设定一次
{
CWnd::SetWindowPos(&wndTopMost,0,0,0,0,SWP_NOREDRAW|SWP_NOSIZE);
IsTop = true;
}
MessageBeep(-1);
}
if(m_remain==0)
{
//shutdowncomputer adjust privalige
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if(!OpenProcessToken(::GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
return;
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, false, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
if(GetLastError()!=ERROR_SUCCESS)
return;
switch(m_ComboBox.GetCurSel())
{
case 0://logoff
ExitWindowsEx(EWX_LOGOFF|EWX_FORCE,0);
break;
case 1://shutdown
ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF | EWX_FORCE, 0);
break;
default://reboot
ExitWindowsEx(EWX_REBOOT | EWX_FORCE,0);
}
PostMessage(WM_QUIT);
}
m_remain -=1;
实现了两点:1是显示剩余时间,2是时间到达后执行操作。
OnBnClickedButton1中添加如下代码
if(IsChanged)//设定关机
{
//取得设定时间
SYSTEMTIME m_data,m_time,m_systime;
__int64 m_Start,m_End;
FILETIME m_filetime,m_sysfile;
PULARGE_INTEGER m_inttime,m_sysint;
GetSystemTime(&m_systime);
m_systime.wHour +=8;
m_Data1.GetTime(&m_data);
m_Data2.GetTime(&m_time);
m_data.wHour = m_time.wHour;
m_data.wMinute = m_time.wMinute;
m_data.wSecond = m_time.wSecond;
m_data.wMilliseconds = m_time.wMilliseconds;
SystemTimeToFileTime(&m_data,&m_filetime);
SystemTimeToFileTime(&m_systime,&m_sysfile);
m_inttime = (PULARGE_INTEGER)&m_filetime;
m_sysint = (PULARGE_INTEGER)&m_sysfile;
m_Start = *(__int64 *)m_sysint;
m_End = *(__int64 *)m_inttime;
if(m_Start>=m_End)
{
MessageBox(_T("不能小于当前时间!"),_T("时间设定出错"),MB_ICONERROR|MB_OK);
return;
}
m_remain = (long)((m_End-m_Start)/10000/1000);
CWnd::SetTimer(1,1000,0);
SetDlgItemText(IDC_BUTTON1,_T("重新设定"));
m_Data1.EnableWindow(false);
m_Data2.EnableWindow(false);
m_ComboBox.EnableWindow(false);
IsChanged = false;
switch(m_ComboBox.GetCurSel())
{
case 0:
SetDlgItemText(IDC_STATIC_NAME,_T("注销倒计时:"));
break;
case 1:
SetDlgItemText(IDC_STATIC_NAME,_T("关机倒计时:"));
break;
default:
SetDlgItemText(IDC_STATIC_NAME,_T("重启倒计时:"));
}
}
else//重新设定
{
IsChanged = true;
m_Data1.EnableWindow();
m_Data2.EnableWindow();
m_ComboBox.EnableWindow();
SetDlgItemText(IDC_BUTTON1,_T("设定"));
// SYSTEMTIME m_time;
// GetSystemTime(&m_time);
m_Data1.SetTime();
m_Data2.SetTime();
CWnd::KillTimer(1);
}
toTray中添加如下代码
NOTIFYICONDATA nid;
nid.cbSize = (DWORD)sizeof(NOTIFYICONDATA);
nid.hWnd = this->m_hWnd;
nid.uID = IDR_MAINFRAME;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_SHOWTASK;
nid.hIcon = LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
strcpy(nid.szTip,"定时关机");
Shell_NotifyIcon(NIM_ADD,&nid);
ShowWindow(SW_HIDE);
主要是实现了最小化到系统托盘
LRESULT CautoshutdownDlg::onShowTask(WPARAM wParam,LPARAM lParam)
{
if(wParam != IDR_MAINFRAME)
return -1;
switch(lParam)
{
case WM_RBUTTONUP:
{
LPPOINT lpoint = new tagPOINT;
::GetCursorPos(lpoint);
CMenu menu;
menu.CreatePopupMenu();
menu.AppendMenu(MF_STRING,WM_DESTROY,"关闭");
menu.TrackPopupMenu(TPM_LEFTALIGN,lpoint->x,lpoint->y,this);
HMENU hmenu=menu.Detach();
menu.DestroyMenu();
delete lpoint;
}
break;
case WM_LBUTTONUP:
{
this->ShowWindow(SW_SHOW);
}
break;
}
return 0;
}
这个就是相应的处理函数。
最后别忘了在OnSysCommand中添加 if(nID == SC_MINIMIZE)
{
toTray();
}
好了,工作到次结束了。
需要说明的几点是:取得系统时间后要把它转化成FILETIME,然后化成ULARGEINTERG,最后转化成__int64之后才比较,然后/10000/1000换成秒。