zoukankan      html  css  js  c++  java
  • Wince后台系统补丁更新实现

    虽说是在WINCE手持平台上,但是系统架构还是C/S模式没有变,C/S模式比较难搞的一个部分就是系统更新,这个在WINCE平台下处理方式跟PC平台上也无特别大的区别,思路差不多都是如下模式:

     1.程序启动之前验证版本

     2.如果版本不一致则下载更新

    WINCE当然也是这个模式,有点不同的就是WINCE更新时候需要安装CAB包,代码下只能用WINCE自带的CAB包安装命令

                    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = @"wceload.exe";
    info.Arguments = @"/noui " + path;

    //info.Arguments = path;
    System.Diagnostics.Process process = new System.Diagnostics.Process();
    process.StartInfo = info;
    process.Start();
    wceload.exe是WINCE自带的安装命令,程序调用可以加参数控制,详情可见MSDN,这个命令有个不好的地方就是如果有DLL,或者EXE占用(线程非安全退出),它在安装的时候是不会呈现出来的,这样就有可能有客户端不完整更新,
    这是个很糟糕的情况,我们要想办法避免这种情况出现。因此我们必须要新建一个project完全负责更新程序,他应该是脱离应用程序的独立程序。

    项目结构如图

    ClientLoader窗体就是更新的主程序,主要逻辑都在这种实现。

    AppConfiger是自己创建的CONFIG读取类,实现可在我之前的博文http://www.cnblogs.com/vinnie520/archive/2012/03/13/2393337.html 中看到。

    .ico是程序图标,因为是系统入口程序,所以把这个程序加上图标而不是加在实际应用程序上。

    updateHelper是创建的静态类,用于下载补丁包,获取客户端\服务器版本号,检验是否需要更新之类的功能,代码如下

     public static void DownloadFile(string URL, string filename, System.Windows.Forms.ProgressBar prog, System.Windows.Forms.Label label1)
    {

    Application.DoEvents();
    float percent = 0;
    try
    {
    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;
    if (prog != null)
    {
    prog.Maximum = (int)totalBytes;
    }
    System.IO.Stream st = myrp.GetResponseStream();
    System.IO.Stream so = new System.IO.FileStream(filename, System.IO.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;
    System.Windows.Forms.Application.DoEvents();
    so.Write(by, 0, osize);
    if (prog != null)
    {
    prog.Value = (int)totalDownloadedByte;
    }
    osize = st.Read(by, 0, (int)by.Length);

    percent = (float)totalDownloadedByte / (float)totalBytes * 100;
    label1.Text = "当前补丁下载进度" + percent.ToString() + "%";
    System.Windows.Forms.Application.DoEvents(); //必须加注这句代码,否则label1将因为循环执行太快而来不及显示信息
    }
    so.Close();
    st.Close();
    }
    catch (System.Exception)
    {
    throw;
    }

    }

    public static string GetServerVersion()
    {
    string result;
    try
    {

    var url = AppConfiger.GetAppSettingValue("VersionUrl");
    System.Net.HttpWebRequest Myrq = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
    System.Net.HttpWebResponse myrp = (System.Net.HttpWebResponse)Myrq.GetResponse();

    System.IO.Stream st = myrp.GetResponseStream();
    byte[] by = new byte[1024];
    StreamReader m_streamReader = new StreamReader(st);

    result = m_streamReader.ReadLine();
    m_streamReader.Close();
    st.Close();
    }
    catch (System.Exception)
    {
    throw;
    }
    return result;
    }

    public static bool IfNoNeedUpdate()
    {
    var clientVersion = GetClientVision();

    var serverVersion = GetServerVersion();

    var clVersion = clientVersion.Split('.');
    var svVersion = serverVersion.Split('.');

    for (int i = 0; i < clVersion.Count(); i++)
    {
    if (clVersion[i] != svVersion[i])
    return false;
    }

    return true;
    // return clientVersion >= serverVersion;
    }
    private static string GetClientVision()
    {
    try
    {
    string res = "";
    string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
    var path = Path.Combine(appDir, "version.ver");
    StreamReader sr = new StreamReader(path, System.Text.Encoding.Default);
    while (sr.Peek() >= 0)
    {
    res = sr.ReadLine();
    }
    sr.Close();
    return res;
    }
    catch
    {
    return "";
    }
    }
    public static void SetClientVision(string vision)
    {
    try
    {
    string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
    var path = Path.Combine(appDir, "version.ver");
    StreamWriter m_streamWriter = new StreamWriter(path);
    m_streamWriter.Flush();
    // 使用StreamWriter来往文件中写入内容
    m_streamWriter.BaseStream.Seek(0, SeekOrigin.Begin);
    // 把richTextBox1中的内容写入文件
    m_streamWriter.Write(vision);
    //关闭此文件
    m_streamWriter.Flush();
    m_streamWriter.Close();
    }catch
    {
    }
    }

    以上可以看到,我们的版本使用文本文件控制,本地文件安装包中有version.ver文件,是个文本文件,其中只有一行内容就是版本号,服务器也有个类似的文件,是放在FTP服务器中,我们

    使用的时候是通过HTTP下载的方式读取到其中内容,至于版本控制,可以用MSBUILD保证此版本跟客户端代码的AssemblyInfo里面的version保持一致。

    这个图就是我们更新使用程序的窗体。下面2个BUTTON是测试时按钮,visiable是否的,所以就请忽略吧。

    此程序所有逻辑都是在LOAD方法里面实现并完成,代码如下

         private void Form1_Load(object sender, EventArgs e)
    {
    lableTitle.Text = "程序正在启动...";
    this.Show();
    Application.DoEvents();

    bool notUpdate = false;




    try
    {
    notUpdate = UpdateHelper.IfNoNeedUpdate();
    }
    catch (Exception)
    {
    MessageBox.Show("程序启动失败,请检查网络连接");
    Application.Exit();
    return;
    }

    if (notUpdate)
    {
    StartApp(); // 没有更新包
    }
    else
    {
    lableTitle.Text = "正在安装更新包...";
    Application.DoEvents();



    if (DownLoadPathFile())
    {
    string patchName = GetPatchFile();

    if (!UpdateApp(patchName))
    {
    MessageBox.Show("更新失败,请重启后重新更新系统");
    CloseMainForm();
    }

    UpdateHelper.SetClientVision(UpdateHelper.GetServerVersion());
    Application.DoEvents();
    StartApp();
    }
    else
    {
    StartApp();
    }
    }


    }

    所有更新思路都在以上代码。首先调用 notUpdate = UpdateHelper.IfNoNeedUpdate(); 检验是否需要更新

    如果不需要更新则直接启动程序(StartApp),如果需要就先下载更新包((DownLoadPathFile()),然后进行更新(UpdateApp(patchName)),最后更新成功后修改客户端版本号

    (UpdateHelper.SetClientVision(UpdateHelper.GetServerVersion());),最后启动程序。

    更新程序:注意到,这里更新之前首先对2个关键文件做了file.delete方法用来检验文件是否被占用,从而实现安全更新

       private bool UpdateApp(string path)
    {
    try
    {
    string exePath =// " \\ResidentFlash\\DeviceClient\\CP.Device.exe";
    Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase),
    "CP.Device.exe");

    string dllPath =// " \\ResidentFlash\\DeviceClient\\CP.Device.exe";
    Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase),
    "CP.Device.Service.dll");

    //更新之前删除EXE文件检查该文件是否被占用
    if (File.Exists(dllPath))
    File.Delete(dllPath);

    if (File.Exists(exePath))
    File.Delete(exePath);


    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = @"wceload.exe";
    info.Arguments = @"/noui " + path;

    //info.Arguments = path;
    System.Diagnostics.Process process = new System.Diagnostics.Process();
    process.StartInfo = info;
    process.Start();
    process.WaitForExit();

    return true;
    }
    catch (Exception ex)
    {
    MessageBox.Show(ex.Message);
    return false;
    }
    }

    启动程序:

    private void StartApp()
    {
    try
    {

    //先写死路径
    string path =// "\\ResidentFlash\\DeviceClient\\CP.Device.exe";
    Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase), AppName);
    if (File.Exists(path))
    {
    Process process = new Process();
    ProcessStartInfo info = new ProcessStartInfo();
    info.FileName = path;
    process.StartInfo = info;
    process.Start();
    CloseMainForm();
    }
    else
    {
    MessageBox.Show("没有找到主程序'" + AppName + "'!请重新安装程序!");
    Application.DoEvents();
    CloseMainForm();
    }
    }
    catch (Exception ex)
    {
    MessageBox.Show(ex.Message);
    Application.DoEvents();
    CloseMainForm();
    }
    }

    下载程序:主要逻辑是在updateHelper中的download方法实现,这里只设置一下路径问题

      private bool DownLoadPathFile()
    {

    string cabPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase), "Update");

    if (!Directory.Exists(cabPath))
    {
    Directory.CreateDirectory(cabPath);
    }

    string cabFile = Path.Combine(cabPath, "patch.cab");
    var patchUrl = AppConfiger.GetAppSettingValue("PatchUrl");
    UpdateHelper.DownloadFile(patchUrl, cabFile, progressBar1, labeProcess);
    return true;
    }

    OK,到此,基本上更新就可以完美实现,程序的入口设置成此程序,每次只要有更新之后就增加服务器版本号文件的版本,然后把编译好的补丁包放到对应的位置,每次程序开机时就会实现自动更新。

    可以想一想,以上程序只完成了程序入口处检查版本,更新版本。如果我要在程序运行中发布客户端代码,那怎么样处理才是比较好呢...






  • 相关阅读:
    学习进度——第十周
    梦断代码读后感03
    DFS入门——数的拆分
    DFS入门——素数环问题
    排列LCS问题
    洛谷P1436 棋盘分割 题解 二维区间DP
    洛谷P1241 括号序列 题解 栈
    《算法艺术与信息学竞赛》第1章 算法与数据结构 学习笔记
    洛谷P1563 玩具谜题(NOIP提高组2016 D1T1)题解 模拟
    洛谷P5022 旅行(NOIP提高组2018 D2T1)题解 贪心/去环
  • 原文地址:https://www.cnblogs.com/vinnie520/p/2434631.html
Copyright © 2011-2022 走看看