Как получить понятное имя, принадлежащее дескриптору HMONITOR в Windows?

Мне нужно понятное имя, принадлежащее дескриптору типа HMONITOR.

WMI позволяет мне составить список всех подключенных мониторов со всей информацией, которая мне когда-либо понадобится, но как я могу связать эту информацию с дескрипторами монитора, которые я получаю через EnumDisplayMonitors?

DDCCI не вариант, так как рассматриваемый монитор не обеспечивает своих возможностей.

Я нашел GetMonitorInfoW(), но это дает мне только Display1 и Display2 как szDevice, так что это не очень полезно.

Ниже вы можете найти мой текущий подход к Python. Все, что вам нужно для запуска кода, — это работать в Windows и установить wmi через pip. Не стесняйтесь отвечать на C++ или любом другом языке, правда :)

import ctypes
from ctypes.wintypes import (
    DWORD,
    RECT,
    BOOL,
    HMONITOR,
    HDC,
    LPARAM,
    HANDLE,
    BYTE,
    WCHAR, WORD, SHORT,
)

def get_vcps():
    vcps = []
    hmonitors = []
    try:
        def _callback(hmonitor, hdc, lprect, lparam):
            hmonitors.append(HMONITOR(hmonitor))
            del hmonitor, hdc, lprect, lparam
            return True  # continue enumeration

        MONITORENUMPROC = ctypes.WINFUNCTYPE(BOOL, HMONITOR, HDC, ctypes.POINTER(RECT), LPARAM)
        callback = MONITORENUMPROC(_callback)
        if not ctypes.windll.user32.EnumDisplayMonitors(0, 0, callback, 0):
            raise RuntimeError("Call to EnumDisplayMonitors failed")
    except OSError as err:
        raise RuntimeError("failed to enumerate VCPs") from err
    print("hmonitors:")
    for hmonitor in hmonitors:
        print(f"hmonitor: {hmonitor}")
    # WMI provides a list of the friendly names of the monitors, but not the handles:
    print("WMI monitor IDs:")
    for monitor in get_wmi_monitor_id():
        print(monitor)
    return vcps

def get_wmi_monitor_id():
    import wmi
    c = wmi.WMI(namespace='root\\wmi')
    monitors = c.WmiMonitorID()
    monitor_data = []
    for monitor in monitors:
        monitor_info = {
            "active": monitor.Active,
            "InstanceName": monitor.InstanceName,
            "ManufacturerName": ''.join(chr(c) for c in monitor.ManufacturerName if c != 0),
            "ProductCodeID": monitor.ProductCodeID,
            "SerialNumberID": ''.join(chr(c) for c in monitor.SerialNumberID if c != 0),
            "UserFriendlyName": ''.join(chr(c) for c in monitor.UserFriendlyName if c != 0),
            "WeekOfManufacture": monitor.WeekOfManufacture,
            "YearOfManufacture": monitor.YearOfManufacture
        }
        monitor_data.append(monitor_info)
    return monitor_data

get_vcps()

Вы пробовали EnumDisplayDevices() ? Кроме того, в WMI класс Win32_DesktopMonitor имеет DeviceID, который, по-видимому, связан с его HMONITOR.

Remy Lebeau 26.08.2024 04:28

Вы можете использовать CCD API stackoverflow.com/a/74605112/403671 или WinRT stackoverflow.com/a/78179702/403671

Simon Mourier 26.08.2024 08:34

Win32_DesktopMonitor: возвращает только мой современный монитор, другой даже не указан. CCD API: возвращает только мой современный монитор, в списке указан другой старый, но DisplayConfigGetDeviceInfo завершается с ошибкой. WinRT: не проверял EnumDisplayDevices(): работает просто великолепно (правда, это заняло некоторое время). Спасибо вам обоим!

Robin 26.08.2024 18:12
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
91
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вероятно, это очень хрупкое решение, которое необходимо доработать, пока я получаю больше отзывов от пользователей. Но для моей установки это работает как шарм.

import binascii
import ctypes
from ctypes.wintypes import DWORD, WCHAR, HMONITOR, BOOL, HDC, RECT, LPARAM, CHAR
from typing import NamedTuple, Optional, Dict

import wmi


class DISPLAY_DEVICE(ctypes.Structure):
    _fields_ = [
        ("cb", DWORD),
        ("DeviceName", WCHAR * 32),
        ("DeviceString", WCHAR * 128),
        ("StateFlags", DWORD),
        ("DeviceID", WCHAR * 128),
        ("DeviceKey", WCHAR * 128)
    ]


class MONITORINFOEXA(ctypes.Structure):
    _fields_ = [
        ("cbSize", DWORD),
        ("rcMonitor", RECT),
        ("rcWork", RECT),
        ("dwFlags", DWORD),
        ("szDevice", CHAR * 32)
    ]


_DISPLAY_DEVICE_ACTIVE = 0x1


def _get_display(device_name):
    # only return the part between \\\\.\\ and \\ or until the end of the string
    import re
    match = re.search(r'\\\\\.\\(.*?)(\\|$)', device_name)
    if match:
        return match.group(1)
    return None


def _handle_to_display_mapping():
    hmonitors = []
    mapping = {}
    try:
        def _callback(hmonitor, hdc, lprect, lparam):
            hmonitors.append(HMONITOR(hmonitor))
            del hmonitor, hdc, lprect, lparam
            return True  # continue enumeration

        MONITORENUMPROC = ctypes.WINFUNCTYPE(BOOL, HMONITOR, HDC, ctypes.POINTER(RECT), LPARAM)
        callback = MONITORENUMPROC(_callback)
        if not ctypes.windll.user32.EnumDisplayMonitors(0, 0, callback, 0):
            raise RuntimeError("Call to EnumDisplayMonitors failed")
    except OSError as _:
        raise RuntimeError("failed to enumerate VCPs")

    for hmonitor in hmonitors:
        monitor_info = MONITORINFOEXA()
        monitor_info.cbSize = ctypes.sizeof(MONITORINFOEXA)
        if not ctypes.windll.user32.GetMonitorInfoA(hmonitor, ctypes.byref(monitor_info)):
            raise RuntimeError("Call to GetMonitorInfoA failed")
        mapping[_get_display(monitor_info.szDevice.decode("ascii"))] = hmonitor
    return mapping


def _display_to_device_id_mapping(only_active=True):
    i = 0
    devices = []
    # first get all display devices:
    while True:
        display_device = DISPLAY_DEVICE()
        display_device.cb = ctypes.sizeof(DISPLAY_DEVICE)
        if not ctypes.windll.user32.EnumDisplayDevicesW(None, i, ctypes.byref(display_device), 0):
            break
        if not only_active or display_device.StateFlags & _DISPLAY_DEVICE_ACTIVE:
            devices.append(display_device.DeviceName)
        i += 1
    mapping = {}
    for device_name in devices:
        j = 0
        while True:
            display_device = DISPLAY_DEVICE()
            display_device.cb = ctypes.sizeof(DISPLAY_DEVICE)
            # Query each monitor associated with the adapter
            if not ctypes.windll.user32.EnumDisplayDevicesW(device_name, j, ctypes.byref(display_device), 0):
                break
            if not only_active or display_device.StateFlags & _DISPLAY_DEVICE_ACTIVE:
                mapping[_get_display(display_device.DeviceName)] = display_device.DeviceID
            j += 1
    return mapping


def _device_id_to_f_name_mapping():
    c = wmi.WMI(namespace='root\\wmi')
    monitors = c.WmiMonitorID()
    mapping = {}
    for monitor in monitors:
        mapping[monitor.InstanceName] = ''.join(chr(c) for c in monitor.UserFriendlyName if c != 0)
    return mapping


def _display_to_handle_and_f_name_mapping(dmapping, hmapping, nmapping):
    dmapping_parts = {k: tuple(v.split('\\')) for k, v in dmapping.items()}
    nmapping_parts = {tuple(k.split('\\')): v for k, v in nmapping.items()}

    mapping: Dict[str, Optional[ctypes.POINTER]] = {display: None for display in hmapping.keys()}
    for device_id_parts, f_name in nmapping_parts.items():
        for did_part in device_id_parts:
            # we need to find the corresponding display in the dmapping parts
            for display, display_parts in dmapping_parts.items():
                if did_part in display_parts and display in hmapping:
                    mapping[display] = (f_name, hmapping[display])
    return mapping


def _get_monitor_descriptions() -> list[NamedTuple]:
    import pyedid
    c = wmi.WMI(namespace='wmi')
    monitors = c.WmiMonitorDescriptorMethods()
    edid_data = []
    for monitor in monitors:
        edid = bytes(monitor.WmiGetMonitorRawEEdidV1Block(0)[0])
        if edid:
            edid_hex = binascii.hexlify(edid).decode('ascii')
            edid_data.append(pyedid.parse_edid(edid_hex))
    return edid_data


def display_to_handle_and_f_name_mapping():
    dmapping = _display_to_device_id_mapping(True)
    hmapping = _handle_to_display_mapping()
    nmapping = _device_id_to_f_name_mapping()
    return _display_to_handle_and_f_name_mapping(dmapping, hmapping, nmapping)


if __name__ == "__main__":
    print(display_to_handle_and_f_name_mapping())

Мы будем рады поделиться своим решением здесь. Пожалуйста, не забудьте принять свой ответ, поскольку решение вам подходит.

Ax1le 28.08.2024 03:00

Другие вопросы по теме