Blog. Just Blog

Как узнать модель и серийный номер монитора

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Как узнать модель и серийный номер монитора
Как узнать модель и серийный номер монитора

EDID - Extended Display Identification Data - стандарт формата данных VESA, расширенные данные идентификации дисплея. Эта информация передается монитором или телевизором на устройство, которое генерирует видео сигнал. EDID содержит базовую информацию о мониторе и его возможностях, включая информацию о производителе, максимальном размере, цветовых характеристиках, заводских таймингах, границах частотного диапазона и другие технические данные. Кроме этого в EDID записаны строки, содержащие модель монитора и его серийный номер. Вот они-то нас и интересуют.

После подключения и установки драйверов монитора в систему, в реестре для него формируется соответствующая запись. Нам остается только ее найти, прочитать и извлечь нужные данные. В интернетах можно найти официальный мануал VESA по стандарту EDID.

VESA Enhanced EDID Standard (ENG)VESA Enhanced EDID Standard (ENG)

VESA.Enhanced.EDID.Standard.zip (1,878,809 bytes)

В документе почти сотня страниц, много теории и всякой технической заумности, поскольку применение EDID очень широкое. Быстро найти нужную информацию, мягко говоря, проблематично. В английском варианте статьи на Википедии эта информация разложена более понятным образом. Итак, в структуре EDID в байтах 54-125 должны быть записаны 4 блока идентификаторов. Структура каждого идентификатора описана там же. Забегая вперед отмечу, что слово "должны" некоторые производители понимают по-своему. Например, для моего маленького китайского монитора серийный номер вообще не узнать, так как в EDID записаны два блока таймингов, диапазоны работы монитора и название модели. При парсинге такие ситуации придется учитывать.

Переходим к программированию. Несколько структур, часть из которых вы уже видели в статье про выключение мониторов. Там же описан и принцип работы с мониторами, как получить их список, как получить хэндл и прочее. Настоятельно рекомендую ознакомиться, чтобы у вас не возникало вопросов, а мне не пришлось дублировать теоретические выкладки.
  1. struct PHYSICAL_MONITOR
  2.         hPhysicalMonitor dd ?
  3.         szPhysicalMonitorDescription rw 128
  4. ends
  5.  
  6. struct MONITORINFOEX
  7.         cbSize    dd ?
  8.         rcMonitor RECT
  9.         rcWork    RECT
  10.         dwFlags   dd ?
  11.         szDevice  rb 32
  12. ends
  13.  
  14. struct  DISPLAY_DEVICE
  15.         cb           dd ?
  16.         DeviceName   rb 32
  17.         DeviceString rb 128
  18.         StateFlags   dd ?
  19.         DeviceID     rb 128
  20.         DeviceKey    rb 128
  21. ends
Callback-функция для EnumDisplayMonitors, помимо уже знакомого вам функционала из статьи по ссылке выше, дополнена кодом получения и парсинга EDID.
  1. ;--------------------------------------------------------
  2. ; Callback-функция перебора мониторов
  3. ;--------------------------------------------------------
  4. proc MonitorEnumProc hMonitor:DWORD,hdc:DWORD,lpRect:DWORD,lParam:DWORD
  5.         locals
  6.             ; Количество мониторов
  7.             num dd ?
  8.  
  9.             ; Массив PHYSICAL_MONITOR
  10.             hMonArray dd ?
  11.             MonArray  dd ?
  12.  
  13.             Manufacturer dd ?
  14.             CurrentValue dd ?
  15.             MaximumValue dd ?
  16.             CurrentS dd ?
  17.             MaximumS dd ?
  18.             ResS dd ?
  19.         endl
  20.  
  21.         ; Получить количество физических мониторов
  22.         lea     eax,[num]
  23.         invoke  GetNumberOfPhysicalMonitorsFromHMONITOR,[hMonitor],eax
  24.  
  25.         ; Физических мониторов нет
  26.         cmp     [num],0
  27.         je      .loc_ret
  28.  
  29.         ; Зарезервировать память под массив PHYSICAL_MONITOR
  30.         mov     eax,[num]
  31.         imul    eax,sizeof.PHYSICAL_MONITOR
  32.  
  33.         invoke  GlobalAlloc,GMEM_MOVEABLE+GMEM_DDESHARE,eax
  34.         mov     [hMonArray],eax
  35.         invoke  GlobalLock,[hMonArray]
  36.         mov     [MonArray],eax
  37.  
  38.         ; Получить информацию о всех физических мониторах
  39.         invoke  GetPhysicalMonitorsFromHMONITOR,[hMonitor],[num],[MonArray]
  40.  
  41.         ; Выключить все мониторы поочередно
  42.         mov     ecx,[num]
  43.         mov     esi,[MonArray]
  44.  
  45. .loc_monitors_loop:
  46.         push    ecx
  47.         push    esi
  48.  
  49.         ; Информация о мониторе
  50.         mov     [minfo.cbSize],sizeof.MONITORINFOEX
  51.         invoke  GetMonitorInfo,[hMonitor],minfo
  52.  
  53.         mov     [dds.cb],sizeof.DISPLAY_DEVICE
  54.         invoke  EnumDisplayDevices,minfo.szDevice,NULL,dds,0
  55.  
  56.         invoke  RtlZeroMemory,edidmodel,256
  57.         invoke  RtlZeroMemory,edidsn,256
  58.         invoke  RtlZeroMemory,regstr,256
  59.  
  60.         ; Сформировать название ключа реестра
  61.         ; SYSTEM\CurrentControlSet\Enum\DISPLAY\
  62.         invoke  lstrcpy,regstr,szReg0
  63.         mov     edi,regstr
  64.         invoke  lstrlen,edi
  65.         add     edi,eax
  66.  
  67.         ; Дописать к нему подстроку из DeviceID
  68.         mov     esi,dds.DeviceID
  69. @@:
  70.         lodsb
  71.         or      al,al
  72.         jz      .loc_no_devid
  73.         cmp     al,'\'
  74.         jne     @b
  75. @@:
  76.         lodsb
  77.         or      al,al
  78.         jz      .loc_no_devid
  79.         stosb
  80.         cmp     al,'\'
  81.         jne     @b
  82.  
  83.         ; Открыть ветку реестра
  84.         ; HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\PHL0904\
  85.         invoke  RegOpenKey,HKEY_LOCAL_MACHINE,regstr,hKey
  86.         or      eax,eax
  87.         ; Открыть ключ не получилось, пропускаем
  88.         jnz     .loc_no_devid
  89.  
  90.         ; Перебрать все дочерние ключи, начиная с 0
  91.         mov     [dKeysIndex],0
  92. .loc_scan_keys_monitor:
  93.         ; Просканировать все ключи
  94.         invoke  RegEnumKey,[hKey],[dKeysIndex],buff,tmp
  95.         or      eax,eax
  96.         jnz     .loc_no_more_keys
  97.  
  98.         ; Сформировать название ключа реестра
  99.         ; SYSTEM\CurrentControlSet\Enum\DISPLAY\PHL0904\
  100.         invoke  lstrcpy,buff2,regstr
  101.         ; 5&217f22ba&0&UID1048848
  102.         invoke  lstrcat,buff2,buff
  103.         ; \Device Parameters
  104.         invoke  lstrcat,buff2,szReg1
  105.  
  106.         ; Открыть параметр EDID в ключе реестра
  107.         ; HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\PHL0904\
  108.         ; 5&217f22ba&0&UID1048848\Device Parameters
  109.         invoke  RegOpenKeyEx,HKEY_LOCAL_MACHINE,buff2,0,KEY_READ,hSubKey
  110.         or      eax,eax
  111.         ; Такого параметра нет, проверить следующий ключ
  112.         jnz     .loc_next_key
  113.  
  114.         ; Прочитать EDID
  115.         invoke  RtlZeroMemory,edid,500h
  116.         mov     [tmp],500h
  117.         invoke  RegQueryValueEx,[hSubKey],szReg2,0,0,edid,tmp
  118.         or      eax,eax
  119.         jnz     .loc_no_edid
  120.  
  121.         ; Найти в EDID строки Serial Number и Model
  122.         xor     ecx,ecx
  123.         ; Начинаем разбор с 72-го байта
  124.         mov     ebx,48h
  125. .loc_scan_edid:
  126.         mov     esi,ebx
  127.         add     esi,edid
  128.         mov     byte[esi+18],0
  129.  
  130.         ; Serial Number
  131.         cmp     dword[esi],0xFF000000
  132.         jne     .loc_not_edidsn
  133.  
  134.         add     esi,5
  135.         mov     edi,edidsn
  136. .loc_move_edidsn:
  137.         lodsb
  138.         cmp     al,20h
  139.         jb      .loc_next_edid
  140.         stosb
  141.         jmp     .loc_move_edidsn
  142.  
  143. .loc_not_edidsn:
  144.         ; Model
  145.         cmp     dword[esi],0xFC000000
  146.         jne     .loc_next_edid
  147.  
  148.         add     esi,5
  149.         mov     edi,edidmodel
  150. .loc_move_edidmodel:
  151.         lodsb
  152.         cmp     al,20h
  153.         jb      .loc_next_edid
  154.         stosb
  155.         jmp     .loc_move_edidmodel
  156.  
  157. .loc_next_edid:
  158.         add     ebx,18
  159.         inc     ecx
  160.         cmp     ecx,2
  161.         jbe     .loc_scan_edid
  162.  
  163. .loc_no_edid:
  164.         ; Закрыть ключ
  165.         invoke  RegCloseKey,[hSubKey]
  166.  
  167. .loc_next_key:
  168.         ; Следующий индекс
  169.         inc     [dKeysIndex]
  170.         jmp     .loc_scan_keys_monitor
  171.  
  172. .loc_no_more_keys:
  173.         invoke  RegCloseKey,[hKey]
  174.  
  175. .loc_no_devid:
  176.         ; edidsn -> Serial Number, если есть
  177.         ; edidmodel -> Model
  178.         ; или пустые строки, если записи о мониторе в реестре нет
  179.  
  180.         invoke  wsprintf,buff,mask,edidmodel,edidsn
  181.         add     esp,16
  182.         invoke  MessageBox,0,buff,dds.DeviceName,0
  183.  
  184. .loc_next_monitor:
  185.         ; Следующая запись из массива PHYSICAL_MONITOR
  186.         pop     esi
  187.         add     esi,sizeof.PHYSICAL_MONITOR
  188.         pop     ecx
  189.         sub     ecx,1
  190.         jnz     .loc_monitors_loop
  191.  
  192.         ; Прибраться за собой
  193.         invoke  DestroyPhysicalMonitors,[num],[MonArray]
  194.  
  195.         ; Освободить память
  196.         invoke  GlobalUnlock,[hMonArray]
  197.         invoke  GlobalFree,[hMonArray]
  198.  
  199. .loc_ret:
  200.         ; Продолжаем обработку
  201.         mov     eax,TRUE
  202.         ret
  203. endp
Теперь подробно, что тут происходит. Получив хэндл монитора, получаем информацию о нем при помощи функции GetMonitorInfo и EnumDisplayDevices. В отличие от устройств ввода, системное имя монитора возвращается в урезанном варианте, например, MONITOR\PHL0904\{4d36e96e-e325-11ce-bfc1-08002be10318}\0007. Из этой строки можно извлечь только класс устройства и Vendor ID. Этого достаточно, чтобы сформировать название ключа реестра вида HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\PHL0904. После этого надо перебрать все вложенные ключи с уникальным идентификатором устройства, так как для одного устройства может быть создано несколько таких записей. Например, при подключении монитора к разным видеовыходам. В каждом вложенном ключе надо будет проверить наличие вложенного ключа Device Parameters с параметром EDID, перебирая их поочередно. Таким образом, в конечном итоге ключ реестра будет иметь вид HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\PHL0904\5&217f22ba&0&UID1048848\Device Parameters или что-то подобное в зависимости от вашего железа.

Прочитав из реестра значение параметра EDID, мы получим EDID монитора. Он хранится в бинарном виде, так что приступаем к парсингу. В соответствии со стандартом, четыре 18-байтных идентификатора находятся в EDID с 54-го байта. Последовательность и назначение блоков ничем не регламентировано, поэтому надо проверять все четыре. Структура текстовых блоков простейшая: три нулевых байта, байт описания идентификатора, снова нулевой байт и затем ASCII-строка, которая заканчивается или на последнем байте идентификатора, или на любом непечатном символе с кодом меньше 20h. Таким образом максимальная длина строки серийного номера или названия модели монитора может быть 13 символов. Байт описания идентификатора 0FFh соответствует серийному номеру монитора, байт 0FCh - названию модели.

Как я говорил выше, в зависимости от производителя монитора, искомых строк в EDID может вообще не оказаться, или может быть только название модели, с этим я уже столкнулся на практике. Также выяснилось, что при подключении одного и того же монитора к разным компьютерам, получаемые значения его серийного номера могут отличаться. Я не могу даже предположить, с чем это связано. Ну и теоретически, раз уж EDID хранится локально в реестре, то его можно модифицировать, изменив какие-нибудь байты и подкорректировав контрольную сумму. Так что безоговорочно доверять данным EDID не надо.

Скриншот программы Phoenix EDID Designer
Скриншот программы Phoenix EDID Designer

Для более детального изучения структуры EDID, а также для редактирования имеющихся значений, можно воспользоваться малюсенькой утилитой Phoenix EDID Designer. Ее разработка прекращена, офсайт недоступен, последнюю версию я выложил сюда.

Phoenix EDID Designer 1.3Phoenix EDID Designer 1.3

Phoenix.EDID.Designer.1.3.zip (175,983 bytes)

В приложении пример программы с исходным текстом, которая выводит название модели и серийный номер для каждого подключенного монитора.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Monitor.EDID.Info.zip (3,968 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 7644 | Комментариев: 2

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (10.12.2023 в 18:24):
Программа сама ничего не придумывает, только отображает информацию, которую ей отдает система. Так что вопросы не к программе, а к системе.
Maxim (10.12.2023 в 16:51):
Интересно. У меня монитор выпущен в двух модификациях, которые отличаются по разъёмам. И есть монитор, но у него две разные матрицы. В обоих случаях программа выводит только первые буквы и цифры без модификации.
Например SX202. А ведь эта модель идёт как SX202H и SX202HQ.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 3 (0.0073 сек.) / Память: 4.5 Mb
Наверх