设计初衷
在使用 COM 模式操作 OFFICE 组件的开发过程中,当操作完相关文档后,在某些情况下仍然无法释放掉 Word 或 EXCEL 等应用进程,因此根据进程活动情况或时间点范围开发了一个强制杀掉指定进程名称的 WinForm 程序,做为一种辅助工具运行在云服务器上,命名为 Craneoffice ProcessGC。
开发运行环境
操作系统: Windows Server 2019 DataCenter
.net版本: .netFramework4.0 或以上
开发工具:VS2019 C#
Craneoffice ProcessGC
该工具软件属绿色版,无须安装,直接运行 bin 目录下的 ProcessGC.exe 即可,同目录下的 ProcessList.txt 为配置文件,用于配置要释放的进程,后续也可用工具程序进行操作。
运行主界面
运行后的主界面如下图:
主界面显示了要回收的进程列表,源引于 ProcessList.txt 文件配置,如图示例我们可以看到欲回收的进程为EXCEL和WORD,下面则显示已被回收的进程(如果有的话)。
下方显示 CPU 的利用率和可用内存情况。
系统会随时监测指定的进程名,如果有则按指定的策略进行管理。
管理任务与策略
点击管理任务选项卡,显示如下图:
策略设置如下表:
序号 |
项 |
说明 |
1 |
要添加回收的进程名 |
请正确添加,无须输入扩展名,如果不确定名称则可以通过任务管理器进行查看 |
2 |
管理策略 |
共分三种方式: 1、Force(默认值,表示符合条件则强制关闭) 2、CPU 3、Memor |
3 |
回收时间差(秒) |
以秒为单位,记录进程停止活动的时间,超过停止活动时间的进程将被回收 |
4 |
上限指标 |
1、上限指标不能小于零。 2、当选用的策略为CPU时,上限指标不能大于100(即最高100%占用率) 3、当选用的策略为Memory时,指定为Mb值,表示内存占用的最高上限 |
5 |
检测时间标准 |
1、ByProcess,以进程时间计算(默认) 2、ByNowTime,以启动当前时间计算 |
6 |
回收动作 | 共有两种方式: 1、Kill,直接关闭释放进程(默认) 2、Command,执行命令行操作 |
7 |
相关动作命令 |
当第6项回收动作为Command时,此项为必输入项,表示要执行的 WINDOWS 命令行操作 |
8 |
重新启用命令 | 设置此项,则当关闭或执行动作命令后,尝试执行此命令 |
9 |
计划强制关闭时间(小时) |
可以设定小时:分:秒(这个值前缀需要设置一个有效日期),代表每到此时此分此秒,则强制关闭进程 |
通过以上设置,我们可以灵活的对进程的关闭方式进行控制,以达到实际应用的目的。
其它设置
选择设置选项卡,如下图: 我们可设置窗口的透明度,另外可以设置两个选项:
1、只回收本程序启动时间以后的进程
2、只回收非激活窗口的进程, 指非操作系统显式的应用进程。
3、管理密码:默认值为111111,用于关闭应用程序等操作。
移动存储设备管理
这个选项如下图: 这是为移动设备存储数据库准备的一个选项,点击停止服务可以有效的、安全的移除存储设备,也可以继续启动服务。这个选项较少使用,也需要谨慎使用(因为它会尝试停止IIS、MS SQL SERVER 数据库服务等)。
核心代码-计时器监控
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { DateTime cur_now=System.DateTime.Now; int cur_hour=cur_now.Hour; int cur_min=cur_now.Minute; int cur_sec=cur_now.Second; pc.CategoryName="Processor"; pc.CounterName="% Processor Time"; pc.InstanceName="_Total"; // pc.MachineName="michaeljane"; float pcv=pc.NextValue(); label7.Text="CPU利用率:"+(pcv).ToString()+" %"; cpubar.Value=(int)(pcv); pcmem.CategoryName="Memory"; pcmem.CounterName="Available MBytes"; pcmem.InstanceName=null; // richTextBox1.Text=pcpu.NextValue().ToString()+"\r\n"; /* System.Diagnostics.PerformanceCounter[] mypc; System.Diagnostics.PerformanceCounterCategory mycat = new System.Diagnostics.PerformanceCounterCategory("process"); // Remove the current contents of the list. // Retrieve the counters. mypc = mycat.GetCounters(); // Add the retrieved counters to the list. richTextBox1.Text=""; for (int i = 0; i < mypc.Length; i++) { richTextBox1.Text+=(mypc[i].CounterName)+"\r\n"; } */ // float cpuLoad = pc.NextValue(); try { label7.Text += "\r\n可用内存:" + pcmem.NextValue().ToString() + "M"; } catch (Exception s) { } statusBar1.Text=cur_now.ToString(); //显示当前时间 Process[] processes; //定义进程组 processes = System.Diagnostics.Process.GetProcesses(); //获得当前进程组 Process process; //定义初始进程中间变量 string _pname,_killstate=""; //定义进程名变量,及进程回收状态字符串 bool _kill=false; //是否要回收标志变量 bool _phandle=checkBox2.Checked; int _gcSpan; //时间差变量 DateTime _pdatetime,_checktime,_stdtime; //进程启动的时间变量和检测时间变量 string[] _rv; //接受检测结果的数组 System.TimeSpan _dd; //时间差的秒数变量 // string[] _processid=new string[1000]; // DateTime[] _processLastTime=new DateTime[1000]; // int[] _processLastMem=new int[1000]; for(int i = 0;i<processes.Length-1;i++) { process= processes[i]; //获得当前进程 _pname=process.ProcessName.ToLower(); //获得进程名并转为小写 try { _pdatetime = process.StartTime; //获得进程的启动时间 }catch(Exception e1) { continue; } // _rv=GetSubValueIndex(listBox1,_pname,"|"); //得到要回收的用户指定进程 for(int li=0;li<listBox1.Items.Count;li++) { _rv=listBox1.Items[li].ToString().Split('|'); string ref_process=_rv[0].ToLower().Trim(); int ref_span=int.Parse(_rv[1].ToString()); string ref_cl=_rv[2].ToLower().Trim(); float ref_rank=float.Parse(_rv[3].ToString()); string ref_stdtime=_rv[4].ToLower().Trim(); string ref_act=_rv[5].ToLower().Trim(); string[] ref_cmd1=_rv[6].Split('↙'); string[] ref_cmd2=_rv[7].Split('↙'); string ref_closetime=_rv[8].Trim(); // richTextBox1.Text+=_rv[0]+_rv[1]+"\r\n"; if(ref_process==_pname) //如果是要回收的进程则进行处理 { //如果在检测数组没有找到,则添加到检测数中 int _curpoint=System.Array.IndexOf(_processid,process.Id); if(_curpoint<0) { _stdtime=process.StartTime; if(ref_stdtime=="bynowtime") { _stdtime=System.DateTime.Now; } System.Diagnostics.PerformanceCounter pcm=new System.Diagnostics.PerformanceCounter("Process","% Processor Time",_pname); try { AddArray(process.Id, _stdtime, process.WorkingSet, pcm); }catch(Exception e3) { } continue; } // richTextBox1.Text+=((System.Diagnostics.PerformanceCounter)_processLastCPU[_curpoint]).NextValue().ToString()+"\r\n"; try { float cur_rank = ((System.Diagnostics.PerformanceCounter)_processLastCPU[_curpoint]).NextValue(); _checktime = System.DateTime.Now; //检测时间为当前时间 //开始分析CPU策略 if (ref_cl == "cpu") { //如果当前进程的CPU占用率没有超过指定的上限,则重置最后的检测时间为当前时间,继续监测。 if (cur_rank < ref_rank) { _processLastTime[_curpoint] = _checktime; } } }catch(Exception e2) { continue; } //开始分析memory策略 if(ref_cl=="memory") { float _curmem=process.WorkingSet/(1024*1024); // richTextBox1.Text+=_pname+" "+_curmem.ToString()+"\r\n"; //如果当前进程的内存占用没有超过指定的上限,则重置最后的检测时间为当前时间,继续监测。 if(_curmem<ref_rank) { _processLastTime[_curpoint]=_checktime; } } _gcSpan=ref_span; //得到用户指定的回收时间差 _kill=false; _pdatetime=_processLastTime[_curpoint]; //得到进程的数组最后指定时间 _dd=_checktime-_pdatetime; //时间差以检测时间 减去 进行启动时间 //如果时间差大于回收指定时间则可以回收进程,KILL变量为真 if(checkBox1.Checked) { //只回收本程序启动以后的进程时间 if((_dd.TotalSeconds>_gcSpan)&&(_starttime<_pdatetime)) { _kill=true; } } else { if(_dd.TotalSeconds>_gcSpan) { _kill=true; } } //如果初期标识为可以关闭该进程,并且策略为强制性关闭,则进行内存判断 if((_kill)&&(ref_cl=="force")) { //如果内存有变化,则表示进程仍在活动,则不进行关闭,并更新检测内容 int _curmem=process.WorkingSet; label6.Text=_curmem.ToString()+" "+_processLastMem[_curpoint]; if(_curmem!=_processLastMem[_curpoint]) { _processLastTime[_curpoint]=_checktime; _processLastMem[_curpoint]=_curmem; _kill=false; } } //如果指定了强制关闭时间,则进行判断 string close_tip=""; if(ref_closetime!="") { DateTime ref_cls=DateTime.Parse(ref_closetime); if((ref_cls.Hour==cur_hour)&&(ref_cls.Minute==cur_min)&&(ref_cls.Second==cur_sec)) { _kill=true; close_tip="强制关闭计划启动,计划时间为:"+ref_closetime; } } //如果只回收死进程,而当前进程为激活的窗口的话,则不关闭 if((_phandle)&&((int)process.MainWindowHandle!=0)) { _kill=false; } //如果可以回收则在文本框中添加回收状态,并将进程关闭 if(_kill) { if(!process.HasExited) { if(ref_act=="kill") { // MessageBox.Show("has exited"); _killstate=close_tip+".进程"+_pname+"已被回收,关闭策略为"+ref_cl+",动作为:"+ref_act+"。进程的启动时间为"+ _pdatetime.ToString()+",检测时间为:"+_checktime.ToString()+ ",现已经超时"+(_dd.TotalSeconds-_gcSpan).ToString()+ "秒,回收时间单位是"+_gcSpan.ToString()+"秒。"+ "进程ID:"+process.Id.ToString()+ "进程主窗口句柄:"+process.MainWindowHandle.ToString(); process.Kill(); richTextBox1.AppendText(_killstate+"\r\n"); } if(ref_act=="command") { // MessageBox.Show("has exited"); string _return=""; for(int st=0;st<ref_cmd1.GetLength(0);st++) { _return+=ref_cmd1[st]+" Result:"+WinExec(ref_cmd1[st],0).ToString()+"↙"; } _killstate=close_tip+".进程"+_pname+"已被回收,关闭策略为"+ref_cl+",动作为:"+ref_act+",执行了命令:"+ ref_cmd1+"。返回值为:"+_return+"。进程的启动时间为"+ _pdatetime.ToString()+",检测时间为:"+_checktime.ToString()+ ",现已经超时"+(_dd.TotalSeconds-_gcSpan).ToString()+ "秒,回收时间单位是"+_gcSpan.ToString()+"秒。"+ "进程ID:"+process.Id.ToString()+ "进程主窗口句柄:"+process.MainWindowHandle.ToString(); richTextBox1.AppendText(_killstate+"\r\n"); // process.Kill(); } //清空当前进程检测数组元素 _processid[_curpoint]=0; _processLastTime[_curpoint]=_checktime; _processLastMem[_curpoint]=0; _processLastCPU[_curpoint]=null; }//判断进程是否已经退出 } }//if proecess else //如果没有找到进程名称,则二次判断任务是否提供了启动命令,如果提供,则运行它 { }//end find process name }//li }//for processes = System.Diagnostics.Process.GetProcesses(); //获得当前进程组 for(int ali=0;ali<listBox1.Items.Count;ali++) { _rv=listBox1.Items[ali].ToString().Split('|'); string ref_process=_rv[0].ToLower().Trim(); int ref_span=int.Parse(_rv[1].ToString()); string ref_cl=_rv[2].ToLower().Trim(); float ref_rank=float.Parse(_rv[3].ToString()); string ref_stdtime=_rv[4].ToLower().Trim(); string ref_act=_rv[5].ToLower().Trim(); string[] ref_cmd1=_rv[6].Split('↙'); string ref_start_cmd=_rv[7]; string[] ref_cmd2=_rv[7].Split('↙'); bool _find=false; if(ref_start_cmd!="") { for(int i = 0;i<processes.Length-1;i++) { process= processes[i]; //获得当前进程 string cur_pname=process.ProcessName.ToLower(); //获得进程名并转为小写 if(cur_pname==ref_process) { _find=true; } } if(!_find) { string _return=""; for(int st=0;st<ref_cmd2.GetLength(0);st++) { _return+=ref_cmd2[st]+" Result:"+WinExec(ref_cmd2[st],0).ToString()+"↙"; } _killstate="进程"+ref_process+"尝试启动,关闭策略为"+ref_cl+",动作为:"+ref_act+",启动命令为:"+ ref_cmd2+"。返回值为:"+_return+"。"; richTextBox1.AppendText(_killstate+"\r\n"); } } }//end for listbox }
小结
开发这款小工具,也是初识 System.Diagnostics(与系统进程、事件日志和性能计数器进行交互的类)的一个过程。
这里可以下载完整源码:https://download.csdn.net/download/michaelline/89140846
工具仅作学习使用,大家感兴趣的话可以按照自己的需求进行修改,感谢您的阅读,希望本文能对您有所帮助。