我如何在VB6中定义POWERBROADCAST_SETTING?

3

我想要检测监视器的状态。

为了做到这一点,我注册了 WM_POWERBROADCAST 消息。

该消息的 lParam 包含 PBT_POWERSETTINGCHANGE

typedef struct {
  GUID  PowerSetting;
  DWORD DataLength;
  UCHAR Data[1];
} POWERBROADCAST_SETTING, *PPOWERBROADCAST_SETTING;

GUID 在 VB6 中被定义为:

Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

How are

  DWORD DataLength;
  UCHAR Data[1];

需要将其翻译为VB6吗?


看起来数据取决于PowerSetting。请查看此页面:https://learn.microsoft.com/zh-cn/windows/win32/power/power-setting-guids。 - Brian M Stafford
尝试使用Long代替DWORD。这里有一个方便的列表供您参考。 - Brian M Stafford
@BrianMStafford 谢谢。那么,UCHAR Data[1] 怎样翻译成 VB6? - tmighty
@tmighty 如果您阅读PowerSettings页面,您会看到像“Data成员是一个DWORD,表示当前监视器状态”的语句,然后列出了预期的值。我认为这就是您想要的。我从未尝试过这个,所以不能确定。 - Brian M Stafford
1个回答

3
POWERBROADCAST_SETTING 结构体的 UCHAR Data[1] 成员表示一个字节数组,其内容取决于 PowerSettingDataLength 成员。 根据文档Data 成员可以是 GUID 或 DWORD。因此,在 VB6 中最简单的方法是声明一个包含固定成员的结构体,然后根据 PowerSetting 成员在第二步中获取剩余数据。
Public Type Guid
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

Private Type PowerBroadcastSetting
    PowerSetting As Guid
    DataLength As Long
End Type

窗口过程应该看起来像这样:
Public Function WindowProc(ByVal hWnd As Long, ByVal iMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Dim g As Guid
    Dim L As Long
    Dim pbs As PowerBroadcastSetting
    
    Select Case iMsg
        Case WM_POWERBROADCAST
            Select Case wParam
                Case PBT_APMPOWERSTATUSCHANGE
                    DebugPrint "PBT_APMPOWERSTATUSCHANGE"
                Case PBT_APMRESUMEAUTOMATIC
                    DebugPrint "PBT_APMRESUMEAUTOMATIC"
                Case PBT_APMRESUMESUSPEND
                    DebugPrint "PBT_APMRESUMESUSPEND"
                Case PBT_APMSUSPEND
                    DebugPrint "PBT_APMSUSPEND"
                Case PBT_POWERSETTINGCHANGE
                    CopyMemory pbs, ByVal lParam, Len(pbs)
                    DebugPrint "PBT_POWERSETTINGCHANGE " & GuidToString(pbs.PowerSetting)
                    Select Case GuidToString(pbs.PowerSetting)
                        Case GUID_POWERSCHEME_PERSONALITY
                            CopyMemory g, ByVal lParam + Len(pbs), 16
                            DebugPrint "New power scheme: " & GuidToString(g)
                        Case GUID_SESSION_DISPLAY_STATUS
                            CopyMemory L, ByVal lParam + Len(pbs), 4
                            DebugPrint "Display status: " & L
                        Case GUID_MONITOR_POWER_ON
                            CopyMemory L, ByVal lParam + Len(pbs), 4
                            DebugPrint "Primary Monitor state: " & L
                        Case GUID_CONSOLE_DISPLAY_STATE
                            CopyMemory L, ByVal lParam + Len(pbs), 4
                            DebugPrint "Console Display state: " & L
                    End Select
            End Select
            'An application should return TRUE if it processes this message.
            WindowProc = 1
            Exit Function
    End Select
    'Pass message to original window proc
    WindowProc = CallWindowProc(ProcOld, hWnd, iMsg, wParam, lParam)
End Function

以下是使用的API声明:

Public Const GWL_WNDPROC As Long = (-4)
Private Const WM_POWERBROADCAST As Long = 536

Public Type Guid
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

Private Type PowerBroadcastSetting
    PowerSetting As Guid
    DataLength As Long
End Type


'Power status has changed.
Private Const PBT_APMPOWERSTATUSCHANGE = 10

'Operation is resuming automatically from a low-power state. This message is sent every time the system resumes.
Private Const PBT_APMRESUMEAUTOMATIC As Long = 18

'Operation is resuming from a low-power state. This message is sent after PBT_APMRESUMEAUTOMATIC if the resume is triggered by user input, such as pressing a key.
Private Const PBT_APMRESUMESUSPEND As Long = 7

'System is suspending operation.
Private Const PBT_APMSUSPEND As Long = 4

'A power setting change event has been received.
Private Const PBT_POWERSETTINGCHANGE As Long = 32787

'Power Setting GUIDs

'The active power scheme personality has changed. All power schemes map to one of these personalities.
'The Data member is a GUID that indicates the new active power scheme personality.
Public Const GUID_POWERSCHEME_PERSONALITY As String = "{245D8541-3943-4422-B025-13A784F679B7}"

'The display associated with the application's session has been powered on or off.
'The Data member is a DWORD with one of the following values.
'0x0 - The display is off.
'0x1 - The display is on.
'0x2 - The display is dimmed.
Public Const GUID_SESSION_DISPLAY_STATUS As String = "{2B84C20E-AD23-4DDF-93DB-05FFBD7EFCA5}"

Public Const GUID_MONITOR_POWER_ON As String = "{02731015-4510-4526-99E6-E5A17EBD1AEA}"
' Windows 8 +
Public Const GUID_CONSOLE_DISPLAY_STATE As String = "{6FE69556-704A-47A0-8F24-C28D936FDA47}"

'Notifications are sent using WM_POWERBROADCAST messages with a wParam parameter of PBT_POWERSETTINGCHANGE.
Public Const DEVICE_NOTIFY_WINDOW_HANDLE As Long = 0
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cbCopy As Long)
Public Declare Function RegisterPowerSettingNotification Lib "user32.dll" (ByVal hRecipient As Long, PowerSettingGuid As Guid, ByVal Flags As Long) As Long
Public Declare Function UnregisterPowerSettingNotification Lib "user32.dll" (ByVal Handle As Long) As Long
Private Declare Function StringFromGUID2 Lib "ole32.dll" (rguid As Guid, ByVal lpsz As Long, ByVal cchMax As Long) As Long
Private Declare Function CLSIDFromString Lib "ole32.dll" (ByVal lpsz As Long, pclsid As Guid) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Sub OutputDebugString Lib "kernel32" Alias "OutputDebugStringA" (ByVal lpOutputString As String)

以及辅助函数:

Public Function GuidToString(g As Guid) As String
    Dim L As Long
    Dim b(0 To 77) As Byte
    
    'we have space for 38 unicode chars (guid incl. brackets) + terminating zero (78 bytes)
    L = StringFromGUID2(g, VarPtr(b(0)), 39)
    'strip terminating 0, convert to string
    GuidToString = Left(b, L - 1)
End Function

Public Function GuidFromString(ByVal gs As String) As Guid
    CLSIDFromString StrPtr(gs), GuidFromString
End Function

Public Sub DebugPrint(ByVal s As String)
    OutputDebugString s & vbCrLf
End Sub

VB6中的测试表单:

Option Explicit

Private isSubclassed As Boolean
Private hScheme As Long
Private hDisplay As Long
Private hMonitor As Long
Private hConsole As Long

Private Sub cmdRegister_Click()
    Unregister
    Register
End Sub

Private Sub cmdUnregister_Click()
    Unregister
End Sub

Private Sub Register()
    ProcOld = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc)
    isSubclassed = True
    MsgBox "Subclassed"
    'Register Power Events
    hScheme = RegisterPowerSettingNotification(hWnd, GuidFromString(GUID_POWERSCHEME_PERSONALITY), DEVICE_NOTIFY_WINDOW_HANDLE)
    hDisplay = RegisterPowerSettingNotification(hWnd, GuidFromString(GUID_SESSION_DISPLAY_STATUS), DEVICE_NOTIFY_WINDOW_HANDLE)
    hConsole = RegisterPowerSettingNotification(hWnd, GuidFromString(GUID_CONSOLE_DISPLAY_STATE), DEVICE_NOTIFY_WINDOW_HANDLE)
    MsgBox "Registered " & hScheme & " " & hDisplay & " " & hMonitor & " " & hConsole
End Sub

Private Sub Unregister()
    'Unregister Power Events
    If hScheme Then
        UnregisterPowerSettingNotification hScheme
        hScheme = 0
    End If
    
    If hDisplay Then
        UnregisterPowerSettingNotification hDisplay
        hDisplay = 0
    End If
    
    If hMonitor Then
        UnregisterPowerSettingNotification hMonitor
        hMonitor = 0
    End If
    If hConsole Then
        UnregisterPowerSettingNotification hConsole
        hConsole = 0
    End If
    'Unsubclass
    If isSubclassed Then
        SetWindowLong hWnd, GWL_WNDPROC, ProcOld
        isSubclassed = False
        MsgBox "Unsubclassed"
    End If
End Sub

Private Sub Form_Unload(Cancel As Integer)
    Unregister
End Sub

编辑:添加了GUID_CONSOLE_DISPLAY_STATE

以下是在Windows 10上,使用DebugView捕获的输出信息:

在Windows电源管理器处于非活动状态时,显示器进入待机状态:

[7752] PBT_POWERSETTINGCHANGE {6FE69556-704A-47A0-8F24-C28D936FDA47}
[7752] 控制台显示器状态: 2
[7752] PBT_POWERSETTINGCHANGE {2B84C20E-AD23-4DDF-93DB-05FFBD7EFCA5}
[7752] 显示器状态: 2

15秒后:

[7752] PBT_POWERSETTINGCHANGE {6FE69556-704A-47A0-8F24-C28D936FDA47}
[7752] 控制台显示器状态: 0
[7752] PBT_POWERSETTINGCHANGE {02731015-4510-4526-99E6-E5A17EBD1AEA}
[7752] 主监视器状态: 0
[7752] PBT_POWERSETTINGCHANGE {2B84C20E-AD23-4DDF-93DB-05FFBD7EFCA5}
[7752] 显示器状态: 0

唤醒:

[7752] PBT_POWERSETTINGCHANGE {6FE69556-704A-47A0-8F24-C28D936FDA47}
[7752] 控制台显示器状态: 1
[7752] PBT_POWERSETTINGCHANGE {02731015-4510-4526-99E6-E5A17EBD1AEA}
[7752] 主监视器状态: 1
[7752] PBT_POWERSETTINGCHANGE {2B84C20E-AD23-4DDF-93DB-05FFBD7EFCA5}
[7752] 显示器状态: 1

如果您手动关闭显示器,则不会收到通知,至少在我的硬件上是这样。不确定在其他系统上是否会引发这些事件。


@tmighty 我可以确认,在 Windows 10 下,如果显示器被 Windows 的电源管理关闭,GUID_MONITOR_POWER_ON 和 GUID_CONSOLE_DISPLAY_STATE 都能正常工作。如果我手动关闭它们(通过按下显示器上的电源按钮),则不会触发任何事件。但这将是另一个问题... - Steeeve
@tmighty,WindowProc函数已经存在。我只是为了测试添加了GUID_CONSOLE_DISPLAY_STATE,但这并不重要。如果显示器被Windows关闭,两个事件都会被触发,如果我手动关闭显示器,则不会出现任何事件。 - Steeeve
@tmighty,我已经添加了新的代码和示例输出。我的测试项目非常简单,只有一个带有最后一个代码块的表单,其他所有内容都在一个模块中。如果您仍然有问题,请告诉我。 - Steeeve
无效使用AdressOf意味着您的WindowProc过程不在模块中。正如我所写的,将标题为“Test Form in VB6”的代码块放在窗体中,其余部分放在单个模块中。 - Steeeve
哦,我忘记了一个变量?我很高兴它终于能运行了 :) - Steeeve
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接