说明
虽然上一篇实现了的定时关机,但是还不够完善,比如开机自动启动,然后按照配置的时间定时关机,并最小化到任务栏。
先来说开机启动怎么实现,开机启动实现的方法有好几种,比如直接在开始菜单启动项里添加一个程序的快捷方式,路径为
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\
不过这个路径不同的电脑可能不一样,因为这个路径可以改变,比如说改到D盘
也可以直接写入注册表,这个比较方便,这个开机启动项在注册表中的路径是
HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run
WIN7 64位的注册表启动位置,这个根键和原来的不一样,路径也不一样。
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run
最小化到任务栏的实现需要NOTIFYICONDATA,这个结构体就可以实现我们需要的功能,并可以处理一些事件。
知识前备
先说注册表的实现,几个和注册表相关的重要函数RegOpenKeyEx
,RegGetValue
,RegSetValueEx
,GetModuleFileName
,RegDeleteValue
,RegCloseKey
打开注册表中的某一项,如果成功就返回ERROR_SUCCESS
,否则返回错误值,可在Winerror.h
查找错误
LSTATUS RegOpenKeyEx( HKEY hKey, //根键 LPCTSTR lpSubKey, //子健 DWORD ulOptions, //保留参数,必须设置为0 REGSAM samDesired, //子健的使用权限 PHKEY phkResult //返回的值项的操作对象 );
然后就是获取值项的键值
,函数成功则返回ERROR_SUCCESS
,错误情况参照Winerror.h
LONG WINAPI RegGetValue( _In_ HKEY hkey, //根键 _In_opt_ LPCTSTR lpSubKey, //子健 _In_opt_ LPCTSTR lpValue, //值项的名称 _In_opt_ DWORD dwFlags, //值项的限制类型,包括RRF_ANY,这个没有类型限制,可以是下一个参数的任何类型,其他的不多说了 _Out_opt_ LPDWORD pdwType, //值项的数据类型,包括主要的四种,REG_BINARY,REG_DWORD,REG_SZ,DEFAULT _Out_opt_ PVOID pvData, //获取的字符串 _Inout_opt_ LPDWORD pcbData //字符串结构的大小 );
打开启动路径后我们首先检查是否已经添加过启动项,结果体现在复选按钮上。然后我们设置值项的值
LONG WINAPI RegSetValueEx( _In_ HKEY hKey, //子健 _In_opt_ LPCTSTR lpValueName, //值项名称 _Reserved_ DWORD Reserved, //保留,为0 _In_ DWORD dwType, //类型 _In_ const BYTE *lpData, //数据字符串 _In_ DWORD cbData //数据大小,不包括结束支字符 );
还有一个函数用来获取当前运行的程序的路径,这个需要在设置注册表值时用到,返回值为程序路径名的长度
DWORD WINAPI GetModuleFileName( _In_opt_ HMODULE hModule, //加载的模式,这里选择为NULL,默认加载当前进程的路径 _Out_ LPTSTR lpFilename, //用来保存文件路径的字符串 _In_ DWORD nSize //字符串大小 );
如果复选按钮为空的话,就从注册表里删除值项
LONG WINAPI RegDeleteValue( _In_ HKEY hKey, //子健 _In_opt_ LPCTSTR lpValueName //值项名称 );
最后操作完成关闭注册表
LONG WINAPI RegCloseKey( _In_ HKEY hKey //子健 );
实现
首先我们在程序启动后检查注册表里是否已经存在这个程序的启动项,如果存在就将结果保存在成员变量isInStartUp
里,否则将isInStartUp设置为false
,如果已经添加,就将复选按钮设置为选中状态,在OnInitDialog()
里实现如下
//获取启动项 HKEY key; LPCTSTR szRun = "Software\\Microsoft\\Windows\\CurrentVersion\\Run";//启动项的路径 if (RegOpenKeyEx(HKEY_CURRENT_USER,szRun,0,KEY_ALL_ACCESS,&key) == ERROR_SUCCESS) { char szFileName[MAX_PATH] = {0}; //DWORD dRet = GetModuleFileName(NULL,szFileName,MAX_PATH); DWORD type = REG_SZ; DWORD bufSize = sizeof(szFileName); char file[MAX_PATH] = {0}; long lRet = RegGetValue(HKEY_CURRENT_USER,szRun,"ShutDown",RRF_RT_ANY,&type,szFileName,&bufSize); if (lRet == ERROR_SUCCESS) { isInStartUp = true;//已经添加启动项,设置checkbox为选中状态 m_checkbox.SetCheck(BST_CHECKED); //MessageBox(szFileName); } else { isInStartUp = false; m_checkbox.SetCheck(BST_UNCHECKED); } } else { MessageBox("打开注册表失败"); } RegCloseKey(key);
这个不用多解释,很简单。接着来,在单击过确定按钮后处理添加启动项,在OnBnClickedOk()
处理后续操作
//首先检查复选框是否选中 UINT ischecked = IsDlgButtonChecked(IDC_CHECK1); //获取选择的时间 hour = m_com_hour.GetCurSel(); minute = m_com_minute.GetCurSel(); second = m_com_second.GetCurSel(); //数据进行格式化处理 CString str = ""; str.Format("%2d时%2d分%2d秒", hour, minute, second); // GetDlgItem(IDC_STATIC_NOW)->SetWindowText(str); int resoult = MessageBox("确定要在"+str+"关机吗?","是否关机?",MB_OKCANCEL); if (resoult == IDOK) { //是否添加到启动项 AutoStart(ischecked); //更新程序窗口中显示的关机时间 GetDlgItem(IDC_STATIC_SHUT)->SetWindowText(str); ........ ......... }
上面关于注册表的操作封装到了函数AutoStart()
中。
//是否添加到启动项 void CAutoShutDownDlg::AutoStart(UINT check) { UINT ischecked = check; //打开注册表选项 HKEY key; LPCTSTR szRun = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"; if (RegOpenKeyEx(HKEY_CURRENT_USER,szRun,0,KEY_SET_VALUE,&key) == ERROR_SUCCESS) { if (ischecked != 0 )//选中 { //打开成功,设置开机启动项 char szFileName[MAX_PATH] = {0}; //获取当前进程的路径 DWORD dRet = GetModuleFileName(NULL,szFileName,MAX_PATH); // 添加一个值项,即添加开机启动项,第二个参数为应用程序名,不加.exe后缀 long lRet = RegSetValueEx(key,"ShutDown",0,REG_SZ,(BYTE *)szFileName,dRet); if (lRet != ERROR_SUCCESS) { MessageBox("添加启动项失败"); } else if(lRet == ERROR_SUCCESS) { isInStartUp = true; MessageBox("添加启动项成功"); } } else if (ischecked == 0 && isInStartUp) { long lRet = RegDeleteValue(key,"ShutDown"); if (lRet != ERROR_SUCCESS) { MessageBox("删除错误"); } } } RegCloseKey(key); }
好了,就这样吧,最小化到任务栏后面再说