Browse Source

在工具类中添加更新工具类(使用直接调用的模式,更新到目标目录)

yuzhengyang 8 years ago
parent
commit
5df937797d

+ 8 - 0
Fork.Net/Test/Y.Test/Commons/R.cs

@@ -8,6 +8,14 @@ namespace Y.Test.Commons
 {
     public static class R
     {
+        public static class Paths
+        {
+            public static string Temp = @"D:\Temp\AppUpdate\Oreo.NetMan\Temp";
+            public static Dictionary<string, string> Relative = new Dictionary<string, string>()
+            {
+                { "|AppRoot|",@"D:\Temp\AppUpdate\Oreo.NetMan"},
+            };
+        }
         public static FormManTool Forms = new FormManTool();
 
     }

+ 2 - 0
Fork.Net/Test/Y.Test/Program.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Windows.Forms;
 using Y.Test.Views;
+using Y.Utils.IOUtils.FileUtils;
 
 namespace Y.Test
 {
@@ -15,6 +16,7 @@ namespace Y.Test
         [STAThread]
         static void Main()
         {
+            //FilePackageTool.Pack(@"D:\Temp\流量测试\Oreo.NetMan", @"D:\Temp\流量测试\bag.bag");
             Application.EnableVisualStyles();
             Application.SetCompatibleTextRenderingDefault(false);
             Application.Run(new MainForm());

+ 13 - 0
Fork.Net/Test/Y.Test/Views/MainForm.Designer.cs

@@ -30,6 +30,7 @@
         {
             this.ChineseCalendarForm = new System.Windows.Forms.Button();
             this.TestComputerInfoForm = new System.Windows.Forms.Button();
+            this.TestUpdateForm = new System.Windows.Forms.Button();
             this.SuspendLayout();
             // 
             // ChineseCalendarForm
@@ -52,11 +53,22 @@
             this.TestComputerInfoForm.UseVisualStyleBackColor = true;
             this.TestComputerInfoForm.Click += new System.EventHandler(this.TestComputerInfoForm_Click);
             // 
+            // TestUpdateForm
+            // 
+            this.TestUpdateForm.Location = new System.Drawing.Point(70, 206);
+            this.TestUpdateForm.Name = "TestUpdateForm";
+            this.TestUpdateForm.Size = new System.Drawing.Size(262, 23);
+            this.TestUpdateForm.TabIndex = 2;
+            this.TestUpdateForm.Text = "TestUpdateForm";
+            this.TestUpdateForm.UseVisualStyleBackColor = true;
+            this.TestUpdateForm.Click += new System.EventHandler(this.TestUpdateForm_Click);
+            // 
             // MainForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.ClientSize = new System.Drawing.Size(564, 427);
+            this.Controls.Add(this.TestUpdateForm);
             this.Controls.Add(this.TestComputerInfoForm);
             this.Controls.Add(this.ChineseCalendarForm);
             this.Name = "MainForm";
@@ -69,5 +81,6 @@
 
         private System.Windows.Forms.Button ChineseCalendarForm;
         private System.Windows.Forms.Button TestComputerInfoForm;
+        private System.Windows.Forms.Button TestUpdateForm;
     }
 }

+ 7 - 2
Fork.Net/Test/Y.Test/Views/MainForm.cs

@@ -19,12 +19,17 @@ namespace Y.Test.Views
 
         private void ChineseCalendarForm_Click(object sender, EventArgs e)
         {
-            R.Forms.Get<ChineseCalendarForm>().Show();
+            R.Forms.GetUnique<ChineseCalendarForm>().Show();
         }
 
         private void TestComputerInfoForm_Click(object sender, EventArgs e)
         {
-            R.Forms.Get<TestComputerInfoForm>().Show();
+            R.Forms.GetUnique<TestComputerInfoForm>().Show();
+        }
+
+        private void TestUpdateForm_Click(object sender, EventArgs e)
+        {
+            R.Forms.GetUnique<TestUpdateForm>().Show();
         }
     }
 }

+ 106 - 0
Fork.Net/Test/Y.Test/Views/TestUpdateForm.Designer.cs

@@ -0,0 +1,106 @@
+namespace Y.Test.Views
+{
+    partial class TestUpdateForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.progressBar1 = new System.Windows.Forms.ProgressBar();
+            this.progressBar2 = new System.Windows.Forms.ProgressBar();
+            this.label1 = new System.Windows.Forms.Label();
+            this.label2 = new System.Windows.Forms.Label();
+            this.button1 = new System.Windows.Forms.Button();
+            this.SuspendLayout();
+            // 
+            // progressBar1
+            // 
+            this.progressBar1.Location = new System.Drawing.Point(12, 12);
+            this.progressBar1.Name = "progressBar1";
+            this.progressBar1.Size = new System.Drawing.Size(260, 23);
+            this.progressBar1.TabIndex = 0;
+            // 
+            // progressBar2
+            // 
+            this.progressBar2.Location = new System.Drawing.Point(12, 84);
+            this.progressBar2.Name = "progressBar2";
+            this.progressBar2.Size = new System.Drawing.Size(260, 23);
+            this.progressBar2.TabIndex = 1;
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(13, 42);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(41, 12);
+            this.label1.TabIndex = 2;
+            this.label1.Text = "label1";
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(13, 114);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(41, 12);
+            this.label2.TabIndex = 3;
+            this.label2.Text = "label2";
+            // 
+            // button1
+            // 
+            this.button1.Location = new System.Drawing.Point(197, 226);
+            this.button1.Name = "button1";
+            this.button1.Size = new System.Drawing.Size(75, 23);
+            this.button1.TabIndex = 4;
+            this.button1.Text = "button1";
+            this.button1.UseVisualStyleBackColor = true;
+            this.button1.Click += new System.EventHandler(this.button1_Click);
+            // 
+            // TestUpdateForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(284, 261);
+            this.Controls.Add(this.button1);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.label1);
+            this.Controls.Add(this.progressBar2);
+            this.Controls.Add(this.progressBar1);
+            this.Name = "TestUpdateForm";
+            this.Text = "TestUpdateForm";
+            this.Load += new System.EventHandler(this.TestUpdateForm_Load);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.ProgressBar progressBar1;
+        private System.Windows.Forms.ProgressBar progressBar2;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.Button button1;
+    }
+}

+ 52 - 0
Fork.Net/Test/Y.Test/Views/TestUpdateForm.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using Y.Test.Commons;
+using Y.Utils.AppUtils.UpdateUtils;
+using Y.Utils.DataUtils.JsonUtils;
+using Y.Utils.DelegateUtils;
+using Y.Utils.IOUtils.TxtUtils;
+
+namespace Y.Test.Views
+{
+    public partial class TestUpdateForm : Form
+    {
+        public TestUpdateForm()
+        {
+            InitializeComponent();
+        }
+
+        private void TestUpdateForm_Load(object sender, EventArgs e)
+        {
+
+        }
+
+        private void button1_Click(object sender, EventArgs e)
+        {
+            AppUpdateTool aut = new AppUpdateTool();
+            aut.Update("Oreo.NetMan", new Version(Application.ProductVersion),
+                "http://localhost:20001/Update/Get?name=lalala",
+                R.Paths.Temp, R.Paths.Relative,
+                UIDownProgress, null, UIUnpackProgress, null);
+        }
+        private void UIDownProgress(object sender, ProgressEventArgs e)
+        {
+            BeginInvoke(new Action(() =>
+            {
+                progressBar1.Value = (int)(e.Current * 100 / e.Total);
+            }));
+        }
+        private void UIUnpackProgress(object sender, ProgressEventArgs e)
+        {
+            BeginInvoke(new Action(() =>
+            {
+                progressBar2.Value = (int)(e.Current * 100 / e.Total);
+            }));
+        }
+    }
+}

+ 120 - 0
Fork.Net/Test/Y.Test/Views/TestUpdateForm.resx

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>

+ 9 - 0
Fork.Net/Test/Y.Test/Y.Test.csproj

@@ -129,6 +129,12 @@
     <Compile Include="Views\TestSimpleTitle.Designer.cs">
       <DependentUpon>TestSimpleTitle.cs</DependentUpon>
     </Compile>
+    <Compile Include="Views\TestUpdateForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Views\TestUpdateForm.Designer.cs">
+      <DependentUpon>TestUpdateForm.cs</DependentUpon>
+    </Compile>
     <Compile Include="Views\TestWebForm.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -166,6 +172,9 @@
     <EmbeddedResource Include="Views\TestSimpleTitle.resx">
       <DependentUpon>TestSimpleTitle.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Views\TestUpdateForm.resx">
+      <DependentUpon>TestUpdateForm.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Views\TestWebForm.resx">
       <DependentUpon>TestWebForm.cs</DependentUpon>
     </EmbeddedResource>

+ 30 - 0
Fork.Net/Y.Utils/IOUtils/PathUtils/AppDirTool.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Y.Utils.IOUtils.PathUtils
+{
+    class AppDirTool
+    {
+        public static string Get(string s, Dictionary<string, string> dictionary)
+        {
+            string path = s.Trim();
+            string result = null;
+
+            if (!string.IsNullOrWhiteSpace(path) && dictionary != null)
+            {
+                foreach (var dic in dictionary)
+                {
+                    if (path.Contains(dic.Key))
+                    {
+                        result = path.Replace(dic.Key, dic.Value);
+                        break;
+                    }
+                }
+            }
+
+            return result;
+        }
+    }
+}

+ 1 - 1
Fork.Net/Y.Utils/NetUtils/FTPUtils/FTPTool.cs

@@ -100,7 +100,7 @@ namespace Y.Utils.NetUtils.FTPUtils
                 {
                     using (Stream responseStream = response.GetResponseStream())
                     {
-                        using (FileStream fs = new FileStream(localFile, FileMode.CreateNew))
+                        using (FileStream fs = new FileStream(localFile, FileMode.Create))
                         {
                             byte[] buffer = new byte[1024 * 1024];
                             int read = 0;

+ 16 - 0
Fork.Net/Y.Utils/TaskServiceUtils/TaskServiceDaddy.cs

@@ -9,6 +9,20 @@ namespace Y.Utils.TaskServiceUtils
 {
     public abstract class TaskServiceDaddy
     {
+        private DateTime StartTime, LastRunTime;
+
+        /// <summary>
+        /// 通过运行时间判断是否运行
+        /// </summary>
+        public bool IsRun
+        {
+            get
+            {
+                if (LastRunTime.AddSeconds(Interval + 1000) > DateTime.Now)
+                    return true;
+                return false;
+            }
+        }
         /// <summary>
         /// 已启动
         /// </summary>
@@ -44,6 +58,7 @@ namespace Y.Utils.TaskServiceUtils
         /// </summary>
         public virtual void Start()
         {
+            StartTime = DateTime.Now;
             if (!IsDestroy)
                 Task.Factory.StartNew(() =>
                 {
@@ -53,6 +68,7 @@ namespace Y.Utils.TaskServiceUtils
                         BeforeTODO();
                         do
                         {
+                            LastRunTime = DateTime.Now;
                             TODO();
                             Thread.Sleep(Interval);
 

+ 66 - 0
Fork.Net/Y.Utils/UpdateUtils/AppUpdateInfo.cs

@@ -0,0 +1,66 @@
+//************************************************************************
+//      https://github.com/yuzhengyang
+//      author:     yuzhengyang
+//      date:       2017.8.1 - 2017.8.1
+//      desc:       程序更新工具模型
+//      Copyright (c) yuzhengyang. All rights reserved.
+//************************************************************************
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Y.Utils.UpdateUtils
+{
+    public class AppUpdateInfo
+    {
+        /// <summary>
+        /// 功能名
+        /// </summary>
+        public string Name { get; set; }
+        /// <summary>
+        /// 版本号(必须是点分四位版本号:1.1.1.1)
+        /// </summary>
+        public string Version { get; set; }
+        /// <summary>
+        /// 作者
+        /// </summary>
+        public string Author { get; set; }
+        /// <summary>
+        /// 发布日期
+        /// </summary>
+        public DateTime DateTime { get; set; }
+        /// <summary>
+        /// 下载方式(0:http;1:ftp)
+        /// </summary>
+        public int DownloadMode { get; set; }
+        /// <summary>
+        /// ftp地址
+        /// </summary>
+        public string FtpIp { get; set; }
+        /// <summary>
+        /// ftp账号
+        /// </summary>
+        public string FtpAccount { get; set; }
+        /// <summary>
+        /// ftp密码
+        /// </summary>
+        public string FtpPassword { get; set; }
+        /// <summary>
+        /// Ftp文件
+        /// </summary>
+        public string FtpFile { get; set; }
+        /// <summary>
+        /// http地址
+        /// </summary>
+        public string HttpUrl { get; set; }
+        /// <summary>
+        /// 文件Md5码
+        /// </summary>
+        public string Md5 { get; set; }
+        /// <summary>
+        /// 释放文件目录
+        /// </summary>
+        public string ReleasePath { get; set; }
+    }
+}

+ 113 - 0
Fork.Net/Y.Utils/UpdateUtils/AppUpdateTool.cs

@@ -0,0 +1,113 @@
+//************************************************************************
+//      https://github.com/yuzhengyang
+//      author:     yuzhengyang
+//      date:       2017.8.1 - 2017.8.1
+//      desc:       程序更新工具
+//      Copyright (c) yuzhengyang. All rights reserved.
+//************************************************************************
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Y.Utils.DelegateUtils;
+using Y.Utils.IOUtils.FileUtils;
+using Y.Utils.IOUtils.PathUtils;
+using Y.Utils.NetUtils.FTPUtils;
+using Y.Utils.NetUtils.HttpUtils;
+
+namespace Y.Utils.UpdateUtils
+{
+    /// <summary>
+    /// 程序更新工具
+    /// </summary>
+    public class AppUpdateTool
+    {
+        /// <summary>
+        /// 更新
+        /// </summary>
+        /// <param name="name">功能名称</param>
+        /// <param name="version">当前版本号</param>
+        /// <param name="url">请求新版本地址</param>
+        /// <param name="path">文件下载位置</param>
+        /// <param name="dictionary">文件相对位置字典</param>
+        /// <param name="downprogress">下载进度回调</param>
+        /// <param name="downsender">下载进度事件数据</param>
+        /// <param name="releaseprogress">释放进度回调</param>
+        /// <param name="releasesender">释放进度事件数据</param>
+        /// <returns>
+        /// -10;//无最新版本,停止操作
+        /// -20;//请求服务器最新版本失败
+        /// -30;//新版本号格式不正确,解析失败
+        /// -40;//文件下载失败
+        /// -50;//文件释放失败
+        /// </returns>
+        public int Update(string name, Version version, string url, string path, Dictionary<string, string> dictionary,
+            ProgressDelegate.ProgressHandler downprogress = null, object downsender = null,
+            ProgressDelegate.ProgressHandler releaseprogress = null, object releasesender = null)
+        {
+            Stopwatch stopwatch = new Stopwatch();
+            stopwatch.Start();
+            //请求最新版本信息
+            AppUpdateInfo info = HttpTool.Get<AppUpdateInfo>(string.Format("{0}?name={1}", url, name));
+            if (info != null)
+            {
+                Version newVersion = GerVersion(info.Version);
+                if (newVersion == null) return -30;//新版本号格式不正确,解析失败
+
+                if (newVersion > version)
+                {
+                    string file = DirTool.Combine(path, name + newVersion.ToString());
+                    //准备更新(下载) 
+                    string downfile = Download(file, info, downprogress, downsender);
+                    if (!string.IsNullOrWhiteSpace(downfile) && File.Exists(downfile))
+                    {
+                        //格式化释放文件目录
+                        string releasepath = AppDirTool.Get(info.ReleasePath, dictionary);
+                        //释放文件
+                        if (FilePackageTool.Unpack(downfile, releasepath, releaseprogress, releasesender) > 0)
+                        {
+                            stopwatch.Stop();
+                            return (int)stopwatch.Elapsed.TotalSeconds;
+                        }
+                        else
+                        {
+                            return -50;//文件释放失败
+                        }
+                    }
+                    else
+                    {
+                        return -40;//文件下载失败
+                    }
+                }
+                else
+                {
+                    return -10;//无最新版本,停止操作
+                }
+            }
+            else
+            {
+                return -20;//请求服务器最新版本失败
+            }
+        }
+        private Version GerVersion(string s)
+        {
+            //解析最新版本号
+            Version version = null;
+            try
+            {
+                version = new Version(s);
+            }
+            catch (Exception e) { }
+            return version;
+        }
+        private string Download(string file, AppUpdateInfo info, ProgressDelegate.ProgressHandler progress = null, object sender = null)
+        {
+            FtpTool ftp = new FtpTool(info.FtpIp, info.FtpAccount, info.FtpPassword);
+            if (ftp.Download(info.FtpFile, file, progress, sender))
+                return file;
+            return null;
+        }
+    }
+}

+ 3 - 0
Fork.Net/Y.Utils/Y.Utils.csproj

@@ -52,6 +52,8 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AppUtils\AppUnique.cs" />
+    <Compile Include="UpdateUtils\AppUpdateInfo.cs" />
+    <Compile Include="UpdateUtils\AppUpdateTool.cs" />
     <Compile Include="DataUtils\RandomUtils\RandomTool.cs" />
     <Compile Include="DataUtils\StringUtils\NumberStringTool.cs" />
     <Compile Include="DelegateUtils\ProgressDelegate.cs" />
@@ -66,6 +68,7 @@
     <Compile Include="IOUtils\FileUtils\FilePackageModel.cs" />
     <Compile Include="IOUtils\FileUtils\FilePackageTool.cs" />
     <Compile Include="IOUtils\FileManUtils\FileWatcher.cs" />
+    <Compile Include="IOUtils\PathUtils\AppDirTool.cs" />
     <Compile Include="IOUtils\TxtUtils\ConfigTool.cs" />
     <Compile Include="NetUtils\NetManUtils\NetConnectionInfo.cs" />
     <Compile Include="NetUtils\NetManUtils\NetFlowService.cs" />