プロが教えるわが家の防犯対策術!

WinXPとVB.NETで休止やスタンバイのイベントを受け取るにはどうしたらよいでしょうか?

休止(スタンバイ)に移行しても良いですか?
のメッセージを出るようにしたいのですが、
良い方法があったら教えてください。

A 回答 (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/migra …

参考[休止状態/サスペンドの実行]を応用
http://www.vbvbvb.com/jp/gtips/0301/gSetSystemPo …

そこで問題となるのが、休止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
    • good
    • 0
この回答へのお礼

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

お礼日時:2005/10/19 04:25

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!