1、本文将使用Autoupdater.NET.Official对应用程序进行在线更新。具体效果如下图所示。示例下载地址:https://download.csdn.net/download/xingchengaiwei/89642250
2、我使用的是本机下载,IIS的部署不是本文的主要内容,需要各位大佬自己解决。
2.1、添加一个网站,我这里叫AutoUpdater。如下图。
2.2、在AutoUpdater网站添加FTP站点,如下图。
2.3、在网站中添加文件Index.html,代码如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>更新说明</title>
</head>
<body>
<h1>程序更新</h1>
<p>更新版本号1.2.0.0</p>
<p>更新内容:</p>
<p>1.修复了部分bug</p>
<p>2.增加了部分新功能</p>
</body>
</html>
这个html文件就是显示在更新界面中的内容。
2.4、添加文件AutoUpdaterStarter.xml,内容如下。
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.2.0.0</version>
<url>http://127.0.0.1:8080/Downloads/WPFIPluginDemo.zip</url>
<changelog>http://127.0.0.1:8080/</changelog>
<mandatory>false</mandatory>
</item>
可以使用我提供的示例中的AutoUpdaterXML.zip中的程序生成XML文件。
2.5、创建文件夹Downloads,将需要更新的文件WPFIPluginDemo.zip放在此文件夹中。具体文件结构如下图。
3、WPF程序端文件。
3.1、首先在NuGet中添加Autoupdater.NET.Official,主要版本问题。我使用的是1.8.6。如下图。
3.2、在程序的适当位置添加 代码如下。下面的代码功能是每隔2秒进行一次更新检查。
DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
timer.Tick += delegate
{
AutoUpdater.Start("http://127.0.0.1:8080/AutoUpdaterStarter.xml");
//启用错误报告
AutoUpdater.ReportErrors = true;
//设置zip解压路径
AutoUpdater.InstallationPath = Environment.CurrentDirectory;
//处理应用程序在下载完成后如何退出
AutoUpdater.ApplicationExitEvent += AutoUpdater_ApplicationExitEvent;
//自定义处理更新逻辑事件
//AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
};
timer.Start();
3.3、AutoUpdater.CheckForUpdateEvent 事件可以自定义更新逻辑事件,通过这个事件我们可以自定义更新的界面等。
3.4、我的主体代码内容如下。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using AbstractionLayer;
using AutoUpdaterDotNET;
using Application = System.Windows.Application;
using MessageBox = System.Windows.MessageBox;
using Path = System.IO.Path;
namespace WPFIPluginDemo
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Assembly assembly = Assembly.GetEntryAssembly();
//SetAutoUpdater();
DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
timer.Tick += delegate
{
AutoUpdater.Start("http://127.0.0.1:8080/AutoUpdaterStarter.xml");
//启用错误报告
AutoUpdater.ReportErrors = true;
//设置zip解压路径
AutoUpdater.InstallationPath = Environment.CurrentDirectory;
//处理应用程序在下载完成后如何退出
AutoUpdater.ApplicationExitEvent += AutoUpdater_ApplicationExitEvent;
//自定义处理更新逻辑事件
//AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
};
timer.Start();
this.tbVersion.Text = $"{assembly.GetName().Version}";
//AutoUpdater.Start("http://127.0.0.1:8080/AutoUpdaterStarter.xml");
//开始监控指定文件
//StartMonitoring(GetCurrentProgramPath() + "\\Plugins");
//加载已存在的插件
LoadPlugins(GetCurrentProgramPath() + "\\Plugins");
}
private void AutoUpdater_ApplicationExitEvent()
{
//Text = @"Closing application...";
Thread.Sleep(5000);
Environment.Exit(0);
}
private void AutoUpdaterOnCheckForUpdateEvent(UpdateInfoEventArgs args)
{
if (args.Error == null)
{
//检测到有可用的更新
if (args.IsUpdateAvailable)
{
MessageBoxResult messageBoxResult;
if (args.Mandatory.Value)
{
messageBoxResult =
MessageBox.Show(
$@"当前有一个新版本{{args.CurrentVersion}}可用.你正在使用版本{{args.InstalledVersion}}.点击确认开始更新\",
@"更新可用\", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
messageBoxResult =
MessageBox.Show(
$@"当前有一个新版本{args.CurrentVersion}可用.你正在使用版本{args.InstalledVersion}.确认要更新吗?", @"更新可用",
MessageBoxButton.YesNo, MessageBoxImage.Information);
}
if (messageBoxResult.Equals(MessageBoxResult.Yes) || messageBoxResult.Equals(MessageBoxResult.OK))
{
try
{
//触发更新下载
if (AutoUpdater.DownloadUpdate(args))
{
//Application.Current.Shutdown();//允许应用程序在关闭前进行一些清理工作。
Environment.Exit(0);//则立即终止应用程序,不进行任何清理工作。
}
}
catch (Exception e)
{
MessageBox.Show(e.Message, e.GetType().ToString(), MessageBoxButton.OK, MessageBoxImage.Error);
Debug.WriteLine(e);
throw;
}
}
else
{
//MessageBox.Show(
// $@"当前为最新新版本", @"更新可用",
// MessageBoxButton.OK,
// MessageBoxImage.Information);
}
}
}
else
{
if (args.Error is WebException)
{
MessageBox.Show(
@"连接更新服务器失败,请检查网络连接.",
@"更新检查失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
MessageBox.Show(args.Error.Message,
args.Error.GetType().ToString(), MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
}
private void SetAutoUpdater()
{
AutoUpdater.LetUserSelectRemindLater = true;
AutoUpdater.RemindLaterTimeSpan = RemindLaterFormat.Minutes;
AutoUpdater.RemindLaterAt = 1;
//若您不想在更新表单上显示“跳过”按钮,那个么只需在上面的代码中添加以下行即可。
AutoUpdater.ShowSkipButton = false;
//如果要同步检查更新,请在启动更新之前将Synchronous设置为true,如下所示。
AutoUpdater.Synchronous = true;
//若你们不想在更新表单上显示“以后提醒”按钮,那个么只需在上面的代码中添加以下一行即可。
AutoUpdater.ShowRemindLaterButton = false;
//如果要忽略先前设置的“以后提醒”和“跳过”设置,则可以将“强制”属性设置为true。它还将隐藏“跳过”和“稍后提醒”按钮。如果在代码中将强制设置为true,那么XML文件中的强制值将被忽略。
AutoUpdater.Mandatory = false;
//如果服务器xml文件的版本大于AssemblyInfo中的版本则触发CheckForUpdateEvent
AutoUpdater.CheckForUpdateEvent += (args) =>
{
if (args.Error == null)
{
//检测到有可用的更新
if (args.IsUpdateAvailable)
{
MessageBoxResult messageBoxResult;
if (args.Mandatory.Value)
{
messageBoxResult =
MessageBox.Show(
$@"当前有一个新版本{{args.CurrentVersion}}可用.你正在使用版本{{args.InstalledVersion}}.点击确认开始更新\",
@"更新可用\", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
messageBoxResult =
MessageBox.Show(
$@"当前有一个新版本{args.CurrentVersion}可用.你正在使用版本{args.InstalledVersion}.确认要更新吗?", @"更新可用",
MessageBoxButton.YesNo, MessageBoxImage.Information);
}
if (messageBoxResult.Equals(MessageBoxResult.Yes) || messageBoxResult.Equals(MessageBoxResult.OK))
{
try
{
//触发更新下载
if (AutoUpdater.DownloadUpdate(args))
{
//Application.Current.Shutdown();//允许应用程序在关闭前进行一些清理工作。
Environment.Exit(0);//则立即终止应用程序,不进行任何清理工作。
}
}
catch (Exception e)
{
MessageBox.Show(e.Message, e.GetType().ToString(), MessageBoxButton.OK,MessageBoxImage.Error);
Debug.WriteLine(e);
throw;
}
}
else
{
MessageBox.Show(
$@"当前为最新新版本", @"更新可用",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
}
}
else
{
if (args.Error is WebException)
{
MessageBox.Show(
@"连接更新服务器失败,请检查网络连接.",
@"更新检查失败", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
MessageBox.Show(args.Error.Message,
args.Error.GetType().ToString(), MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
};
}
/// <summary>
/// 手动更新
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnAutoUpdater_OnClick(object sender, RoutedEventArgs e)
{
AutoUpdater.Start("http://127.0.0.1:8080/AutoUpdaterStarter.xml");
}
/// <summary>
/// 文件监视
/// </summary>
private FileSystemWatcher _fileWatcher;
/// <summary>
/// 开始监视文件夹
/// </summary>
/// <param name="directoryPath"></param>
private void StartMonitoring(string directoryPath)
{
// 创建 FileSystemWatcher 实例
_fileWatcher = new FileSystemWatcher();
// 设置要监视的目录路径
_fileWatcher.Path = directoryPath;
// 设置要监视的更改类型(例如:修改、创建、删除)
_fileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
// 添加事件处理程序
_fileWatcher.Changed += OnChanged;
_fileWatcher.Created += OnChanged;
_fileWatcher.Deleted += OnChanged;
// 开启事件监听
_fileWatcher.EnableRaisingEvents = true;
Debug.WriteLine($"已开始监视文件夹: {directoryPath}");
}
/// <summary>
/// 当文件夹中内容发生改变时触发时间
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void OnChanged(object source, FileSystemEventArgs e)
{
//Debug.WriteLine($"文件或文件夹发生变更: {e.FullPath},事件类型: {e.ChangeType}");
if ((e.ChangeType & WatcherChangeTypes.Created) != 0)
{
if (File.Exists(e.FullPath))
{
Debug.WriteLine("创建了文件");
LoadPlugins(GetCurrentProgramPath() + "\\Plugins");
Debug.WriteLine($"文件或文件夹发生变更: {e.FullPath},事件类型: {e.ChangeType}");
}
else
{
Debug.WriteLine("创建了文件夹");
}
}
}
private bool IsDirectory(string path)
{
try
{
FileAttributes attr = File.GetAttributes(path);
if ((attr & FileAttributes.Directory) ==FileAttributes.Directory)
{
return true;
}
else
{
return false;
}
}
catch (Exception e)
{
return false;
}
}
/// <summary>
/// 停止监视文件夹
/// </summary>
public void StopMonitoring()
{
if (_fileWatcher != null)
{
// 停止引发事件
_fileWatcher.EnableRaisingEvents = false;
// 清理资源
_fileWatcher.Dispose();
_fileWatcher = null;
Debug.WriteLine("已停止监视文件夹");
}
}
/// <summary>
/// 点击加载插件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnSearchPlugins_OnClick(object sender, RoutedEventArgs e)
{
LoadPlugins(GetCurrentProgramPath()+"\\Plugins");
}
/// <summary>
/// 获取当前程序集的路径
/// </summary>
/// <returns></returns>
public static string GetCurrentProgramPath()
{
// 获取当前执行的程序集
var assembly = Assembly.GetEntryAssembly();
// 获取程序集所在的路径
var assemblyLocation = assembly.Location;
// 获取程序集所在的目录
var programPath = Path.GetDirectoryName(assemblyLocation);
return programPath;
}
/// <summary>
/// 插件列表
/// </summary>
public List<IPlugin> ListIPlugins = new List<IPlugin>();
/// <summary>
/// 加载指定插件
/// </summary>
/// <param name="pluginDirectory"></param>
public void LoadPlugins(string pluginDirectory)
{
try
{
Dispatcher.BeginInvoke(new Action(delegate
{
// 获取所有插件DLL
var pluginDlls = Directory.GetFiles(pluginDirectory, "*.exe", SearchOption.TopDirectoryOnly);
foreach (var dll in pluginDlls)
{
try
{
Assembly pluginAssembly = Assembly.LoadFrom(dll);
// 查找实现了IPlugin接口的类型
var pluginTypes = pluginAssembly.GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface);
foreach (var pluginType in pluginTypes)
{
IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType);
plugin.Initialize();
// 管理插件的代码
ListIPlugins.Add(plugin);
}
}
catch (Exception e)
{
Debug.WriteLine(e);
//throw;
}
}
}));
}
catch (Exception e)
{
Debug.WriteLine(e);
//throw;
}
}
/// <summary>
/// 调用插件中的方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnShowPlugin_OnClick(object sender, RoutedEventArgs e)
{
Type plugin = ListIPlugins[0].GetType();
MethodInfo showInfo = plugin.GetMethod("Show");//得到dll文件的中的方法
showInfo.Invoke(ListIPlugins[0], null);//方法调用
}
}
}
4、以下内容是我开始做在线更新前查询的内容,希望能够帮助大家拓宽一下视野。
在C# WPF(Windows Presentation Foundation)应用程序中实现在线更新功能,可以通过以下几种方法和工具来实现:
-
Squirrel: Squirrel是一个开源的安装和更新框架,专为.NET应用程序设计。它可以自动处理应用程序的安装、更新和卸载过程。Squirrel可以与WPF应用程序一起使用,以实现在线更新。https://github.com/Squirrel
https://github.com/Squirrel
-
ClickOnce: ClickOnce是Microsoft提供的一种部署技术,允许开发者将应用程序发布到Web服务器或网络共享上。用户可以通过浏览器或Windows资源管理器安装和更新应用程序。ClickOnce适用于WPF应用程序,但可能需要一些配置和设置。(个人觉得操作比较繁琐,不喜欢)
-
WyUpdate: WyUpdate是一个用于.NET应用程序的自动更新框架。它可以与WPF应用程序集成,实现在线更新。WyUpdate提供了一个服务器端组件,用于管理更新文件,以及一个客户端组件,用于在应用程序中检查和应用更新。(收费)
-
AutoUpdater.NET: AutoUpdater.NET是一个简单易用的.NET库,用于实现WPF应用程序的自动更新。它允许开发者指定更新源(例如,Web服务器或FTP服务器),并在应用程序启动时检查更新。如果发现新版本,AutoUpdater.NET将自动下载并安装更新。AutoUpdater.NET: AutoUpdater 是c#.net下桌面应用程序自动更新的类库,使用非常简单,只需要一行代码就可以实现程序自动更新功能,支持.net4.5+,netcore3.1+,net5等,桌面程序支持winform和wpf
https://gitee.com/stylexing/AutoUpdater.NET
在选择在线更新工具时,请考虑以下因素:
- 应用程序的复杂性
- 更新频率
- 用户体验
- 安全性
- 跨平台支持(如果需要)
根据项目需求和团队技能选择合适的工具,以实现高效、安全的在线更新功能。
参考https://www.cnblogs.com/congroo/p/15655717.html