- ベストアンサー
サスペンド(休止やスタンバイ)のイベントを得る方法
WinXPとVB.NETで休止やスタンバイのイベントを受け取るにはどうしたらよいでしょうか? 休止(スタンバイ)に移行しても良いですか? のメッセージを出るようにしたいのですが、 良い方法があったら教えてください。
- みんなの回答 (1)
- 専門家の回答
質問者が選んだベストアンサー
>WinXPとVB.NETで休止やスタンバイのイベントを受け取るには? WndProc (WindowsProcとも言われたりします) を利用します。 ただ、これはシステムフックをする事になるので、ここでは 1・重い負荷を与える処理 2・ユーザからの入力を待機する処理 はご法度です!! もしそのような処理を入れた場合、 1・システムが次の処理を走らすことができず、動作が不安定になる 2・システムが次の処理を走らすために、アプリ側で捕まえられた処理をタイムアウトとして扱い、アプリ側の変更を受け付けなくなる という事がありえます。 これを回避するには処理を分割しましょう。↓にまとめます。 1.休止orサスペンド要求イベントを認識する 2.休止orサスペンド要求イベントの継続を破棄する 3.ユーザに休止orサスペンドがあったことを通知する 4.ユーザに休止orサスペンドを行うかを問い合わせをする 5.ユーザへの問い合わせ結果によって、処理を行う 参考[休止状態/サスペンドの破棄]を応用 http://www.microsoft.com/japan/msdn/vbasic/migration/tips/PowerMode/ 参考[休止状態/サスペンドの実行]を応用 http://www.vbvbvb.com/jp/gtips/0301/gSetSystemPowerState.html そこで問題となるのが、休止orサスペンドのどちらの要求が送られてきているのかがわからないということです。こればかりは仕方がありません。 なので、どのような処理にするかはユーザに任せてしまうか、サスペンド限定するなどの対応が必要になります。 電源イベント通知を即座に行わず、後で通知する方法は、私の場合であれば、タイマを利用する方法しか思いつきませんが、もっとよい方法があるかも知れません。 以下が、サンプルです。 WindowsApplication1.vbproj ├Form1.vb(コントロールは、何も置かないでいいです) └Class1.vb ※Form1.vb -------------------------------------------------------------------------------- Public Class Form1 Inherits System.Windows.Forms.Form Private WithEvents m_obj休止 As New 休止監視() Private m_objListデバッグ用 As New ListBox() #Region " Windows フォーム デザイナで生成されたコード " ~~~ここは略します~~~ #End Region 'イベント/フォーム/ロード Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'デバッグ用のリストボックスの初期設定 Me.Controls.Add(m_objListデバッグ用) m_objListデバッグ用.Size = Me.ClientSize End Sub 'イベント/休止オブジェクト/発生処理 Private Sub obj休止_発生処理(ByVal モード As 休止監視.処理モード) Handles m_obj休止.発生処理 '発生したイベント内容をデバッグ用リストボックスに追加 Dim l_strワーク As String = IIf(モード = 休止監視.処理モード.再開, "再開", "休止") m_objListデバッグ用.Items.Add(System.DateTime.Now.ToString & " " & l_strワーク) m_objListデバッグ用.SelectedIndex = m_objListデバッグ用.Items.Count - 1 End Sub 'イベント/休止オブジェクト/発生通知 Private Sub obj休止_発生通知(ByRef モード As 休止監視.休止モード) Handles m_obj休止.発生通知 'サスペンドor休止状態が起ころうとしたことを通知 'さらに、遂行する処理を返却する Dim l_msgRet As Microsoft.VisualBasic.MsgBoxResult Dim l_strMsg As String = "" l_strMsg &= "は い: サスペンド" & vbCrLf l_strMsg &= "いいえ: 休止状態" & vbCrLf l_strMsg &= "CANCEL: 何もしない" & vbCrLf l_msgRet = MsgBox(l_strMsg, MsgBoxStyle.YesNoCancel Or MsgBoxStyle.Question, "休止モード継続お知らせ") Select Case l_msgRet Case MsgBoxResult.Yes モード = 休止監視.休止モード.サスペンド Case MsgBoxResult.No モード = 休止監視.休止モード.休止状態 Case MsgBoxResult.Cancel モード = 休止監視.休止モード.未処理 End Select End Sub 'イベント/システム/WindowsProc Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) If Not m_obj休止.メッセージ処理(m) Then Return End If MyBase.WndProc(m) End Sub End Class ※Class1.vb -------------------------------------------------------------------------------- Imports System Imports System.Runtime.InteropServices Imports Microsoft.Win32 Public Class 休止監視 #Region "属性" Public Event 発生通知(ByRef モード As 休止モード) Public Event 発生処理(ByVal モード As 処理モード) #Region "定数" Private Const WM_POWERBROADCAST As Integer = &H218 Private Const PBT_APMQUERYSUSPEND As Integer = &H0 Private Const BROADCAST_QUERY_DENY As Integer = &H424D5144 #End Region #Region "型" Private Structure tagLUID Dim LowPart As Integer Dim HighPart As Integer End Structure Private Structure LUID_AND_ATTRIBUTES Dim Luid As tagLUID Dim Attributes As Integer End Structure Private Structure TOKEN_PRIVILEGES Dim PrivilegeCount As Integer Dim Privileges As LUID_AND_ATTRIBUTES End Structure #End Region #Region "列挙" Public Enum 休止モード 未処理 サスペンド 休止状態 End Enum Public Enum 処理モード 再開 休止 End Enum #End Region #Region "API" <DllImport("advapi32.dll", SetLastError:=True)> _ Private Shared Function OpenProcessToken( _ ByVal ProcessHandle As IntPtr, _ ByVal DesiredAccess As Integer, _ ByRef TokenHandle As IntPtr _ ) As Boolean End Function <DllImport("advapi32.dll", SetLastError:=True)> _ Private Shared Function LookupPrivilegeValue( _ ByVal pSystemName As String, _ ByVal lpName As String, _ ByRef lpLuid As tagLUID _ ) As Boolean End Function <DllImport("advapi32.dll", SetLastError:=True)> _ Private Shared Function AdjustTokenPrivileges( _ ByVal TokenHandle As IntPtr, _ ByVal DisableAllPrivileges As Boolean, _ ByRef NewState As TOKEN_PRIVILEGES, _ ByVal BufferLength As Integer, _ ByVal PreviousState As IntPtr, _ ByVal ReturnLength As IntPtr _ ) As Boolean End Function <DllImport("kernel32.dll", SetLastError:=True)> _ Private Shared Function SetSystemPowerState( _ ByVal fSuspend As Boolean, _ ByVal fForce As Boolean _ ) As Boolean End Function <DllImport("user32.dll", SetLastError:=True)> _ Private Shared Function ExitWindowsEx( _ ByVal flag As Integer, _ ByVal reserved As Integer _ ) As Boolean End Function #End Region #End Region #Region "メソッド" #Region "メソッド_PUBLIC" 'サスペンドを行う Public Sub 実行_サスペンド() Call 実行_休止処理(True) End Sub '休止状態を行う Public Sub 実行_休止状態() Call 実行_休止処理(False) End Sub 'WindowsProc処理 Public Function メッセージ処理(ByRef m As Windows.Forms.Message) As Boolean Dim l_blnRet As Boolean = True If (m.Msg = WM_POWERBROADCAST) And (m.WParam.ToInt32 = PBT_APMQUERYSUSPEND) Then l_blnRet = False '休止イベントの破棄 m.Result = New IntPtr(BROADCAST_QUERY_DENY) 'この中でイベントを発生させず、タイマを生成し、そのタイマイベント内部で処理を行う Dim l_objタイマ As New tmpタイマ() AddHandler l_objタイマ.Elapsed, AddressOf objタイマ_Elapsed End If Return l_blnRet End Function #End Region #Region "メソッド_PRIVATE" '休止処理メイン Private Sub 実行_休止処理(ByVal l_blnサスペンド As Boolean) Const TOKEN_QUERY As Integer = &H8 Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20 Const SE_SHUTDOWN_NAME As String = "SeShutdownPrivilege" Const SE_PRIVILEGE_ENABLED As Integer = &H2 Dim l_blnRet As Boolean '// プロセスのハンドルを取得する。 Dim l_PronWnd As IntPtr = Diagnostics.Process.GetCurrentProcess().Handle '// Token を取得する。 Dim l_TpkenWnd As IntPtr = IntPtr.Zero l_blnRet = OpenProcessToken(l_PronWnd, (TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY), l_TpkenWnd) '// LUID を取得する。 Dim l_udtLuid As tagLUID l_blnRet = LookupPrivilegeValue(vbNullString, SE_SHUTDOWN_NAME, l_udtLuid) '// 特権をセットする。 Dim tp As TOKEN_PRIVILEGES = New TOKEN_PRIVILEGES() tp.PrivilegeCount = 1 tp.Privileges = New LUID_AND_ATTRIBUTES() tp.Privileges.Luid = l_udtLuid tp.Privileges.Attributes = SE_PRIVILEGE_ENABLED l_blnRet = AdjustTokenPrivileges(l_TpkenWnd, False, tp, 0, IntPtr.Zero, IntPtr.Zero) '休止処理実行 '第2引数をTRUEにすることで、強制実行 Call SetSystemPowerState(l_blnサスペンド, True) End Sub #End Region #End Region #Region "イベント" #Region "イベント_定義" 'イベント/クラス/生成時 Public Sub New() 'システムの電源状態変化イベントを、内部メソッドへ引き込む AddHandler SystemEvents.PowerModeChanged, AddressOf SystemEvents_PowerModeChanged End Sub 'イベント/クラス/破棄時 Protected Overrides Sub Finalize() MyBase.Finalize() 'システムの電源状態変化イベントを、内部メソッドからはずす RemoveHandler SystemEvents.PowerModeChanged, AddressOf SystemEvents_PowerModeChanged End Sub #End Region #Region "イベント_WindowsProc_PowerModeChanged" 'イベント/電源/状態変化時 Private Sub SystemEvents_PowerModeChanged(ByVal sender As Object, ByVal e As PowerModeChangedEventArgs) Select Case e.Mode Case PowerModes.Resume RaiseEvent 発生処理(処理モード.再開) Case PowerModes.Suspend RaiseEvent 発生処理(処理モード.休止) End Select End Sub #End Region #Region "イベント_内部タイマ" 'イベント/タイマ/タイマ Private Sub objタイマ_Elapsed(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs) Dim l_objタイマ As tmpタイマ = CType(sender, tmpタイマ) Dim l_休止モード As 休止モード = 休止モード.未処理 'イベントとの関連付けを切り離す RemoveHandler l_objタイマ.Elapsed, AddressOf objタイマ_Elapsed l_objタイマ.Dispose() '問合せ中に、再度休止関連イベントが行われたとき対策 Static s_bln多重問合せ回避フラグ As Boolean = False If s_bln多重問合せ回避フラグ Then Return End If s_bln多重問合せ回避フラグ = True '問い合わせを行う RaiseEvent 発生通知(l_休止モード) '戻り値によって処理を行う Select Case l_休止モード Case 休止モード.サスペンド Call 実行_サスペンド() Case 休止モード.休止状態 Call 実行_休止状態() End Select s_bln多重問合せ回避フラグ = False End Sub #End Region #End Region #Region "内部タイマー" Private Class tmpタイマ Inherits Timers.Timer Public Sub New() Me.Interval = 1 Me.Enabled = True End Sub End Class #End Region End Class
お礼
こんなにも完璧な回答を頂けるとは!! ありがとうございます。 早速試してみます!m(__)m