Мне нужно понятное имя, принадлежащее дескриптору типа 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()
Вы можете использовать CCD API stackoverflow.com/a/74605112/403671 или WinRT stackoverflow.com/a/78179702/403671
Win32_DesktopMonitor: возвращает только мой современный монитор, другой даже не указан. CCD API: возвращает только мой современный монитор, в списке указан другой старый, но DisplayConfigGetDeviceInfo завершается с ошибкой. WinRT: не проверял EnumDisplayDevices(): работает просто великолепно (правда, это заняло некоторое время). Спасибо вам обоим!
Вероятно, это очень хрупкое решение, которое необходимо доработать, пока я получаю больше отзывов от пользователей. Но для моей установки это работает как шарм.
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())
Мы будем рады поделиться своим решением здесь. Пожалуйста, не забудьте принять свой ответ, поскольку решение вам подходит.
Вы пробовали EnumDisplayDevices() ? Кроме того, в WMI класс Win32_DesktopMonitor имеет
DeviceID
, который, по-видимому, связан с егоHMONITOR
.