• ベストアンサー

サスペンド(休止やスタンバイ)のイベントを得る方法

WinXPとVB.NETで休止やスタンバイのイベントを受け取るにはどうしたらよいでしょうか? 休止(スタンバイ)に移行しても良いですか? のメッセージを出るようにしたいのですが、 良い方法があったら教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.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

popopompom
質問者

お礼

こんなにも完璧な回答を頂けるとは!! ありがとうございます。 早速試してみます!m(__)m

関連するQ&A