天天看點

C#編寫軟體更新更新程式的完整示例

作者:逍遙總遙

一個軟體在做調整以後,都是要更新更新的,如果這個時候要求使用者重新去下載下傳,體驗非常不好,是以程式一運作起來,就應該判斷是不是要更新,如果要的話,直接自動更新,再運作,這樣使用者體驗會好很多。

任務過程:更新的檔案是放在一個遠端目錄http://192.168.1.100/Resourse/Update/中,軟體啟動後,先判斷是不是需要更新,如果要,去遠端目錄下載下傳檔案更新,然後再運作,中間需要使用者看到下載下傳更新的過程

效果如圖:這裡我隻是實作的過程,細節比如文字啥的比較簡單,就沒有弄

C#編寫軟體更新更新程式的完整示例

1、建立一個名為Update的項目,窗體名稱改為DownFileFromURL,窗體代碼如下:(大多數函數我已經弄好,直接複制就可以,注釋的地方看下應該不難)

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows.Forms;

namespace Update
{
    public partial class DownFileFromURL : Form
    {
        string webURL;        //遠端下載下傳檔案位址
        string[] FileNames;   //要下載下傳的檔案名數組
        //下載下傳的檔案是否加上tmp,這個是我當時做别的加的功能,這裡沒什麼用 
        string ReName;    
        //更新成功後,運作哪個檔案      
        string RunEXEname;
        //完成成功标志,初始值為false
        bool ok = false;

        //構造函數
        public DownFileFromURL(string theurl, string filenames, string renametoaddtmp,string runwho)
        {
            #region 調用示例
            //string filenames = "a.exe|b.txt|c.bmp";

            //string args = "";
            ////下載下傳位址
            //string args += "http://192.168.1.100/Resourse/Update/ ";
            ////下載下傳檔案名字元串,用“|”隔開
            //args += filenames + " ";
            ////更新檔案不改名
            //args += "false" + " ";
            ////更新結束後運作哪個檔案
            //args += "\\a.exe" + " ";
            #endregion

            //初始化本地參數
            webURL = theurl;
            FileNames = filenames.Split('|');
            ReName = renametoaddtmp;
            RunEXEname = runwho;

            InitializeComponent();
        }

        private void DownFileFromURL_Load(object sender, EventArgs e)
        {
            //線上程中操作控件值的話要加上下面這句話
            CheckForIllegalCrossThreadCalls = false;

            label1.Text = "開始下載下傳新版本,請稍等...";
            Thread t = new Thread(new ThreadStart(DownFileGo));
            t.Start();
        }

        void DownFileGo()
        {
            int howmanyfiles = FileNames.Length;
            for (int i = 0; i < howmanyfiles; i++)
            {
                //逐個下載下傳
                DownFile(mybar, Application.StartupPath + "\\", webURL + "/" + FileNames[i], ReName);

                if (i == howmanyfiles - 1)
                {
                    //下載下傳完以後如果參數中要求直接運作指定程式,就運作它
                    if (RunEXEname.Length != 0)
                    {
                        Process.Start(Application.StartupPath + RunEXEname);
                    }

                    //成功标志改為true
                    ok = true;
                    this.Close();
                    break;
                }
            }
        }
        private void DownFile(ProgressBar thebar, string dir, string URL, string rename)
        {
            string filename = URL.Substring(URL.LastIndexOf("/") + 1);
            string FileName = dir + filename;  //下載下傳到其它目錄下,dir參數未測試

            if (rename == "true") { FileName = FileName + ".tmp"; }

            //得到遠端檔案資訊
            HttpWebRequest Myrq = (HttpWebRequest)HttpWebRequest.Create(URL);
            HttpWebResponse myrp = (HttpWebResponse)Myrq.GetResponse();
            long totalBytes = myrp.ContentLength;
            thebar.Maximum = (int)totalBytes;

            //正常檔案流處理代碼
            Stream st = myrp.GetResponseStream();
            Stream so = new FileStream(FileName.Replace("%20", " ").Replace("%2520", " "), FileMode.Create);
            long totalDownloadedByte = 0;
            byte[] by = new byte[1024];
            int osize = st.Read(by, 0, (int)by.Length);
            while (osize > 0)
            {
                totalDownloadedByte = osize + totalDownloadedByte;
                Application.DoEvents();
                so.Write(by, 0, osize);
                thebar.Value = (int)totalDownloadedByte;
                osize = st.Read(by, 0, (int)by.Length);
                if (thebar.Value == (int)totalBytes)
                {
                    thebar.Value = 0;
                }
            }
            so.Close();
            st.Close();
        }

        private void DownFileFromURL_FormClosing(object sender, FormClosingEventArgs e)
        {
            //如果還沒下載下傳完,退出
            if (!ok)
            {
                e.Cancel = true;
            }
        }
    }
}           

2、設定它的啟動參數,修改program.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Update
{
    static class Program
    {
        /// <summary>
        /// 應用程式的主入口點。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Process instance = RunningInstance();
            //這個程式是否運作,因為我自己用,是以我一直用程序名判斷
            if (instance == null)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                //如果參數個數正确
                if (args.Length == 5)
                {
                    string theurl = args[0];
                    string filenames = args[1];
                    string rename = args[2];
                    string runexename = args[3];

                    //這裡先把自己的主程式程序殺掉,
                    //再運作更新窗體
                    string killexename = args[4];
                    Kill(killexename);

                    Application.Run(new DownFileFromURL(theurl, filenames, rename, runexename));
                }

                else
                {
                    MessageBox.Show("^#$#!#@!*^*@%#$#!@%\r\n\r\n@%#!@%!#@!*^@%#!@%!@%@%#$#!@%#@!*^\r\n\r\n#@!*^@%#!@%!@%#@!*^#$#@%!@%#@!*^#\r\n\r\n................");
                }
            }
            else
            {
                Process.GetCurrentProcess().Kill();
            }
        }
        /// <summary>
        /// 殺死程序
        /// <para>Kill("winword");不帶exe</para>
        /// </summary>
        /// <param name="pname">程序名稱,不帶exe</param>
        public static void Kill(string pname)
        {
            try
            {
                Process[] p = Process.GetProcesses();
                foreach (Process tp in p)
                {

                    if (tp.ProcessName == pname | tp.ProcessName.ToLower() == pname)
                    {
                        tp.Kill();
                    }
                }
            }
            catch
            { }
        }
        public static Process RunningInstance()
        {
            Process current = Process.GetCurrentProcess();
            Process[] processes = Process.GetProcessesByName(current.ProcessName);
            foreach (Process process in processes)
            {
                if (process.Id != current.Id)
                {
                    if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName)
                    {
                        return process;
                    }
                }
            }
            return null;
        }
    }
}           

經過這兩步,更新程式本身就完成了,檔案名就是項目名:update.exe.現在我們來看下如何用其它程式來調用它。

建立一個主程式 ksh.exe,在它運作時,比如可以放在program.cs中,加入以下子產品

//我更改了資料庫中的更新版本号
//此時從庫中讀出來,也就是庫中片版本号,當然也可以想其它辦法
string cmdstr = "select Cver from Ver";
string dbver = GetFirstData(cmdstr);

//目前片版本号
//[assembly: AssemblyFileVersion("1.0")]
//這個也可以把目前版本号放在xml中與dbver對比,更新完再更新xm即可
string nowver = Application.ProductVersion;

//更新檔案檔案名
string exename = Application.StartupPath + "\\Update.exe";

//如果版本号不一緻
if (dbverf > nowverf)  
{
    //添加update.exe運作需要的參數
    //下載下傳位址
    string args = "http://192.168.1.100/Resourse/Update/C ";
    //要下載下傳的檔案名清單
    string filenames = "a.exe|b.txt|c.xml";
    string[] filenamesarr = filenames.Split('|');
    args += filenames + " ";
    //下載下傳的檔案是否改名
    args += "false" + " ";
    //更新後運作哪個檔案
    args += "\\a.exe" + " ";
    //我目前的程序名,因為要更新自己的話
    //萬一自己還沒有退出,則需要讓update.exe殺死自己
    //當然你也可以讓update檢測,等它退出再更新
    args += "ksh";

    //運作update.exe
    RunINCMD(exename, args);

    //退出自己,準備更新
    Application.ExitThread();
    this.DialogResult = DialogResult.No;
}
void RunINCMD(string EXEname, string Args)
{
    System.Diagnostics.Process Process1 = new System.Diagnostics.Process();
    Process1.StartInfo.FileName = EXEname;
    Process1.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;

    Process1.StartInfo.Arguments = " " + Args;
    Process1.Start();
    Process1.WaitForExit();
}           

這樣當ksh.exe運作後,就會先判斷自己的版本号與遠端版本号是不是一緻,如果不一緻,就提供update.exe需要的參數,然後運作update.exe,自己退出或被update.exe檔案kill掉,保證更新進行。

在更新過程中,因為我是寫線上程中的,是以有幾個檔案,大小多少,下載下傳進度這些都是可控的,我沒有寫

最後,

//要下載下傳的檔案名清單

string filenames = "a.exe|b.txt|c.xml";

有一天你想在下載下傳清單中新加一些檔案,如果你這樣寫就得重新改程式,我用的方法是浏覽更新檔案目錄,把目錄下所有檔案名讀出來,這樣你下次要求軟體更新時,新增檔案直接複制到遠端目錄中就可以了。

這個功能前幾天我寫過,有要用的可以點下面去看:

C#得到遠端目錄中的檔案名稱

繼續閱讀