[fix]Config创建默认配置文件的开关Runtime.CreateConfigOnMissing,仅需对自动创建生效,而不应该阻止用户主动Save
智能大石头 authored at 2024-08-09 00:30:41 石头 committed at 2024-08-10 14:22:24
11.88 KiB
X
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using NewLife.Compression;
using NewLife.Log;
using NewLife.Threading;
using NewLife.Web;

namespace XCoder
{
    /// <summary>自动更新</summary>
    class AutoUpdate
    {
        #region 属性
        private String _VerSrc = "http://j.NewLifeX.com/?ID=1&f=XCoderVer.xml";
        /// <summary>版本地址</summary>
        public String VerSrc { get { return _VerSrc; } set { _VerSrc = value; } }

        const String verfile = "XCoderVer.xml";
        #endregion

        #region 方法
        /// <summary>执行处理</summary>
        public void UpdateAsync() { ThreadPoolX.QueueUserWorkItem(Update); }

        public void Update()
        {
            // 取版本
            // 对比版本
            // 拿出更新源
            // 下载更新包
            // 执行更新

            #region 准备工作
            var curdir = AppDomain.CurrentDomain.BaseDirectory;

            String ProcessHelper = Path.Combine(curdir, "NewLife.ProcessHelper.exe");
            if (File.Exists(ProcessHelper)) File.Delete(ProcessHelper);

            // 删除Update目录,避免重复使用错误的升级文件
            var update = Path.Combine(curdir, "Update");
            if (Directory.Exists(update)) { try { Directory.Delete(update, true); } catch { } }
            var updatebat = Path.Combine(curdir, "Update.bat");
            if (File.Exists(updatebat)) { try { File.Delete(updatebat); } catch { } }

            // 开发环境下,自动生成版本文件
            if (IsDevelop()) MakeVerFile();
            #endregion

            #region 取版本、对比版本
            var client = new WebClientX();
            client.Encoding = Encoding.UTF8;
            // 同步下载,3秒超时
            client.Timeout = 3000;
            XTrace.WriteLine("准备从{0}下载版本文件!", VerSrc);
            String xml = client.DownloadString(VerSrc);
            if (String.IsNullOrEmpty(xml))
            {
                XTrace.WriteLine("无法从{0}获取版本信息!", VerSrc);
                return;
            }

            var ver = new VerFile(xml);
            var asm = Assembly.GetExecutingAssembly();
            if (asm.GetName().Version >= ver.GetVersion()) return;
            #endregion

            #region 提示更新
            {
                var sb = new StringBuilder();
                sb.AppendFormat("是否更新到最新版本:{0}", ver.Ver);
                sb.AppendLine();
                sb.AppendLine("XCoder会自动关闭以进行更新,更新完成后会启动新版本!");
                if (!String.IsNullOrEmpty(ver.Description))
                {
                    sb.AppendLine("更新内容:");
                    sb.Append(ver.Description);
                }

                if (MessageBox.Show(sb.ToString(), "发现新版本", MessageBoxButtons.OKCancel) == DialogResult.Cancel) return;
            }
            #endregion

            #region 下载
            String file = Path.Combine(update, "XCoder.zip");
            CheckAndDownload(client, file, ver.Src);

            // 检查是否需要更新源码
            String xfile = Path.Combine(@"C:\X\Src", "Src.zip");
            String dfile = Path.Combine(@"C:\X\DLL", "DLL.zip");

            CheckAndDownload(client, xfile, ver.XSrc);
            CheckAndDownload(client, dfile, ver.DLL);
            #endregion

            #region 更新
            // 先更新源代码
            Extract(xfile, null);
            Extract(dfile, null);

            var dir = Path.GetDirectoryName(file);
            if (Extract(file, dir))
            {
                var cfgfile = Path.Combine(curdir, "XCoder.exe.config");
                var xmlfile = Path.Combine(curdir, "XCoder.xml");

                var sb = new StringBuilder();
                // 备份配置文件
                sb.AppendFormat("mov \"{0}\" \"{0}.bak\" /Y", cfgfile);
                sb.AppendFormat("mov \"{0}\" \"{0}.bak\" /Y", xmlfile);

                // 复制
                sb.AppendFormat("copy \"{0}\\*.*\" \"{1}\" /y", dir, curdir);
                sb.AppendLine();
                sb.AppendLine("rd \"" + dir + "\" /s /q");

                // 还原配置文件
                sb.AppendFormat("mov \"{0}.bak\" \"{0}\" /Y", cfgfile);
                sb.AppendFormat("mov \"{0}.bak\" \"{0}\" /Y", xmlfile);

                // 启动XCoder
                sb.AppendFormat("start {0}", Path.GetFileName(Application.ExecutablePath));
                sb.AppendLine();
                // 删除dir目录
                //sb.AppendLine("rd \"" + dir + "\" /s /q");

                sb.AppendFormat("del \"{0}\" /f/q", ProcessHelper);
                sb.AppendLine();
                sb.AppendFormat("del \"{0}\" /f/q", updatebat);
                sb.AppendLine();

                //String tmpfile = Path.GetTempFileName() + ".bat";
                //String tmpfile = "Update.bat";
                File.WriteAllText(updatebat, sb.ToString(), Encoding.Default);

                #region 老树修改
                // FileSource.ReleaseFile("XCoder.NewLife.ProcessHelper.exe", ProcessHelper);
                //修改部分
                string assemFullName = Assembly.GetExecutingAssembly().FullName;
                string assemNamespace = assemFullName.Substring(0, assemFullName.IndexOf(',')); //命名空间名称
                Source.ReleaseFile(assemNamespace + ".NewLife.ProcessHelper.exe", ProcessHelper);
                #endregion

                ProcessStartInfo si = new ProcessStartInfo();
                si.FileName = ProcessHelper;
                si.WorkingDirectory = Path.GetDirectoryName(si.FileName);
                si.Arguments = Process.GetCurrentProcess().Id + " " + updatebat;
                if (!XTrace.Debug)
                {
                    si.CreateNoWindow = true;
                    si.WindowStyle = ProcessWindowStyle.Hidden;
                }
                si.UseShellExecute = false;
                Process.Start(si);

                XTrace.WriteLine("已启动进程助手来升级,升级脚本:{0}", updatebat);

                Application.Exit();
            }
            #endregion
        }

        static Boolean IsDevelop(String path = null)
        {
            var svn = Path.Combine(path ?? AppDomain.CurrentDomain.BaseDirectory, ".svn");
            return Directory.Exists(svn);
        }

        void MakeVerFile()
        {
            var asm = Assembly.GetExecutingAssembly();
            var stream = asm.GetManifestResourceStream(this.GetType(), "UpdateInfo.txt");
            var sb = new StringBuilder();
            // 只要前五行
            sb.AppendLine();
            using (var reader = new StreamReader(stream))
            {
                for (int i = 0; i < 5 && !reader.EndOfStream; i++)
                {
                    sb.AppendLine(reader.ReadLine());
                }
            }

            var mver = new VerFile();
            mver.Ver = asm.GetName().Version.ToString();
            mver.Src = VerSrc.Replace(verfile, "XCoder.zip");
            mver.XSrc = VerSrc.Replace(verfile, "Src.zip");
            mver.DLL = VerSrc.Replace(verfile, "DLL.zip");
            mver.Description = sb.ToString();

            var mxml = mver.GetXml();
            File.WriteAllText(verfile, mxml);
        }

        void CheckAndDownload(WebClient client, String file, String url)
        {
            if (File.Exists(file) || String.IsNullOrEmpty(url)) return;

            String dir = Path.GetDirectoryName(file);
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
            else if (IsDevelop(dir))
                return;

            if (!url.StartsWithIgnoreCase("http")) url = VerSrc.Replace(verfile, url);

            XTrace.WriteLine("准备从{0}下载相关文件到{1}!", url, file);

            client.DownloadFile(url, file);
        }

        Boolean Extract(String file, String path)
        {
            if (!File.Exists(file)) return false;
            if (String.IsNullOrEmpty(path)) path = Path.GetDirectoryName(file);

            // 解压缩,删除压缩文件
            XTrace.WriteLine("解压缩{0}到{1}!", file, path);
            try
            {
                ZipFile.Extract(file, path);
            }
            catch (Exception ex) { XTrace.WriteException(ex); }
            try
            {
                File.Delete(file);
            }
            catch (Exception ex) { XTrace.WriteException(ex); return false; }

            return true;
        }
        #endregion

        #region 版本
        class VerFile
        {
            #region 属性
            private String _Ver;
            /// <summary>版本</summary>
            public String Ver { get { return _Ver; } set { _Ver = value; } }

            private String _Src;
            /// <summary>文件源</summary>
            public String Src { get { return _Src; } set { _Src = value; } }

            private String _XSrc;
            /// <summary>X组件源代码</summary>
            public String XSrc { get { return _XSrc; } set { _XSrc = value; } }

            private String _DLL;
            /// <summary>X组件目录</summary>
            public String DLL { get { return _DLL; } set { _DLL = value; } }

            private String _Description;
            /// <summary>描述</summary>
            public String Description { get { return _Description; } set { _Description = value; } }
            #endregion

            public VerFile() { }

            public VerFile(String xml)
            {
                try
                {
                    Parse(xml);
                }
                catch (Exception ex)
                {
                    ex.Data["xml"] = xml;
                    XTrace.WriteLine(xml);

                    throw;
                }
            }

            void Parse(String xml)
            {
                var doc = new XmlDocument();
                doc.LoadXml(xml);

                XmlNode node = doc.SelectSingleNode(@"//ver");
                if (node != null) Ver = node.InnerText;
                node = doc.SelectSingleNode(@"//src");
                if (node != null) Src = node.InnerText;
                node = doc.SelectSingleNode(@"//xsrc");
                if (node != null) XSrc = node.InnerText;
                node = doc.SelectSingleNode(@"//dll");
                if (node != null) DLL = node.InnerText;
                node = doc.SelectSingleNode(@"//description");
                if (node != null) Description = node.InnerText;
            }

            public Version GetVersion() { return new Version(Ver); }

            public String GetXml()
            {
                var doc = new XmlDocument();
                var root = doc.CreateElement("r");
                doc.AppendChild(root);

                var node = doc.CreateElement("ver");
                node.InnerText = Ver;
                root.AppendChild(node);

                node = doc.CreateElement("src");
                node.InnerText = Src;
                root.AppendChild(node);

                node = doc.CreateElement("xsrc");
                node.InnerText = XSrc;
                root.AppendChild(node);

                node = doc.CreateElement("dll");
                node.InnerText = DLL;
                root.AppendChild(node);

                node = doc.CreateElement("description");
                node.InnerText = Description;
                root.AppendChild(node);

                return doc.OuterXml;
            }
        }
        #endregion
    }
}