changeset 10:9e77c0dccb66

Add update checker
author Brad Greco <brad@bgreco.net>
date Mon, 08 Apr 2019 21:31:03 -0400
parents 7127d5b5ac75
children 75ca86e0862c
files ServerMonitor/App.config ServerMonitor/Forms/ServerSummaryForm.Designer.cs ServerMonitor/Forms/ServerSummaryForm.cs ServerMonitor/Forms/UpdateDialog.Designer.cs ServerMonitor/Forms/UpdateDialog.cs ServerMonitor/Forms/UpdateDialog.resx ServerMonitor/Properties/Settings.Designer.cs ServerMonitor/Properties/Settings.settings ServerMonitor/ServerMonitor.csproj ServerMonitor/packages.config
diffstat 10 files changed, 439 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/ServerMonitor/App.config	Mon Apr 08 21:29:54 2019 -0400
+++ b/ServerMonitor/App.config	Mon Apr 08 21:31:03 2019 -0400
@@ -32,6 +32,9 @@
             <setting name="SummaryFormSize" serializeAs="String">
                 <value>0, 0</value>
             </setting>
+            <setting name="IgnoreUpdate" serializeAs="String">
+                <value />
+            </setting>
         </ServerMonitorApp.Properties.Settings>
         <ServerMonitor.Properties.Settings>
             <setting name="ConfirmDeleteCheck" serializeAs="String">
--- a/ServerMonitor/Forms/ServerSummaryForm.Designer.cs	Mon Apr 08 21:29:54 2019 -0400
+++ b/ServerMonitor/Forms/ServerSummaryForm.Designer.cs	Mon Apr 08 21:31:03 2019 -0400
@@ -31,16 +31,19 @@
             this.components = new System.ComponentModel.Container();
             this.ServerPanel = new System.Windows.Forms.FlowLayoutPanel();
             this.NotifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
+            this.NotificationIconMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
+            this.ShowServerMonitorMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.ExitMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.ServerContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
             this.ToggleEnableServerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.DeleteServerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.SettingsButton = new System.Windows.Forms.Button();
             this.NewServerButton = new System.Windows.Forms.Button();
-            this.NotificationIconMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
-            this.ShowServerMonitorMenuItem = new System.Windows.Forms.ToolStripMenuItem();
-            this.ExitMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.UpdatePanel = new System.Windows.Forms.Panel();
+            this.UpdateLabel = new System.Windows.Forms.Label();
+            this.NotificationIconMenu.SuspendLayout();
             this.ServerContextMenu.SuspendLayout();
-            this.NotificationIconMenu.SuspendLayout();
+            this.UpdatePanel.SuspendLayout();
             this.SuspendLayout();
             // 
             // ServerPanel
@@ -62,6 +65,28 @@
             this.NotifyIcon.BalloonTipClicked += new System.EventHandler(this.NotifyIcon_BalloonTipClicked);
             this.NotifyIcon.MouseClick += new System.Windows.Forms.MouseEventHandler(this.NotifyIcon_MouseClick);
             // 
+            // NotificationIconMenu
+            // 
+            this.NotificationIconMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.ShowServerMonitorMenuItem,
+            this.ExitMenuItem});
+            this.NotificationIconMenu.Name = "NotificationIconMenu";
+            this.NotificationIconMenu.Size = new System.Drawing.Size(161, 48);
+            this.NotificationIconMenu.ItemClicked += new System.Windows.Forms.ToolStripItemClickedEventHandler(this.NotificationIconMenu_ItemClicked);
+            // 
+            // ShowServerMonitorMenuItem
+            // 
+            this.ShowServerMonitorMenuItem.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
+            this.ShowServerMonitorMenuItem.Name = "ShowServerMonitorMenuItem";
+            this.ShowServerMonitorMenuItem.Size = new System.Drawing.Size(160, 22);
+            this.ShowServerMonitorMenuItem.Text = "Server Monitor";
+            // 
+            // ExitMenuItem
+            // 
+            this.ExitMenuItem.Name = "ExitMenuItem";
+            this.ExitMenuItem.Size = new System.Drawing.Size(160, 22);
+            this.ExitMenuItem.Text = "Exit";
+            // 
             // ServerContextMenu
             // 
             this.ServerContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
@@ -110,33 +135,35 @@
             this.NewServerButton.UseVisualStyleBackColor = true;
             this.NewServerButton.Click += new System.EventHandler(this.NewServerButton_Click);
             // 
-            // NotificationIconMenu
+            // UpdatePanel
             // 
-            this.NotificationIconMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.ShowServerMonitorMenuItem,
-            this.ExitMenuItem});
-            this.NotificationIconMenu.Name = "NotificationIconMenu";
-            this.NotificationIconMenu.Size = new System.Drawing.Size(181, 70);
-            this.NotificationIconMenu.ItemClicked += new System.Windows.Forms.ToolStripItemClickedEventHandler(this.NotificationIconMenu_ItemClicked);
-            // 
-            // ShowServerMonitorMenuItem
+            this.UpdatePanel.BackColor = System.Drawing.Color.Aquamarine;
+            this.UpdatePanel.Controls.Add(this.UpdateLabel);
+            this.UpdatePanel.Dock = System.Windows.Forms.DockStyle.Top;
+            this.UpdatePanel.Location = new System.Drawing.Point(0, 0);
+            this.UpdatePanel.Name = "UpdatePanel";
+            this.UpdatePanel.Size = new System.Drawing.Size(672, 22);
+            this.UpdatePanel.TabIndex = 0;
+            this.UpdatePanel.Visible = false;
             // 
-            this.ShowServerMonitorMenuItem.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
-            this.ShowServerMonitorMenuItem.Name = "ShowServerMonitorMenuItem";
-            this.ShowServerMonitorMenuItem.Size = new System.Drawing.Size(180, 22);
-            this.ShowServerMonitorMenuItem.Text = "Server Monitor";
+            // UpdateLabel
             // 
-            // ExitMenuItem
-            // 
-            this.ExitMenuItem.Name = "ExitMenuItem";
-            this.ExitMenuItem.Size = new System.Drawing.Size(180, 22);
-            this.ExitMenuItem.Text = "Exit";
+            this.UpdateLabel.Cursor = System.Windows.Forms.Cursors.Hand;
+            this.UpdateLabel.Dock = System.Windows.Forms.DockStyle.Fill;
+            this.UpdateLabel.Location = new System.Drawing.Point(0, 0);
+            this.UpdateLabel.Name = "UpdateLabel";
+            this.UpdateLabel.Size = new System.Drawing.Size(672, 22);
+            this.UpdateLabel.TabIndex = 0;
+            this.UpdateLabel.Text = "Program update available";
+            this.UpdateLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+            this.UpdateLabel.Click += new System.EventHandler(this.UpdateLabel_Click);
             // 
             // ServerSummaryForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.ClientSize = new System.Drawing.Size(672, 395);
+            this.Controls.Add(this.UpdatePanel);
             this.Controls.Add(this.SettingsButton);
             this.Controls.Add(this.NewServerButton);
             this.Controls.Add(this.ServerPanel);
@@ -146,8 +173,9 @@
             this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ServerSummaryForm_FormClosing);
             this.Load += new System.EventHandler(this.ServerSummaryForm_Load);
             this.ResizeEnd += new System.EventHandler(this.ServerSummaryForm_ResizeEnd);
+            this.NotificationIconMenu.ResumeLayout(false);
             this.ServerContextMenu.ResumeLayout(false);
-            this.NotificationIconMenu.ResumeLayout(false);
+            this.UpdatePanel.ResumeLayout(false);
             this.ResumeLayout(false);
 
         }
@@ -164,6 +192,8 @@
         private System.Windows.Forms.ContextMenuStrip NotificationIconMenu;
         private System.Windows.Forms.ToolStripMenuItem ShowServerMonitorMenuItem;
         private System.Windows.Forms.ToolStripMenuItem ExitMenuItem;
+        private System.Windows.Forms.Panel UpdatePanel;
+        private System.Windows.Forms.Label UpdateLabel;
     }
 }
 
--- a/ServerMonitor/Forms/ServerSummaryForm.cs	Mon Apr 08 21:29:54 2019 -0400
+++ b/ServerMonitor/Forms/ServerSummaryForm.cs	Mon Apr 08 21:31:03 2019 -0400
@@ -1,4 +1,7 @@
-using ServerMonitorApp.Properties;
+using NAppUpdate.Framework;
+using NAppUpdate.Framework.Sources;
+using NAppUpdate.Framework.Tasks;
+using ServerMonitorApp.Properties;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -68,6 +71,7 @@
             monitor.CheckStatusChanged += Monitor_CheckStatusChanged;
             RefreshDisplay();
             CollectPrivateKeyPasswords();
+            CheckForUpdate();
         }
 
         private ServerForm ShowServerForm(Server server, bool activate = true)
@@ -287,5 +291,64 @@
             else if (e.ClickedItem == ExitMenuItem)
                 Application.Exit();
         }
+
+        private void CheckForUpdate()
+        {
+            //System.Threading.Thread.Sleep(5000);
+            UpdateManager manager = UpdateManager.Instance;
+            manager.ReinstateIfRestarted();
+            manager.UpdateSource = new SimpleWebSource(@"c:\temp\feed.xml");
+            if (manager.State == UpdateManager.UpdateProcessState.NotChecked)
+                manager.BeginCheckForUpdates(CheckForUpdatesCallback, null);
+        }
+
+        private void CheckForUpdatesCallback(IAsyncResult result)
+        {
+            UpdateManager manager = UpdateManager.Instance;
+            if (manager.UpdatesAvailable > 0)
+            {
+                GetUpdateInfo(out string version, out string _);
+                if (Settings.Default.IgnoreUpdate != version)
+                    Invoke((MethodInvoker)(() => UpdatePanel.Show()));
+            }
+        }
+
+        private void PrepareUpdatesCallback(IAsyncResult result)
+        {
+            UpdateManager manager = UpdateManager.Instance;
+            manager.EndCheckForUpdates(result);
+            manager.ApplyUpdates(true);
+        }
+
+        private void UpdateLabel_Click(object sender, EventArgs e)
+        {
+            GetUpdateInfo(out string version, out string changeMessage);
+            string message = "Server Monitor version {0} is available for download." + Environment.NewLine
+                + Environment.NewLine
+                + "What's new:" + Environment.NewLine
+                + "{1}" + Environment.NewLine
+                + Environment.NewLine
+                + "Would you like to download and apply the update now?";
+            using (UpdateDialog dialog = new UpdateDialog { Message = string.Format(message, version, changeMessage) })
+            {
+                DialogResult result = dialog.ShowDialog();
+                if (dialog.Checked && result == DialogResult.Cancel)
+                {
+                    Settings.Default.IgnoreUpdate = version;
+                    Settings.Default.Save();
+                    UpdatePanel.Hide();
+                }
+                if (result != DialogResult.OK)
+                    return;
+            }
+            UpdateManager.Instance.BeginPrepareUpdates(PrepareUpdatesCallback, null);
+        }
+
+        private void GetUpdateInfo(out string version, out string changeMessage)
+        {
+            string[] parts = UpdateManager.Instance.Tasks.First().Description.Split(new char[] { ':' }, 2);
+            version = parts[0];
+            changeMessage = parts[1];
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/UpdateDialog.Designer.cs	Mon Apr 08 21:31:03 2019 -0400
@@ -0,0 +1,138 @@
+namespace ServerMonitorApp
+{
+    partial class UpdateDialog
+    {
+        /// <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.PromptCheckBox = new System.Windows.Forms.CheckBox();
+            this.YesButton = new System.Windows.Forms.Button();
+            this.NoButton = new System.Windows.Forms.Button();
+            this.MessageLabel = new System.Windows.Forms.Label();
+            this.MessageIcon = new System.Windows.Forms.PictureBox();
+            this.panel1 = new System.Windows.Forms.Panel();
+            ((System.ComponentModel.ISupportInitialize)(this.MessageIcon)).BeginInit();
+            this.panel1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // PromptCheckBox
+            // 
+            this.PromptCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+            this.PromptCheckBox.AutoSize = true;
+            this.PromptCheckBox.Location = new System.Drawing.Point(12, 17);
+            this.PromptCheckBox.Name = "PromptCheckBox";
+            this.PromptCheckBox.Size = new System.Drawing.Size(178, 17);
+            this.PromptCheckBox.TabIndex = 3;
+            this.PromptCheckBox.Text = "&Do not ask again for this version";
+            this.PromptCheckBox.UseVisualStyleBackColor = true;
+            this.PromptCheckBox.CheckedChanged += new System.EventHandler(this.PromptCheckBox_CheckedChanged);
+            // 
+            // YesButton
+            // 
+            this.YesButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.YesButton.DialogResult = System.Windows.Forms.DialogResult.OK;
+            this.YesButton.Location = new System.Drawing.Point(228, 13);
+            this.YesButton.Name = "YesButton";
+            this.YesButton.Size = new System.Drawing.Size(75, 23);
+            this.YesButton.TabIndex = 1;
+            this.YesButton.Text = "&Yes";
+            this.YesButton.UseVisualStyleBackColor = true;
+            // 
+            // NoButton
+            // 
+            this.NoButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.NoButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.NoButton.Location = new System.Drawing.Point(309, 13);
+            this.NoButton.Name = "NoButton";
+            this.NoButton.Size = new System.Drawing.Size(75, 23);
+            this.NoButton.TabIndex = 2;
+            this.NoButton.Text = "&No";
+            this.NoButton.UseVisualStyleBackColor = true;
+            // 
+            // MessageLabel
+            // 
+            this.MessageLabel.AutoSize = true;
+            this.MessageLabel.Location = new System.Drawing.Point(70, 22);
+            this.MessageLabel.Name = "MessageLabel";
+            this.MessageLabel.Size = new System.Drawing.Size(0, 13);
+            this.MessageLabel.TabIndex = 3;
+            // 
+            // MessageIcon
+            // 
+            this.MessageIcon.Location = new System.Drawing.Point(25, 49);
+            this.MessageIcon.Name = "MessageIcon";
+            this.MessageIcon.Size = new System.Drawing.Size(32, 32);
+            this.MessageIcon.TabIndex = 4;
+            this.MessageIcon.TabStop = false;
+            // 
+            // panel1
+            // 
+            this.panel1.BackColor = System.Drawing.SystemColors.Control;
+            this.panel1.Controls.Add(this.YesButton);
+            this.panel1.Controls.Add(this.NoButton);
+            this.panel1.Controls.Add(this.PromptCheckBox);
+            this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
+            this.panel1.Location = new System.Drawing.Point(0, 123);
+            this.panel1.Name = "panel1";
+            this.panel1.Size = new System.Drawing.Size(396, 49);
+            this.panel1.TabIndex = 5;
+            // 
+            // UpdateDialog
+            // 
+            this.AcceptButton = this.YesButton;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.BackColor = System.Drawing.Color.White;
+            this.CancelButton = this.NoButton;
+            this.ClientSize = new System.Drawing.Size(396, 172);
+            this.Controls.Add(this.panel1);
+            this.Controls.Add(this.MessageIcon);
+            this.Controls.Add(this.MessageLabel);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+            this.MaximizeBox = false;
+            this.MinimizeBox = false;
+            this.Name = "UpdateDialog";
+            this.ShowInTaskbar = false;
+            this.Text = "Update";
+            this.Load += new System.EventHandler(this.CheckBoxDialog_Load);
+            ((System.ComponentModel.ISupportInitialize)(this.MessageIcon)).EndInit();
+            this.panel1.ResumeLayout(false);
+            this.panel1.PerformLayout();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.CheckBox PromptCheckBox;
+        private System.Windows.Forms.Button YesButton;
+        private System.Windows.Forms.Button NoButton;
+        private System.Windows.Forms.Label MessageLabel;
+        private System.Windows.Forms.PictureBox MessageIcon;
+        private System.Windows.Forms.Panel panel1;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/UpdateDialog.cs	Mon Apr 08 21:31:03 2019 -0400
@@ -0,0 +1,33 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    /// <summary>Message dialog with an additional checkbox.</summary>
+    public partial class UpdateDialog : Form
+    {
+        /// <summary>Message to show.</summary>
+        public string Message { get; set; }
+
+        /// <summary>Check state of the checkbox.</summary>
+        public bool Checked { get; private set; }
+
+        public UpdateDialog()
+        {
+            InitializeComponent();
+        }
+
+        private void CheckBoxDialog_Load(object sender, EventArgs e)
+        {
+            MessageIcon.Image = SystemIcons.Question.ToBitmap();
+            MessageLabel.Text = Message;
+        }
+
+        /// <summary>Updates the public property with the checked state so it can be read by the dialog owner.</summary>
+        private void PromptCheckBox_CheckedChanged(object sender, EventArgs e)
+        {
+            Checked = PromptCheckBox.Checked;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/UpdateDialog.resx	Mon Apr 08 21:31:03 2019 -0400
@@ -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>
\ No newline at end of file
--- a/ServerMonitor/Properties/Settings.Designer.cs	Mon Apr 08 21:29:54 2019 -0400
+++ b/ServerMonitor/Properties/Settings.Designer.cs	Mon Apr 08 21:31:03 2019 -0400
@@ -106,5 +106,17 @@
                 this["SummaryFormSize"] = value;
             }
         }
+        
+        [global::System.Configuration.UserScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("")]
+        public string IgnoreUpdate {
+            get {
+                return ((string)(this["IgnoreUpdate"]));
+            }
+            set {
+                this["IgnoreUpdate"] = value;
+            }
+        }
     }
 }
--- a/ServerMonitor/Properties/Settings.settings	Mon Apr 08 21:29:54 2019 -0400
+++ b/ServerMonitor/Properties/Settings.settings	Mon Apr 08 21:31:03 2019 -0400
@@ -23,5 +23,8 @@
     <Setting Name="SummaryFormSize" Type="System.Drawing.Size" Scope="User">
       <Value Profile="(Default)">0, 0</Value>
     </Setting>
+    <Setting Name="IgnoreUpdate" Type="System.String" Scope="User">
+      <Value Profile="(Default)" />
+    </Setting>
   </Settings>
 </SettingsFile>
\ No newline at end of file
--- a/ServerMonitor/ServerMonitor.csproj	Mon Apr 08 21:29:54 2019 -0400
+++ b/ServerMonitor/ServerMonitor.csproj	Mon Apr 08 21:31:03 2019 -0400
@@ -40,6 +40,9 @@
     <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="NAppUpdate.Framework, Version=0.5.1.0, Culture=neutral, PublicKeyToken=d1f1d1f19f9e5a56, processorArchitecture=MSIL">
+      <HintPath>..\packages\NAppUpdate.Framework.0.5.1.0\lib\net40\NAppUpdate.Framework.dll</HintPath>
+    </Reference>
     <Reference Include="Renci.SshNet, Version=2016.1.0.0, Culture=neutral, PublicKeyToken=1cee9f8bde3db106, processorArchitecture=MSIL">
       <HintPath>..\packages\SSH.NET.2016.1.0\lib\net40\Renci.SshNet.dll</HintPath>
     </Reference>
@@ -95,6 +98,12 @@
     <Compile Include="Controls\MatchComboBox.cs">
       <SubType>Component</SubType>
     </Compile>
+    <Compile Include="Forms\UpdateDialog.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\UpdateDialog.Designer.cs">
+      <DependentUpon>UpdateDialog.cs</DependentUpon>
+    </Compile>
     <Compile Include="Forms\InputDialog.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -179,6 +188,9 @@
     <EmbeddedResource Include="Controls\HttpCheckControl.resx">
       <DependentUpon>HttpCheckControl.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Forms\UpdateDialog.resx">
+      <DependentUpon>UpdateDialog.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Forms\CheckBoxDialog.resx">
       <DependentUpon>CheckBoxDialog.cs</DependentUpon>
     </EmbeddedResource>
--- a/ServerMonitor/packages.config	Mon Apr 08 21:29:54 2019 -0400
+++ b/ServerMonitor/packages.config	Mon Apr 08 21:31:03 2019 -0400
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
+  <package id="NAppUpdate.Framework" version="0.5.1.0" targetFramework="net45" />
   <package id="SSH.NET" version="2016.1.0" targetFramework="net45" />
 </packages>
\ No newline at end of file