【发布时间】:2016-04-06 16:49:54
【问题描述】:
在我的 WPF 应用程序中,我需要在启动时运行一个快速例程来检查新的可用版本。如果版本可用,我们会进行更新,然后希望立即重新启动应用程序。由于这是在用户看到主窗口之前运行的,所以它看起来好像应用程序需要多一秒钟才能启动。
我们使用Squirrel.Windows 作为我们的更新程序。我已经制作了下面的课程来处理检查/应用更新。
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();
}
}
}
}
不幸的是,Squirrel 的更新过程只有async,这意味着CheckForUpdate 和UpdateApp 方法必须使用await,使得整个更新方法是异步的。我将 asnyc 调用分配给 Task,然后简单地分配给 .Wait() 以完成更新。
当我尝试重新启动我的应用程序时出现问题。根据我所阅读的内容,我需要使用Dispatcher.Invoke 来调用重新启动,因为我在执行更新时处于非 UI 线程上。但是,尽管有下面的代码,我仍然收到相同的错误消息:
调用线程无法访问此对象,因为不同的线程拥有它
知道如何正确实现Dispatcher.Invoke 以重新启动应用程序吗?
// Instantiate new UpdateVersion object passing in the URL
UpdateVersion updateVersion = new UpdateVersion(System.Configuration.ConfigurationManager.AppSettings.Get("SquirrelDirectory"));
// Assign Dispatch.Invoke as Restart action delegate
updateVersion.Restart += () =>
{
Dispatcher.Invoke(() =>
{
Process.Start(ResourceAssembly.Location);
Current.Shutdown();
});
};
// This is here for debugging purposes so I know the update is occurring
updateVersion.Progress += (count) =>
{
Debug.WriteLine($"Progress.. {count}");
};
var task = Task.Run(async () => { await updateVersion.UpdateVersions(); });
task.Wait();
编辑
下面是Restart 操作的Target 属性的屏幕截图。调试器从上面的Restar?.Invoke 行暂停。
【问题讨论】:
-
代码在哪里运行,是在您的 Program.cs 中还是在
Application事件中? -
没有 Program.cs 文件,它是应用程序的
OnStartup方法。这是程序运行的第一个命令。 -
您可能会考虑添加带有
Main方法的Program.cs(如控制台应用程序),将其设置为您的启动对象(在属性-> 应用程序下),然后从那里进行更新。这样您就可以简单地选择在需要更新时不启动主窗口。 -
很遗憾,此时无法更改启动过程。
-
根据您在此处显示的代码和您说代码在
OnStartup()方法中执行的评论,我希望程序会死锁,而不是引发异常。事实上,当我试图重现这个问题时,这正是发生的事情(即死锁)。请提供一个好的minimal reproducible example,它可以可靠地重现问题。请注意,当您使用第三方库时,似乎没有任何关于该问题本身的特定内容;您应该能够简单地通过模拟该库的操作来重现该问题。
标签: c# wpf multithreading dispatcher