여러 가지 방법으로 사용자의 존재를 모니터링하기 위해 독립적 인 스레드 클래스
이것은 모니터 상태 (OFF, ON 또는 DIMMED), 시스템의 '사용자 존재'메시지, 랩톱에있는 경우 뚜껑 상태 (열기, 닫기)의 3 가지 이벤트를 모니터링하는 간단한 클래스입니다. 그것은 전적으로 자체적으로 포함 된 클래스에서 이것을 수행합니다.이 사건에 대한 합병증은 이러한 사건이 WM_POWERBROADCAST 메시지의 형태로 전송되기 때문에 창을 통신하기 때문에 발생합니다. 그러나 클래스를 그래픽 앱으로 제한하고 싶지 않고 HWND 양식을 서브 클래스하기 만하면됩니다. 따라서 클래스는 메시지 루프가 존재하는 경우 양식의 메시지 루프를 차단하는 것을 피하기 위해 별도의 스레드로 완전히 사용자 정의 숨겨진 창을 만듭니다.
이 클래스는 Twinbasic의 매개 변수화 된 생성자를 활용하여 새로운 키워드에서 사용 가능한 인수를 바로 지정합니다. 여기에서는 이벤트를 제기 할 수있는 이벤트와 등록 할 수있는 역사 ( App.hInstance 99%; 생략하면 GetModuleHandleW() 사용하여 winnativeforms에 종속성을 만들지 않고 App.hInstance 와 동일한 값을 반환합니다. 그런 다음 우리는 TB의 위대한 즐거움 중 하나에 도달합니다. 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 및 GUIDS를 제공합니다.
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
이미 언급했듯이 UMSG WM_POWERBROADCAST 가 WPARAM PBT_POWERSETTINGCHANGE 와 함께 제공합니다. 우리는 MSDN에서 lparam POWERBROADCAST_SETTING 알고 있습니다. 그러나 우리는 문제가 발생합니다.
typedef struct {
GUID PowerSetting;
DWORD DataLength;
UCHAR Data[1];
} POWERBROADCAST_SETTING, *PPOWERBROADCAST_SETTING;
그것은 SAFEARRAY 가 아닌 C 스타일 어레이입니다. 데이터는 즉시 메모리에서 이어집니다. 여기서 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 (data (0))에 별도의 사본을 수행하는 것입니다. 이 수업은 바로 가기를 취합니다. 우리는 우리가 관심있는 속성에 대해 단일 4 바이트 DWORT로 만 작업하기 때문에 다음과 같습니다.
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()
고정 된 부분을 복사하여 이번 이벤트가 어떤 이벤트에 대한 안내서를 확인할 수 있지만, Data 오프셋에서 시작하여 4 바이트를 복사하면 (안내서는 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
그리고 그게 전부입니다! 그것은 모두 매우 간단하지만, 많은 사람들이 전체 '쉬운 멀티 스레딩'에 새로운 것이기 때문에 글을 쓸 가치가 있습니다.