目录
在我们常用的一些软件中,经常有些软件,双击之后根本打不开,这是因为启动时做了限制,我们需要传入一些参数才能打开,在工作中,这个需求也可以用在软件的自动更新上,在Unity里,有XLua,ILruntime 等框架进行热更新,在Winform中,由于Windows的机制,打开软件后,不能做文件替换工作,我们可以利用一个软件启动器,更新本地软件的文件,然后再启动软件,就达到了自动更新的目的,当然,软件本地也不能直接双击打开。
新建一个winform 项目,界面如下,用来做一个软件启动器
只需要添加一个按钮,给按钮添加点击事件,代码如下:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Diagnostics;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
- namespace 启动器
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
-
- private void button1_Click(object sender, EventArgs e)
- {
- string path = "E:\\CSharp Project\\Test\\Test\\bin\\Debug\\Test.exe";
- string[] parameter = {"启动器参数1", "启动器参数2", "启动器参数3", "启动器参数4" };
- bool startResult = StartProcess(path, parameter);
- if (startResult)
- System.Environment.Exit(0);
- }
-
- ///
- /// 启动一个软件,并传入参数
- ///
- ///
- ///
- ///
- public bool StartProcess(string runFilePath, params string[] args)
- {
- string s = "";
- foreach (string arg in args)
- {
- s = s + arg + " ";
- }
- s = s.Trim();
- Process process = new Process();//创建进程对象
- ProcessStartInfo startInfo = new ProcessStartInfo(runFilePath, s); // 括号里是(程序名,参数)
- process.StartInfo = startInfo;
- process.Start();
- return true;
- }
-
- }
- }
下面新建第二个项目,用来做软件的本体
新建一个winform项目,里面什么都不用加,先设置软件的输出类型为控制台
然后打开启动脚本 Program.cs ,添加参数的打印,由于软件刚启动时,控制台也刚打开,立马打印会看不见日志,于是我这里加了异步延时操作。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
- namespace Test
- {
- static class Program
- {
- ///
- /// 应用程序的主入口点。
- ///
- [STAThread]
- static void Main(string[] args)
- {
- Task.Run(async () =>
- {
- await Task.Delay(TimeSpan.FromSeconds(5));
- if (args.Length > 0)
- {
- for (int i = 0; i < args.Length; i++)
- {
- Console.WriteLine("参数是:" + args[i]);
- }
- }
- else
- Console.WriteLine("args参数是0");
- });
-
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
- Application.Run(new Form1());
- }
- }
- }
现在开始测试,打开软件启动器,点击按钮,5秒后,然后就可以看到控制台的输出
这里就可以对参数做一个限制了,如果参数不正确,直接return,用户就不能直接打开软件了
在Winform中,ListBox用来显示日志还不错,如图:
用法:
- ///
- /// 添加普通日志
- ///
- /// 内容
- public void AddOrdinaryLog(string content)
- {
- if (this.InvokeRequired)
- {
- //切换到UI线程
- this.Invoke(new MethodInvoker(delegate
- {
- AddOrdinaryLog_UI(content);
- }));
- }
- else
- {
- AddOrdinaryLog_UI(content);
- }
- }
-
-
- private void AddOrdinaryLog_UI(string content)
- {
- //读取当前ListBox列表长度
- int len = ListBox_OrdinaryLogList.Items.Count;
- //插入新的一行
- ListBox_OrdinaryLogList.Items.Insert(len, content);
- //列表长度大于30,那么就删除第1行的数据
- if (len > 30)
- ListBox_OrdinaryLogList.Items.RemoveAt(0);
- //插入新的数据后,将滚动条移动到最下面
- int visibleItems = ListBox_OrdinaryLogList.ClientSize.Height / ListBox_OrdinaryLogList.ItemHeight;
- ListBox_OrdinaryLogList.TopIndex = Math.Max(ListBox_OrdinaryLogList.Items.Count - visibleItems + 1, 0);
- }
这里我加了一个线程的判断,不需要直接复制 AddOrdinaryLog_UI 这个方法即可。
代码:
- ///
- /// Http下载文件
- ///
- public static string HttpDownloadFile(string url, string path)
- {
- // 设置参数
- HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
- //发送请求并获取相应回应数据
- HttpWebResponse response = request.GetResponse() as HttpWebResponse;
- //直到request.GetResponse()程序才开始向目标网页发送Post请求
- Stream responseStream = response.GetResponseStream();
- //创建本地文件写入流
- Stream stream = new FileStream(path, FileMode.Create);
-
- long totalBytes = response.ContentLength;
- Console.WriteLine("请求内容的长度:" + totalBytes);
-
- byte[] bArr = new byte[1024];
- int size = responseStream.Read(bArr, 0, (int)bArr.Length);
- long totalDownloadBytes = 0;
- while (size > 0)
- {
- totalDownloadBytes += size;
- stream.Write(bArr, 0, size);
- Console.WriteLine("下载进度:" + (int)totalDownloadBytes);
- size = responseStream.Read(bArr, 0, (int)bArr.Length);
- }
- stream.Close();
- responseStream.Close();
- return path;
- }
调用:
- //下载360压缩的安装包
- string url1 = "https://dl.360safe.com/360zip_setup.exe";
- //下载到Debug目录下
- string url2 = Application.StartupPath + "\\360zip.exe";
-
- string res = HttpDownloadFile(url1, url2);
- Console.WriteLine("下载完成:" + res);
效果:
HTTPDownManager.cs
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Utils
- {
- public class HTTPDownManager
- {
- ///
- /// 下载实时返回下载进度
- ///
- /// 下载地址
- /// 本地存储地址
- /// 委托回调函数
- public static void DownloadFileData(string URL, string savePath, Action<string, string, int> downAction, Action downEndAction)
- {
- try
- {
- float percent = 0;
- string fileName = Path.GetFileName(savePath);
- System.Net.HttpWebRequest Myrq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(URL);
- System.Net.HttpWebResponse myrp = (System.Net.HttpWebResponse)Myrq.GetResponse();
- long totalBytes = myrp.ContentLength, totalDownloadedByte = 0;
- System.IO.Stream st = myrp.GetResponseStream(), so = new System.IO.FileStream(savePath, System.IO.FileMode.Create);
- byte[] by = new byte[1024];
- int osize = st.Read(by, 0, (int)by.Length);
- while (osize > 0)
- {
- totalDownloadedByte = osize + totalDownloadedByte;
- so.Write(by, 0, osize);
- osize = st.Read(by, 0, (int)by.Length);
- percent = (float)totalDownloadedByte / (float)totalBytes * 100;//当前位置
- //totalBytes 是总字节数
- downAction(fileName, GetSize(totalBytes), (int)percent);
- }
- if (downEndAction != null) downEndAction();
- so.Close();
- st.Close();
- }
- catch (System.Exception)
- {
- throw;
- }
- }
-
- ///
- /// 获取文件大小
- ///
- ///
- ///
- private static string GetSize(double size)
- {
- String[] units = new String[] { "B", "KB", "MB", "GB", "TB", "PB" };
- double mod = 1024.0;
- int i = 0;
- while (size >= mod)
- {
- size /= mod;
- i++;
- }
- return Math.Round(size) + units[i];
- }
- }
- }
Winform 界面
Form1.cs
- using FileBootUp;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using Utils;
-
- namespace 下载文件1
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
-
- private string url = "http://localhost/vodo.mp4"; //下载地址
- private string loadFilefolder = Application.StartupPath + "//vodo.mp4";//保存位置
-
- private void Form1_Load(object sender, EventArgs e)
- {
- Task.Run(() =>
- {
- HTTPDownManager.DownloadFileData(url, loadFilefolder, DownProgress, DownEnd);
- });
- }
-
-
- private void DownProgress(string fileName, string size, int percent)
- {
- FormControlExtensions.InvokeIfRequired(this, () =>
- {
- this.Label_FileName.Text = string.Format("正在下载:{0}", fileName);
- this.Label_Size.Text = size;
- this.Label_DownProgress.Text = string.Format("{0}%", percent);
- this.ProgressBar_DownProgress.Value = percent;
- });
- }
-
- private void DownEnd()
- {
- Console.WriteLine("下载完成");
- }
-
- }
- }
FormControlExtensions.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
- namespace FileBootUp
- {
- public static class FormControlExtensions
- {
- ///
- /// Invokes actions in UI thread.
- ///
- public static void InvokeIfRequired(this Control control, Action action)
- {
- if (control.InvokeRequired)
- {
- control.Invoke(new MethodInvoker(action));
- }
- else
- {
- action();
- }
- }
- }
- }
效果:
HttpHeper.cs
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Windows.Forms;
- using System.Threading.Tasks;
- using System.Threading;
-
- namespace HTTPTool
- {
- public class HttpHelper
- {
- const int bytebuff = 1024;
- const int ReadWriteTimeOut = 2 * 1000;//超时等待时间
- const int TimeOutWait = 5 * 1000;//超时等待时间
- const int MaxTryTime = 12;
-
- private double totalSize, curReadSize, speed;
- private int proc, remainTime;
- private int totalTime = 0;
-
- private bool downLoadWorking = false;
-
- private string StrFileName = "";
- private string StrUrl = "";
-
- private string outMsg = "";
-
- private string fileName = string.Empty;
-
- public HttpHelper(string url, string savePath)
- {
- this.StrUrl = url;
- this.StrFileName = savePath;
-
- this.fileName = Path.GetFileName(savePath);
- }
-
- ///
- /// 下载数据更新
- ///
- /// 下载文件总大小
- /// 已下载文件大小
- /// 下载进度百分比
- /// 下载速度
- /// 剩余下载时间
- public delegate void delDownFileHandler(string totalNum, string num, int proc, string speed, string remainTime, string outMsg,string fileName);
- public delDownFileHandler processShow;
-
- public delegate void delDownCompleted();
- public delDownCompleted processCompleted;
-
- public System.Windows.Forms.Timer timer;
-
- public void init()
- {
- timer.Interval = 50;
- timer.Tick -= TickEventHandler;
- timer.Tick += TickEventHandler;
- timer.Enabled = true;
- downLoadWorking = true;
- }
-
- ///
- /// 获取文件大小
- ///
- ///
- ///
- private string GetSize(double size)
- {
- String[] units = new String[] { "B", "KB", "MB", "GB", "TB", "PB" };
- double mod = 1024.0;
- int i = 0;
- while (size >= mod)
- {
- size /= mod;
- i++;
- }
- return Math.Round(size) + units[i];
- }
-
- ///
- /// 获取时间
- ///
- ///
- ///
- private string GetTime(int second)
- {
- return new DateTime(1970, 01, 01, 00, 00, 00).AddSeconds(second).ToString("HH:mm:ss");
- }
-
- ///
- /// 下载文件(同步) 支持断点续传
- ///
- public void DowLoadFile()
- {
- totalSize = GetFileContentLength(StrUrl);
-
- //打开上次下载的文件或新建文件
- long lStartPos = 0;
- System.IO.FileStream fs;
- if (System.IO.File.Exists(StrFileName))
- {
- fs = System.IO.File.OpenWrite(StrFileName);
- lStartPos = fs.Length;
- fs.Seek(lStartPos, System.IO.SeekOrigin.Current); //移动文件流中的当前指针
- }
- else
- {
- fs = new System.IO.FileStream(StrFileName, System.IO.FileMode.Create);
- lStartPos = 0;
- }
-
- curReadSize = lStartPos;
-
- if (curReadSize == totalSize)
- {
- outMsg = "文件已下载!";
- processCompleted?.Invoke();
- timer.Enabled = false;
- return;
- }
-
- //打开网络连接
- try
- {
- System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(StrUrl);
- if (lStartPos > 0)
- request.AddRange((int)lStartPos); //设置Range值
-
- //向服务器请求,获得服务器回应数据流
- System.IO.Stream ns = request.GetResponse().GetResponseStream();
- byte[] nbytes = new byte[bytebuff];
- int nReadSize = 0;
- proc = 0;
-
- do
- {
-
- nReadSize = ns.Read(nbytes, 0, bytebuff);
- fs.Write(nbytes, 0, nReadSize);
-
- //已下载大小
- curReadSize += nReadSize;
- //进度百分比
- proc = (int)((curReadSize / totalSize) * 100);
- //下载速度
- speed = (curReadSize / totalTime) * 10;
- //剩余时间
- remainTime = (int)((totalSize / speed) - (totalTime / 10));
-
- if (downLoadWorking == false)
- break;
-
- } while (nReadSize > 0);
-
- fs.Close();
- ns.Close();
-
- if (curReadSize == totalSize)
- {
- outMsg = "下载完成!";
- processCompleted?.Invoke();
- downLoadWorking = false;
- }
- }
- catch (Exception ex)
- {
- fs.Close();
- outMsg = string.Format("下载失败:{0}", ex.ToString());
- }
- }
-
- public void DownLoadPause()
- {
- outMsg = "下载已暂停";
- downLoadWorking = false;
- }
-
- public void DownLoadContinue()
- {
- outMsg = "正在下载";
- downLoadWorking = true;
- DownLoadStart();
- }
-
- public void DownLoadStart()
- {
- Task.Run(() =>
- {
- DowLoadFile();
- });
- }
-
-
- ///
- /// 定时器方法
- ///
- ///
- ///
- private void TickEventHandler(object sender, EventArgs e)
- {
- processShow?.Invoke(GetSize(totalSize),
- GetSize(curReadSize),
- proc,
- string.Format("{0}/s", GetSize(speed)),
- GetTime(remainTime),
- outMsg,
- fileName
- );
- if (downLoadWorking == true)
- {
- totalTime++;
- }
- }
-
- ///
- /// 获取下载文件长度
- ///
- ///
- ///
- public long GetFileContentLength(string url)
- {
- HttpWebRequest request = null;
- try
- {
- request = (HttpWebRequest)HttpWebRequest.Create(url);
- //request.Timeout = TimeOutWait;
- //request.ReadWriteTimeout = ReadWriteTimeOut;
- //向服务器请求,获得服务器回应数据流
- WebResponse respone = request.GetResponse();
- request.Abort();
- return respone.ContentLength;
- }
- catch (WebException e)
- {
- if (request != null)
- request.Abort();
- return 0;
- }
- }
- }
- }
winform 界面:
代码:
- using HTTPTool;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
-
- namespace 下载文件
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
-
-
- static string url = "http://localhost/vodo.mp4"; //下载地址
- static string loadFilefolder = Application.StartupPath + "//vodo.mp4";//保存位置
- HttpHelper httpManager = new HttpHelper(url, loadFilefolder);
-
-
-
-
- private void Form1_Load(object sender, EventArgs e)
- {
- httpManager.timer = new System.Windows.Forms.Timer();
- httpManager.processShow += processSho;
- httpManager.processCompleted += processComplete;
- httpManager.init();
- }
-
- public void processSho(string totalNum, string num, int proc, string speed, string remainTime, string msg, string fileName)
- {
- this.label1.Text = string.Format("文件大小:{0}", totalNum);
- this.label2.Text = string.Format("已下载:{0}", num);
- this.label3.Text = string.Format("进度:{0}%", proc);
- this.label4.Text = msg;
- this.label5.Text = string.Format("速度:{0}", speed);
- this.label6.Text = string.Format("剩余时间:{0}", remainTime);
- this.label7.Text = fileName;
-
- progressBar1.Value = proc;
- }
-
- private void processComplete()
- {
- MessageBox.Show("文件下载完成!", "提示");
- }
-
- private void button1_Click(object sender, EventArgs e)
- {
- httpManager.DownLoadStart();
- }
-
- private void button2_Click(object sender, EventArgs e)
- {
- httpManager.DownLoadPause();
- }
-
- private void button3_Click(object sender, EventArgs e)
- {
- httpManager.DownLoadContinue();
- }
-
- }
- }
源码文件:点击下载
this.Invoke 必须运行在winform的代码中,所有下面的代码也只能写在Form 界面代码中。
代码:
- var task = Task.Run(async delegate
- {
- await Task.Delay(200);
- this.Invoke(new MethodInvoker(delegate {
- Label_Log.Text = string.Empty;
- }));
- });
如果在其他的线程中,调用UI 相关的控件需要切换线程,否则会报错。
下面方法可以切换到UI 线程并执行 Action
- public static class FormControlExtensions
- {
- ///
- /// Invokes actions in UI thread.
- ///
- public static void InvokeIfRequired(this Control control, Action action)
- {
- if (control.InvokeRequired)
- {
- control.Invoke(new MethodInvoker(action));
- }
- else
- {
- action();
- }
- }
- }
其实也可以这样写,申请一个线程,执行完成后,再切换到 UI 线程,这样就不会卡界面了
- new Thread((ThreadStart)(delegate()
- {
- //线程内的计算
- //......
- //切换回UI线程
- this.Dispatcher.BeginInvoke((Action)delegate
- {
- //Label1.Text = "执行完成";
- });
- })).Start();
代码
- label1.Font = new Font("隶书", 20, FontStyle.Bold); //第一个是字体,第二个大小,第三个是样式,
- label1.ForeColor = Color.Red; //颜色
-
- label2.Font = new Font("华文行楷", 20, FontStyle.Bold); //第一个是字体,第二个大小,第三个是样式,
- label2.ForeColor = Color.Green; //颜色
-
- label3.Font = new Font("宋体", 20, FontStyle.Bold); //第一个是字体,第二个大小,第三个是样式,
- label3.ForeColor = Color.Blue; //颜色
- label3.Text = DateTime.Now.ToString("yyyy-MM-dd");
end