いくつかの方法でユーザーの存在を監視するための自己完結型のスレッドクラス
これは、モニター状態(オフ、オン、または薄暗い)、システムの「ユーザープレゼント」メッセージ、およびラップトップにいる場合は、蓋のステータス(開いて、閉じる)の3つのイベントを監視する単純なクラスです。これは、完全に自己包まれたクラスでこれを行います。これは、これらのイベントがWM_POWERBROADCASTメッセージの形で送信され、ウィンドウを受け取るために窓を再Quingしているために発生します。しかし、クラスをグラフィカルなアプリに制限し、フォームhwndをサブクラス化したくありませんでしたが、完全性のためにデモアプリもそのように行う方法を示しています。したがって、クラスは、存在する場合にフォームのメッセージループをブロックするメッセージループを回避するために、独自のカスタム隠されたウィンドウを別のスレッドに作成します。
このクラスは、Twinbasicのパラメーター化されたコンストラクターを利用して、新しいキーワードで利用可能な引数を指定します。ここでは、どのイベントを提起するか、登録するヒンスがどのオプションの引数ですApp.hInstance App.hInstance 99%の時間;省略すると、 GetModuleHandleW()を使用します。次に、結核の偉大な喜びの1つに到達します。VB6のようにクールな精巧なハッキングなしでCreateThreadを呼び出します。 ThreadProc自体は、単にコードの残りを呼び出すだけです。
Sub New(Optional ByVal dwNotifyMask As CPMonEventNotify = CPMEN_ALL, Optional ByVal hInst As LongPtr)
m_hInst = If(hInst = 0, GetModuleHandleW(), hInst)
If dwNotifyMask = CPMEN_ERROR Then Exit Sub
m_Mask = dwNotifyMask
tConfig.hInst = m_hInst
tConfig.Mask = m_Mask
m_hThread = CreateThread(ByVal 0, 0, AddressOf CPMonProc, tConfig, 0, m_idThread)
End Sub
RegisterClassExとCreateWindowEx 、通常のルーチンで使用されて隠されたウィンドウを作成します。WNDProcは、ここでのTBのサポートAddressOfのおかげでクラス内の関数です。セットアップの最後の重要な部分は、必要なイベントに登録する必要があることです。それらはすべて、 PBT_POWERSETTINGCHANGEメッセージとして配信されます。クラス変数としてそれぞれのハンドルを保持する必要がありますが、登録は簡単です。 tbshelllibはAPIとGUIDを提供します。
Private Function RegisterEvents() As Boolean
m_hEventM = RegisterPowerSettingNotification(m_hWnd, GUID_SESSION_DISPLAY_STATUS, DEVICE_NOTIFY_WINDOW_HANDLE)
m_hEventP = RegisterPowerSettingNotification(m_hWnd, GUID_SESSION_USER_PRESENCE, DEVICE_NOTIFY_WINDOW_HANDLE)
m_hEventL = RegisterPowerSettingNotification(m_hWnd, GUID_LIDSWITCH_STATE_CHANGE, DEVICE_NOTIFY_WINDOW_HANDLE)
If m_hEventM Then Return True
End Function
すでに述べたように、それらはWPALAM PBT_POWERSETTINGCHANGEを使用してUMSG WM_POWERBROADCASTによって配信されます。 MSDNからLPARAMがPOWERBROADCAST_SETTING UDTを指していることを知っています。しかし、私たちは問題に遭遇します:
typedef struct {
GUID PowerSetting;
DWORD DataLength;
UCHAR Data[1];
} POWERBROADCAST_SETTING, *PPOWERBROADCAST_SETTING;
それはCスタイルの配列であり、 SAFEARRAYではありません。データはすぐにメモリに続き、TB(現在)の可変長さアレイは、まったく異なる構造を持つSAFEARRAYのみになります。 tbshelllibが使用するソリューションは少し不格好です。
[ Description ("WARNING: You can't use this directly due to the SAFEARRAY. To receive, fill the first 20 bytes, then the data in the array. To send, create a byte buffer excluding the safearray member.") ]
Public Type POWERBROADCAST_SETTING
PowerSetting As UUID
DataLength As Long
Data() As Byte
End Type
アイデアは、固定部品をコピーしてから、長さを使用して変数部品を再表示し、別のコピーをvarptr(データ(0))に実行することです。ただし、このクラスにはショートカットがあります。私たちは興味のあるプロパティの4バイトのDWORDを1つだけ使用しているため、次のように見えます。
Case WM_POWERBROADCAST
If wParam = PBT_POWERSETTINGCHANGE Then
Dim pSetting As POWERBROADCAST_SETTING
CopyMemory pSetting, ByVal lParam, 20
If IsEqualGUID(pSetting.PowerSetting, GUID_SESSION_DISPLAY_STATUS) Then
Dim pState As MONITOR_DISPLAY_STATE
CopyMemory pState, ByVal PointerAdd(lParam, 20), 4
Select Case pState
Case PowerMonitorOff
If (m_Mask And CPMEN_MONITOROFF) Then RaiseEvent MonitorOff()
固定部品をコピーして、今回のイベントのGUIDを確認できるようにしますが、 Dataのオフセットから始まる4バイト(GUIDは16バイト、4バイトの長さ、= 20)を直接コピーして、可能な値の列を表す変数に直接コピーします。最後のライン以外のすべては、Tbshelllibによって提供されます。これには、署名されていない追加を安全に実行する一般的なPointerAddが含まれます。次に、発信者がイベントを望んでいるかどうかを確認し、それを上げます。驚いたことに、このようなスレッドからRaiseeventを呼び出すことは、特別な取り扱いなしで機能していて、まだクラッシュしていません。私はクラスを6時間以上のストレッチで走り去り、多くのイベントが提起されています。
注記
最初にイベントに登録するときに現在のステータスを受け取ります。したがって、モニターがオンになっているだけでなく、「モニターオン」が送信されます。
クラスは、監視をオフにして隠されたウィンドウを取り除くためのDestroy()メソッドを提供します。アプリが実行を続けることを計画している場合は、これをNothing設定しようとする前にこれを呼び出す必要があります(終了する場合は、システムにすべてを破壊させることができます)。スレッドはメッセージループの中にあるため、メッセージループが終了することを終了しないため、そのためにはウィンドウを破壊する必要があります。別のスレッドのウィンドウでDestroyWindowを呼び出すことはできないので、代わりに行うことはWM_CLOSE PostMessageで送信し、ウィンドウはそれ自体を破壊し、イベントを登録していません。
Case WM_CLOSE
DestroyWindow m_hWnd
Case WM_DESTROY
UnregisterEvents
PostQuitMessage 0
それに加えて、シャットダウンするためにスレッドを数秒に与え、カスタムウィンドウクラスを解除します。その後、すべてが片付けられ、クラス自体が破壊される準備ができています。
Public Sub Destroy()
If m_hWnd Then PostMessageW(m_hWnd, WM_CLOSE, 0, ByVal 0)
Dim lRet As WaitForObjOutcomes = WaitForSingleObject(m_hThread, 5000)
Debug.Print "Wait outcome=" & lRet
Dim hr As Long = UnregisterClassW(StrPtr(wndClass), m_hInst)
Debug.Print "Unregister hr=" & hr & ", lastErr=" & Err.LastDllError
End Sub
そして、それだけです!それはすべて非常に簡単ですが、多くの人が「簡単なマルチスレッド」のこと全体に新しいので、書く価値があります。