1

たとえば、メモ帳を開いて何かを入力し、保存しない場合は、同じユーザーセッションから次のAPIを呼び出します。

ExitWindowsEx(EWX_LOGOFF, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED);

そのユーザーセッションは「シャットダウン状態」になり、OSは、メモ帳がシステムによるユーザーのログオフを妨げるメッセージを表示するオーバーレイウィンドウを表示します。このオーバーレイは、ユーザーが[キャンセル]または[強制終了]ボタンをクリックするまで消えません。

したがって、2つの部分からなる質問:

  1. どのプロセスがログオフ/シャットダウンプロセスをブロックしたかを知る方法はありますか?

  2. このユーザーセッションの「シャットダウン状態」をプログラムでキャンセルする方法はありますか?

PS。この状態は、GetSystemMetrics(SM_SHUTTINGDOWN);を呼び出すことで検出できます。

編集:以下の答えとは反対に、私システムのシャットダウンを止めようとはしていません。また、ユーザーモードのプロセスが「ハング」しているわけでもありません。

EDIT2:これがキャンセル/クローズしようとしているオーバーレイのスクリーンショットです: ここに画像の説明を入力してください

4

2 に答える 2

4

質問2:「このシャットダウン状態をプログラムでキャンセルする方法はありますか?」

簡単に言えば、答えは実際にはありません。また、プログラムでシャットダウンを実際に停止する必要もありません。シャットダウンすると、深刻なデータ損失が発生したり、後続のシステム起動時のユーザーエクスペリエンスに大きな影響を及ぼしたりします。しかし、ほんの一例を挙げると、コンピューターが過熱していると想像してください。プログラムでシャットダウンを停止すると、システムが炒められる可能性があります(そして非常にイライラするユーザー)。

監視する必要があるのは、システムのシャットダウンだけではありません。休止状態および一時停止イベントもあります(WM_POWERBROADCASTメッセージを参照してください)。

とは言うものの、Windowsは、システムのシャットダウンを検出するための多数のメカニズムを提供します。例えば:

アプリケーションにメッセージポンプがある場合、Windowsが実行中のアプリケーションをポーリングしてWM_QUERYENDSESSIONに投票するときに、FALSEを返すように選択できますが 、Vista以降のWindowsは、タイムアウト後も強制的にシャットダウンします。Vista以降では、WM_QUERYENDSESSIONにfalseを返した後、ShutdownBlockReasonCreateを実行できます(実行する必要があります) 。

アプリケーションがサービスとして実行されている場合は、RegisterServiceCtrHandlerExを使用してからSetServiceStatusを使用し、SERVICE_ACCEPT_PRESHUTDOWNを設定して3分間のシャットダウン拡張機能を取得します。これにより、SERVICE_CONTROL_PRESHUTDOWN通知が送信されます。当然、サービスはログオフの影響を受けないため、ログオフ通知は受信されません。Pre-Vistaでは、SERVICE_CONTROL_SHUTDOWN通知に登録できます。

コンソールアプリケーション(およびGUIアプリも同様ですが、意味がありません)は、SetConsoleCtrlHandlerを使用して、CTRL_LOGOFFおよびCTRL_SHUTDOWN_EVENTの通知を受け取ることができます。

はるかに低いレベルでは、NTShutdownやNtSetSystemPowerStateなどのAPI関数をフックしてみることができます。これは、明らかに「あらゆるタイプの再起動中に呼び出される最後のもの」です。しかし、私はこれを試みないことを強くお勧めします。

とはいえ、システムをシャットダウンしてはならないことを強く主張する方法があります。次のことを考慮してください。

1.)シャットダウン通知を受信するためにアプリケーションを最初に登録してみてください。何かのようなもの:

    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms686227(v=vs.85).aspx
    if(!SetProcessShutdownParameters(0x4ff, 0)) //  greedy highest documented System reserved FirstShutdown
    {
        // Fallback
        if(!SetProcessShutdownParameters(0x3ff, 0)) // highest notification range for applications
        {
             // shouldn't happen
        }
    }

2.)WM_QUERYENDSESSIONでFALSEを返します。Vista以降、WM_QUERYENDSESSIONでfalseを返した後、ShutdownBlockReasonCreate()を呼び出します。

3.)システムを稼働状態に保ち、使用可能にする必要があることをWindowsに通知します。http://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspxを ご覧ください

SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);

4.)クリーンアップし、Vista以降でShutdownBlockReasonDestroy()を呼び出してから、システムをクリーンにシャットダウンします。

文書化されていない関数を試すこともできます(少なくともMSDNにはありません)。 "user32.dll"のCancelShutdownは、ある時点で(まだ)、abortフラグを指定してshutdown.exeを呼び出すのと非常によく似た機能を果たしていました。

あなたのマイレージは異なる場合があります。

于 2012-11-10T11:42:55.107 に答える
0

あなたの質問をより明確にするあなたの編集に続いて:

WM_QUERYENDSESSION を監視し、それに FALSE で応答する場合、まだ実行中のプロセスから所定の期間ポーリングしてから、たとえば WM_ENDSESSION でEWX_FORCEIFHUNGフラグを指定して ExitWindowsEx 呼び出しを発行できます。または、実際に WM_QUERYENDSESSION の受信時に先制的にこれを呼び出すこともできます。問題は、シャットダウンを強制すると深刻なデータ損失が発生した場合はどうなるかということです。その時点で、あなたはシステムのユーザーに何の役にも立ちません。

コメントに従って更新します。

これはどう:

ブロックしているアプリケーションを見つけるには:

  1. アプリを SetProcessShutdownParameters に登録して、最初に WM_QUERYENDSESSION を取得します。
  2. WM_QUERYENDSESSION に FALSE を応答し、Vista 以降では ShutdownBlockReasonCreate を呼び出して時間を稼いでください。
  3. チェーンの最初のウィンドウを取得します HWND top = GetTopWindow(NULL)
  4. hwnd GetWindowThreadProcessId() からプロセス ThreadId を取得します (実行中のアプリと比較してください ;) )
  5. hWnd へのメッセージで SendMessageTimeOut を使用します。応答がない場合 (タイムアウト) は、ブロッキング ウィンドウが見つかった可能性があります。手順 6 に進みます。そうでない場合は、7 に進みます。
  6. GetWindowThreadProcessId から返されたハンドルで OpenProcess() を使用してプロセス ハンドルを取得し、GetModuleBaseName() を呼び出してハングしたプロセスの名前を取得します。
  7. ハングしたウィンドウが見つからない場合は、GetNextWindow() を使用して次のウィンドウを列挙し、手順 4 に戻ります。

上記の列挙ウィンドウ ハンドル手法を使用して、「ハング ウィンドウ」オーバーレイへのハンドルを取得できるかどうかを調べることもできます。これがあれば、キーを送信して状態をキャンセルする機会が得られる可能性があります。私の賭けは、あなたがそれにアクセスできないということですが、私は試していません:)

繰り返しますが、走行距離は異なる場合があります:)

于 2012-11-12T23:32:12.137 に答える