DirectX 8.1 в MASM32: Урок 3 — Архив WASM.RU

Все статьи

DirectX 8.1 в MASM32: Урок 3 — Архив WASM.RU

Урок 3. Создаем диалоговое окно для определения всех доступных режимов.

Как определить все доступные полноэкранные режимы и при этом сделать так, чтобы пользователь смог выбирать в каком режиме работать приложению ? В этом нам поможет диалоговое окно.

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

Готовы ? Тогда вперед !

Диалоговое окно в нашем приложении можно реализовать двумя путями. Первый путь - это встроить его в само приложение. Второй путь - это реализовать его как самостоятельное приложение. Что выбрать ? Оба варианта имеют свои плюсы и минусы.

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

Реализация второго варианта несколько сложнее. Нужно создавать два экзешника. Один для диалогового окна, другой - собственно для приложения. При этом все настройки, произведенные пользователем, придется записывать в файл. Но зато появляется возможность переделывать Setup снова и снова, не трогая при этом приложение, ведь всё равно все настройки сохраняются в файле.

В итоге каждый вариант подходит для определенных задач. Например, для какой - нибудь демки встроенное диалоговое окно самое то. А вот для игрушки - наоборот. Можно также придумать какой нибудь гибрид. Я долго размышлял какой вариант использовать для урока и в конце концов решил сделать оба, чтобы у вас была возможность сравнить. Это потребовало довольно много времени, и как следствие, урок был готов гораздо позже, чем было запланировано. Мы с вами рассмотрим только второй вариант, так как он немного сложнее.

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

Начнем с файла ресурсов. Берем любой редактор, пара минут и все готово. Кратко рассмотрим по частям, как он должен выглядеть.

#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, так мы сможем узнать с каким элементом в данный момент работает пользователь.

SetupDialog  DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 175, 91
             STYLE DS_MODALFRAME | DS_CENTER
             EXSTYLE WS_EX_TOOLWINDOW
             CAPTION " Настройка параметров запуска"
             FONT 8, "Verdana"

Здесь имя диалога, его вид и размер, параметры вывода на экран, текст заголовка и используемый шрифт.

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:

 .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. Интересующиеся всегда смогут посмотреть ее в исходнике.

Теперь все разберем по косточкам.

  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 оконных режимов.

  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.

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 или нет. )

  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.

Справка

 GetAdapterDisplayMode передается два параметра.

 1. Номер адаптера. D3DADAPTER_DEFAULT - адаптер по умолчанию.
 2. Адрес структуры D3DDISPLAYMODE. ( См. предыдущие уроки )

 Метод возвращает D3D_OK и заполняет структуру данными если все прошло успешно. 
 Если нет, то D3DERR_INVALIDCALL - недействительный вызов.

  d3d8    GetAdapterModeCount, pd3d, D3DADAPTER_DEFAULT
  dec     eax

Далее получаем количество всех доступных полноэкранных режимов и уменьшаем его на единицу, т.к. нумерация начинается с нуля.

Справка

 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. Соответственно, если эта сумма есть, то ничего заполнять нам не нужно.

Справка

 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 ( вроде эти :) ). Глубже лезть и выяснять, почему такое есть, мне неохота.

  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 всех элементов и, в зависимости от результата, переходим на нужную метку.

    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.

    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.

    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 и можно все записывать в файл.

Если мы хотим чтобы из диалога вызывалась еще и программа, то нужно написать следующее.

    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

2002-2013 (c) wasm.ru