2016-03-31 16 views
1

私のWPFアプリケーションでは、使用可能な新しいバージョンを確認する起動時にクイックルーチンを実行する必要があります。バージョンが利用可能な場合は、アップデートを行い、直ちにアプリを再起動します。これは、メインウィンドウがユーザーに表示される前に実行されるため、アプリケーションが起動するまでにもう2分の1を要したように見えます。UI以外のスレッドから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(); 
      } 
     } 
    } 
} 

残念ながら、リスはCheckForUpdateUpdateApp方法は、全体のupdateメソッドを非同期になって、awaitを使用しなければならないことを意味している、唯一の彼らの更新プロセスasyncを行いました。 Taskにasnycコールを割り当て、次にアップデートを完了するために単に.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(); 

EDIT以下

RestartアクションのTarget属性のスクリーンショットです。デバッガは上のRestar?.Invoke行で一時停止しました。

enter image description here

+0

、それはあなたのプログラムです。 csか 'Ap​​plication'イベントですか? – CodingGorilla

+0

Program.csファイルがありません。アプリケーションの 'OnStartup'メソッドです。プログラムが最初に実行するコマンドです。 – NealR

+0

'Program.cs'を' Main'メソッド(コンソールアプリケーションのような)で追加し、それをあなたのスタートアップオブジェクト(プロパティ - >アプリケーションの下)に設定してそこから更新してください。そうすることで、実行する更新があるときにメインウィンドウを起動しないようにすることができます。 – CodingGorilla

答えて

0

だから、実際の例外はMainWindowは、スタックトレースに基づいて別のスレッドからプロパティを取得アクセスしようとしているどこかRestartハンドラです。これは完全な推測ですが、元のDispatcherOnStartupメソッドに格納し、Restartイベントハンドラに格納されているDispatcherを使用します。

1

だけではなく、それを適切に使用し、古いイベントベースのパターンに非同期プログラミングを変換しようとしています。非同期操作が終了したときにイベントを検出する必要はなく、InvokeをUIスレッドに戻す必要はありません。 awaitは両方を処理します。あなたはこのような単純なコード書くことができ

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(); 
} 

をこれは別のクラスに抽出するだけの十分なコードです:コードが実行されている

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(); 
    } 
} 
関連する問題