前回に続き今回もデスクトップアプリ開発におけるリスタートマネージャー(Restart Manager)について投稿したいと思います。今回は停止される側でのアプリとしてFile Package Appで作成したパッケージを例として設計・実装の説明をしたいと思います。
アプリをリスタートマネージャーに対応する
アプリをリスタートマネージャーに対応するとき、アプリの実行状態により、以下のいずれかの対応をします。
- ただ単にアプリを終了するのみ。アプリの再起動はしない。
- アプリの状態を保存して、アプリを再起動し、アプリの状態を復元する。
- アプリの終了要求を拒否する。拒否する理由をシステムに登録する。
最後の「拒否の対応方法」は、システムが破壊される、ライブ放送の録音・録画しているなどの特別な理由がない限り使用しません。
前者の二つの対応方法は、アプリの状態や特性に合わせて使い分けます。
File Package Appのパッケージの振る舞い
パッケージのリスタートマネージャー対応として、前者の二つの対応方法のどちらで対応するかは、アプリの処理の状態に合わせて検討します。まず、アプリの各処理を確認します。
File Package Appで作成したパッケージは、おおまかに以下の順番で処理が進みます。
- ファイルの破損チェック。破損していればエラーメッセージを表示して終了する
- 評価版パッケージであれば使用期限のメッセージを表示する
- 進捗ダイアログを表示する(最後まで表示したまま)
- ファイルの展開のために管理者権限が必要であればエレベーションする
- 実行制限の条件をチェックする。実行制限の条件に一致すればエラーメッセージを表示して終了する
- 展開先フォルダーの設定がユーザー選択であればフォルダー選択ダイアログを表示する
- ファイルを展開する
- ファイルを実行する設定であれば、ファイルを実行する
- 作業フォルダーを削除する
- 進捗ダイアログのメッセージを完了に変更する
アプリの終了時に状態を保存する必要があるか検討する
この処理の順番を見てわかるように、自己展開形式EXEファイルであるため、実行時にユーザー入力はほとんどありません。
唯一のユーザー入力が、展開先フォルダーのユーザー選択です。しかし、通常は展開先フォルダーをユーザーに選択させることはありません。パッケージの作成時にユーザーに選択させることを意図的に設定した時のみです。そのため、アプリの再起動時は、もう一度ユーザーにフォルダー選択してもらうこととします。
上記のように方針を決定すると、アプリの終了時にアプリの状態を保存する必要はなく、また、再起動時には状態を復元する必要はありません。アプリの再起動のときは、単純に最初から再実行すればよいことになります。
アプリを終了するのみにするか、アプリを再起動するかを検討する
各手順ごとに、「アプリを終了するのみ」にするか、「アプリを再起動する」かを決めます。
処理手順1は、パッケージファイルが壊れているときに表示されるメッセージとなります。ファイルが壊れているときは起動しないほうが良いので、再起動はせず「アプリを終了するのみ」とします。
処理手順2は、評価版パッケージのみで表示されるメッセージとなります。通常のパッケージでは表示されません。再起動して同じ状態に戻すこととします。そのため、「アプリを再起動する」とします。
処理手順3から処理手順7までは、パッケージのファイル展開処理となります。途中で中断した時は、再起動して最後まで行う必要があります。そのため、この処理手順では「アプリを再起動する」ものとします。
処理手順8は、展開したファイルを起動する処理となります。ファイルを展開した後に起動する設定をしたパッケージでのみ、展開した指定のファイルを起動します。多くの場合、インストーラーなどの実行ファイルになると思います。プロセスが起動できていれば、その起動したプロセスに対するリスタートマネージャー対応は、起動されたプロセス側で対応すべき処理となります。そのため、パッケージ側では、特に再起動はしないものとします。したがって、「アプリを終了するのみ」とします。
処理手順9は、最後のクリーンナップ処理となります。この処理は、途中で中断すると一時ファイルなどがごみとして残ってしまいます。そのため、常に完了させたいので、この処理中は中断しないものとします。したがって、リスタートマネージャーには対応せず、この手順は最後まで処理します。
処理手順10は、進捗ダイアログに完了状態を表示しているのみです。パッケージとしては、ファイルの展開も完了し、ファイルの実行も完了しています。アプリを再起動して、もう一度ファイルの展開をする必要はありません。そのため、このタイミングでは、「アプリを終了するのみ」とし、再起動はしないものとします。
上記の検討の結果、処理手順9は、処理を中断させません。そのため、必ず処理手順10に移行することになります。結果として、処理手順9は、処理手順10と同じ振舞になります。
最終的に以下の対応となります。
- アプリを終了するのみ
処理手順1, 8, (9), 10 - アプリを再起動する
処理手順2, 3, 4, 5, 6, 7
となります。
実装の方法
通常の対応方法であれば、WM_QUERYENDSESSIONのメッセージを受信したときに、「アプリを終了するのみ」の場合は単純にTRUEを返します。「アプリを再起動する」の場合は、RegisterApplicationRestart関数(英語)を使ってアプリの再起動の登録をします。WM_ENDSESSIONのメッセージでは特に何もしません。
ところが、パッケージファイルでは、通常の方法で対応できない状態があります。
パッケージファイルで、再起動の登録をするタイミングについて考えます。「アプリを再起動する」ことが必要なすべての処理手順(2, 3, 4, 5, 6, 7)でWM_QUERYENDSESSIONのメッセージを受信できるのであれば、そのタイミングで、 RegisterApplicationRestart関数(英語)を使ってアプリの再起動の登録ができます。
しかし、WM_QUERYENDSESSIONのメッセージを受信するためには、アプリが自身のウィンドウを作成している必要があります。パッケージファイルの場合、アプリのウィンドウを作成しているのは、処理手順3から処理手順7までです。処理手順2ではまだウィンドウを作成していません。
そのため、WM_QUERYENDSESSIONのメッセージを受信のタイミングのみですべてのリスタートマネージャー対応をすることはできません。
パッケージでは最終的に以下の実装をしました。
- 処理手順2の直前に「アプリを再起動する」登録をします。RegisterApplicationRestart関数(英語)を使用します。
- 処理手順7の直後に「アプリを再起動する」登録を解除します。UnregisterApplicationRestart関数(英語)を使用します。
- WM_QUERYENDSESSIONのメッセージを受信のタイミングでは、処理手順9を実行中の場合は完了するまで待ちます。処理手順9以外の場合は、最新の状態に更新します。RegisterApplicationRestart関数(英語)または、UnregisterApplicationRestart関数(英語)を使用します。メッセージの戻り値としてはTRUEを返します。
RegisterApplicationRestart関数(英語)を使ってアプリの再起動を登録するときの第1引数であるコマンドライン引数は、パッケージが起動されたときの引数と同じものを設定するものとします。これにより、再起動後も同じモードでパッケージが起動することになります。
これで、必要に応じてアプリの再起動を伴うリスタートマネージャー対応ができました。この状況に応じてアプリを再起動する対応は、File Package Appのバージョン0.6.2でしました。バージョン0.6.2以前ではすべての状況で「アプリを終了するのみ」の対応となっています。
リスタートマネージャー対応できているかを確認する方法は、別の機会に投稿したいと思います。
“デスクトップアプリのリスタートマネージャー3(展開アプリへの適用)” への1件の返信