电脑安装了360之后
我开始怀疑人生了
开发环境:Visual Studio 2013、Win7 x64、MFC
【 功能模块目录】:
1. 病毒查杀:MD5查杀、白名单查杀、单个文件和全路径查杀,可使用本地病毒库或网络病毒库(网络病毒库自己搭建服务器通信)。
2. 垃圾清理:系统垃圾、浏览器垃圾、注册表的清理。
3. 电脑加速:简单的内存优化,释放内存。
4. 软件管理:显示本机上所安装的软件,并可以进行卸载操作。
5 .启动项管理:显示本机的启动项,并可以进行删除和添加操作。
6. 服务管理:显示本机的所有服务,列举服务状态、启动类型、服务类型等信息,可进行启动和停止服务操作。
附几张图项目效果图预览一下:
本项目用的是MFC框架开发,按钮基本都是用的自绘,图片素材是我从一个外国的资源网站下载来然后自己PS的颜色。
自我感觉界面还不错,总比原生的MFC界面强吧哈哈!
项目框架:
【首页信息】是主窗口,其他各个模块都是主窗口的子窗口,各个模块的功能都是独立的,每个子窗口都已自己的类。需要的时候,各个模块会启用多线程处理。模块之间的切换,其实就是现实当前选择的功能模块窗口,隐藏其他的子窗口。
每个模块可以分开来单独看,模块之间的联系不大。
1.【病毒查杀】模块
首先要做的就是确定用户扫描的选项,比如,是单个文件查杀还是全路径查杀,是用本地病毒库还是用云端病毒库,这些信息我都会设置一个BOOL值,在开始扫描之前将他们赋值,然后在扫描的时候只需判断这些选项的BOOL值来做出不同的扫描方案。
接下来就是按照选项进行扫描了,当进行单个文件扫描的时候,只需弹出一个选择单个文件的CFileDialog保存其路径即可;如果是全路径扫描,则需要保存用户所选要扫描的路径并保存。
然后就是开始扫描文件了,我觉得管它叫遍历文件更准确,如果是单个文件扫描的话,文件路径之前已经保存,就不需要遍历,直接通过绝对地址对文件进行病毒判断就行;如果是全路径扫描,就需要遍历指定文件加下的所有文件了。具体的功能是通过两个API实现的:FindFirstFile(
)和FindNextFile( ),首先调用FindFirstFile(
),获取遍历出来的第一个文件的路径,然后再循环执行FindNextFile( )寻找下一个文件,直到FindNextFile(
)返回FALSE就跳出循环,遍历结束。需要注意的是,当遍历到的路径是文件夹的时候,需要将这个文件夹的路径当做参数传入本函数,也就是递归执行此函数,这样就不会遗漏一个路径下所有的文件了。
还有一点就是,在执行扫描部分的代码的时候,最好另起一个线程,这样就不至于在扫描的时候主界面会卡在那不动。
具体的代码实现如下:
1 void Scam_All(LPCTSTR szPath) 2 { 3 WIN32_FIND_DATA wfd; 4 HANDLE hFind; 5 CString sFullPath; 6 CString sFindFilter; 7 DWORD dwAttributes = 0; 8 sFindFilter = szPath; 9 sFindFilter += TEXT("\*.*"); 10 if ((hFind = FindFirstFile(sFindFilter, &wfd)) == INVALID_HANDLE_VALUE) 11 return; 12 do 13 { 14 if (_tcscmp(wfd.cFileName, TEXT(".")) == 0 || 15 _tcscmp(wfd.cFileName, TEXT("..")) == 0) 16 { 17 continue; 18 } 19 //获取完整路径名 20 sFullPath = szPath; 21 sFullPath += TEXT(""); 22 sFullPath += wfd.cFileName; 23 //如果当前路径是文件夹,则需要递归文件夹中的文件 24 if (wfd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) 25 { 26 Scam_All(sFullPath); 27 } 28 //否则,这是一个文件 29 else 30 { 31 //输出正在搜索的文件路径界面,让用户直观的看到正在扫描的文件路径 32 m_szStatus = _T("正在扫描 : ")+sFullPath; 33 SendMessage(WM_MYUPDATEDATA, FALSE); 34 //判断是否是病毒 35 if (IsMD5) 36 { 37 //MD5查杀 38 //如果是病毒 将文件信息保存到病毒Vector 39 if (MD5Scan(sFullPath)) 40 { 41 //插入List中显示 42 m_List_Vir.InsertItem(0, wfd.cFileName); //文件名 43 m_List_Vir.SetItemText(0, 1, sFullPath); //文件路径 44 m_List_Vir.SetItemText(0, 2, //文件大小 45 GetShowSize(wfd.nFileSizeLow)); 46 //保存病毒信息到Vector容器 47 VIRINFO VirInfo; 48 _tcscpy_s(VirInfo.szVirName, wfd.cFileName); 49 _tcscpy_s(VirInfo.szVirPath, sFullPath); 50 _tcscpy_s(VirInfo.szVirSize, GetShowSize(wfd.nFileSizeLow)); 51 m_vecVirInfo.push_back(VirInfo); 52 } 53 } 54 else 55 { 56 //白名单查杀 57 //如果是病毒 将文件信息保存到病毒Vector 58 if (WhiteScan(sFullPath)) 59 { 60 //插入List中显示 61 m_List_Vir.InsertItem(0, wfd.cFileName); //文件名 62 m_List_Vir.SetItemText(0, 1, sFullPath); //文件路径 63 m_List_Vir.SetItemText(0, 2, //文件大小 64 GetShowSize(wfd.nFileSizeLow)); 65 //保存病毒信息到Vector容器 66 VIRINFO VirInfo; 67 _tcscpy_s(VirInfo.szVirName, wfd.cFileName); 68 _tcscpy_s(VirInfo.szVirPath, sFullPath); 69 _tcscpy_s(VirInfo.szVirSize, GetShowSize(wfd.nFileSizeLow)); 70 m_vecVirInfo.push_back(VirInfo); 71 } 72 } 73 } 74 } while (FindNextFile(hFind, &wfd)); 75 FindClose(hFind); 76 }
在遍历文件的时候,当获取到文件的完整路径的时候,就可以对其进行病毒检测了。我这里仅仅是象征性的使用了两种检测方式,MD5对比查杀和白名单查杀(当然,真正的病毒查杀肯定不会这么简单),将要杀的文件路径当做参数传递给检测病毒的函数,MD5扫描函数是:MD5Scan(sFullPath),白名单扫描函数是:WhiteScan(sFullPath),如果判断是病毒的话返回TRUE,这时就可以保存这个路径,并将这个“病毒文件”信息输出到List控件中。
这两个函数的代码如下:
1 BOOL MD5Scan(LPCTSTR szPath) 2 { 3 //LPCTSTR 转CHAR* 4 int num = WideCharToMultiByte(CP_OEMCP, NULL, szPath, -1, NULL, 0, NULL, FALSE); 5 char *pchar = new char[num]; 6 WideCharToMultiByte(CP_OEMCP, NULL, szPath, -1, pchar, num, NULL, FALSE); 7 //获取MD5值 8 char* md5 = md5FileValue(pchar); 9 //CHAR* 转LPCTSTR 10 num = MultiByteToWideChar(0, 0, md5, -1, NULL, 0); 11 wchar_t *wide = new wchar_t[num]; 12 MultiByteToWideChar(0, 0, md5, -1, wide, num); 13 m_szMD5 = wide; 14 delete[]pchar; 15 delete[]wide; 16 17 //获取文件MD5信息完毕 18 //判断是本地查杀还是云端查杀 19 if (IsLocal) 20 { 21 //本地MD5查杀 与m_LocalMD5作对比 22 for (DWORD i = 0; i < m_LocalMD5.size();i++) 23 { 24 if (m_LocalMD5[i] == m_szMD5) 25 { 26 //是病毒 返回真 27 return TRUE; 28 } 29 } 30 } 31 else 32 { 33 //云端MD5查杀 与m_ServerMD5作对比 34 //本地MD5查杀 与m_LocalMD5作对比 35 for (DWORD i = 0; i < m_ServerMD5.size(); i++) 36 { 37 if (m_ServerMD5[i] == m_szMD5) 38 { 39 //是病毒 返回真 40 return TRUE; 41 } 42 } 43 } 44 return FALSE; 45 }
1 BOOL WhiteScan(LPCTSTR szPath) 2 { 3 //判断是本地查杀还是云端查杀 4 if (IsLocal) 5 { 6 //本地白名单查杀 与m_LocalWhite作对比 7 for (DWORD i = 0; i < m_LocalWhite.size(); i++) 8 { 9 if (m_LocalWhite[i] == szPath) 10 { 11 //是病毒 返回真 12 return FALSE; 13 } 14 } 15 } 16 else 17 { 18 //云端MD5查杀 与m_ServerWhite作对比 19 for (DWORD i = 0; i < m_ServerWhite.size(); i++) 20 { 21 if (m_ServerWhite[i] == szPath) 22 { 23 //是病毒 返回真 24 return FALSE; 25 } 26 } 27 } 28 return TRUE; 29 }
MD5查杀对比的时候,需要获取一下这个文件的MD5值,这里用到了一个MD5类的函数,这个类我用的是网上开源的代码(源码中包含)。
MD5数据库和白名单查杀数据库,是在扫描开始前根据用户选项,然后从本地或云端将数据库信息载入内存的,本地数据库我用的是ini配置文件(象征性表示一下),云端查杀数据库是通过TCP通信下载的云端数据库信息。TCP服务端的代码在源码中有,但由于服务端还需要连接一个MySQL数据库,这样测试起来比较麻烦,所以建议只测试本地查杀功能,反正这个病毒查杀只是个象征性的对比,仅仅用于展现杀毒模块的功能、给大家提供个这个框架而已。
判断该文件为病毒以后,将该文件的信息保存在一个vector容器成员变量中(m_vecVirInfo),在删除的时候,遍历这个vector,然后依次删除即可。
删除病毒代码:
1 void DeleteVir() 2 { 3 //先判断病毒Vector是否为空,若为空,则提示没有可以清除的文件 4 if (m_vecVirInfo.size() == 0) 5 { 6 return; 7 } 8 //删除病毒 9 for (DWORD i = 0; i < m_vecVirInfo.size();i++) 10 { 11 DeleteFile(m_vecVirInfo[i].szVirPath); 12 } 13 m_vecVirInfo.clear(); 14 m_List_Vir.DeleteAllItems(); 15 m_szStatus = _T("病 毒 清 除 完 毕 !"); 16 UpdateData(FALSE); 17 }
注:本地病毒库位置为exe文件所在目录下: .LocalVirBaseVir.ini
2.【清理垃圾】模块
清理方式主要有两种,一种是删除磁盘上的文件,另一种是删除注册表项。像清理系统临时垃圾、清理浏览器垃圾这样的功能,是通过第一种清理方式实现的;而像清理最近文档记录、文件查找记录则是通过第二种清理方式实现的。
第一种清理方式的具体操作:
以清理Interner临时文件为例,首先需要获取Interner临时文件的路径,由于每台电脑这个路径或许会有所不同,所以不能写死,这时候利用一个Windows API可以轻松的获取到你想要获取的各种系统路径:
1 BOOL SHGetSpecialFolderPath( 2 HWND hwndOwner, 3 _Out_ LPTSTR lpszPath, 4 _In_ int csidl, 5 _In_ BOOL fCreate 6 );
第一个参数传NULL,最后一个参数传FALSE。
第二个参数是保存路径的缓冲区。
第三个参数是一个宏,传入不同的宏可以获取不同的路径,例如传入 CSIDL_INTERNET_CACHE ,获取的就是Interner临时文件的路径。
这些宏在MSDN上有给出,数量很多,基本可以获取到你想要获取的所有系统路径。
SHGetSpecialFolderPath 介绍:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb762204(v=vs.85).aspx
第三个参数宏:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb762494(v=vs.85).aspx
获取完需要清理的路径以后就需要扫描或清理此路径下的文件了,扫描和清理功能我用的是同一个函数,区别就是定义了一个BOOL值
IsScanFile
,如果为TRUE,就只是扫描垃圾文件,并将垃圾文件显示出来,然后计算总的垃圾文件大小;如果IsScan为FALSE,在扫描的同时就会进行删除。
这个扫描垃圾文件的函数同【病毒查杀】模块中的扫描函数类似,都是传入一个路径参数,然后遍历该路径下的所有文件,原理大同小异,唯一不同的就是对文件的处理而已,所以这里扫描和清理垃圾文件的代码我就不贴出来了,具体的也可以在源码中找到。
第二种清理方式的具体操作:
以清理运行记录为例,删除注册表键值之前,同样需要获取要删除注册表键值的路径。我没有找到获取某些指定注册表项路径的API,所以我就用了最笨的办法,手动写死要删除的注册表路径。保存运行记录的注册表位置为:"Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU",删除该注册表的API为:SHDeleteKey(
),代码实现如下:
1 VOID CleanRunHistory() 2 { 3 if (IsScanFile) 4 return; //如果是正在扫描垃圾文件,则直接返回,不进行清理操作。 5 SHDeleteKey(HKEY_CURRENT_USER, 6 TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU")); 7 }
要想丰富项目的清理功能,很多时候就需要自己搜集垃圾文件的路径或注册表键值,拓展一下可以是清理某些指定软件的垃圾文件,搜集这些信息都是体力活。我这个项目只是清理了部分系统的垃圾,清理其他软件的垃圾原理类似。
3.【电脑加速】模块
上面两大模块功能已经讲完了,剩下的几个模块就简单了许多。
电脑加速模块的功能类似于很多优化软件的内存优化,所谓的优化其实就是腾出更多的可用内存空间。
用到的两个函数:
1 BOOL WINAPI SetProcessWorkingSetSize( 2 _In_ HANDLE hProcess, 3 _In_ SIZE_T dwMinimumWorkingSetSize, 4 _In_ SIZE_T dwMaximumWorkingSetSize 5 );
1 BOOL WINAPI EmptyWorkingSet( _In_ HANDLE hProcess);
关于SetProcessWorkingSetSize,如果第二个参数和第三个参数都传“-1”的话,MSDN上的解释如下:
If both dwMinimumWorkingSetSize and dwMaximumWorkingSetSize have
the value (SIZE_T)–1, the function removes as many pages as possible
from the working set of the specified process.
直译为尽可能多的将指定进程的页面从工作区中移除,我的理解就是将进程的页面置换到虚拟内存(磁盘)中,这样就可以腾出更多的内存空间了。
另外一个函数EmptyWorkingSet,这是个宏,MSDN的解释也是Removes as many pages as
possible from the working set of the specified
process.所以这两个函数的功能貌似是一样的,不知道这两个函数的效果是否会叠加,反正对于一个进程,我都会调用一下这两个函数。、
调用上面两个函数需要传入进程句柄,也就是说同时只能优化一个进程的内存空间,那么我们就还需要遍历整个系统的所有进程,然后获取每个进程的句柄,将该句柄当做参数传入上面两个函数,就会达到优化所有进程内存空间的效果。
遍历进程并进行优化的代码如下:
1 void ClearMemory() 2 { 3 HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 4 if (hProcessSnap == INVALID_HANDLE_VALUE) 5 { 6 return; 7 } 8 9 PROCESSENTRY32 ProcessInfo; 10 ProcessInfo.dwSize = sizeof(ProcessInfo); 11 int count = 0; 12 //获取系统中第一个进程的信息 13 BOOL Status = Process32First(hProcessSnap, &ProcessInfo); 14 while (Status) 15 { 16 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, 17 ProcessInfo.th32ProcessID); 18 if (hProcess) 19 { 20 //设置进程工作区大小 21 SetProcessWorkingSetSize(hProcess, -1, -1); 22 //尽可能多的将指定进程的页面从工作区移除 23 EmptyWorkingSet(hProcess); 24 CloseHandle(hProcess); 25 } 26 //获取下一个进程的信息 27 Status = Process32Next(hProcessSnap, &ProcessInfo); 28 } 29 }
执行完上面这个函数以后,为了直观的显示优化后所腾出的内存空间,可以通过一个获取当前内存信息的代码计算,在执行清理之前计算一下当前的内存使用量,在执行完清理之后再计算一下内存的使用量,两个计算结果相减就可以得出本次优化后所腾出的内存空间。获取当前内存使用量的代码如下:
1 m_szStatus = _T("正在清理内存中... ..."); 2 UpdateData(FALSE); 3 //获取清理前的内存信息 4 MEMORYSTATUSEX memStatus = { sizeof(MEMORYSTATUSEX) }; 5 GlobalMemoryStatusEx(&memStatus); 6 DOUBLE preUsedMem = (DOUBLE)(memStatus.ullTotalPhys - memStatus.ullAvailPhys) / 1024 / 1024; 7 //开始清理内存 8 ClearMemory(); 9 //获取清理后的内存信息 10 GlobalMemoryStatusEx(&memStatus); 11 DOUBLE afterUsedMem = (DOUBLE)(memStatus.ullTotalPhys - memStatus.ullAvailPhys) / 1024 / 1024; 12 //输出清理信息 13 m_szStatus.Format(_T("内存清理完毕!本次清理 : %.2f MB"), preUsedMem - afterUsedMem);
我自己关于内存优化的看法:
首先从一种暴力清理内存的方式展开说。上面的内存清理方式,所腾出的可用内存空间并不明显,于是我从网上找到了另一种暴力清理方式,原理就是用一个进程大量的申请内存空间,瞬间填满你的内存,这时候系统就会调用其本身的置换操作,将其他进程的内存空间置换到虚拟内存(磁盘)中,该进程再突然释放所申请的大量空间,然后内存就会瞬间腾出很大的空间。我在我的4GB内存电脑上实测了这种内存清理方法,效果要好于我上面所说的清理方式,我所说的“效果好”其实是指的所腾出的可用内存空间大,虽然你看上去会有很大的空闲内存可用,但你会发现当你优化完,再去执行其他的程序时,会出现明显的卡顿,在卡顿的过程中,你的内存使用量又涨上去了。
上面所说的卡顿现象,我觉得是进程又将自己原有的内存信息从磁盘中读入内存,这会给系统带来很大的开销,因为需要大量IO操作,而磁盘的读写速度与内存完全不是一个量级的,总之我觉得这种极端暴力的内存优化着实鸡肋。从这个极端地例子可以得出结论,并不是“清理”的内存越多系统就越快,反而清理过多了会拖慢系统的运行,因为在内存中的速度那么快,干嘛要把它置换到磁盘中呢?
综上所述,所谓的内存优化其效果是仁者见仁智者见智,就我个人而言觉得这个功能并不实用,对于小内存的电脑来说优化其实是在拖慢系统,而对于大内存的电脑来说又没有什么必要。
So,优化内存最有效的方式就是——插内存条。。。
4.【软件管理】模块
获取安装软件的信息,其实就是枚举注册表项,查询注册表键值然后保存。
定义一个保存软件信息的结构体(自己定义的):
1 typedef struct _SOFTINFO 2 { 3 TCHAR szSoftName[50]; //软件名称 4 TCHAR szSoftVer[50]; //软件版本号 5 TCHAR strSoftVenRel[50]; //软件发布厂商 6 TCHAR szSoftData[20]; //软件安装日期 7 TCHAR strSoftInsPath[MAX_PATH]; //软件安装路径 8 TCHAR strSoftUniPath[MAX_PATH]; //软件卸载路径 9 }SOFTINFO, *PSOFTINFO;
32位和64位系统的查询位置也略有不同,可以自己定义两个宏,根据判断是否为64位进行选择。具体实现代码如下:
1 #define Win32PATH _T("SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") 2 #define Win64PATH _T("SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall") 3 void GetSoftwareInfo() 4 { 5 m_Dlg5_List.DeleteAllItems(); 6 m_vecSoftInfo.clear(); 7 SOFTINFO stcSoftInfo = {0}; // 软件信息结构体 8 HKEY RootKey; // 主键 9 LPCTSTR lpSubKey; // 子键名称 10 HKEY hkResult; // 将要打开键的句柄 11 HKEY hkRKey; 12 LONG lReturn; // 记录读取注册表是否成功 13 CString strBuffer; 14 CString strMidReg; 15 DWORD index = 0; 16 TCHAR szKeyName[255] = { 0 }; // 注册表项名称 17 DWORD dwKeyLen = 255; 18 DWORD dwNameLen = 255; 19 DWORD dwType = 0; 20 RootKey = HKEY_LOCAL_MACHINE; 21 lpSubKey = Is64() ? Win64PATH : Win32PATH; 22 lReturn = RegOpenKeyEx(RootKey, lpSubKey, 0, 23 KEY_ALL_ACCESS, &hkResult); 24 if (lReturn == ERROR_SUCCESS) 25 { 26 DWORD index = 0; 27 DWORD ListIndex = 0; 28 while (ERROR_NO_MORE_ITEMS != 29 RegEnumKeyEx(hkResult, index, szKeyName, &dwKeyLen, 30 0, NULL, NULL, NULL)) 31 { 32 strBuffer.Format(_T("%s"), szKeyName); 33 if (!strBuffer.IsEmpty()) 34 { 35 strMidReg = (CString)lpSubKey + _T("") + strBuffer; 36 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, strMidReg, 37 0, KEY_ALL_ACCESS, &hkRKey) == ERROR_SUCCESS) 38 { 39 RegQueryValueEx(hkRKey, _T("DisplayName"), 40 0, &dwType, (LPBYTE)stcSoftInfo.szSoftName, &dwNameLen); 41 dwNameLen = 255; 42 RegQueryValueEx(hkRKey, _T("DisplayVersion"), 43 0, &dwType, (LPBYTE)stcSoftInfo.szSoftVer, &dwNameLen); 44 dwNameLen = 255; 45 RegQueryValueEx(hkRKey, _T("InstallLocation"), 46 0, &dwType, (LPBYTE)stcSoftInfo.strSoftInsPath, &dwNameLen); 47 dwNameLen = 255; 48 RegQueryValueEx(hkRKey, _T("Publisher"), 49 0, &dwType, (LPBYTE)stcSoftInfo.strSoftVenRel, &dwNameLen); 50 dwNameLen = 255; 51 RegQueryValueEx(hkRKey, _T("UninstallString"), 52 0, &dwType, (LPBYTE)stcSoftInfo.strSoftUniPath, &dwNameLen); 53 dwNameLen = 255; 54 RegQueryValueEx(hkRKey, _T("InstallDate"), 55 0, &dwType, (LPBYTE)stcSoftInfo.szSoftData, &dwNameLen); 56 if (stcSoftInfo.szSoftData[0]) 57 { 58 stcSoftInfo.szSoftData[9] = stcSoftInfo.szSoftData[7]; 59 stcSoftInfo.szSoftData[8] = stcSoftInfo.szSoftData[6]; 60 stcSoftInfo.szSoftData[7] = '-'; 61 stcSoftInfo.szSoftData[6] = stcSoftInfo.szSoftData[5]; 62 stcSoftInfo.szSoftData[5] = stcSoftInfo.szSoftData[4]; 63 stcSoftInfo.szSoftData[4] = '-'; 64 } 65 dwNameLen = 255; 66 //保存数据 67 if (stcSoftInfo.szSoftName[0]!='