デスクトップアプリのリスタートマネージャー2(アプリ停止のためのウィンドウメッセージ)

前回に続き今回もデスクトップアプリ開発におけるリスタートマネージャー(Restart Manager)について投稿したいと思います。今回は停止される側であるアプリ側が受け取るウィンドウメッセージとその対応する方法です。

デスクトップアプリのリスタートマネージャー対応

前回の投稿に書きましたが、リスタートマネージャーは、Windows Vistaから導入されました。そのときデスクトップアプリの開発者向けにリスタートマネージャーの対応方法のガイドラインも公開されました。Windows Vista Application Compatibility Cookbookとして公開されていましたが、現在はその情報のリンクはなくなっているようです。

現在では、デスクトップアプリのリスタートマネージャー対応のガイドラインは、Guidelines for Applications and Services(英語)の中のGuidelines for Applications(英語)として公開されています。

なお、今回の内容は、ウィンドウをもつデスクトップアプリを対象としています。ウィンドウを持たないデスクトップアプリ(コンソールアプリ)は、ウィンドウメッセージを受け取ることはできないため、別の手段でリスタートマネージャーのイベントを受け取ることになります。

デスクトップアプリの終了通知メッセージ

リスタートマネージャー対応の前に、まずは、デスクトップアプリが終了するときに通知されるウィンドウメッセージについて確認します。

ウィンドウを持つデスクトップアプリの場合、アプリを終了するトリガーは二つあります。

  • ユーザーがアプリを明示的に終了する
    アプリのウィンドウの「閉じるボタンをクリック」やアプリメニューの「終了を選択」などの方法でユーザーがアプリを明示的に終了する方法です。
  • ユーザーセッションを終了する
    デスクトップのログオフ、PCのシャットダウン・再起動などは、ユーザーセッション(デスクトップ)を終了します。ユーザーセッションが終了するとそのセッション内で実行されていたアプリはすべて終了されます。ユーザーはアプリを明示的には終了しませんが、結果として、間接的にアプリを終了することになります。

この二つの終了方法は、アプリが受け取るウィンドウメッセージが異なります。

ユーザーによる明示的なアプリの終了

前者の方法によるアプリの終了の場合、最終的にはウィンドウを閉じることになるため、アプリのウィンドウはWM_CLOSEのウインドウメッセージを受け取ります。マイクロソフトのサイトにこのメッセージの仕様の詳細(英語)があります。

WM_CLOSEメッセージ
パラメーター:

WPARAM wParam このパラメータは使用されません。
LPARAM lParam このパラメータは使用されません。

戻り値:

LRESULT アプリがこのメッセージを処理した場合0を返す必要があります。

WM_CLOSEの仕様の通り、このメッセージには、追加の情報はありません。

ウィンドウを閉じることがアプリが終了することを意味するアプリはこのメッセージをトリガーにして、アプリの終了処理をします。アプリの終了時には、保存していないデータなどがあれば、ユーザーに保存するかを問い合わせたうえでアプリを終了します。

ユーザーによる直接的なアプリの終了であるため、そのタイミングでアプリからユーザーに問い合わせることは不自然ではありません。

ユーザーセッションの終了

後者によるアプリの終了時には、アプリのウィンドウは、WM_QUERYENDSESSIONとWM_ENDSESSIONのウィンドウメッセージを受け取ります。マイクロソフトのサイトにこれらのメッセージの仕様の詳細WM_QUERYENDSESSION (英語)WM_QUERYENDSESSION (英語)があります。ユーザーセッションの終了時にはWM_CLOSEメッセージも受け取ることもありますが、WM_CLOSEメッセージを受け取ることができることは保証さていません。

WM_QUERYENDSESSIONメッセージ
パラメーター:

WPARAM wParam このパラメータは、将来の使用のために予約されています。

LPARAM lParam このパラメータにはアプリケーションの終了の理由が設定されます。0もしくは、以下の値(ビットフラグ)の一つ以上の組み合わせの値となります。0の場合は、システムはシャットダウンもしくは再起動しています(どの理由でイベントが発生しているかを特定することはできません)。このパラメータはビットフラグなのでを評価するときはビット判定を必要とします。そのため値全体を等価で評価してはいけません。

意味
ENDSESSION_CLOSEAPP アプリケーションは終了してリソースを解放する必要があります。
ENDSESSION_CRITICAL アプリケーションは強制的にシャットダウンされます。
ENDSESSION_LOGOFF ユーザーがログオフを要求しました。
戻り値:

  LRESULT シャットダウン処理を続行することをシステムに伝えるにはTRUEを返します。シャットダウン処理を中断してほしいことをシステムに伝えるためにはFALSEを返します。アプリはユーザーの終了の意図を尊重し、TRUEを返す必要があります。 このメッセージを処理しない場合はTRUEを返したものとして処理されます。 シャットダウンするとシステムまたは作成中のメディアが破損する場合、アプリケーションはFALSEを返します。 ただし、ユーザーの操作を尊重すること(TRUEを返すこと)を優先してください。

WM_ENDSESSIONメッセージ
パラメーター:

WPARAM wParam セッションが終了する場合、このパラメータはTRUEになります。 すべてのアプリケーションがこのメッセージの処理から戻った後にユーザーセッションは、任意のタイミングで終了します。セッションの終了が中断されたなど、セッションが終了しない場合はFALSEです。

LPARAM lParam このパラメータにはアプリケーションの終了の理由が設定されます。0または以下の値の一つ以上の組み合わせの値となります。このパラメータが0の場合、システムはシャットダウンまたは再起動しています(どのイベントが発生しているかを特定することはできません)。このパラメータはビットフラグなのでを評価するときはビット判定を必要とします。そのため値全体を等価で評価してはいけません。

意味
ENDSESSION_CLOSEAPP wParamがTRUEの場合、アプリケーションはシャットダウンする必要があります。 すべてのデータは、ユーザーにプロンプトを表示せずに自動的に保存する必要があります(詳細については、「備考」を参照)。 リスタートマネージャは、更新が必要なファイルをアプリケーションが使用しているとき、システムにサービスを提供する必要があるとき、またはシステムリソースが足りなくなったときにこのメッセージを送信します。 RegisterApplicationRestart関数(英語)を使用して再起動を登録したアプリケーションは、リスタートマネージャーの処理が終わった後に再起動されます。 詳細については、「アプリケーションのガイドライン」(英語)を参照してください。 wParamがFALSEの場合、アプリケーションをシャットダウンしないでください。
ENDSESSION_CRITICAL アプリケーションは強制的にシャットダウンされます。
ENDSESSION_LOGOFF ユーザーがログオフを要求しました。
戻り値:

LRESULT アプリがこのメッセージを処理した場合0を返す必要があります。

WM_CLOSEメッセージとは異なり、WM_QUERYENDSESSIONやWM_ENDSESSIONのメッセージは、wParam/lParamの値に追加の情報が設定されており、その情報に従って適切な処理をする必要があります。

アプリはWM_QUERYENDSESSIONを受け取った後に、WM_ENDSESSIONを受け取ります。WM_QUERYENDSESSIONは、アプリが終了される前の事前通告となります。場合によっては、このメッセージを受け取った後に、アプリの終了がキャンセルされることもあります。WM_ENDSESSIONは、アプリの終了の直前通知となります。シャットダウン処理の継続のフラグとともに、このメッセージを受け取った後は、アプリは終了されます。

WM_QUERYENDSESSIONのメッセージの位置づけ

WM_QUERYENDSESSIONのメッセージの位置づけは、Windows XPまでとWindows Vista以降で異なっています。

Windows XPまで

Windows XPまでは、このWM_QUERYENDSESSIONメッセージの通知の処理中にユーザーに対して問い合わせをしたり、また、FALSEを返すことによって、アプリの終了の原因となったユーザーセッションの終了を中断することができました。

Widnows Vista以降

Windows Vistaでリスタートマネージャーの仕組みが導入されたことに伴い、このこのWM_QUERYENDSESSIONメッセージの処理中にユーザーに対して問い合わせることや、メッセージの戻り値として、FALSEを返すことは非推奨となりました。

このメッセージを受信したあと、5秒後には全画面でシステムのメッセージが表示されます。そのため、ユーザーに問い合わせたとしても、ユーザーはアプリの問い合わせに応答することはできません。

ベストプラクティス

Windows Vistaでの変更の詳細はShutdown Changes for Windows Vista (英語)に記載されています。それによると、最良の方法は以下の通りとなります。

  • アプリケーションはシャットダウンをブロックしてはいけません。 可能な限り迅速にWM_QUERYENDSESSION(英語)に応答します。また、WM_ENDSESSION(英語)メッセージを処理するまでクリーンアップ処理をを延期します。
  • シャットダウンをブロックする必要のあるアプリケーションでは、新しいShutdownBlockReasonCreate関数(英語)を使用して、理由をユーザーに説明する文字列を登録する必要があります。 ユーザーは、システムによる全画面のメッセージにより、シャットダウンを続行するか、または中止するかを決定できます。
  • アプリケーションはシャットダウンをブロックすることに依存することはできません。

アプリがシャットダウンをブロックしてよいのは、アプリを終了するとシステムが壊れる、または、ライブ放送を録音・録画するなど本当にその時に処理しなければならないものに限られます。これらの理由以外の処理は、処理を一時停止して、次回のアプリの再開後に続きの処理をするように実装します。

Windows Vista以降では、WM_QUERYENDSESSIONおよび、WM_ENDSESSIONの推奨の処理は以下の通りとなります。

推奨処理 (一般アプリ)

WM_QUERYENDSESSION

アプリの終了の準備を開始します。メッセージの戻り値は常にTRUEとします。アプリはこのメッセージの処理中にユーザーに対して問い合わせなどのメッセージを表示してはいけません。

lParam にENDSESSION_CLOSEAPPフラグがセットされていた場合、 RegisterApplicationRestart関数(英語)を使用してアプリの再起動の登録をします。WM_ENDSESSIONでは、RegisterApplicationRestart関数を呼び出しても登録内容が反映されません。このメッセージのタイミングが、RegisterApplicationRestart関数を使って、アプリの再起動の登録およびパラメータの更新ができる最後の機会となります。

WM_ENDSESSION

wParamが0(FALSE)の場合は、アプリの終了が中断されたことを意味します。そのため、このメッセージを抜けた後でもアプリは終了されません。必要に応じて、WM_QUERYENDSESSIONで行ったアプリの終了準備などがあればキャンセルします。

wParamが0以外(TRUE)の場合は、アプリの終了が続行していることを意味します。このメッセージを抜けた後、アプリは終了されます。保存していないデータがあるときは、このメッセージを抜ける前に一時ファイルへの保存を完了します。一時ファイルへの保存が、大量となる場合は、普段から自動保存をすることにより、WM_ENDSESSIONのメッセージ処理中に保存しなければならないデータ量を減らすようにします。

推奨処理 (シャットダウンのブロックが必要な特殊アプリ)

WM_QUERYENDSESSION

シャットダウンブロックが必要な時はメッセージの戻り値は、FALSEとします。そうでない場合は、TRUEとし、アプリの終了の準備を開始します。どちらの場合でも、アプリはユーザーに対して問い合わせなどのメッセージを表示してはいけません。

FALSEを返す場合は、ShutdownBlockReasonCreate関数(英語)使用して、終了できない理由を文字列として登録します。

lParam にENDSESSION_CLOSEAPPフラグがセットされていた場合、 「推奨処理 (一般アプリ)」の場合と同様に、アプリの再起動の登録をします。なぜなら、シャットダウンをブロックをするように戻り値をFALSEとしても、ユーザーの意思次第では、シャットダウンが継続される可能性があるからです。

WM_ENDSESSION

「推奨処理 (一般アプリ)」の場合と同様の処理をします。


以上がアプリケーションの終了時に通知されるウィンドウメッセージの処理方法となります。

まとめると、Windows Vistaでリスタートマネージャーが導入されたことにより、変更になった概要は以下の通りとなります。

WM_QUERYENDSESSION
WM_ENDSESSION
  • lParamにENDSESSION_CLOSEAPPが追加された。このフラグが指定されたときは、再起動のためのデータの保存などの準備を完了する。

このメッセージの対応のみをするのは容易です。しかし、アプリとしては、ENDSESSION_CLOSEAPPフラグが指定されたときは、アプリの状態をすべて保存して、次の再起動時にその状態を復帰する必要があります。多くのアプリでは、上記のウィンドウメッセージ対応よりも、この状態保存・復帰の処理に多くの実装が必要になると思います。

アプリにおける具体的な処理方法は機会があれば次回以降に投稿したいと思います。

One Reply to “デスクトップアプリのリスタートマネージャー2(アプリ停止のためのウィンドウメッセージ)”

コメントを残す