您的位置:首页 > 其它

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

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

 

【一期开发】

 

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

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

CTime apt_date;
CTime apt_time;

m_appointDate.GetTime(apt_date);  // 获取指定日期
m_appointTime.GetTime(apt_time);  // 获取指定时间

time_t osBinaryTime;
time(&osBinaryTime);		  // Get the current time

CTime curtime(osBinaryTime);
CTime apttime(
apt_date.GetYear(),
apt_date.GetMonth(),
apt_date.GetDay(),
apt_time.GetHour(),
apt_time.GetMinute(),
apt_time.GetSecond()
);


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
 

【二期开发】

对于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()函数中)调用它以提升当前进程的权限,问题迎刃而解。

 

void CTimeSwitchDlg::SetPriviles()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
MessageBox("OpenProcessToken Error!", "Error");
}

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)
{
MessageBox("AdjustTokenPrivileges Error!", "Error");
}

return;
}


 



TimeSwitch_V1.0_FINAL
 

【三期开发】

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

 
void ShutdownTimer(long countDownSeconds)
{
time_t osCurrentTime;		// 定义系统时间变量
time_t deadLine;		// 定义截止时间变量
time(&osCurrentTime);		// Get the current time from the OS
deadLine = osCurrentTime + countDownSeconds;

long span;			// 定义时间差变量
int nDays;			// 定义时间差天数变量
int nHours;			// 定义时间差小时数变量
int nMinutes;			// 定义时间差分钟数变量
int nSeconds;			// 定义时间差秒钟数变量
CString strSpan;		// 定义用于显示时间差的字符串变量

do
{
// 更新当前时间
time(&osCurrentTime);	// update the current time
span = deadLine - osCurrentTime; // update the current span

// 显示倒计时提示信息
nDays = span / 86400;
nHours = (span % 86400) / 3600;
nMinutes = (span % 3600) / 60;
nSeconds = span % 60;

strSpan.Format("离关机还有:%ddays %d:%d:%d", nDays, nHours, nMinutes, nSeconds);

if (g_pMainDlg->m_staticInfo2 != strSpan)
{
g_pMainDlg->m_staticInfo2 = strSpan;
g_pMainDlg->UpdateData(FALSE);
}

Sleep(200);

} while(span > 0);

// 调用关机函数
UINT uFlags;			// shutdown operation
DWORD dwReserved;		// reserved

//	BOOL result = ExitWindowsEx(uFlags, dwReserved);

return;
}


 

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

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

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

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

完成后的代码如下:

 

// TimeSwitchDlg.cpp : implementation file
//

#include "stdafx.h"
#include "TimeSwitch.h"
#include "TimeSwitchDlg.h"
#include "HelpDlg.h"
#include <time.h>
#include <stdlib.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// 自定义全局变量及引用

extern CTimeSwitchDlg* g_pMainDlg;
UINT g_uFlags; // 定义全局变量g_uFlags,用于保存关机参数
time_t g_deadLine; // 定义全局变量g_deadLine,用于保存倒计时完成时间

void initShutdownTimer(UINT uFlags, DWORD dwTimeout);
BOOL parameterVerify(long lTimeout);

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
CAboutDlg();

// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTimeSwitchDlg dialog

CTimeSwitchDlg::CTimeSwitchDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTimeSwitchDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CTimeSwitchDlg)
m_cdh = 0;
m_cdm = 0;
m_cds = 0;
m_staticInfo1 = _T("");
m_staticInfo2 = _T("");
m_staticInfo3 = _T("");
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CTimeSwitchDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CTimeSwitchDlg)
DDX_Control(pDX, IDC_BTNCANCLE, m_btnCancle);
DDX_Control(pDX, IDC_BTNSTART, m_btnStart);
DDX_Control(pDX, IDC_CHECKFORCEIFHUNG, m_btnForceIfHung);
DDX_Control(pDX, IDC_EDITCDS, m_editCDS);
DDX_Control(pDX, IDC_EDITCDM, m_editCDM);
DDX_Control(pDX, IDC_EDITCDH, m_editCDH);
DDX_Control(pDX, IDC_CHECKFORCE, m_btnForce);
DDX_Control(pDX, IDC_SPIN3, m_spinEditCDS);
DDX_Control(pDX, IDC_SPIN1, m_spinEditCDH);
DDX_Control(pDX, IDC_SPIN2, m_spinEditCDM);
DDX_Control(pDX, IDC_DTPDATE, m_appointDate);
DDX_Control(pDX, IDC_DTPTIME, m_appointTime);
DDX_Text(pDX, IDC_EDITCDH, m_cdh);
DDV_MinMaxInt(pDX, m_cdh, 0, 999);
DDX_Text(pDX, IDC_EDITCDM, m_cdm);
DDV_MinMaxInt(pDX, m_cdm, 0, 59);
DDX_Text(pDX, IDC_EDITCDS, m_cds);
DDV_MinMaxInt(pDX, m_cds, 0, 59);
DDX_Text(pDX, IDC_STATIC_INFOLINE1, m_staticInfo1);
DDX_Text(pDX, IDC_STATIC_INFOLINE2, m_staticInfo2);
DDX_Text(pDX, IDC_STATIC_INFOLINE3, m_staticInfo3);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CTimeSwitchDlg, CDialog)
//{{AFX_MSG_MAP(CTimeSwitchDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTNSTART, OnBtnstart)
ON_BN_CLICKED(IDC_BTNCANCLE, OnBtncancle)
ON_BN_CLICKED(IDC_RADIOMODE1, OnRadiomode1)
ON_BN_CLICKED(IDC_RADIOMODE2, OnRadiomode2)
ON_BN_CLICKED(IDC_BTNABOUT, OnBtnabout)
ON_BN_CLICKED(IDC_BTNHELP, OnBtnhelp)
ON_WM_TIMER()
ON_BN_CLICKED(IDC_CHECKFORCE, OnCheckforce)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTimeSwitchDlg message handlers

BOOL CTimeSwitchDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// Add "About..." menu item to system menu.

// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}

// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon

// TODO: Add extra initialization here

m_btnStart.EnableWindow(TRUE);
m_btnCancle.EnableWindow(FALSE);

m_spinEditCDH.SetRange(0, 999);
m_spinEditCDM.SetRange(0, 59);
m_spinEditCDS.SetRange(0, 59);
m_spinEditCDM.SetPos(24);
m_spinEditCDM.SetPos(0);
m_spinEditCDS.SetPos(0);

CheckRadioButton(IDC_RADIOMODE1, IDC_RADIOMODE2, IDC_RADIOMODE2);
CheckRadioButton(IDC_RADIOOFF, IDC_RADIOLOGOFF, IDC_RADIOOFF);
m_btnForce.SetCheck(1);
m_btnForceIfHung.SetCheck(1);
m_btnForceIfHung.EnableWindow(FALSE);

m_cdh = 24;
m_cdm = 0;
m_cds = 0;

m_staticInfo1 = "- 点击“启动”按钮启动关机倒计时;";
m_staticInfo2 = "- 点击“停止”按钮停止关机倒计时;";
m_staticInfo3 = "如需更多帮助请点击右侧的“帮助”按钮!";

UpdateData(FALSE);

m_appointDate.EnableWindow(FALSE);
m_appointTime.EnableWindow(FALSE);
m_editCDH.EnableWindow(TRUE);
m_editCDM.EnableWindow(TRUE);
m_editCDS.EnableWindow(TRUE);

return TRUE; // return TRUE unless you set the focus to a control
}

void CTimeSwitchDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}

// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.

void CTimeSwitchDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting

SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}

// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CTimeSwitchDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}

void CTimeSwitchDlg::OnTimer(UINT nIDEvent)
{
static long span; // 定义时间差变量
static int nDays; // 定义时间差天数变量
static int nHours; // 定义时间差小时数变量
static int nMinutes; // 定义时间差分钟数变量
static int nSeconds; // 定义时间差秒钟数变量
static CString strSpan; // 定义用于显示时间差的字符串变量

// 更新当前剩余时间
span = g_deadLine - time(NULL); // update the current span

// 创建倒计时提示信息
nDays = span / 86400;
nHours = (span % 86400) / 3600;
nMinutes = (span % 3600) / 60;
nSeconds = span % 60;

strSpan.Format("目前离关机还有:%2ddays %02d:%02d:%02d", nDays, nHours, nMinutes, nSeconds);

// 判断是否有必要更新界面提示,如果有必要则更新它
if (m_staticInfo3 != strSpan)
{
m_staticInfo3 = strSpan;
UpdateData(FALSE);
}

if (span <= 0)
{
// 调用关机函数
if(!ExitWindowsEx(g_uFlags, 0))
{
CString strMsg;
strMsg.Format("ErrorCode: %d", GetLastError());
MessageBox(strMsg, "ExitWindowsEx Error");
}
else
{
m_staticInfo3 = "Windows正在关闭计算机...";
UpdateData(FALSE);
}

// 销毁定时器
KillTimer(1);
}

return;
}

void CTimeSwitchDlg::OnRadiomode1()
{
m_appointDate.EnableWindow(TRUE);
m_appointTime.EnableWindow(TRUE);
m_editCDH.EnableWindow(FALSE);
m_editCDM.EnableWindow(FALSE);
m_editCDS.EnableWindow(FALSE);
}

void CTimeSwitchDlg::OnRadiomode2()
{
m_appointDate.EnableWindow(FALSE);
m_appointTime.EnableWindow(FALSE);
m_editCDH.EnableWindow(TRUE);
m_editCDM.EnableWindow(TRUE);
m_editCDS.EnableWindow(TRUE);
}

void CTimeSwitchDlg::OnCheckforce()
{
if (m_btnForce.GetCheck() == 1)
{
m_btnForceIfHung.SetCheck(1);
m_btnForceIfHung.EnableWindow(FALSE);
}
else
{
m_btnForceIfHung.EnableWindow(TRUE);
}
}

void CTimeSwitchDlg::OnBtnstart()
{
int nID;
time_t lTimeout;

// 读取倒计时设置并转换为秒数
nID = GetCheckedRadioButton(IDC_RADIOMODE1, IDC_RADIOMODE2);
if (nID == IDC_RADIOMODE1)
{
CTime apt_date;
CTime apt_time;

m_appointDate.GetTime(apt_date); // 获取指定日期
m_appointTime.GetTime(apt_time); // 获取指定时间

time_t osBinaryTime;
time(&osBinaryTime); // Get the current time from the OS

CTime curtime(osBinaryTime);
CTime apttime(
apt_date.GetYear(),
apt_date.GetMonth(),
apt_date.GetDay(),
apt_time.GetHour(),
apt_time.GetMinute(),
apt_time.GetSecond()
);

CTimeSpan timeSpan = apttime - curtime;
lTimeout = timeSpan.GetTotalSeconds();
}
else
{
UpdateData(TRUE);
lTimeout = m_cdh * 3600;
lTimeout += m_cdm * 60;
lTimeout += m_cds;
}

// 倒计时秒数合法性检验
if (!parameterVerify(lTimeout))
{
return;
}

UINT uFlags = 0; // shutdown operation

// 设置关机参数
nID = GetCheckedRadioButton(IDC_RADIOPOWEROFF, IDC_RADIOLOGOFF);
switch (nID)
{
case IDC_RADIOPOWEROFF: uFlags |= EWX_POWEROFF; break;
case IDC_RADIOREBOOT: uFlags |= EWX_REBOOT; break;
case IDC_RADIOLOGOFF: uFlags |= EWX_LOGOFF; break;
default:
{
AfxMessageBox("GetCheckedRadioButton(IDC_RADIOPOWEROFF, IDC_RADIOLOGOFF) ERROR!");
return;
}
}

if (m_btnForce.GetCheck() == 1)
{
uFlags |= EWX_FORCE;
}

if (m_btnForceIfHung.GetCheck() == 1)
{
uFlags |= 0x00000010; // EWX_FORCEIFHUNG (_WIN32_WINNT >= 0x0500, Win2000 or later)
}

// 初始化关机定时器
initShutdownTimer(uFlags, lTimeout);

m_btnStart.EnableWindow(FALSE);
m_btnCancle.EnableWindow(TRUE);

return;
}

void CTimeSwitchDlg::OnBtncancle()
{
KillTimer(1);
m_staticInfo1 = "当前倒计时已成功停止...";
m_staticInfo2 = "如需重新开始请点击“启动”按钮!";
UpdateData(FALSE);

m_btnStart.EnableWindow(TRUE);
m_btnCancle.EnableWindow(FALSE);

return;
}

void CTimeSwitchDlg::OnBtnhelp()
{
CHelpDlg dlg;
dlg.DoModal();
}

void CTimeSwitchDlg::OnBtnabout()
{
CAboutDlg dlg;
dlg.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
// 自定义类成员函数

void CTimeSwitchDlg::SetPriviles() { HANDLE hToken; TOKEN_PRIVILEGES tkp; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { MessageBox("OpenProcessToken Error!", "Error"); } 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) { MessageBox("AdjustTokenPrivileges Error!", "Error"); } return; }

/////////////////////////////////////////////////////////////////////////////
// 自定义功能函数

void initShutdownTimer(UINT uFlags, DWORD dwTimeout)
{
g_uFlags = uFlags;
g_deadLine = time(NULL) + dwTimeout;

g_pMainDlg->m_staticInfo1 = "倒计时已经启动,请注意保存您的工作,避";
g_pMainDlg->m_staticInfo2 = "免强制关机时丢失尚未保存的数据!";
g_pMainDlg->UpdateData(FALSE);

g_pMainDlg->SetTimer(1, 200, NULL);

return;
}

BOOL parameterVerify(long lTimeout)
{
if (lTimeout < 0)
{
MessageBox(
NULL,
"您指定的时间晚于当前时间,请返回修改后再试!",
"参数出错",
MB_OK | MB_ICONWARNING);
return FALSE;
}

if (lTimeout < 30)
{
MessageBox(
NULL,
"根据您的设定倒计时将少于30秒,请再次确认您的设置!",
"安全提示",
MB_OK | MB_ICONWARNING);
return FALSE;
}

if (lTimeout > 2592000)
{
int result;
result = MessageBox(
NULL,
"根据您的设定倒计时将大于30天,您真的要继续吗?",
"安全提示",
MB_YESNO | MB_ICONQUESTION);
if (result == IDNO)
{
return FALSE;
}
}

return TRUE;
}


 



TimeSwitch_V2.0_FINAL
 

 

如需索取全部源代码请联系本人,欢迎批评指教!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息