C# 从非UI线程重新启动WPF应用程序
在我的WPF应用程序中,我需要在启动时运行一个快速例程,检查新的可用版本。如果版本可用,我们将进行更新,然后立即重新启动应用程序。因为这是在用户看到主窗口之前运行的,所以它看起来就像应用程序启动需要一秒钟的时间 我们正在使用更新程序。我创建了下面的类来处理检查/应用更新C# 从非UI线程重新启动WPF应用程序,c#,wpf,multithreading,dispatcher,C#,Wpf,Multithreading,Dispatcher,在我的WPF应用程序中,我需要在启动时运行一个快速例程,检查新的可用版本。如果版本可用,我们将进行更新,然后立即重新启动应用程序。因为这是在用户看到主窗口之前运行的,所以它看起来就像应用程序启动需要一秒钟的时间 我们正在使用更新程序。我创建了下面的类来处理检查/应用更新 public class UpdateVersion { private readonly UpdateManager _updateManager; public Action<int> Prog
public class UpdateVersion
{
private readonly UpdateManager _updateManager;
public Action<int> Progress;
public event Action Restart;
public UpdateVersion(string squirrelUrl)
{
_updateManager = new UpdateManager(squirrelUrl);
}
public async Task UpdateVersions()
{
using (_updateManager)
{
UpdateInfo updateInfo = await _updateManager.CheckForUpdate(progress:Progress);
if (updateInfo.CurrentlyInstalledVersion == null)
{
if (updateInfo.FutureReleaseEntry != null)
{
await _updateManager.UpdateApp(Progress);
// Job crashes here
Restart?.Invoke();
}
}
else if (updateInfo.CurrentlyInstalledVersion.Version < updateInfo.FutureReleaseEntry.Version)
{
await _updateManager.UpdateApp(Progress);
// Job crashes here
Restart?.Invoke();
}
}
}
}
编辑
下面是重启
操作的目标
属性的屏幕截图。调试器在Restar?.Invoke
行上暂停
因此,实际异常在
重新启动
处理程序中的某个位置,该处理程序正试图基于堆栈跟踪从另一个线程访问main窗口
get属性。这是一个完全的猜测,但我会将原始的调度程序
存储在OnStartup
方法中,并在重新启动
事件处理程序中使用存储的调度程序。为什么不使用SplashScreen
?此SplashScreen
将检查新版本,并下载更新或启动旧应用程序
这是一个很好的入门教程:与其尝试将异步编程转换为旧的基于事件的模式,不如正确地使用它。您不需要事件来检测异步操作何时完成,也不需要
Invoke
返回UI线程<代码>等待同时处理这两个问题
您可以编写如下简单的代码:
static readonly SemanticVersion ZeroVersion = new SemanticVersion(0, 0, 0, 0);
private async void Application_Startup(object sender, StartupEventArgs e)
{
await CheckForUpdatesAsync();
}
private async Task CheckForUpdatesAsync()
{
string squirrelUrl = "...";
var updateProgress = new Progress<int>();
IProgress<int> progress = updateProgress;
//Create a splash screen that binds to progress and show it
var splash = new UpdateSplash(updateProgress);
splash.Show();
using (var updateManager = new UpdateManager(squirrelUrl))
{
//IProgress<int>.Report matches Action<i>
var info = await updateManager.CheckForUpdate(progress: progress.Report);
//Get the current and future versions.
//If missing, replace them with version Zero
var currentVersion = info.CurrentlyInstalledVersion?.Version ?? ZeroVersion;
var futureVersion = info.FutureReleaseEntry?.Version ?? ZeroVersion;
//Is there a newer version?
if (currentVersion < futureVersion)
{
await updateManager.UpdateApp(progress.Report);
Restart();
}
}
splash.Hide();
}
private void Restart()
{
Process.Start(ResourceAssembly.Location);
Current.Shutdown();
}
static readonly SemanticVersion ZeroVersion=新的SemanticVersion(0,0,0,0);
私有异步无效应用程序\u启动(对象发送方、StartupEventArgs e)
{
等待CheckForUpdatesAsync();
}
私有异步任务CheckForUpdatesAsync()
{
字符串squirreURL=“…”;
var updateProgress=新进度();
IProgress progress=updateProgress;
//创建一个绑定到progress的启动屏幕并显示它
var splash=newupdatesplash(updateProgress);
splash.Show();
使用(var updateManager=newupdatemanager(squirrelUrl))
{
//IProgress。报告匹配操作
var info=await updateManager.CheckForUpdate(进度:progress.Report);
//获取当前和将来的版本。
//如果丢失,请将其替换为零版
var currentVersion=info.currentlysinstalledVersion?.Version??零版本;
var futureVersion=信息。FutureReleaseEntry?.Version??零版本;
//有更新的版本吗?
如果(当前版本<未来版本)
{
等待updateManager.UpdateApp(progress.Report);
重启();
}
}
splash.Hide();
}
私有void Restart()
{
进程启动(ResourceAssembly.Location);
当前关闭();
}
这刚好足够将代码提取到一个单独的类:
private async void Application_Startup(object sender, StartupEventArgs e)
{
var updater = new Updater();
await updater.CheckForUpdatesAsync(...);
}
// ...
class Updater
{
static readonly SemanticVersion ZeroVersion = new SemanticVersion(0, 0, 0, 0);
public async Task CheckForUpdatesAsync(string squirrelUrl)
{
var updateProgress = new Progress<int>();
IProgress<int> progress = updateProgress;
//Create a splash screen that binds to progress and show it
var splash = new UpdateSplash(updateProgress);
splash.Show();
using (var updateManager = new UpdateManager(squirrelUrl))
{
var updateInfo = await updateManager.CheckForUpdate(progress: progress.Report);
//Get the current and future versions. If missing, replace them with version Zero
var currentVersion = updateInfo.CurrentlyInstalledVersion?.Version ?? ZeroVersion;
var futureVersion = updateInfo.FutureReleaseEntry?.Version ?? ZeroVersion;
//Is there a newer version?
if (currentVersion < futureVersion)
{
await updateManager.UpdateApp(progress.Report);
Restart();
}
}
splash.Hide();
}
private void Restart()
{
Process.Start(Application.ResourceAssembly.Location);
Application.Current.Shutdown();
}
}
private async void应用程序\u启动(对象发送方,StartupEventArgs e)
{
var updater=new updater();
等待更新程序。检查更新同步(…);
}
// ...
类更新程序
{
静态只读SemanticVersion ZeroVersion=新SemanticVersion(0,0,0,0);
公共异步任务CheckForUpdatesAsync(字符串URL)
{
var updateProgress=新进度();
IProgress progress=updateProgress;
//创建一个绑定到progress的启动屏幕并显示它
var splash=newupdatesplash(updateProgress);
splash.Show();
使用(var updateManager=newupdatemanager(squirrelUrl))
{
var updateInfo=await updateManager.CheckForUpdate(进度:progress.Report);
//获取当前和未来版本。如果缺少,请将其替换为零版本
var currentVersion=updateInfo.CurrentlyInstalledVersion?.Version??ZeroVersion;
var futureVersion=updateInfo.FutureReleaseEntry?.Version??零版本;
//有更新的版本吗?
如果(当前版本<未来版本)
{
等待updateManager.UpdateApp(progress.Report);
重启();
}
}
splash.Hide();
}
私有void Restart()
{
Process.Start(Application.resourcepassembly.Location);
Application.Current.Shutdown();
}
}
代码在哪里运行,是在Program.cs中还是在应用程序
事件中?没有Program.cs文件这是应用程序的启动
方法。这是程序运行的第一个命令。您可以考虑添加<代码>程序。CS < /代码>使用<代码>主< /COD>方法(如控制台应用程序),将其设置为启动对象(在属性->应用程序下),然后从那里进行更新。这样,当需要进行更新时,您只需选择不启动主窗口。不幸的是,此时无法更改启动过程。根据您在此处显示的代码和您的注释,即代码是在OnStartup()
方法中执行的,我预计程序会死锁,而不是抛出异常。事实上,当我试图重现这个问题时,这正是发生的情况(即僵局)。请提供一份可靠地再现问题的良好报告。请注意,当您使用第三方库时,似乎没有任何问题本身与此相关;您应该能够通过模拟库的操作来重现问题。
private async void Application_Startup(object sender, StartupEventArgs e)
{
var updater = new Updater();
await updater.CheckForUpdatesAsync(...);
}
// ...
class Updater
{
static readonly SemanticVersion ZeroVersion = new SemanticVersion(0, 0, 0, 0);
public async Task CheckForUpdatesAsync(string squirrelUrl)
{
var updateProgress = new Progress<int>();
IProgress<int> progress = updateProgress;
//Create a splash screen that binds to progress and show it
var splash = new UpdateSplash(updateProgress);
splash.Show();
using (var updateManager = new UpdateManager(squirrelUrl))
{
var updateInfo = await updateManager.CheckForUpdate(progress: progress.Report);
//Get the current and future versions. If missing, replace them with version Zero
var currentVersion = updateInfo.CurrentlyInstalledVersion?.Version ?? ZeroVersion;
var futureVersion = updateInfo.FutureReleaseEntry?.Version ?? ZeroVersion;
//Is there a newer version?
if (currentVersion < futureVersion)
{
await updateManager.UpdateApp(progress.Report);
Restart();
}
}
splash.Hide();
}
private void Restart()
{
Process.Start(Application.ResourceAssembly.Location);
Application.Current.Shutdown();
}
}