DirectX 8.1 в MASM32: Урок 3

Дата публикации 26 мар 2003

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

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

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

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

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

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

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

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

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

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

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

Код (Text):
  1.  
  2. #define DS_CENTER            0x00000800L
  3. #define DS_MODALFRAME        0x00000080L
  4. #define WS_EX_TOOLWINDOW     0x00000080L
  5. #define WS_GROUP             0x00020000L
  6. #define WS_EX_STATICEDGE     0x00020000L
  7. #define BS_AUTORADIOBUTTON   0x00000009L
  8. #define BS_FLAT              0x00008000L
  9. #define CBS_DROPDOWNLIST     0x00000003L
  10. #define WS_VSCROLL           0x00200000L
  11. #define WS_TABSTOP           0x00010000L
  12. #define WS_DISABLED          0x08000000L
  13.  
  14. #define ID_OK                10
  15. #define ID_CANCEL            11
  16. #define ID_SAVE              12
  17.  
  18. #define IDC_STATIC           -1
  19.  
  20. #define IDC_WINDOWED         20
  21. #define IDC_FULLSCREEN       21
  22. #define IDC_SCREENMODESFS    22
  23. #define IDC_SCREENMODESW     23
  24. #define IDC_HERZ             24

Здесь у нас константы, в частности, номера от 10 до 24 - это идентификаторы элементов нашего диалогового окна. Они будут нам передаваться в сообщениях от Windows, так мы сможем узнать с каким элементом в данный момент работает пользователь.

Код (Text):
  1.  
  2. SetupDialog  DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 175, 91
  3.              STYLE DS_MODALFRAME | DS_CENTER
  4.              EXSTYLE WS_EX_TOOLWINDOW
  5.              CAPTION " Настройка параметров запуска"
  6.              FONT 8, "Verdana"

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

Код (Text):
  1.  
  2. BEGIN
  3. CONTROL "В окошке",IDC_WINDOWED,"Button",BS_AUTORADIOBUTTON | BS_FLAT | WS_TABSTOP ,12,14,80,14
  4.  
  5. COMBOBOX IDC_SCREENMODESW,92,14,71,50,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
  6.  
  7. CONTROL "Полноэкранное",IDC_FULLSCREEN,"Button",BS_AUTORADIOBUTTON | BS_FLAT | WS_TABSTOP,12,30,80,12
  8.  
  9. COMBOBOX IDC_SCREENMODESFS,12,43,71,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_DISABLED | WS_TABSTOP
  10.  
  11. LTEXT "Частота в герцах",IDC_STATIC,94,33,65,10
  12.  
  13. COMBOBOX  IDC_HERZ, 92, 43, 71, 80, CBS_DROPDOWNLIST | WS_VSCROLL | WS_DISABLED | WS_TABSTOP
  14.  
  15. GROUPBOX  "",  IDC_STATIC, 6,3,163,61
  16.  
  17. DEFPUSHBUTTON "Запуск", ID_OK,118,69,51,17,0,WS_EX_STATICEDGE
  18. PUSHBUTTON  "Выход", ID_CANCEL,62,69,51,17,0,WS_EX_STATICEDGE
  19. PUSHBUTTON  "Сохранить", ID_SAVE,6,69,51,17,0,WS_EX_STATICEDGE
  20.  
  21. END

Между строками BEGIN и END располагаются строки, описывающие элементы расположенные на диалоговом окне. Здесь определены два RadioButton, три ComboBox, одно текстовое поле, одна рамка и три простых кнопки.

А вот и исходный текст Setup.exe:

Код (Text):
  1.  
  2.  .686
  3.  .MMX
  4.  .XMM  
  5.  .MODEL FLAT, STDCALL  
  6.   OPTION CASEMAP:none  
  7.                    
  8.  INCLUDE  \masm32\include\windows.inc        
  9.  INCLUDE  \masm32\include\kernel32.inc      
  10.  INCLUDE  \masm32\include\user32.inc   
  11.  INCLUDE  \masm32\include\gdi32.inc    
  12.  INCLUDE  \masm32\include\shell32.inc      
  13.  INCLUDE  \masm32\DirectX81\d3d8.inc       
  14.  
  15.  INCLUDELIB  \masm32\lib\kernel32.lib
  16.  INCLUDELIB  \masm32\lib\user32.lib
  17.  INCLUDELIB  \masm32\lib\gdi32.lib
  18.  INCLUDELIB  \masm32\lib\shell32.lib    
  19.  INCLUDELIB  \masm32\directx81\d3d8.lib    
  20.  
  21.  SetupDialog  PROTO :DWORD,:DWORD,:DWORD,:DWORD
  22.  DW2A         PROTO :DWORD, :DWORD
  23.        
  24.  .DATA ;------------------------------------------
  25.  
  26.   szDialogName  db  "SetupDialog",0
  27.   szAppName     db  "Tutorial03.exe",0
  28.   szFileName    db  "Settings.ini",0
  29.   szOperation   db  "open",0
  30.    
  31.   WinModeTable    dw  320,240, 640,480, 800,600, 1024,768, 1280,1024, 1600,1200    
  32.   WinModeStrTable   dd  OFFSET szMode1 , OFFSET szMode2
  33.                   dd  OFFSET szMode3 , OFFSET szMode4
  34.             dd  OFFSET szMode5 , OFFSET szMode6
  35.  
  36.   szMode1         db  "320x240",0
  37.   szMode2         db  "640x480",0  
  38.   szMode3         db  "800x600",0
  39.   szMode4         db  "1024x768",0
  40.   szMode5         db  "1280x1024",0
  41.   szMode6         db  "1600x1200",0            
  42.    
  43.  .DATA? ;-----------------------------------------
  44.    
  45.   pd3d     dd  ?                      ; Указатель на Direct3D               
  46.   d3ddm    D3DDISPLAYMODE <?>         ; Для получения информации о доступных режимах
  47.   d3dpp    D3DPRESENT_PARAMETERS <?>  ; Структура для создания Direct3DDevice8
  48.   WinStyle dd  ?               
  49.  
  50.   CmbWindow      dd  ?                ; ID ComboBox оконных режимов
  51.   cmbFullScreen  dd  ?                ; ID ComboBox полноэкранных режимов
  52.   cmbHerz        dd  ?                ; ID ComboBox герц
  53.        
  54.   FullModeTable     dd  50 dup (?)    ; Для хранения доступных полноэкранных режимов
  55.   FullModeBPPTable  db  50 dup (?)    ; Для хранения форматов поверхности.
  56.   FullModeHerzTable db  50 dup (?)    ; Для хранения частоты в Герцах.
  57.   FullModeStrBuffer db  16 dup (?)    ; Буфер для формирования строки
  58.  
  59.   AppPath   db  260 dup (?)           ; Буфер для пути к Setup.exe
  60.   hfile     dd  ?
  61.   brr       dd  ?
  62.        
  63.  .CODE ;------------------------------------------
  64.  
  65.  Start:                                            
  66.    
  67.   Invoke  DialogBoxParam,400000h,ADDR szDialogName,NULL,OFFSET SetupDialog,NULL
  68.   Invoke  ExitProcess,0
  69.  
  70.   Ret
  71.  
  72.  SetupDialog proc hDlg:HWND , iMsg:DWORD , wParam:WPARAM , lParam:LPARAM
  73.  ;----------------------------------------------------------------------
  74.   mov  eax, iMsg  
  75.  
  76.   cmp  eax, WM_INITDIALOG  ; Обрабатывая это сообщ. надо ОБЯЗАТЕЛЬНО вернуть EAX=1
  77.   je   wmInitDialog        
  78.  
  79.   cmp  eax, WM_COMMAND
  80.   je   wmCommandDialog
  81.          
  82.   xor  eax, eax
  83.   ret
  84.    
  85.  wmInitDialog:
  86.  ;--------------
  87.  
  88.   pushad
  89.  
  90.   invoke  CheckRadioButton, hDlg , 20 , 21 , 20  
  91.   invoke  GetDlgItem, hDlg , 23                
  92.   mov     cmbWindow,  eax
  93.   invoke  GetDlgItem, hDlg , 22
  94.   mov     cmbFullScreen,  eax
  95.   invoke  GetDlgItem, hDlg , 24
  96.   mov     cmbHerz,  eax  
  97.    
  98.   invoke  Direct3DCreate8, D3D_SDK_VERSION 
  99.   mov     pd3d, eax
  100.  
  101.   d3d8    GetAdapterDisplayMode, pd3d, D3DADAPTER_DEFAULT, ADDR d3dpp
  102.    
  103.   lea     esi, d3dpp.BackBufferWidth
  104.   mov     eax, [esi]           
  105.   add     eax, [esi+4]             
  106.   mov     ebx, [esi+12]
  107.   mov     [esi+8], ebx
  108.   mov     DWORD PTR [esi+12], 3
  109.   xor     ebx, ebx  
  110.  
  111.  ; Проверка доступных оконных режимов и заполнение списка
  112.  
  113.   getNextMode:
  114.   push    ebx
  115.   push    eax
  116.   invoke  SendMessage,cmbWindow,CB_ADDSTRING,0,[WinModeStrTable + ebx*4]
  117.   pop     eax
  118.   pop     ebx
  119.   movzx   ecx, [WinModeTable+ebx*4]
  120.   movzx   edx, [WinModeTable+ebx*4+2]
  121.   add     ecx, edx  
  122.   inc     ebx
  123.   cmp     eax, ecx
  124.   ja      getNextMode              
  125.    
  126.  ; В итоге в ComboBox занесены строки доступных нам оконных режимов
  127.  ; от минимальных до текущих размеров экрана включительно
  128.    
  129.  ; Проверка доступных полноэкранных режимов и заполнение списка                  
  130.   d3d8    GetAdapterModeCount, pd3d, D3DADAPTER_DEFAULT
  131.   dec     eax
  132.  
  133.   lea     esi, FullModeTable
  134.   xor       ebx, ebx  
  135.   mov       edx, ebx
  136.   mov       ecx, eax        
  137.    
  138.   getMode:     
  139.   push    ecx
  140.    
  141.   push    esi
  142.   push    ebx
  143.   push    edx
  144.    
  145.   d3d8    EnumAdapterModes, pd3d, D3DADAPTER_DEFAULT, ecx, ADDR d3ddm
  146.   mov     eax, d3ddm.Width1                        
  147.   mov     ecx, d3ddm.Height
  148.   mov     edi, eax
  149.   add     edi, ecx
  150.   add     edi, d3ddm.Format
  151.    
  152.   pop     edx
  153.   pop     ebx
  154.   pop     esi  
  155.    
  156.   cmp     edx, edi
  157.   je      noAdd  
  158.   mov     edx, d3ddm.RefreshRate
  159.   mov     BYTE PTR [FullModeHerzTable+ebx], dl
  160.   mov     edx, d3ddm.Format
  161.   mov     [FullModeBPPTable+ebx], dl
  162.   push    edx
  163.   push    edi
  164.   pop       edx
  165.   pop       edi
  166.    
  167.   mov       [esi+ebx*4], eax
  168.   mov       [esi+ebx*4+2], ecx
  169.   inc       ebx  
  170.    
  171.   push    ebx      
  172.   push    esi
  173.   push    edx
  174.    
  175.  ; Формирование строки и добавление ее в Combobox
  176.  
  177.   invoke  DW2A, eax, ADDR FullModeStrBuffer
  178.   mov     BYTE PTR [ebx], 'x'
  179.   inc       ebx
  180.   invoke  DW2A, ecx, ebx
  181.   mov     BYTE PTR [ebx], 'x'
  182.   inc     ebx
  183.   mov     ecx, 16
  184.  
  185.   cmp     edi, D3DFMT_X8R8G8B8
  186.   jne     nextFormat
  187.   mov     ecx, 32
  188.  
  189.   nextFormat:  
  190.   cmp     edi, D3DFMT_A8R8G8B8
  191.   jne     nextFormat2
  192.   mov     ecx, 32
  193.  
  194.   nextFormat2:
  195.   cmp     edi, D3DFMT_R8G8B8
  196.   jne     exitCompare      
  197.   mov     ecx, 24
  198.  
  199.   exitCompare:
  200.   invoke  DW2A, ecx , ebx
  201.   mov     BYTE PTR [ebx], 0        
  202.   invoke  SendMessage, cmbFullScreen , CB_ADDSTRING , 0 , ADDR FullModeStrBuffer   
  203.        
  204.   pop     edx
  205.   pop     esi
  206.   pop     ebx  
  207.      
  208.   noAdd:    
  209.   pop     ecx
  210.   dec     ecx
  211.   test    ecx, ecx
  212.   jne     getMode
  213.    
  214.  ; В итоге в ComboBox занесены строки доступных нам полноэкранных режимов
  215.  ; с разными форматами поверхности и самым высоким числом Герц для каждого режима
  216.                  
  217.   invoke  SendMessage, cmbWindow , CB_SETCURSEL , 0 , 0
  218.   invoke  SendMessage, cmbFullScreen , CB_SETCURSEL , 0 , 0
  219.   invoke  SendMessage, hDlg, WM_COMMAND , 22 + CBN_SELCHANGE shl 16, 0
  220.   popad
  221.  
  222.   xor     eax, eax
  223.   inc     eax
  224.   ret    
  225.  
  226.   wmCommandDialog:
  227.  ;----------------
  228.   movzx  eax, WORD PTR wParam ; Здесь нам нужно получить HIWORD и LOWORD из wParam
  229.   mov    ecx, wParam          ; HIWORD - код уведомления от элемента диалога
  230.                               ; LOWORD - ID элемента диалога
  231.   xor    ebx, ebx
  232.  
  233.   cmp    eax, 10              ; Получаем если нажали кнопку Запуск
  234.   je   cmStartProgram
  235.  
  236.   cmp    eax, 12              ; Получаем если нажали кнопку Сохранить
  237.   je   cmSaveData
  238.    
  239.   cmp    eax, 11              ; Получаем если нажали кнопку Закрыть
  240.   je   cmCloseProgram
  241.  
  242.   cmp      eax, 20              ; Получаем если нажали RadioButton "В окошке"
  243.   je   cmRadioWin
  244.    
  245.   cmp    eax, 21              ; Получаем если нажали RadioButton "Полноэкранное"
  246.   je   cmRadioFull
  247.    
  248.   cmp    ecx, 22 + CBN_SELCHANGE shl 16
  249.   je   cmHerz              
  250.  
  251.   xor    eax, eax
  252.   ret  
  253.                    
  254.     cmRadioFull:
  255.  
  256.     inc     ebx
  257.    
  258.     cmRadioWin:
  259.  
  260.     invoke  EnableWindow, cmbFullScreen , ebx      
  261.     invoke  EnableWindow, cmbHerz , ebx    
  262.     xor     ebx, 1
  263.     invoke  EnableWindow, cmbWindow , ebx  
  264.  
  265.     xor     eax, eax
  266.     ret      
  267.            
  268.     cmHerz:
  269.  
  270.     invoke  SendMessage, cmbHerz , CB_RESETCONTENT , 0 , 0              
  271.     invoke  SendMessage, cmbFullScreen , CB_GETCURSEL , 0 , 0
  272.     movzx   ebx, BYTE PTR [FullModeHerzTable+eax]
  273.     invoke  DW2A, ebx , ADDR FullModeStrBuffer
  274.     invoke  SendMessage, cmbHerz , CB_ADDSTRING , 0 , ADDR FullModeStrBuffer
  275.     invoke  SendMessage, cmbHerz , CB_SETCURSEL , 0 , 0
  276.  
  277.     xor     eax, eax
  278.     ret
  279.            
  280.     cmSaveData:
  281.  
  282.     invoke  IsDlgButtonChecked, hDlg , 20
  283.     test    eax, eax           
  284.     je      EnableFull                  
  285.    
  286.     invoke  SendMessage, cmbWindow , CB_GETCURSEL , 0 , 0
  287.     lea     esi, WinModeTable
  288.     shl     eax, 2
  289.     add     esi, eax       
  290.     xor     ecx, ecx
  291.     mov     edx, ecx
  292.     push    [esi]          
  293.     pop     cx                         
  294.     pop     dx     
  295.     mov     d3dpp.BackBufferWidth, ecx
  296.     mov     d3dpp.BackBufferHeight, edx    
  297.     mov     d3dpp.Windowed, 1
  298.     mov     WinStyle, WS_SYSMENU
  299.     jmp     Create
  300.  
  301.     EnableFull:
  302.  
  303.     invoke  SendMessage, cmbFullScreen , CB_GETCURSEL , 0 , 0
  304.     movzx   ebx, BYTE PTR [FullModeHerzTable+eax]
  305.     mov     d3dpp.FullScreen_RefreshRateInHz, ebx
  306.     movzx   ebx, BYTE PTR [FullModeBPPTable+eax]
  307.     mov     d3dpp.BackBufferFormat, ebx
  308.  
  309.     lea     esi, FullModeTable
  310.     shl     eax, 2
  311.     add     esi, eax
  312.     push    [esi]      
  313.     xor     ecx, ecx
  314.     mov     edx, ecx
  315.     mov     d3dpp.Windowed, ecx
  316.     pop     cx
  317.     pop     dx
  318.     mov     d3dpp.BackBufferWidth, ecx
  319.     mov     d3dpp.BackBufferHeight, edx
  320.     mov     WinStyle, WS_POPUP
  321.    
  322.     Create:
  323.  
  324.     mov     d3dpp.SwapEffect, D3DSWAPEFFECT_FLIP
  325.  
  326.     invoke  CreateFile , ADDR szFileName , GENERIC_WRITE, NULL, \
  327.             NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
  328.     mov     hfile, eax
  329.  
  330.     invoke  WriteFile , hfile , ADDR d3dpp , 56 , ADDR brr , NULL
  331.     invoke  CloseHandle , hfile  
  332.  
  333.     xor     eax, eax
  334.     ret
  335.        
  336.     cmStartProgram:  
  337.  
  338.     invoke  EndDialog, hDlg , 0        
  339.     d3d8    Release, pd3d
  340.  
  341.     invoke  GetModuleFileName , 0 , ADDR AppPath , 260
  342.  
  343.     lea     edi,AppPath
  344.     lea     edi,[edi+eax-6]              
  345.     @@:
  346.     mov     al,[edi]                    
  347.     dec     edi                          
  348.     cmp     al,'\'                      
  349.     jne     @B
  350.     inc     edi
  351.     inc     edi
  352.     cld
  353.     lea     esi, szAppName
  354.     mov     ecx, SIZEOF szAppName
  355.     shr     ecx, 2
  356.     rep     movsd
  357.     mov     ecx, [SIZEOF szAppName ]
  358.     and     ecx, 3
  359.     rep     movsb
  360.  
  361.     invoke  ShellExecute , NULL , ADDR szOperation , ADDR AppPath , \
  362.             NULL , NULL , SW_SHOWNORMAL
  363.     xor     eax, eax
  364.     ret
  365.                
  366.     cmCloseProgram:        
  367.  
  368.     invoke  EndDialog, hDlg , 0  
  369.     d3d8    Release, pd3d
  370.  
  371.     xor     eax, eax
  372.     ret
  373.      
  374.  SetupDialog endp

Еще после SetupDialog находится текст функции DW2A. Я не стал ее тут приводить, т.к. она является слегка измененной dw2a из библиотеки masm32. Интересующиеся всегда смогут посмотреть ее в исходнике.

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

Код (Text):
  1.  
  2.   WinModeTable    dw  320,240, 640,480, 800,600, 1024,768, 1280,1024, 1600,1200    
  3.   WinModeStrTable dd  OFFSET szMode1 , OFFSET szMode2
  4.                   dd  OFFSET szMode3 , OFFSET szMode4
  5.             dd  OFFSET szMode5 , OFFSET szMode6
  6.  
  7.   szMode1         db  "320x240",0
  8.   szMode2         db  "640x480",0  
  9.   szMode3         db  "800x600",0
  10.   szMode4         db  "1024x768",0
  11.   szMode5         db  "1280x1024",0
  12.   szMode6         db  "1600x1200",0

Массив WinModeTable содержит размеры шести определенных нами окошек. Он также нужен нам для сравнения с текущими размерами рабочего стола. Массив WinModeStrTable содержит указатели на текстовые строки. Эти строки впоследствии будут занесены в ComboBox оконных режимов.

Код (Text):
  1.  
  2.   FullModeTable     dd  50 dup (?)    ; Для хранения доступных полноэкранных режимов
  3.   FullModeBPPTable  db  50 dup (?)    ; Для хранения форматов поверхности.
  4.   FullModeHerzTable db  50 dup (?)    ; Для хранения частоты в Герцах.
  5.   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):
  1.  
  2. wmInitDialog:
  3.  ;--------------
  4.  
  5.   pushad
  6.   invoke  CheckRadioButton, hDlg , 20 , 21 , 20  
  7.   invoke  GetDlgItem, hDlg , 23                
  8.   mov     cmbWindow,  eax
  9.   invoke  GetDlgItem, hDlg , 22
  10.   mov     cmbFullScreen,  eax
  11.   invoke  GetDlgItem, hDlg , 24
  12.   mov     cmbHerz,  eax

Ну вот добрались до самого главного: определения доступных режимов. Вначале, устанавливаем отметку на RadioButton "В окошке" и снимаем с "Полноэкранное". Затем получаем ID всех наших ComboBox'ов. ( Примечание: При выходе из обработки сообщения INIT_DIALOG, когда мы уже возвращаем EAX=1 и передаем управление системе, в Win98 происходила ошибка. На мой взгляд, причина кроется в каких - то регистрах, которые мы изменили в процессе работы. Используя PUSHAD при входе и POPAD при выходе, этот баг можно ликвидировать. Что касается XP, то ему, похоже, до лампочки есть PUSHAD, POPAD или нет. )

Код (Text):
  1.  
  2.   invoke  Direct3DCreate8, D3D_SDK_VERSION 
  3.   mov     pd3d, eax
  4.  
  5.   d3d8    GetAdapterDisplayMode, pd3d, D3DADAPTER_DEFAULT, ADDR d3dpp
  6.    
  7.   lea     esi, d3dpp.BackBufferWidth
  8.   mov     eax, [esi]           
  9.   add     eax, [esi+4]             
  10.   mov     ebx, [esi+12]
  11.   mov     [esi+8], ebx
  12.   mov     DWORD PTR [esi+12], 3
  13.   xor     ebx, ebx  
  14.  
  15. ; Проверка доступных оконных режимов и заполнение списка
  16.  
  17.   getNextMode:
  18.   push    ebx
  19.   push    eax
  20.   invoke  SendMessage,cmbWindow,CB_ADDSTRING,0,[WinModeStrTable + ebx*4]
  21.   pop     eax
  22.   pop     ebx
  23.   movzx   ecx, [WinModeTable+ebx*4]
  24.   movzx   edx, [WinModeTable+ebx*4+2]
  25.   add     ecx, edx  
  26.   inc     ebx
  27.   cmp     eax, ecx
  28.   ja      getNextMode

Создаем Direct3D, получаем текущие параметры рабочего стола посредством GetAdapterDisplayMode. Так как мы помещаем данные в D3DPRESENT_PARAMETERS, нам придется провести небольшую манипуляцию, занося формат поверхности с четвертого поля структуры на третье. В четвертое поле заносим число BackBuffer равное трем. Сделав это, часть D3DPRESENT_PARAMETERS мы уже сформировали. Далее идет цикл на сравнение суммы текущих ширины и высоты с суммой размеров записанных нами в WinModeTable. Одновременно с этим заполняем ComboBox.

Справка

Код (Text):
  1.  
  2.  GetAdapterDisplayMode передается два параметра.
  3.  
  4.  1. Номер адаптера. D3DADAPTER_DEFAULT - адаптер по умолчанию.
  5.  2. Адрес структуры D3DDISPLAYMODE. ( См. предыдущие уроки )
  6.  
  7.  Метод возвращает D3D_OK и заполняет структуру данными если все прошло успешно.
  8.  Если нет, то D3DERR_INVALIDCALL - недействительный вызов.
  9.  
  10.   d3d8    GetAdapterModeCount, pd3d, D3DADAPTER_DEFAULT
  11.   dec     eax

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

Справка

Код (Text):
  1.  
  2.  GetAdapterModeCount передается только номер адаптера.
  3.  
  4.  Метод возвращает кол-во всех доступных режимов если все прошло успешно.  
  5.  Если нет, то ноль.
  6.  
  7.   lea     esi, FullModeTable
  8.   xor       ebx, ebx  
  9.   mov       edx, ebx
  10.   mov       ecx, eax        
  11.    
  12.   getMode:     
  13.   push    ecx
  14.    
  15.   push    esi
  16.   push    ebx
  17.   push    edx
  18.    
  19.   d3d8    EnumAdapterModes, pd3d, D3DADAPTER_DEFAULT, ecx, ADDR d3ddm
  20.   mov     eax, d3ddm.Width1                        
  21.   mov     ecx, d3ddm.Height
  22.   mov     edi, eax
  23.   add     edi, ecx
  24.   add     edi, d3ddm.Format
  25.    
  26.   pop     edx
  27.   pop     ebx
  28.   pop     esi  
  29.    
  30.   cmp     edx, edi
  31.   je      noAdd  
  32.   mov     edx, d3ddm.RefreshRate
  33.   mov     BYTE PTR [FullModeHerzTable+ebx], dl
  34.   mov     edx, d3ddm.Format
  35.   mov     [FullModeBPPTable+ebx], dl
  36.   push    edx
  37.   push    edi
  38.   pop       edx
  39.   pop       edi
  40.    
  41.   mov       [esi+ebx*4], eax
  42.   mov       [esi+ebx*4+2], ecx
  43.   inc       ebx

С загрузки адреса массива начинается цикл сравнения и заполнения его доступными полноэкранными режимами. Каждый раз, уменьшая номер режима и вызывая EnumAdapterModes, мы спрашиваем данные у Direct3D. Затем берем полученные ширину и высоту, складываем их и прибавляем к этой сумме формат поверхности. Сравниваем с предыдущим значением суммы и, если таковой у нас нет ( это значит новый режим ), заносим всю информацию по разным массивам, и начинаем формировать строку в буфере, чтобы ее можно было занести в Combobox. Соответственно, если эта сумма есть, то ничего заполнять нам не нужно.

Справка

Код (Text):
  1.  
  2.  EnumAdapterModes передается три параметра.
  3.  
  4.  1. Номер адаптера.
  5.  2. Номер режима. От 0 и выше.
  6.  3. Адрес структуры D3DDISPLAYMODE
  7.  
  8.  Метод возвращает D3D_OK и заполняет структуру данными если все прошло успешно.
  9.  Если нет, то D3DERR_INVALIDCALL - недействительный вызов.
  10.  
  11.   push    ebx      
  12.   push    esi
  13.   push    edx
  14.      
  15.  ; Формирование строки и добавление ее в Combobox
  16.  
  17.   invoke  DW2A, eax, ADDR FullModeStrBuffer
  18.   mov     BYTE PTR [ebx], 'x'
  19.   inc       ebx
  20.   invoke  DW2A, ecx, ebx
  21.   mov     BYTE PTR [ebx], 'x'
  22.   inc     ebx
  23.   mov     ecx, 16
  24.  
  25.   cmp     edi, D3DFMT_X8R8G8B8
  26.   jne     nextFormat
  27.   mov     ecx, 32
  28.  
  29.   nextFormat:  
  30.   cmp     edi, D3DFMT_A8R8G8B8
  31.   jne     nextFormat2
  32.   mov     ecx, 32
  33.  
  34.   nextFormat2:
  35.   cmp     edi, D3DFMT_R8G8B8
  36.   jne     exitCompare      
  37.   mov     ecx, 24
  38.  
  39.   exitCompare:
  40.   invoke  DW2A, ecx , ebx
  41.   mov     BYTE PTR [ebx], 0        
  42.   invoke  SendMessage, cmbFullScreen , CB_ADDSTRING , 0 , ADDR FullModeStrBuffer   
  43.        
  44.   pop     edx
  45.   pop     esi
  46.   pop     ebx  
  47.      
  48.   noAdd:    
  49.   pop     ecx
  50.   dec     ecx
  51.   test    ecx, ecx
  52.   jne     getMode
  53.    
  54.  ; В итоге в ComboBox занесены строки доступных нам полноэкранных режимов
  55.  ; с разными форматами поверхности и самым высоким числом Герц для каждого режима

Здесь у нас идет формирование строки. Функцией DW2A преобразуем значения в текст, добавляем иксы. Проверяем ID формата и то что получилось добавляем в качестве окончания строки. Теперь нам только осталось занести эту строку туда куда нужно и все. Это мы и делаем, посылая сообщение СomboBox. А далее снова на начало цикла. На первый взгляд все отлично, но есть здесь одно маленькое но - проверка на формат. Осуществляя сравнение с 24 и 32 битным форматом мы считаем все другие форматы 16-битными. Как мне кажется, это не столь важно. Сколько я ни запускал программу на разных видюхах, будь то ATI или NVIDIA они мне выдавали только 2 формата: D3DFMT_A8R8G8B8 и D3DFMT_R5G6B5 ( вроде эти :smile3: ). Глубже лезть и выяснять, почему такое есть, мне неохота.

Код (Text):
  1.  
  2.   invoke  SendMessage, cmbWindow , CB_SETCURSEL , 0 , 0
  3.   invoke  SendMessage, cmbFullScreen , CB_SETCURSEL , 0 , 0
  4.   invoke  SendMessage, hDlg, WM_COMMAND , 22 + CBN_SELCHANGE shl 16, 0

Последними командами устанавливаем выбранными первые строки в каждом из ComboBox и обработка WM_INITDIALOG завершена. Но впереди не менее увлекательное занятие: сделать так, чтобы все работало как единое целое. Приступаем к рассмотрению обработки события WM_COMMAND. В нем мы проверяем ID всех элементов и, в зависимости от результата, переходим на нужную метку.

Код (Text):
  1.  
  2.     cmRadioFull:
  3.  
  4.     inc     ebx
  5.    
  6.     cmRadioWin:
  7.  
  8.     invoke  EnableWindow, cmbFullScreen , ebx      
  9.     invoke  EnableWindow, cmbHerz , ebx    
  10.     xor     ebx, 1
  11.     invoke  EnableWindow, cmbWindow , ebx  
  12.  
  13.     xor     eax, eax
  14.     ret

Здесь у нас идет включение одних ComboBox и выключение других. Срабатывает это дело только, когда тыкнуть мышкой на одну из RadioButton.

Код (Text):
  1.  
  2.     cmHerz:
  3.  
  4.     invoke  SendMessage, cmbHerz , CB_RESETCONTENT , 0 , 0              
  5.     invoke  SendMessage, cmbFullScreen , CB_GETCURSEL , 0 , 0
  6.     movzx   ebx, BYTE PTR [FullModeHerzTable+eax]
  7.     invoke  DW2A, ebx , ADDR FullModeStrBuffer
  8.     invoke  SendMessage, cmbHerz , CB_ADDSTRING , 0 , ADDR FullModeStrBuffer
  9.     invoke  SendMessage, cmbHerz , CB_SETCURSEL , 0 , 0
  10.  
  11.     xor     eax, eax
  12.     ret

А это срабатывает, когда нам нужно обновить содержимое ComboBox, содержащего информацию о частоте. Мы все стираем, берем номер выбранного элемента в ComboBox, содержащего доступные полноэкранные режимы, и по нему получаем из массива частоту. Преобразуем ее к удобному варианту и заносим в ComboBox.

Код (Text):
  1.  
  2.     cmSaveData:
  3.  
  4.     invoke  IsDlgButtonChecked, hDlg , 20
  5.     test    eax, eax           
  6.     je      EnableFull                  
  7.    
  8.     invoke  SendMessage, cmbWindow , CB_GETCURSEL , 0 , 0
  9.     lea     esi, WinModeTable
  10.     shl     eax, 2
  11.     add     esi, eax       
  12.     xor     ecx, ecx
  13.     mov     edx, ecx
  14.     push    [esi]          
  15.     pop     cx                         
  16.     pop     dx     
  17.     mov     d3dpp.BackBufferWidth, ecx
  18.     mov     d3dpp.BackBufferHeight, edx    
  19.     mov     d3dpp.Windowed, 1
  20.     mov     WinStyle, WS_SYSMENU
  21.     jmp     Create
  22.  
  23.     EnableFull:
  24.  
  25.     invoke  SendMessage, cmbFullScreen , CB_GETCURSEL , 0 , 0
  26.     movzx   ebx, BYTE PTR [FullModeHerzTable+eax]
  27.     mov     d3dpp.FullScreen_RefreshRateInHz, ebx
  28.     movzx   ebx, BYTE PTR [FullModeBPPTable+eax]
  29.     mov     d3dpp.BackBufferFormat, ebx
  30.  
  31.     lea     esi, FullModeTable
  32.     shl     eax, 2
  33.     add     esi, eax
  34.     push    [esi]      
  35.     xor     ecx, ecx
  36.     mov     edx, ecx
  37.     mov     d3dpp.Windowed, ecx
  38.     pop     cx
  39.     pop     dx
  40.     mov     d3dpp.BackBufferWidth, ecx
  41.     mov     d3dpp.BackBufferHeight, edx
  42.     mov     WinStyle, WS_POPUP
  43.  
  44.     Create:
  45.  
  46.     mov     d3dpp.SwapEffect, D3DSWAPEFFECT_FLIP
  47.  
  48.     invoke  CreateFile , ADDR szFileName , GENERIC_WRITE, NULL, \
  49.             NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
  50.     mov     hfile, eax
  51.  
  52.     invoke  WriteFile , hfile , ADDR d3dpp , 56 , ADDR brr , NULL
  53.     invoke  CloseHandle , hfile  
  54.  
  55.     xor     eax, eax
  56.     ret

Окончательное формирование D3DPRESENT_PARAMETERS, плюс сохранение всех настроек в файле. Поздравляю 90% работы позади. Сюда мы попадаем, если пользователь нажал кнопку "Сохранить". Ну значит проверяем какая RadioButton выделена и идем по нужному пути. Если "В окошке", то поле Windowed равно 1 и стиль окна WS_SYSMENU. Если "Полноэкранное", то берем номер выбранной строки в ComboBox полноэкранных режимов и уже от этого пляшем. Хватаем из массивов размеры, формат поверхности, частоту и в структуру все укладываем. Поле Windowed равно нулю, а стиль окна будет WS_POPUP. Окончательный штришок в виде SwapEffect и можно все записывать в файл.

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

Код (Text):
  1.  
  2.     cmStartProgram:  
  3.  
  4.     invoke  EndDialog, hDlg , 0        
  5.     d3d8    Release, pd3d
  6.  
  7.     invoke  GetModuleFileName , 0 , ADDR AppPath , 260
  8.  
  9.     lea     edi,AppPath
  10.     lea     edi,[edi+eax-6]              
  11.     @@:
  12.     mov     al,[edi]                    
  13.     dec     edi                          
  14.     cmp     al,'\'                      
  15.     jne     @B
  16.     inc     edi
  17.     inc     edi
  18.     cld
  19.     lea     esi, szAppName
  20.     mov     ecx, SIZEOF szAppName
  21.     shr     ecx, 2
  22.     rep     movsd
  23.     mov     ecx, [SIZEOF szAppName ]
  24.     and     ecx, 3
  25.     rep     movsb
  26.  
  27.     invoke  ShellExecute , NULL , ADDR szOperation , ADDR AppPath , \
  28.             NULL , NULL , SW_SHOWNORMAL
  29.     xor     eax, eax
  30.     ret

Закрываем диалог. Освобождаем все связанное с Direct3D. Затем получаем путь по которому лежит наш Setup посредством GetModuleFileName. На всякий случай размер буфера под путь равен 260 байт ( если не ошибаюсь это максимальный размер пути в окошках ). Вот мы получили путь, но в нем присутствует и имя нашего Setup. Как его убрать ? Делаем цикл для проверки на '\' ,начиная от конца пути. Найдя его, копируем на место следующего за ним имя файла, который хотим запустить. А дальше все просто. ShellExecute с командой open в качестве параметра, и приложение запущено по нашему приказу, а работа самого Setup завершена.

Осталось только написать текст самого приложения. В нем реализовать загрузку сохраненных настроек и на их основе запускать Direct3D. Описывать здесь я это не буду, лучше гляньте исходник.

Ну все, пора закругляться, а то и так много понаписал, да и чего - то под конец статьи совсем туго соображать стал.

PS: В следующей статье ждите полноценное трехмерное приложение с матрицами, векторами, полигонами и прочей математикой.

Да и не забудьте глянуть в папку Include там лежат файлы d3d.inc и d3dcaps.inc окончательной версии.

Исходник обоих вариантов прилагается (1, 2).

Все вопросы мыльте сюда mybox@aib.ru © keYMax


0 1.321
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532