คลาสเกลียวที่มีอยู่ในตัวเองเพื่อตรวจสอบสถานะของผู้ใช้ในหลายวิธี
นี่คือคลาสง่าย ๆ ที่ตรวจสอบ 3 เหตุการณ์: สถานะการตรวจสอบ (ปิด, เปิดหรือหรี่ลง) ข้อความ 'ผู้ใช้นำเสนอ' ของระบบและหากคุณใช้แล็ปท็อปสถานะฝา (เปิดปิด) มันทำสิ่งนี้ในชั้นเรียนที่มีตัวตนทั้งหมด- ภาวะแทรกซ้อนกับสิ่งนี้เกิดขึ้นเนื่องจากเหตุการณ์เหล่านี้ถูกส่งในรูปแบบของข้อความ WM_POWERBROADCAST โดยส่งหน้าต่างเพื่อรับ แต่ฉันไม่ต้องการ จำกัด คลาสไว้ในแอพกราฟิกและเพียงแค่ subclass เป็นรูปแบบ hwnd แม้ว่าเพื่อความสมบูรณ์แอปสาธิตแสดงวิธีการทำเช่นนั้นด้วย ดังนั้นคลาสจะสร้างหน้าต่างที่ซ่อนอยู่ที่กำหนดเองทั้งหมดในเธรดแยกต่างหากเพื่อหลีกเลี่ยงการวนซ้ำข้อความที่ปิดกั้นข้อความของรูปแบบถ้ามีอยู่
คลาสใช้ประโยชน์จากตัวสร้างพารามิเตอร์ของ Twinbasic เพื่อระบุอาร์กิวเมนต์ที่มีอยู่ในคำหลักใหม่ ที่นี่เป็นข้อโต้แย้งที่เป็นตัวเลือกสำหรับเหตุการณ์ที่จะเพิ่มและ hinstance ในการลงทะเบียนภายใต้ ( App.hInstance 99% ของเวลาถ้าคุณละเว้นมันจะใช้ GetModuleHandleW() ซึ่งส่งคืนค่าเดียวกับ App.hInstance โดยไม่ต้องสร้างการพึ่งพา จากนั้นเราไปถึงหนึ่งในความสุขที่ยิ่งใหญ่ของวัณโรค: เรียก CreateThread โดยไม่มีการแฮ็กที่ซับซ้อนใด ๆ ที่ไม่จำเป็นต้องใช้งานได้ดีพอ ๆ กับที่อยู่ใน VB6 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 เป็นฟังก์ชั่นภายในชั้นเรียนขอบคุณ 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 The LPARAM จากนั้นชี้ไปที่ POWERBROADCAST_SETTING UDT แต่เราพบปัญหา:
typedef struct {
GUID PowerSetting;
DWORD DataLength;
UCHAR Data[1];
} POWERBROADCAST_SETTING, *PPOWERBROADCAST_SETTING;
นั่นคืออาร์เรย์สไตล์ C ไม่ใช่ SAFEARRAY ข้อมูลจะตามมาในหน่วยความจำทันทีที่อาร์เรย์ความยาวตัวแปรในวัณโรค (ปัจจุบัน) สามารถเป็น SAFEARRAY ที่มีโครงสร้างที่แตกต่างกันโดยสิ้นเชิง การแก้ปัญหาการใช้ Tbshelllib นั้นค่อนข้าง clunky;
[ 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)) ชั้นเรียนนี้ใช้ทางลัด เนื่องจากเราทำงานกับ DWOR 4 ไบต์เดียวสำหรับคุณสมบัติที่เราสนใจจึงดูเหมือนว่า:
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 สำหรับเหตุการณ์ที่เป็นเวลานี้ แต่จากนั้นเพียงแค่คัดลอก 4 ไบต์เริ่มต้นจากการชดเชยของ Data (GUID คือ 16 ไบต์รวมถึงความยาว 4 ไบต์ = 20) โดยตรงไปยังตัวแปรที่แสดงถึงค่าที่เป็นไปได้ ทุกอย่างนอกเหนือจากบรรทัดสุดท้ายนั้นจัดทำโดย TBShelllib รวมถึง PointerAdd ทั่วไปซึ่งดำเนินการเพิ่มที่ไม่ได้ลงนามอย่างปลอดภัย จากนั้นเราเพียงแค่ตรวจสอบว่าผู้โทรต้องการกิจกรรมและเพิ่มขึ้นหรือไม่ น่าแปลกที่การโทรหา Raisevent จากการออกจากกระทู้แบบนี้ทำงานโดยไม่มีการจัดการพิเศษและยังไม่ได้ชนและฉันออกจากชั้นเรียนที่วิ่งไป 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
และนั่นคือทั้งหมดที่มีอยู่! มันค่อนข้างตรงไปตรงมา แต่คุ้มค่าที่จะเขียนเนื่องจากมีจำนวนมากที่ยังใหม่กับสิ่งที่ 'มัลติเธรดง่าย' ทั้งหมด