DirectX 8.1 в MASM32: Урок 3 — Архив WASM.RU
Урок 3. Создаем диалоговое окно для определения всех доступных режимов.
Как определить все доступные полноэкранные режимы и при этом сделать так, чтобы пользователь смог выбирать в каком режиме работать приложению ? В этом нам поможет диалоговое окно.
Если вы уже знаете, что такое диалоговое окно и представляете, как оно выглядит, как его создать и как с ним работать, то все равно советую вам почитать эту статью. Тем же, кто плохо понимает, что это за зверь такой, следует дополнительно посмотреть статьи на эту тему.
Готовы ? Тогда вперед !
Диалоговое окно в нашем приложении можно реализовать двумя путями. Первый путь - это встроить его в само приложение. Второй путь - это реализовать его как самостоятельное приложение. Что выбрать ? Оба варианта имеют свои плюсы и минусы.
Среди плюсов первого варианта то, что оно находится внутри нашего приложения. При этом нам нужно написать не такой уж большой обьем кода. Также все настройки, выбранные пользователем, доступны немедленно. Среди минусов то, что при каждом запуске приложения пользователю приходиться выбирать настройки.
Реализация второго варианта несколько сложнее. Нужно создавать два экзешника. Один для диалогового окна, другой - собственно для приложения. При этом все настройки, произведенные пользователем, придется записывать в файл. Но зато появляется возможность переделывать Setup снова и снова, не трогая при этом приложение, ведь всё равно все настройки сохраняются в файле.
В итоге каждый вариант подходит для определенных задач. Например, для какой - нибудь демки встроенное диалоговое окно самое то. А вот для игрушки - наоборот. Можно также придумать какой нибудь гибрид. Я долго размышлял какой вариант использовать для урока и в конце концов решил сделать оба, чтобы у вас была возможность сравнить. Это потребовало довольно много времени, и как следствие, урок был готов гораздо позже, чем было запланировано. Мы с вами рассмотрим только второй вариант, так как он немного сложнее.
По плану нам требуется: определить все доступные оконные режимы, определить все доступные полоноэкранные режимы, запихать всю эту информацию в диалоговое окно, вывести его на экран, обработать все действия пользователя, сохранить настройки в файле и при выходе из Setup запустить само приложение если требуется. Вроде бы целая куча работы, но на самом деле все гораздо проще.
Начнем с файла ресурсов. Берем любой редактор, пара минут и все готово. Кратко рассмотрим по частям, как он должен выглядеть.
Код (Text):
#define DS_CENTER 0x00000800L #define DS_MODALFRAME 0x00000080L #define WS_EX_TOOLWINDOW 0x00000080L #define WS_GROUP 0x00020000L #define WS_EX_STATICEDGE 0x00020000L #define BS_AUTORADIOBUTTON 0x00000009L #define BS_FLAT 0x00008000L #define CBS_DROPDOWNLIST 0x00000003L #define WS_VSCROLL 0x00200000L #define WS_TABSTOP 0x00010000L #define WS_DISABLED 0x08000000L #define ID_OK 10 #define ID_CANCEL 11 #define ID_SAVE 12 #define IDC_STATIC -1 #define IDC_WINDOWED 20 #define IDC_FULLSCREEN 21 #define IDC_SCREENMODESFS 22 #define IDC_SCREENMODESW 23 #define IDC_HERZ 24Здесь у нас константы, в частности, номера от 10 до 24 - это идентификаторы элементов нашего диалогового окна. Они будут нам передаваться в сообщениях от Windows, так мы сможем узнать с каким элементом в данный момент работает пользователь.
Код (Text):
SetupDialog DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 175, 91 STYLE DS_MODALFRAME | DS_CENTER EXSTYLE WS_EX_TOOLWINDOW CAPTION " Настройка параметров запуска" FONT 8, "Verdana"Здесь имя диалога, его вид и размер, параметры вывода на экран, текст заголовка и используемый шрифт.
Код (Text):
BEGIN CONTROL "В окошке",IDC_WINDOWED,"Button",BS_AUTORADIOBUTTON | BS_FLAT | WS_TABSTOP ,12,14,80,14 COMBOBOX IDC_SCREENMODESW,92,14,71,50,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "Полноэкранное",IDC_FULLSCREEN,"Button",BS_AUTORADIOBUTTON | BS_FLAT | WS_TABSTOP,12,30,80,12 COMBOBOX IDC_SCREENMODESFS,12,43,71,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_DISABLED | WS_TABSTOP LTEXT "Частота в герцах",IDC_STATIC,94,33,65,10 COMBOBOX IDC_HERZ, 92, 43, 71, 80, CBS_DROPDOWNLIST | WS_VSCROLL | WS_DISABLED | WS_TABSTOP GROUPBOX "", IDC_STATIC, 6,3,163,61 DEFPUSHBUTTON "Запуск", ID_OK,118,69,51,17,0,WS_EX_STATICEDGE PUSHBUTTON "Выход", ID_CANCEL,62,69,51,17,0,WS_EX_STATICEDGE PUSHBUTTON "Сохранить", ID_SAVE,6,69,51,17,0,WS_EX_STATICEDGE ENDМежду строками BEGIN и END располагаются строки, описывающие элементы расположенные на диалоговом окне. Здесь определены два RadioButton, три ComboBox, одно текстовое поле, одна рамка и три простых кнопки.
А вот и исходный текст Setup.exe:
Код (Text):
.686 .MMX .XMM .MODEL FLAT, STDCALL OPTION CASEMAP:none INCLUDE \masm32\include\windows.inc INCLUDE \masm32\include\kernel32.inc INCLUDE \masm32\include\user32.inc INCLUDE \masm32\include\gdi32.inc INCLUDE \masm32\include\shell32.inc INCLUDE \masm32\DirectX81\d3d8.inc INCLUDELIB \masm32\lib\kernel32.lib INCLUDELIB \masm32\lib\user32.lib INCLUDELIB \masm32\lib\gdi32.lib INCLUDELIB \masm32\lib\shell32.lib INCLUDELIB \masm32\directx81\d3d8.lib SetupDialog PROTO :DWORD,:DWORD,:DWORD,:DWORD DW2A PROTO :DWORD, :DWORD .DATA ;------------------------------------------ szDialogName db "SetupDialog",0 szAppName db "Tutorial03.exe",0 szFileName db "Settings.ini",0 szOperation db "open",0 WinModeTable dw 320,240, 640,480, 800,600, 1024,768, 1280,1024, 1600,1200 WinModeStrTable dd OFFSET szMode1 , OFFSET szMode2 dd OFFSET szMode3 , OFFSET szMode4 dd OFFSET szMode5 , OFFSET szMode6 szMode1 db "320x240",0 szMode2 db "640x480",0 szMode3 db "800x600",0 szMode4 db "1024x768",0 szMode5 db "1280x1024",0 szMode6 db "1600x1200",0 .DATA? ;----------------------------------------- pd3d dd ? ; Указатель на Direct3D d3ddm D3DDISPLAYMODE <?> ; Для получения информации о доступных режимах d3dpp D3DPRESENT_PARAMETERS <?> ; Структура для создания Direct3DDevice8 WinStyle dd ? CmbWindow dd ? ; ID ComboBox оконных режимов cmbFullScreen dd ? ; ID ComboBox полноэкранных режимов cmbHerz dd ? ; ID ComboBox герц FullModeTable dd 50 dup (?) ; Для хранения доступных полноэкранных режимов FullModeBPPTable db 50 dup (?) ; Для хранения форматов поверхности. FullModeHerzTable db 50 dup (?) ; Для хранения частоты в Герцах. FullModeStrBuffer db 16 dup (?) ; Буфер для формирования строки AppPath db 260 dup (?) ; Буфер для пути к Setup.exe hfile dd ? brr dd ? .CODE ;------------------------------------------ Start: Invoke DialogBoxParam,400000h,ADDR szDialogName,NULL,OFFSET SetupDialog,NULL Invoke ExitProcess,0 Ret SetupDialog proc hDlg:HWND , iMsg:DWORD , wParam:WPARAM , lParam:LPARAM ;---------------------------------------------------------------------- mov eax, iMsg cmp eax, WM_INITDIALOG ; Обрабатывая это сообщ. надо ОБЯЗАТЕЛЬНО вернуть EAX=1 je wmInitDialog cmp eax, WM_COMMAND je wmCommandDialog xor eax, eax ret wmInitDialog: ;-------------- pushad invoke CheckRadioButton, hDlg , 20 , 21 , 20 invoke GetDlgItem, hDlg , 23 mov cmbWindow, eax invoke GetDlgItem, hDlg , 22 mov cmbFullScreen, eax invoke GetDlgItem, hDlg , 24 mov cmbHerz, eax invoke Direct3DCreate8, D3D_SDK_VERSION mov pd3d, eax d3d8 GetAdapterDisplayMode, pd3d, D3DADAPTER_DEFAULT, ADDR d3dpp lea esi, d3dpp.BackBufferWidth mov eax, [esi] add eax, [esi+4] mov ebx, [esi+12] mov [esi+8], ebx mov DWORD PTR [esi+12], 3 xor ebx, ebx ; Проверка доступных оконных режимов и заполнение списка getNextMode: push ebx push eax invoke SendMessage,cmbWindow,CB_ADDSTRING,0,[WinModeStrTable + ebx*4] pop eax pop ebx movzx ecx, [WinModeTable+ebx*4] movzx edx, [WinModeTable+ebx*4+2] add ecx, edx inc ebx cmp eax, ecx ja getNextMode ; В итоге в ComboBox занесены строки доступных нам оконных режимов ; от минимальных до текущих размеров экрана включительно ; Проверка доступных полноэкранных режимов и заполнение списка d3d8 GetAdapterModeCount, pd3d, D3DADAPTER_DEFAULT dec eax lea esi, FullModeTable xor ebx, ebx mov edx, ebx mov ecx, eax getMode: push ecx push esi push ebx push edx d3d8 EnumAdapterModes, pd3d, D3DADAPTER_DEFAULT, ecx, ADDR d3ddm mov eax, d3ddm.Width1 mov ecx, d3ddm.Height mov edi, eax add edi, ecx add edi, d3ddm.Format pop edx pop ebx pop esi cmp edx, edi je noAdd mov edx, d3ddm.RefreshRate mov BYTE PTR [FullModeHerzTable+ebx], dl mov edx, d3ddm.Format mov [FullModeBPPTable+ebx], dl push edx push edi pop edx pop edi mov [esi+ebx*4], eax mov [esi+ebx*4+2], ecx inc ebx push ebx push esi push edx ; Формирование строки и добавление ее в Combobox invoke DW2A, eax, ADDR FullModeStrBuffer mov BYTE PTR [ebx], 'x' inc ebx invoke DW2A, ecx, ebx mov BYTE PTR [ebx], 'x' inc ebx mov ecx, 16 cmp edi, D3DFMT_X8R8G8B8 jne nextFormat mov ecx, 32 nextFormat: cmp edi, D3DFMT_A8R8G8B8 jne nextFormat2 mov ecx, 32 nextFormat2: cmp edi, D3DFMT_R8G8B8 jne exitCompare mov ecx, 24 exitCompare: invoke DW2A, ecx , ebx mov BYTE PTR [ebx], 0 invoke SendMessage, cmbFullScreen , CB_ADDSTRING , 0 , ADDR FullModeStrBuffer pop edx pop esi pop ebx noAdd: pop ecx dec ecx test ecx, ecx jne getMode ; В итоге в ComboBox занесены строки доступных нам полноэкранных режимов ; с разными форматами поверхности и самым высоким числом Герц для каждого режима invoke SendMessage, cmbWindow , CB_SETCURSEL , 0 , 0 invoke SendMessage, cmbFullScreen , CB_SETCURSEL , 0 , 0 invoke SendMessage, hDlg, WM_COMMAND , 22 + CBN_SELCHANGE shl 16, 0 popad xor eax, eax inc eax ret wmCommandDialog: ;---------------- movzx eax, WORD PTR wParam ; Здесь нам нужно получить HIWORD и LOWORD из wParam mov ecx, wParam ; HIWORD - код уведомления от элемента диалога ; LOWORD - ID элемента диалога xor ebx, ebx cmp eax, 10 ; Получаем если нажали кнопку Запуск je cmStartProgram cmp eax, 12 ; Получаем если нажали кнопку Сохранить je cmSaveData cmp eax, 11 ; Получаем если нажали кнопку Закрыть je cmCloseProgram cmp eax, 20 ; Получаем если нажали RadioButton "В окошке" je cmRadioWin cmp eax, 21 ; Получаем если нажали RadioButton "Полноэкранное" je cmRadioFull cmp ecx, 22 + CBN_SELCHANGE shl 16 je cmHerz xor eax, eax ret cmRadioFull: inc ebx cmRadioWin: invoke EnableWindow, cmbFullScreen , ebx invoke EnableWindow, cmbHerz , ebx xor ebx, 1 invoke EnableWindow, cmbWindow , ebx xor eax, eax ret cmHerz: invoke SendMessage, cmbHerz , CB_RESETCONTENT , 0 , 0 invoke SendMessage, cmbFullScreen , CB_GETCURSEL , 0 , 0 movzx ebx, BYTE PTR [FullModeHerzTable+eax] invoke DW2A, ebx , ADDR FullModeStrBuffer invoke SendMessage, cmbHerz , CB_ADDSTRING , 0 , ADDR FullModeStrBuffer invoke SendMessage, cmbHerz , CB_SETCURSEL , 0 , 0 xor eax, eax ret cmSaveData: invoke IsDlgButtonChecked, hDlg , 20 test eax, eax je EnableFull invoke SendMessage, cmbWindow , CB_GETCURSEL , 0 , 0 lea esi, WinModeTable shl eax, 2 add esi, eax xor ecx, ecx mov edx, ecx push [esi] pop cx pop dx mov d3dpp.BackBufferWidth, ecx mov d3dpp.BackBufferHeight, edx mov d3dpp.Windowed, 1 mov WinStyle, WS_SYSMENU jmp Create EnableFull: invoke SendMessage, cmbFullScreen , CB_GETCURSEL , 0 , 0 movzx ebx, BYTE PTR [FullModeHerzTable+eax] mov d3dpp.FullScreen_RefreshRateInHz, ebx movzx ebx, BYTE PTR [FullModeBPPTable+eax] mov d3dpp.BackBufferFormat, ebx lea esi, FullModeTable shl eax, 2 add esi, eax push [esi] xor ecx, ecx mov edx, ecx mov d3dpp.Windowed, ecx pop cx pop dx mov d3dpp.BackBufferWidth, ecx mov d3dpp.BackBufferHeight, edx mov WinStyle, WS_POPUP Create: mov d3dpp.SwapEffect, D3DSWAPEFFECT_FLIP invoke CreateFile , ADDR szFileName , GENERIC_WRITE, NULL, \ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL mov hfile, eax invoke WriteFile , hfile , ADDR d3dpp , 56 , ADDR brr , NULL invoke CloseHandle , hfile xor eax, eax ret cmStartProgram: invoke EndDialog, hDlg , 0 d3d8 Release, pd3d invoke GetModuleFileName , 0 , ADDR AppPath , 260 lea edi,AppPath lea edi,[edi+eax-6] @@: mov al,[edi] dec edi cmp al,'\' jne @B inc edi inc edi cld lea esi, szAppName mov ecx, SIZEOF szAppName shr ecx, 2 rep movsd mov ecx, [SIZEOF szAppName ] and ecx, 3 rep movsb invoke ShellExecute , NULL , ADDR szOperation , ADDR AppPath , \ NULL , NULL , SW_SHOWNORMAL xor eax, eax ret cmCloseProgram: invoke EndDialog, hDlg , 0 d3d8 Release, pd3d xor eax, eax ret SetupDialog endpЕще после SetupDialog находится текст функции DW2A. Я не стал ее тут приводить, т.к. она является слегка измененной dw2a из библиотеки masm32. Интересующиеся всегда смогут посмотреть ее в исходнике.
Теперь все разберем по косточкам.
Код (Text):
WinModeTable dw 320,240, 640,480, 800,600, 1024,768, 1280,1024, 1600,1200 WinModeStrTable dd OFFSET szMode1 , OFFSET szMode2 dd OFFSET szMode3 , OFFSET szMode4 dd OFFSET szMode5 , OFFSET szMode6 szMode1 db "320x240",0 szMode2 db "640x480",0 szMode3 db "800x600",0 szMode4 db "1024x768",0 szMode5 db "1280x1024",0 szMode6 db "1600x1200",0Массив WinModeTable содержит размеры шести определенных нами окошек. Он также нужен нам для сравнения с текущими размерами рабочего стола. Массив WinModeStrTable содержит указатели на текстовые строки. Эти строки впоследствии будут занесены в ComboBox оконных режимов.
Код (Text):
FullModeTable dd 50 dup (?) ; Для хранения доступных полноэкранных режимов FullModeBPPTable db 50 dup (?) ; Для хранения форматов поверхности. FullModeHerzTable db 50 dup (?) ; Для хранения частоты в Герцах. FullModeStrBuffer db 16 dup (?) ; Буфер для формирования строкиВ массиве FullModeTable число 50 является количеством всех полноэкранных режимов, которые мы можем поместить. Я, на всякий случай, сделал его с запасом. Вообще, когда мы спрашиваем Direct3D о количестве доступных режимов он возвращает нам огромное их число, куда входят режимы с одинаковым разрешением, но с разным числом герц и форматом поверхности. Например, на моей видюхе он выдает 90 доступных режимов. Чтобы не возиться с разной частотой я решил определять только те режимы, в которых число Герц максимально. В результате мы ничего не теряем, т.к. при выборе настроек включать частоту в 60, 70 и т.п. Герц все равно никто не будет. Взяв на вооружение этот факт, мы сокращаем число режимов до двух - трех десятков (Примечание: В Win98 Direct3D выдает вместо числа Герц ноль, поэтому, если занести в структуру D3DPRESENT_PARAMETERS определенную частоту, а не ноль, то создать устройство не удастся). В массиве FullModeStrBuffer мы будем формировать строку вида: 1024х768х32 и затем заносить ее в ComboBox.
Invoke DialogBoxParam,400000h,ADDR szDialogName,NULL,OFFSET SetupDialog,NULL
Наш диалог из ресурсов вызываем вот такой строкой где 400000h - это hInstance.
Код (Text):
wmInitDialog: ;-------------- pushad invoke CheckRadioButton, hDlg , 20 , 21 , 20 invoke GetDlgItem, hDlg , 23 mov cmbWindow, eax invoke GetDlgItem, hDlg , 22 mov cmbFullScreen, eax invoke GetDlgItem, hDlg , 24 mov cmbHerz, eaxНу вот добрались до самого главного: определения доступных режимов. Вначале, устанавливаем отметку на RadioButton "В окошке" и снимаем с "Полноэкранное". Затем получаем ID всех наших ComboBox'ов. ( Примечание: При выходе из обработки сообщения INIT_DIALOG, когда мы уже возвращаем EAX=1 и передаем управление системе, в Win98 происходила ошибка. На мой взгляд, причина кроется в каких - то регистрах, которые мы изменили в процессе работы. Используя PUSHAD при входе и POPAD при выходе, этот баг можно ликвидировать. Что касается XP, то ему, похоже, до лампочки есть PUSHAD, POPAD или нет. )
Код (Text):
invoke Direct3DCreate8, D3D_SDK_VERSION mov pd3d, eax d3d8 GetAdapterDisplayMode, pd3d, D3DADAPTER_DEFAULT, ADDR d3dpp lea esi, d3dpp.BackBufferWidth mov eax, [esi] add eax, [esi+4] mov ebx, [esi+12] mov [esi+8], ebx mov DWORD PTR [esi+12], 3 xor ebx, ebx ; Проверка доступных оконных режимов и заполнение списка getNextMode: push ebx push eax invoke SendMessage,cmbWindow,CB_ADDSTRING,0,[WinModeStrTable + ebx*4] pop eax pop ebx movzx ecx, [WinModeTable+ebx*4] movzx edx, [WinModeTable+ebx*4+2] add ecx, edx inc ebx cmp eax, ecx ja getNextModeСоздаем Direct3D, получаем текущие параметры рабочего стола посредством GetAdapterDisplayMode. Так как мы помещаем данные в D3DPRESENT_PARAMETERS, нам придется провести небольшую манипуляцию, занося формат поверхности с четвертого поля структуры на третье. В четвертое поле заносим число BackBuffer равное трем. Сделав это, часть D3DPRESENT_PARAMETERS мы уже сформировали. Далее идет цикл на сравнение суммы текущих ширины и высоты с суммой размеров записанных нами в WinModeTable. Одновременно с этим заполняем ComboBox.
Справка
Код (Text):
GetAdapterDisplayMode передается два параметра. 1. Номер адаптера. D3DADAPTER_DEFAULT - адаптер по умолчанию. 2. Адрес структуры D3DDISPLAYMODE. ( См. предыдущие уроки ) Метод возвращает D3D_OK и заполняет структуру данными если все прошло успешно. Если нет, то D3DERR_INVALIDCALL - недействительный вызов. d3d8 GetAdapterModeCount, pd3d, D3DADAPTER_DEFAULT dec eaxДалее получаем количество всех доступных полноэкранных режимов и уменьшаем его на единицу, т.к. нумерация начинается с нуля.
Справка
Код (Text):
GetAdapterModeCount передается только номер адаптера. Метод возвращает кол-во всех доступных режимов если все прошло успешно. Если нет, то ноль. lea esi, FullModeTable xor ebx, ebx mov edx, ebx mov ecx, eax getMode: push ecx push esi push ebx push edx d3d8 EnumAdapterModes, pd3d, D3DADAPTER_DEFAULT, ecx, ADDR d3ddm mov eax, d3ddm.Width1 mov ecx, d3ddm.Height mov edi, eax add edi, ecx add edi, d3ddm.Format pop edx pop ebx pop esi cmp edx, edi je noAdd mov edx, d3ddm.RefreshRate mov BYTE PTR [FullModeHerzTable+ebx], dl mov edx, d3ddm.Format mov [FullModeBPPTable+ebx], dl push edx push edi pop edx pop edi mov [esi+ebx*4], eax mov [esi+ebx*4+2], ecx inc ebxС загрузки адреса массива начинается цикл сравнения и заполнения его доступными полноэкранными режимами. Каждый раз, уменьшая номер режима и вызывая EnumAdapterModes, мы спрашиваем данные у Direct3D. Затем берем полученные ширину и высоту, складываем их и прибавляем к этой сумме формат поверхности. Сравниваем с предыдущим значением суммы и, если таковой у нас нет ( это значит новый режим ), заносим всю информацию по разным массивам, и начинаем формировать строку в буфере, чтобы ее можно было занести в Combobox. Соответственно, если эта сумма есть, то ничего заполнять нам не нужно.
Справка
Код (Text):
EnumAdapterModes передается три параметра. 1. Номер адаптера. 2. Номер режима. От 0 и выше. 3. Адрес структуры D3DDISPLAYMODE Метод возвращает D3D_OK и заполняет структуру данными если все прошло успешно. Если нет, то D3DERR_INVALIDCALL - недействительный вызов. push ebx push esi push edx ; Формирование строки и добавление ее в Combobox invoke DW2A, eax, ADDR FullModeStrBuffer mov BYTE PTR [ebx], 'x' inc ebx invoke DW2A, ecx, ebx mov BYTE PTR [ebx], 'x' inc ebx mov ecx, 16 cmp edi, D3DFMT_X8R8G8B8 jne nextFormat mov ecx, 32 nextFormat: cmp edi, D3DFMT_A8R8G8B8 jne nextFormat2 mov ecx, 32 nextFormat2: cmp edi, D3DFMT_R8G8B8 jne exitCompare mov ecx, 24 exitCompare: invoke DW2A, ecx , ebx mov BYTE PTR [ebx], 0 invoke SendMessage, cmbFullScreen , CB_ADDSTRING , 0 , ADDR FullModeStrBuffer pop edx pop esi pop ebx noAdd: pop ecx dec ecx test ecx, ecx jne getMode ; В итоге в ComboBox занесены строки доступных нам полноэкранных режимов ; с разными форматами поверхности и самым высоким числом Герц для каждого режимаЗдесь у нас идет формирование строки. Функцией DW2A преобразуем значения в текст, добавляем иксы. Проверяем ID формата и то что получилось добавляем в качестве окончания строки. Теперь нам только осталось занести эту строку туда куда нужно и все. Это мы и делаем, посылая сообщение СomboBox. А далее снова на начало цикла. На первый взгляд все отлично, но есть здесь одно маленькое но - проверка на формат. Осуществляя сравнение с 24 и 32 битным форматом мы считаем все другие форматы 16-битными. Как мне кажется, это не столь важно. Сколько я ни запускал программу на разных видюхах, будь то ATI или NVIDIA они мне выдавали только 2 формата: D3DFMT_A8R8G8B8 и D3DFMT_R5G6B5 ( вроде эти ). Глубже лезть и выяснять, почему такое есть, мне неохота.
Код (Text):
invoke SendMessage, cmbWindow , CB_SETCURSEL , 0 , 0 invoke SendMessage, cmbFullScreen , CB_SETCURSEL , 0 , 0 invoke SendMessage, hDlg, WM_COMMAND , 22 + CBN_SELCHANGE shl 16, 0Последними командами устанавливаем выбранными первые строки в каждом из ComboBox и обработка WM_INITDIALOG завершена. Но впереди не менее увлекательное занятие: сделать так, чтобы все работало как единое целое. Приступаем к рассмотрению обработки события WM_COMMAND. В нем мы проверяем ID всех элементов и, в зависимости от результата, переходим на нужную метку.
Код (Text):
cmRadioFull: inc ebx cmRadioWin: invoke EnableWindow, cmbFullScreen , ebx invoke EnableWindow, cmbHerz , ebx xor ebx, 1 invoke EnableWindow, cmbWindow , ebx xor eax, eax retЗдесь у нас идет включение одних ComboBox и выключение других. Срабатывает это дело только, когда тыкнуть мышкой на одну из RadioButton.
Код (Text):
cmHerz: invoke SendMessage, cmbHerz , CB_RESETCONTENT , 0 , 0 invoke SendMessage, cmbFullScreen , CB_GETCURSEL , 0 , 0 movzx ebx, BYTE PTR [FullModeHerzTable+eax] invoke DW2A, ebx , ADDR FullModeStrBuffer invoke SendMessage, cmbHerz , CB_ADDSTRING , 0 , ADDR FullModeStrBuffer invoke SendMessage, cmbHerz , CB_SETCURSEL , 0 , 0 xor eax, eax retА это срабатывает, когда нам нужно обновить содержимое ComboBox, содержащего информацию о частоте. Мы все стираем, берем номер выбранного элемента в ComboBox, содержащего доступные полноэкранные режимы, и по нему получаем из массива частоту. Преобразуем ее к удобному варианту и заносим в ComboBox.
Код (Text):
cmSaveData: invoke IsDlgButtonChecked, hDlg , 20 test eax, eax je EnableFull invoke SendMessage, cmbWindow , CB_GETCURSEL , 0 , 0 lea esi, WinModeTable shl eax, 2 add esi, eax xor ecx, ecx mov edx, ecx push [esi] pop cx pop dx mov d3dpp.BackBufferWidth, ecx mov d3dpp.BackBufferHeight, edx mov d3dpp.Windowed, 1 mov WinStyle, WS_SYSMENU jmp Create EnableFull: invoke SendMessage, cmbFullScreen , CB_GETCURSEL , 0 , 0 movzx ebx, BYTE PTR [FullModeHerzTable+eax] mov d3dpp.FullScreen_RefreshRateInHz, ebx movzx ebx, BYTE PTR [FullModeBPPTable+eax] mov d3dpp.BackBufferFormat, ebx lea esi, FullModeTable shl eax, 2 add esi, eax push [esi] xor ecx, ecx mov edx, ecx mov d3dpp.Windowed, ecx pop cx pop dx mov d3dpp.BackBufferWidth, ecx mov d3dpp.BackBufferHeight, edx mov WinStyle, WS_POPUP Create: mov d3dpp.SwapEffect, D3DSWAPEFFECT_FLIP invoke CreateFile , ADDR szFileName , GENERIC_WRITE, NULL, \ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL mov hfile, eax invoke WriteFile , hfile , ADDR d3dpp , 56 , ADDR brr , NULL invoke CloseHandle , hfile xor eax, eax retОкончательное формирование D3DPRESENT_PARAMETERS, плюс сохранение всех настроек в файле. Поздравляю 90% работы позади. Сюда мы попадаем, если пользователь нажал кнопку "Сохранить". Ну значит проверяем какая RadioButton выделена и идем по нужному пути. Если "В окошке", то поле Windowed равно 1 и стиль окна WS_SYSMENU. Если "Полноэкранное", то берем номер выбранной строки в ComboBox полноэкранных режимов и уже от этого пляшем. Хватаем из массивов размеры, формат поверхности, частоту и в структуру все укладываем. Поле Windowed равно нулю, а стиль окна будет WS_POPUP. Окончательный штришок в виде SwapEffect и можно все записывать в файл.
Если мы хотим чтобы из диалога вызывалась еще и программа, то нужно написать следующее.
Код (Text):
cmStartProgram: invoke EndDialog, hDlg , 0 d3d8 Release, pd3d invoke GetModuleFileName , 0 , ADDR AppPath , 260 lea edi,AppPath lea edi,[edi+eax-6] @@: mov al,[edi] dec edi cmp al,'\' jne @B inc edi inc edi cld lea esi, szAppName mov ecx, SIZEOF szAppName shr ecx, 2 rep movsd mov ecx, [SIZEOF szAppName ] and ecx, 3 rep movsb invoke ShellExecute , NULL , ADDR szOperation , ADDR AppPath , \ NULL , NULL , SW_SHOWNORMAL xor eax, eax retЗакрываем диалог. Освобождаем все связанное с Direct3D. Затем получаем путь по которому лежит наш Setup посредством GetModuleFileName. На всякий случай размер буфера под путь равен 260 байт ( если не ошибаюсь это максимальный размер пути в окошках ). Вот мы получили путь, но в нем присутствует и имя нашего Setup. Как его убрать ? Делаем цикл для проверки на '\' ,начиная от конца пути. Найдя его, копируем на место следующего за ним имя файла, который хотим запустить. А дальше все просто. ShellExecute с командой open в качестве параметра, и приложение запущено по нашему приказу, а работа самого Setup завершена.
Осталось только написать текст самого приложения. В нем реализовать загрузку сохраненных настроек и на их основе запускать Direct3D. Описывать здесь я это не буду, лучше гляньте исходник.
Ну все, пора закругляться, а то и так много понаписал, да и чего - то под конец статьи совсем туго соображать стал.
PS: В следующей статье ждите полноценное трехмерное приложение с матрицами, векторами, полигонами и прочей математикой.
Да и не забудьте глянуть в папку Include там лежат файлы d3d.inc и d3dcaps.inc окончательной версии.
Исходник обоих вариантов прилагается (1, 2).
Все вопросы мыльте сюда mybox@aib.ru © keYMax
DirectX 8.1 в MASM32: Урок 3
Дата публикации 26 мар 2003