allasm.ru

    Меню

 

А у вас сколько мониторов? (DirectX) /04.08.05/

исходники к статье

Да, это снова SolidCode со своими мониторами. Очень уж меня интересует эта тема. В предыдущей статье мы разбирались, какую информацию о мониторах можно получить средствами Win32 API. Тогда я наивно полагал, что DirectX – это рулез, потому что глубже работает с видеодрайверами и железом.

Но меня мелкомягкие сильно разочаровали. Оказалось, что с помощью DirectX можно узнать ещё меньше информации по сабджу. Хотя и появляется кое-что дополнительное. Но расскажу обо всём по порядку. Эта статья предполагает, что вы прочитали предыдущую статью и знаете общую логику работы с мониторами в системе Windows . Исследования для этой статьи проводились в рамках DirectX 8.1. Большое спасибо keyMax за прекрасные статьи и переведённые инклюды. Более мелким шрифтом будет даваться информация по некоторым вопросам, которые изменились в DirectX от версии 8.1 до версии 9.

Для инициализации DirectX 8.1 требуется выполнить следующее:

invoke  Direct3DCreate8,D3D_SDK_VERSION
mov     pd3d,eax

Если функция вернула нормальный указатель на интерфейс, то начинаем исследование ситуации с видеоадаптерами/мониторами. Надо сказать, что DirectX более интересуется видеоадаптерами. А монитор уж там чего-нибудь покажет. В отличие от GDI , здесь можно узнать количество видеоадаптеров заранее. Вот объявление метода из MSDN.

UINT GetAdapterCount();

И его вызов средствами MASM32.

d3d8    GetAdapterCount,pd3d
mov     nAdapters,eax
  

Видимо DirectX каждому реальному монитору сопоставляет свою логическую «копию» видеоадаптера, что и определяет возвращаемое значение. При ОДНОМ физическом видеоадаптере и двух реальных мониторах мы получаем значение 2. Да, два видеоадаптера в системе, ни больше и не меньше.

Теперь в цикле будем перебирать адаптеры по номерам и получать доступную информацию в структуру D3DADAPTER_IDENTIFIER8 . Перебор начинаем с 0, что есть D3DADAPTER_DEFAULT . Это первичный адаптер/монитор. Он существует в любом случае. Последним будет адаптер под номером nAdapters -1 . Обычно это значение не превышает двух. Информацию о каждом мониторе получаем с помощью метода GetAdapterIdentifier . Вот его объявление из MSDN.

HRESULT GetAdapterIdentifier(
  UINT Adapter,
  DWORD Flags,
  D3DADAPTER_IDENTIFIER8* pIdentifier
);

Далее следует пример кода в MASM 32, который циклом WHILE обрабатывает все адаптеры. Номер адаптера содержится в регистре ESI . Метод должен возвращать значение D3D_OK , если всё нормально и информация получена.

xor     esi,esi
mov nAdapters,eax
.while esi<nAdapters
lea edx,[ebx].AdapterInfo
mov [ebx].nAdapterOrdinal,esi
d3d8 GetAdapterIdentifier, pd3d, esi, D3DENUM_NO_WHQL_LEVEL, edx
.break.if eax!=D3D_OK
...
inc esi
add ebx,sizeof DXMON_INFO
.endw

Этот метод предоставляет нам максимум интересующей нас информации, которую можно выжать из DirectX. При этом я рекомендую использовать флаг D 3 DENUM _ NO _ WHQL _ LEVEL EQU 2. Это запрещает функции зависать надолго разыскивая бесполезные подписи мелкомягких ( WHQL расшифровывается Windows Hardware Quality Labs). У меня функция вообще вылетала в WinXP при использовании без этого флага. А OllyDbg ругался на кучу дополнительных потоков, которые она порождала и забывала убивать. Но использование этого флага рулез, имхо.

Самое интересное – это содержимое структуры D3DADAPTER_IDENTIFIER8.

  
MAX_DEVICE_IDENTIFIER_STRING equ 512
D3DADAPTER_IDENTIFIER8 STRUC Driver BYTE MAX_DEVICE_IDENTIFIER_STRING DUP (?) Description BYTE MAX_DEVICE_IDENTIFIER_STRING DUP (?) DriverVersion DWORD ? ;LARGE_INTEGER DriverVersionHi DWORD ? VendorId DWORD ? DeviceId DWORD ? SubSysId DWORD ? Revision DWORD ? DeviceIdentifier DWORD 4 DUP (?) ;GUID ? WHQLLevel DWORD ? D3DADAPTER_IDENTIFIER8 ENDS

Давайте посмотрим, что здесь есть интересного. Прежде всего разберём строку Driver. Здесь мы получаем имя основного файла драйвера. Строка Description даёт нормальное имя видеоадаптера, презентабельное для пользователя. Далее следует 64-битное число, представляющее версию драйвера. Оно делится на 4 16-битных числа. В примере, прилагаемом к статье, вы найдёте готовую функцию, превращающую это число в нормальную строку, идентичную той, которую показывает диагностическая утилита DirectX ( dxdiag. exe). Следующие 4 поля особой пользы для нас не предоставляют. В SDK о них сказано, что их можно использовать для определения конкретного чипсета, однако там же советуют использовать эти значения с осторожностью. Далее следует DeviceIdentifier в формате GUID. Функция для его превращения в нормальную строку также находится в примере к статье. WHQLLevel всегда будет ноль, если мы используем флаг D 3 DENUM _ NO _ WHQL _ LEVEL.

Как видите, реально полезной информации мы получили весьма немного. Правда, узнали имя главного файла драйвера и версию драйвера.

Версия 9.

Применимо к методу GetAdapterIdentifier произошло следующее изменение. Убрали флаг D 3 DENUM _ NO _ WHQL _ LEVEL и поставили флаг D3DENUM_WHQL_LEVEL . Видимо их достали с руганью по поводу лабораторий качества. Теперь, если в соответствующем аргументе передаётся NULL , то WHQL не получают, а если уж сильно захотели, то ставьте флаг D3DENUM_WHQL_LEVEL .

Структура D 3 DADAPTER _ IDENTIFIER тоже немного изменилась. Примечательно, что добавили строку DeviceName, в которой возвращается внутреннее имя дисплея (типа “\\.\ DISPLAY 1”) с намёком на то, что дальше сами получайте информацию об устройстве с помощью стандартных API из user 32. dll.

В восьмой версии DirectX мы ещё можем получить текущий видеорежим соответствующего адаптера с помощью метода GetAdapterDisplayMode;

HRESULT GetAdapterDisplayMode(
UINT Adapter,
D3DDISPLAYMODE* pMode
);

Для получения всех доступных видеорежимов используем метод GetAdapterModeCount, чтобы получить количество режимов. А потом в цикле перебираем все эти режимы, начиная с 0. В этом нам поможет метод EnumAdapterModes.

UINT GetAdapterModeCount(
UINT Adapter
); HRESULT EnumAdapterModes( UINT Adapter, UINT Mode, D3DDISPLAYMODE* pMode );

В DirectX видеорежимы описываются несколько иначе, чем в GDI. Вот объявление структуры D3DDISPLAYMODE , которая содержит информацию о видеорежиме.

D3DDISPLAYMODE STRUC
Width1 UINT ?
Height UINT ?
RefreshRate UINT ?
Format DWORD ? ;D3DFORMAT
D3DDISPLAYMODE ENDS

Глубина цвета здесь описывается не количеством битов для описания цвета, а с помощью специальных форматов поверхностей ( D 3 DFORMAT). Они более информативны, чем количество битов.

Если же вы всё-таки слишком любопытные и хотите узнать всё про мониторы, то используете метод GetAdapterMonitor.

HMONITOR GetAdapterMonitor(
UINT Adapter
);

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

На этом наше повествование заканчивается. Более мне не удалось найти способы получить информацию о видеосистеме компьютера. Правда, в интерфейсах DirectDraw (версии DirectX 7 и меньше) можно ещё получить размер видеопамяти. В версии 8 мелкомягкие, по видимому, решили, что эта информация лишняя. Так что Win32API всё-равно незаменимы.

Успешного творчества.