You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python 3下Windows系统显示器电源状态检测实现求助

监测Windows显示器电源状态的Python实现

嘿,我来帮你搞定这个需求!要在Python里监测Windows显示器的电源状态(比如屏幕关闭时触发事件),咱们确实得靠Windows API的RegisterPowerSettingNotification配合消息循环来实现,下面是完整的可运行代码,我会一步步给你解释关键部分。

完整代码示例

import ctypes
from ctypes import wintypes

# 定义Windows API所需的常量和结构体
USER32 = ctypes.WinDLL('user32', use_last_error=True)
KERNEL32 = ctypes.WinDLL('kernel32', use_last_error=True)

# 窗口相关常量
WC_DIALOG = '#32770'
WS_OVERLAPPEDWINDOW = 0x00CF0000
CW_USEDEFAULT = 0x80000000
COLOR_WINDOW = 5

# 电源消息相关常量
WM_POWERBROADCAST = 0x0218
PBT_POWERSETTINGCHANGE = 0x8013

# 显示器电源状态的GUID(系统固定值)
GUID_MONITOR_POWER_ON = ctypes.GUID("{02731015-4510-4526-99E6-E5A17EBD1AEA}")

# 定义MSG结构体,用于消息循环
class MSG(ctypes.Structure):
    _fields_ = [
        ("hwnd", wintypes.HWND),
        ("message", wintypes.UINT),
        ("wParam", wintypes.WPARAM),
        ("lParam", wintypes.LPARAM),
        ("time", wintypes.DWORD),
        ("pt", wintypes.POINT)
    ]

# 定义POWERBROADCAST_SETTING结构体,解析电源通知数据
class POWERBROADCAST_SETTING(ctypes.Structure):
    _fields_ = [
        ("PowerSetting", ctypes.GUID),
        ("DataLength", wintypes.DWORD),
        ("Data", wintypes.BYTE * 1)
    ]

# 窗口过程函数,处理收到的系统消息
def WindowProc(hwnd, msg, wParam, lParam):
    if msg == WM_POWERBROADCAST:
        if wParam == PBT_POWERSETTINGCHANGE:
            # 解析电源设置变化的数据
            setting = ctypes.cast(lParam, ctypes.POINTER(POWERBROADCAST_SETTING)).contents
            if setting.PowerSetting == GUID_MONITOR_POWER_ON:
                # 获取显示器状态:0=关闭,1=打开
                monitor_state = setting.Data[0]
                if monitor_state == 0:
                    print("屏幕已关闭!触发自定义事件...")
                    # 这里可以添加你需要执行的代码,比如调用其他函数、发送通知等
                elif monitor_state == 1:
                    print("屏幕已打开!")
        return True
    elif msg == 0x0010:  # WM_CLOSE:窗口关闭消息
        USER32.DestroyWindow(hwnd)
        return 0
    elif msg == 0x0002:  # WM_DESTROY:窗口销毁消息
        USER32.PostQuitMessage(0)
        return 0
    else:
        return USER32.DefWindowProcW(hwnd, msg, wParam, lParam)

def main():
    # 注册窗口类
    WNDCLASSEXW = ctypes.Structure()
    WNDCLASSEXW._fields_ = [
        ("cbSize", wintypes.UINT),
        ("style", wintypes.UINT),
        ("lpfnWndProc", ctypes.WINFUNCTYPE(wintypes.LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)),
        ("cbClsExtra", wintypes.INT),
        ("cbWndExtra", wintypes.INT),
        ("hInstance", wintypes.HINSTANCE),
        ("hIcon", wintypes.HICON),
        ("hCursor", wintypes.HCURSOR),
        ("hbrBackground", wintypes.HBRUSH),
        ("lpszMenuName", wintypes.LPCWSTR),
        ("lpszClassName", wintypes.LPCWSTR),
        ("hIconSm", wintypes.HICON)
    ]

    wndclass = WNDCLASSEXW()
    wndclass.cbSize = ctypes.sizeof(WNDCLASSEXW)
    wndclass.style = 0
    wndclass.lpfnWndProc = ctypes.WINFUNCTYPE(wintypes.LRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM)(WindowProc)
    wndclass.cbClsExtra = 0
    wndclass.cbWndExtra = 0
    wndclass.hInstance = KERNEL32.GetModuleHandleW(None)
    wndclass.hIcon = None
    wndclass.hCursor = None
    wndclass.hbrBackground = ctypes.cast(COLOR_WINDOW + 1, wintypes.HBRUSH)
    wndclass.lpszMenuName = None
    wndclass.lpszClassName = "MonitorPowerMonitorClass"
    wndclass.hIconSm = None

    USER32.RegisterClassExW(ctypes.byref(wndclass))

    # 创建隐藏窗口(不需要显示,仅用于接收消息)
    hwnd = USER32.CreateWindowExW(
        0,
        wndclass.lpszClassName,
        "MonitorPowerMonitor",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        None, None, wndclass.hInstance, None
    )

    if not hwnd:
        raise ctypes.WinError(ctypes.get_last_error())

    # 注册显示器电源状态通知
    h_notify = USER32.RegisterPowerSettingNotification(
        hwnd,
        ctypes.byref(GUID_MONITOR_POWER_ON),
        0  # DEVICE_NOTIFY_WINDOW_HANDLE:通知发送给窗口句柄
    )

    if not h_notify:
        raise ctypes.WinError(ctypes.get_last_error())

    print("开始监测显示器电源状态...")
    print("按Ctrl+C退出程序")

    # 消息循环:持续接收系统发送的消息
    msg = MSG()
    while USER32.GetMessageW(ctypes.byref(msg), None, 0, 0) > 0:
        USER32.TranslateMessage(ctypes.byref(msg))
        USER32.DispatchMessageW(ctypes.byref(msg))

    # 清理资源:注销电源通知
    USER32.UnregisterPowerSettingNotification(h_notify)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n程序已退出")

关键部分解释

1. 为什么需要创建窗口?

Windows的电源通知是绑定窗口句柄发送的,所以咱们必须创建一个窗口(哪怕是隐藏的)来接收这些系统消息。代码里创建的窗口可以最小化或者隐藏,完全不影响监测功能。

2. 注册电源通知的核心逻辑

RegisterPowerSettingNotification函数的三个关键参数:

  • hwnd:接收通知的窗口句柄,就是咱们刚创建的窗口
  • GUID参数:GUID_MONITOR_POWER_ON是系统预定义的、专门用于监测显示器电源状态的标识
  • 通知类型:0对应DEVICE_NOTIFY_WINDOW_HANDLE,表示通知会发送给指定的窗口

3. 处理电源状态变化

在窗口过程函数WindowProc里,咱们重点监听WM_POWERBROADCAST消息:

  • 当收到PBT_POWERSETTINGCHANGE类型的通知时,解析lParam里的结构体
  • Data[0]的值直接对应显示器状态:0代表屏幕关闭,1代表屏幕打开
  • 你可以在对应的分支里添加自己的业务逻辑,比如触发告警、执行特定脚本等

4. 消息循环的作用

GetMessageW、TranslateMessage、DispatchMessageW组成的消息循环是Windows程序的核心,它会持续捕获系统发送给窗口的所有消息,直到收到退出信号(比如关闭窗口、按下Ctrl+C)。

运行注意事项

  • 支持Windows 10+、Python 3.x环境
  • 不需要管理员权限,普通用户权限即可运行
  • 退出程序可以按Ctrl+C,或者直接关闭控制台窗口

内容的提问来源于stack exchange,提问作者Victor Marcelino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:35:30