我认为写博客还是比较重要的,特别是短时间写出一个含有新知识点的软件。这样总结下这次编程经验和再捋顺一下这次编程思路。首先来谈谈为什么想做这个小程序,一是感觉自己太贪玩想控制一下,二是也锻炼下自己的编程。好,话不多说进入正题吧。
文件源代码在这个https://i.cnblogs.com/Files.aspx?order=1下面。名字是ApplicationControl。不过使用的时候你还是要到子窗体中设置你的游戏库,我这里只有WeGame、LOL登陆程序的进程名称。源码有点乱,见谅。如需改进,请多指教。
首先给大家展示一下程序结果图:
这里的原理很简单,就是写一个监视进程的函数如果发现进程列表中有相同名字的进程则调用process类中的kill方法杀死进程,再设置一个计时器来循环调用该函数即可。
首先我们窗体加载函数中调用一个初始化函数。
Initial函数:
1 public void Initial() 2 { 3 timer1.Enabled = false; 4 for (int i = 0; i < 24; i++) 5 { 6 Hour.Items.Add(i); //combox控件名称 7 } 8 Hour.SelectedIndex = 0; 9 for (int i = 0; i < 60; i++) 10 { 11 Minute.Items.Add(i); 12 Sceond.Items.Add(i); 13 } 14 Minute.SelectedIndex = 0; 15 Sceond.SelectedIndex = 0; 16 LoadTime(); //后面解释说明 17 string fileName = @"\gamelist.txt"; //后面解释说明 首次加载程序 18 string dirpath = System.Environment.CurrentDirectory; 19 string path = dirpath + fileName;
20 if (File.Exists(path)) 21 { 22 try 23 { 24 StreamReader sr = new StreamReader(path, Encoding.UTF8); 25 string line; 26 while ((line = sr.ReadLine()) != null) 27 { 28 gamename.Add(line); 29 textBox1.AppendText(line + "\r\n"); 30 } 31 sr.Dispose(); 32 } 33 catch { }; 34 } 35 36 }
其中Hour、Minute、sceond(秒翻译错了,应该是second,哈哈)分别是Combox控件的名称。计时器控件则是Timer1。
之后就可以为启动button写函数了。
1 private void button3_Click(object sender, EventArgs e) 2 { 3 flag = 1; 4 timer1.Enabled = true; 5 timer1.Interval = 500; 6 h = Convert.ToInt32(Hour.SelectedItem); //用户选择的时间 7 m = Convert.ToInt32(Minute.SelectedItem); 8 s = Convert.ToInt32(Sceond.SelectedItem); 9 TS = new TimeSpan(h, m, s); //设置一个TimeSpan对象来存放时间 10 }
点击按钮,告诉计时器开始工作,并把用户选择的时间存储起来。
1 //计时器函数。 2 private void timer1_Tick(object sender, EventArgs e) 3 { 4 if (timer1.Enabled == true) 5 { 6 GameControl();//调用进程检查函数,如果存在则查杀 7 string str1 = TS.Hours.ToString() + ":" + TS.Minutes.ToString() + ":" + TS.Seconds.ToString(); 8 time.Text = str1; //设置时间标签 9 string str2 = TSsum.Hours.ToString() + ":" + TSsum.Minutes.ToString() + ":" + TSsum.Seconds.ToString(); 10 label8.Text = str2; //总时间显示标签 11 TS = TS.Subtract(new TimeSpan(0, 0, 1)); //TS 每隔一秒设置时间减一倒计时效果 12 TSsum = TSsum.Add(new TimeSpan(0, 0, 1)); //TSsum 每隔一秒总时间加一 13 if (TS.TotalSeconds < 0.0) //倒计时完毕 14 { 15 timer1.Enabled = false; 16 MessageBox.Show("你离成功又进了一步"); 17 flag = 0; 18 flagtime = 1; 19 LoadTime(); //另外说明 20 } 21 } 22 }
每次调用GameControl去查杀进程。
1 public void GameControl() 2 { 3 List<Process[]> pro = new List<Process[]>(); //进程的泛型集合 因为可能对多个进程进行监视 可以理解为装箱 4 Process[] gameProcess =Process.GetProcessesByName("Taskmgr"); //防止用任务管理器关闭这个程序 5 pro.Add(gameProcess); 6 //gamename是所有游戏进程名字的泛型集合。 7 for (int i = 0; i < gamename.Count; i++) 8 { 9 gameProcess = Process.GetProcessesByName(gamename[i].Replace(".exe", "").Trim()); 10 pro.Add(gameProcess); 11 } 12 try 13 { 14 for (int i = 0; i < pro.Count;i++ ) 15 { 16 Process[] game = pro[i]; //可以理解为拆箱 17 foreach (Process p in game) 18 { 19 p.Kill(); 20 } 21 } 22 } 23 catch (Exception e) 24 { 25 26 } 27 }
这里用一个trycatch防止程序抛异常。
这样连任务管理器都关不了的程序,怎么办呢,我们还是比较人性化设置一个结束button来提前结束这个程序。只是有很多弹出框。~嘿嘿!
1 //结束按钮 2 private void shutdown_Click(object sender, EventArgs e) 3 { 4 Thread th = new Thread(ShutDown); //开启一个新线程运行 5 th.Start(); 6 } 7 public void ShutDown() 8 { 9 if (flag == 1) //flag 标识程序是否跑起来了 0--否 1--是 跑起来才有作用 10 { 11 DialogResult re = MessageBox.Show("能不能再坚持一会儿?", "提示", MessageBoxButtons.YesNo); 12 if (re == DialogResult.No) 13 { 14 Thread.Sleep(5000); //选择一次否,休眠5s(给你时间冷静下冲动的大脑) 15 re = MessageBox.Show("坚持就是胜利,不要放弃!", "提示", MessageBoxButtons.YesNo); 16 if (re == DialogResult.No) 17 { 18 Thread.Sleep(5000); 19 re = MessageBox.Show("你离成功就差一步了!", "提示", MessageBoxButtons.YesNo); 20 if (re == DialogResult.No) 21 { 22 Thread.Sleep(5000); 23 re = MessageBox.Show("想一想自己还有没有其他更重要的事做没做!", "提示", MessageBoxButtons.YesNo); 24 if (re == DialogResult.No) 25 { 26 Thread.Sleep(5000); 27 re = MessageBox.Show("比你牛逼的人还在努力!", "提示", MessageBoxButtons.YesNo); 28 timer1.Enabled = false; //关闭计时器 29 flagtime = 1; 30 flag = 0; //设置程序状态--关闭 31 LoadTime(); //将这段时间加到总时间上 32 } 33 } 34 } 35 } 36 } 37 }
这里开启一个新线程运行这个函数是为了防止我们我们主线程休眠。如果不开,就整个程序休眠5s。开了就相当于这个函数休眠5s。
当然呢,任务管理器关不掉那我们可以点击窗口关闭按钮啦(不过NO way~)
1 //窗体关闭时发生 2 private void ApplicationControl_FormClosing(object sender, FormClosingEventArgs e) 3 { 4 if (flag != 0) //判断程序是否运行 5 { 6 e.Cancel = true;//取消这个动作 7 } 8 }
到这里我们基本已经将这个程序的主要功能做出来了。那我们再做点东西来润色、润色。别看看它只是修饰功能,它的难度一点都不比我们主要功能小。
首先我们不是有个叫“控制总时长”的标签吗:这个是记录我们每次设置时间到结束的总时长。它的原理就是本次程序结束后,生成一个txt文件将这个时间记录时间,如果存在这个文件则覆盖上去,就实现了时间的刷新。当然了,我们每次首先需要在程序加载时,把这个时间读取出来。
1 //加载、更新总时间 2 public void LoadTime() 3 { 4 string fileName = @"\sumtime.txt"; //生成一个名字为sumtime的文本文件 5 string str = System.Environment.CurrentDirectory; //程序所在路径 6 string path = str + fileName; //路径拼接 7 FileStream fs; 8 if (!File.Exists(path)) //如果不存在此文件(首次运行该程序) 9 { 10 fs = new FileStream(path, FileMode.Create); 11 } 12 else if (flagtime == 0) //读取时间 窗体加载时完成 13 { 14 try 15 { 16 StreamReader sr = new StreamReader(path, Encoding.UTF8); 17 DateTime line; 18 line = Convert.ToDateTime(sr.ReadLine()); 19 hsum = line.Hour; 20 msum = line.Minute; 21 ssum = line.Second; 22 TSsum = new TimeSpan(hsum, msum, ssum); //记录读取的总时间 23 label8.Text = line.Hour.ToString() + ":" + line.Minute.ToString() + ":" + line.Second.ToString(); 24 sr.Dispose(); 25 } 26 catch { }; 27 }
你能看到这个函数有两个功能,一个读取时间、一个写入时间,所以这个函数有两个地方被调用到。
读取:初始化程序时调用了(Initial());
写入:当计时器为0时调用(timer);
当点击结束按钮时,设置时间并未到0(相当于强制关闭)用到了。
好了,这个功能做了。那么我们还有一个功能没做,那就是添加游戏名称到我们gamename泛型集合中,当然你可以手动gamename.add("游戏名称");但是我们能不能更人性化、智能化一点呢,答案肯定是可以的。
那我们要做的就是点击“扫描全盘按钮”。它的原理是点击后开启一个新窗体(子窗体)选择要搜索的分区,如果找到了与我们游戏库(这也是一个泛型集合)中名称相同的就记录返回到我们主窗体中的gamename集合中。
先上图
3个checkbox根据选择状态,返回一个值(C、D、E),点击确定开始搜索。搜索完毕,返回找到的游戏名称。
这里特别说一下checkbox的size要重新设置下,我原来以为它的大小就是那个小方框。其实它要大的多,如果间距设小了,其他的checkbox就被挡住了(效果就是只有一个),我当时做的时候卡在这里半天,我还以为出现了灵异事件,最后问了下老师才知道。
那么有人要问了为什么会是C、D、E,我有F盘呢?别急。我们首先在初始化窗体时遍历一遍盘符、统计。
1 List<string> gameBase = new List<string>(); //我们的游戏库 2 //窗体加载 3 private void ChooseArea_Load(object sender, EventArgs e) 4 { 5 int drivesCount = System.IO.Directory.GetLogicalDrives().Length;//获取盘符数 6 CheckBox[] box=new CheckBox[drivesCount]; 7 int x = 110; 8 for (int i = 0; i < drivesCount;i++ ) 9 { 10 box[i] = new CheckBox(); 11 char abc =Convert.ToChar('C'+i); //从C开始+1,转成(D、E...) 12 box[i].Text = abc+ "盘"; //checkbox空间名称 生成 13 box[i].Name=abc.ToString(); 14 box[i].Size = new Size(50,50); 15 box[i].Location = new Point(x,80); 16 groupBox1.Controls.Add(box[i]); 17 x += 55;//3个box的横轴间距 18 } 19 gameBase.Add("tgp_daemon.exe"); //添加游戏进程名称 20 gameBase.Add("Client.exe"); 21 }
这样就能生成相应的checkbox数量了。
然后点击确定button,返回有哪些盘被选中再构建路径去磁盘里搜索。
1 //确定按钮 2 private void button1_Click(object sender, EventArgs e) 3 { 4 List<string> check = new List<string>();//存储选中状态下的checkbox值(C盘、D盘、...) 5 foreach (Control c in groupBox1.Controls)// 遍历groupbox容器里的控件 6 { 7 if (c is CheckBox) //如果类型为Checkbox 8 { 9 if (((CheckBox)c).Checked == true) //判断是否为选中状态 10 { 11 check.Add(((CheckBox)c).Text); 12 } 13 } 14 } 15 for (int i = 0; i < check.Count; i++) 16 { 17 //构建寻找路径(C:\\、D:\\...) 18 string path = check[0].Substring(0, 1)+":\\"; 19 FindFiles(path); //开始搜索 20 this.Close(); //搜索完毕后,自动关闭子窗口 21 } 22 }
其中最重要的一环就是FindFiles(path)函数了。我们怎样将子窗口的搜索到的值返回给我们主窗体呢,那么委托和事件就登场了。
public delegate void GameListDelegate(string str);//声明委托 public event GameListDelegate GameNameUpdate;//声明事件 public void FindFiles(string subpath) { FindFile ff = new FindFile(); //新建一个FindFile类 for (int i = 0; i < gameBase.Count; i++) { ff.getFile(subpath, gameBase[i]); //调用getFile函数,结果会存储在lis集合里。 textBox1.AppendText(ff.lst[i].ToString() + "\r\n");//显示 } if (GameNameUpdate != null) //判断事件是否被注册 { GameNameUpdate(textBox1.Text.ToString()); } }
然后GameNameUpdate这个事件在主窗体‘扫描全盘游戏’按钮点击事件里被注册上LoadGame;
1 private void addGame_Click(object sender, EventArgs e) 2 { 3 chooseArea ca = new chooseArea(); //实例化子窗体 4 ca.GameNameUpdate += LoadGame; //注册LoadGame事件 5 //ca.GameNameUpdate+=new chooseArea.GameListDelegate(LoadGame); 6 ca.Show(); 7 } 8 //LoadGame函数 9 public void LoadGame(string str) 10 { 11 string fileName = @"\gamelist.txt"; 12 string dirpath = System.Environment.CurrentDirectory; 13 string path = dirpath + fileName; 14 FileStream fs; 15 if (!File.Exists(path)) 16 { 17 try 18 { 19 textBox1.Text = str; 20 Regex regex = new Regex("\r\n"); //因为子窗体传过来的值有换行符"\r\n";这里实例化一个正则表达式 regex类的对象来以字符串"\r\n"的形式进行分割 21 string[] name = regex.Split(str); 22 for (int i = 0; i < name.Length-1; i++)//因为分割后或多出一个""变量;所以长度-1 23 { 24 gamename.Add(name[i]); //导入gamename集合中 去查杀 25 } 26 fs = new FileStream(path, FileMode.Create); 27 StreamWriter sw = new StreamWriter(fs); 28 for (int i = 0; i < gamename.Count; i++) 29 { 30 sw.WriteLine(gamename[i]); 31 } 32 sw.Flush(); 33 sw.Close(); 34 } 35 catch { }; 36 } 37 else 38 { 39 try 40 { 41 StreamReader sr = new StreamReader(path, Encoding.UTF8); 42 string line; 43 while ((line=sr.ReadLine()) != null) 44 { 45 gamename.Add(line); 46 textBox1.AppendText(line + "\r\n"); 47 } 48 sr.Dispose(); 49 } 50 catch { }; 51 } 52 }
同样的生成一个文本文件,下次进入程序如果有就不用再扫描全盘了。
再返回我们的子窗体中的FindFiles函数中的FindFile类,它是我们搜索文件的关键。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.IO; 7 namespace ChooseArea 8 { 9 class FindFile 10 { 11 public List<FileInfo> lst = new List<FileInfo>(); 12 public List<FileInfo> getFile(string path, string extName)//创建一个List<FileInfo>类型的函数 13 { 14 getdir(path, extName);//传入路径、文件名 15 return lst; 16 } 17 18 private void getdir(string path, string extName) 19 { 20 try 21 { 22 //获取文件夹列表 23 string[] dirs = Directory.GetDirectories(path); 24 DirectoryInfo fdir = new DirectoryInfo(path); 25 FileInfo[] file = fdir.GetFiles(); 26 27 //当前目录文件或目录不为空 28 if (file.Length != 0 || dirs.Length != 0) 29 { 30 foreach (FileInfo f in file) 31 { 32 if (extName.ToLower().IndexOf(f.Name.ToLower()) >= 0) //匹配上 33 { 34 lst.Add(f); 35 } 36 } 37 foreach (string d in dirs) 38 { 39 getdir(d, extName);//递归 40 } 41 } 42 } 43 catch 44 { 45 46 } 47 } 48 49 } 50 }
好,这样一个程序就做成功了。呱唧,呱唧。