Проект Комментария к "Win32 API Tutorial" by Iczelion

Тема в разделе "WASM.PROJECTS", создана пользователем kero, 20 июн 2008.

  1. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
    По банальному недомыслию или, наоборот, по хитроумному педагогическому замыслу, но в знаменитом "Win32 API Tutorial" by Iczelion немало ошибок. И хотя туториал от этого даже поучительнее - поправки к ошибкам не помешали бы. Отсюда предложение: издать Туториал с комментариями. И ниже - несколько таковых от автора этих строк.


    Диалоговое окно (уроки 10-11)


    10-й урок начинается так:
    Однако насколько разобрался в этой теме сам Iczelion?

    Вот DIALOG.asm из tut10-1.zip:
    Код (Text):
    1. .386
    2. .model flat,stdcall
    3. option casemap:none
    4. WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
    5. include \masm32\include\windows.inc
    6. include \masm32\include\user32.inc
    7. include \masm32\include\kernel32.inc
    8. includelib \masm32\lib\user32.lib
    9. includelib \masm32\lib\kernel32.lib
    10.  
    11. .data
    12. ClassName db "DLGCLASS",0
    13. MenuName db "MyMenu",0
    14. DlgName db "MyDialog",0
    15. AppName db "Our First Dialog Box",0
    16. TestString db "Wow! I'm in an edit box now",0
    17.  
    18. .data?
    19. hInstance HINSTANCE ?
    20. CommandLine LPSTR ?
    21. buffer db 512 dup(?)
    22.  
    23. .const
    24. IDC_EDIT        equ 3000
    25. IDC_BUTTON      equ 3001
    26. IDC_EXIT        equ 3002
    27. IDM_GETTEXT     equ 32000
    28. IDM_CLEAR       equ 32001
    29. IDM_EXIT        equ 32002
    30.  
    31. .code
    32. start:
    33.     invoke GetModuleHandle, NULL
    34.     mov    hInstance,eax
    35.     invoke GetCommandLine
    36.     mov CommandLine,eax
    37.     invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
    38.     invoke ExitProcess,eax
    39. WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    40.     LOCAL wc:WNDCLASSEX
    41.     LOCAL msg:MSG
    42.     LOCAL hDlg:HWND
    43.     mov   wc.cbSize,SIZEOF WNDCLASSEX
    44.     mov   wc.style, CS_HREDRAW or CS_VREDRAW
    45.     mov   wc.lpfnWndProc, OFFSET WndProc
    46.     mov   wc.cbClsExtra,NULL
    47.     mov   wc.cbWndExtra,DLGWINDOWEXTRA
    48.     push  hInst
    49.     pop   wc.hInstance
    50.     mov   wc.hbrBackground,COLOR_BTNFACE+1
    51.     mov   wc.lpszMenuName,OFFSET MenuName
    52.     mov   wc.lpszClassName,OFFSET ClassName
    53.     invoke LoadIcon,NULL,IDI_APPLICATION
    54.     mov   wc.hIcon,eax
    55.     mov   wc.hIconSm,eax
    56.     invoke LoadCursor,NULL,IDC_ARROW
    57.     mov   wc.hCursor,eax
    58.     invoke RegisterClassEx, addr wc
    59.     invoke CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL
    60.     mov   hDlg,eax
    61.       invoke GetDlgItem,hDlg,IDC_EDIT
    62.     invoke SetFocus,eax
    63.     INVOKE ShowWindow, hDlg,SW_SHOWNORMAL
    64.     INVOKE UpdateWindow, hDlg
    65.     .WHILE TRUE
    66.                 INVOKE GetMessage, ADDR msg,NULL,0,0
    67.                 .BREAK .IF (!eax)
    68.                 invoke IsDialogMessage, hDlg, ADDR msg
    69.                 .if eax==FALSE
    70.                         INVOKE TranslateMessage, ADDR msg
    71.                         INVOKE DispatchMessage, ADDR msg
    72.                 .endif
    73.     .ENDW
    74.     mov     eax,msg.wParam
    75.     ret
    76. WinMain endp
    77. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    78.     .if uMsg==WM_CREATE
    79.         invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR AppName
    80.     .ELSEIF uMsg==WM_DESTROY
    81.         invoke PostQuitMessage,NULL
    82.     .ELSEIF uMsg==WM_COMMAND
    83.         mov eax,wParam
    84.         .IF lParam==0
    85.             .IF ax==IDM_GETTEXT
    86.                 invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
    87.                 invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
    88.             .ELSEIF ax==IDM_CLEAR
    89.                 invoke SetDlgItemText,hWnd,IDC_EDIT,NULL
    90.             .ELSE
    91.                 invoke DestroyWindow,hWnd
    92.             .ENDIF
    93.         .ELSE
    94.             mov edx,wParam
    95.             shr edx,16
    96.             .IF dx==BN_CLICKED
    97.                 .IF ax==IDC_BUTTON
    98.                     invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString
    99.                         .ELSEIF ax==IDC_EXIT
    100.                     invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
    101.                 .ENDIF
    102.             .ENDIF
    103.         .ENDIF
    104.     .ELSE
    105.         invoke DefWindowProc,hWnd,uMsg,wParam,lParam
    106.         ret
    107.     .ENDIF
    108.     xor    eax,eax
    109.     ret
    110. WndProc endp
    111. end start
    Запускаем DIALOG.exe, нажимаем ENTER, и DIALOG - закрывается!
    А ведь Iczelion явно не этого хотел, достаточно заглянуть в DIALOG.rc:
    Код (Text):
    1. #include "resource.h"
    2.  
    3. #define IDC_EDIT      3000
    4. #define IDC_BUTTON    3001
    5. #define IDC_EXIT      3002
    6. #define IDM_GETTEXT  32000
    7. #define IDM_CLEAR    32001
    8. #define IDM_EXIT     32003
    9.  
    10. MyDialog DIALOG 10, 10, 205, 60
    11. STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
    12. WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
    13. CAPTION "Our First Dialog Box"
    14. CLASS "DLGCLASS"
    15. BEGIN
    16.     EDITTEXT         IDC_EDIT,   15,17,111,13, ES_AUTOHSCROLL | ES_LEFT |WS_TABSTOP
    17.     DEFPUSHBUTTON   "Say Hello", IDC_BUTTON,    141,10,52,13
    18.     PUSHBUTTON      "E&xit", IDC_EXIT,  141,26,52,13
    19. END
    20.  
    21. MyMenu  MENU
    22. BEGIN
    23.     POPUP "Test Controls"
    24.     BEGIN
    25.         MENUITEM "Get Text", IDM_GETTEXT
    26.         MENUITEM "Clear Text", IDM_CLEAR
    27.         MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/
    28.         MENUITEM "E&xit", IDM_EXIT
    29.     END
    30. END
    Видите - DEFPUSHBUTTON. А DEFPUSHBUTTON в rc-файле задается не ради жирной окантовки,
    а когда надо, чтобы ENTER было равносильно клику по этой кнопке, даже если в фокусе - другой контрол (не кнопка).

    (Но все-таки почему ENTER закрывает этот DIALOG?
    На ENTER IsDialogMessage (в message loop) шлет диалогу WM_COMMAND с wParam=IDOK=1,
    и хотя ни кнопки, ни пункта меню с id=1 в этом диалоге нет, но зато фрагмент кода
    Код (Text):
    1.    .ELSE
    2.        invoke DestroyWindow,hWnd
    - настолько ошибочен, что даже преодолевает ошибочное несовпадение значений IDM_EXIT в .asm и в .rc !)

    Не повезло и TAB-навигации (по контролам с WS_TABSTOP): если в DIALOG Iczelion-а добавить EDIT с ES_MULTILINE - то на нем TAB-навигация и заткнется...

    Не ладно и с WM_ACTIVATE: если DIALOG деактивировать и снова активировать - фокус не возвращается не только к последнему обладавшему им контролу, но и вообще ни к какому. После следующего TAB - передается исключительно первому из контролов.

    Ну, а строчки в WinMain -
    Код (Text):
    1. invoke GetDlgItem,hDlg,IDC_EDIT
    2. invoke SetFocus,eax
    - зачем они? Ведь этот EDIT - первый контрол диалога, и потому фокус после запуска и так его?
    Но если эти строчки убрать, то EDIT примера tut10-1 действительно не получит фокуса! Что за фигня?

    DefWindowProc и DefDlgProc

    Так вот, перечисленные выше недомогания - закономерны: о DefDlgProc у Iczelion-а ни звука!
    Как, кстати, и у Petzold-а в "Programming Windows" (1998), откуда Iczelion, очевидно, и сдувал.
    И это - еще одна ошибка Iczelion-а, сдувать надо было с опубликованного за 5 лет до Petzold-а "Microsoft Windows Programmer FAQ":
    Короче, чтобы окну быть диалогом - мало (и даже не обязательно) происходить из ресурсов: необходимо, чтобы в WndProc было не
    Код (Text):
    1.     .ELSE
    2.         invoke DefWindowProc,hWnd,uMsg,wParam,lParam
    а
    Код (Text):
    1.     .ELSE
    2.         invoke DefDlgProc,hWnd,uMsg,wParam,lParam
    Далее.

    Ошибка с WM_CREATE в WndProc (tut10-1.zip):
    Код (Text):
    1.     .if uMsg==WM_CREATE
    2.         invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR AppName
    WM_CREATE - не WM_INITDIALOG: контролы еще не созданы и AppName вписывать некуда.

    Ошибка с WM_INITDIALOG в DlgProc (tut10-2.zip, tut11-1.zip, tut11-2.zip, ...):
    Код (Text):
    1.     .IF uMsg==WM_INITDIALOG
    2.         invoke GetDlgItem, hWnd,IDC_EDIT
    3.         invoke SetFocus,eax
    4.     .ELSEIF ...
    5.     ...
    6.     .ENDIF
    7.     mov eax,TRUE
    8.     ret
    На (WM_INITDIALOG + SetFocus) надо возвращать не TRUE, а FALSE (см. MSDN) !
    Т.е. данный EDIT получает свой фокус только по счастливой случайности: оказался 1-ым контролом диалога (см. MSDN).

    В конце 10-ого урока читаем:
    А не проще ли выкинуть DS_MODALFRAME из .rc последнего примера, с тем же результатом? -
    Код (Text):
    1. ...
    2.    MyDialog DIALOG 10, 10, 205, 60
    3.    STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
    4.    WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
    5.    CAPTION "Our Second Dialog Box"
    6.    ...
    Кстати, если диалог WS_OVERLAPPED (т.е. без WS_POPUP и WS_CHILD) или есть строчка CAPTION - то WS_CAPTION и так обеспечен... А 0x0004 и есть DS_3DLOOK...
    Впрочем, не будем мелочиться. Тем более что рядом - действительно чреватое наставление:
    В ближайшем же уроке Iczelion сам последовал своему указанию, и вот что из этого вышло.

    EndDialog и DestroyWindow

    DIALOG.asm из tut11-1.zip:
    Код (Text):
    1. .386
    2. .model flat,stdcall
    3. option casemap:none
    4. include \masm32\include\windows.inc
    5. include \masm32\include\user32.inc
    6. include \masm32\include\kernel32.inc
    7. includelib \masm32\lib\user32.lib
    8. includelib \masm32\lib\kernel32.lib
    9.  
    10. WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
    11. DlgProc PROTO :HWND, :DWORD, :DWORD, :DWORD
    12.  
    13. .data
    14. ClassName db "SimpleWinClass",0
    15. AppName  db "Our Main Window",0
    16. MenuName db "FirstMenu",0
    17. DlgName db "MyDialog",0
    18. TestString db "Hello, everybody",0
    19. hwndDlg dd 0  ; Handle to the dialog box
    20.  
    21. .data?
    22. hInstance HINSTANCE ?
    23. CommandLine LPSTR ?
    24.  
    25. .const
    26. IDM_EXIT equ 1
    27. IDM_ABOUT equ 2
    28. IDC_EDIT  equ 3000
    29. IDC_BUTTON equ 3001
    30. IDC_EXIT equ 3002
    31.  
    32. .code
    33. start:
    34.     invoke GetModuleHandle, NULL
    35.     mov    hInstance,eax
    36.     invoke GetCommandLine
    37.     mov CommandLine,eax
    38.     invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
    39.     invoke ExitProcess,eax
    40. WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    41.     LOCAL wc:WNDCLASSEX
    42.     LOCAL msg:MSG
    43.     LOCAL hwnd:HWND
    44.     mov   wc.cbSize,SIZEOF WNDCLASSEX
    45.     mov   wc.style, CS_HREDRAW or CS_VREDRAW
    46.     mov   wc.lpfnWndProc, OFFSET WndProc
    47.     mov   wc.cbClsExtra,NULL
    48.     mov   wc.cbWndExtra,NULL
    49.     push  hInst
    50.     pop   wc.hInstance
    51.     mov   wc.hbrBackground,COLOR_WINDOW+1
    52.     mov   wc.lpszMenuName,OFFSET MenuName
    53.     mov   wc.lpszClassName,OFFSET ClassName
    54.     invoke LoadIcon,NULL,IDI_APPLICATION
    55.     mov   wc.hIcon,eax
    56.     mov   wc.hIconSm,eax
    57.     invoke LoadCursor,NULL,IDC_ARROW
    58.     mov   wc.hCursor,eax
    59.     invoke RegisterClassEx, addr wc
    60.     INVOKE CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
    61.            WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
    62.            CW_USEDEFAULT,300,200,NULL,NULL,\
    63.            hInst,NULL
    64.     mov   hwnd,eax
    65.     INVOKE ShowWindow, hwnd,SW_SHOWNORMAL
    66.     INVOKE UpdateWindow, hwnd
    67.     .WHILE TRUE
    68.                 INVOKE GetMessage, ADDR msg,NULL,0,0
    69.                 .BREAK .IF (!eax)
    70.                 .if hwndDlg!=0
    71.                         invoke IsDialogMessage,hwndDlg,ADDR msg
    72.                         .if eax==TRUE
    73.                                 .continue
    74.                         .endif
    75.                 .endif
    76.                 INVOKE TranslateMessage, ADDR msg
    77.                 INVOKE DispatchMessage, ADDR msg
    78.     .ENDW
    79.     mov     eax,msg.wParam
    80.     ret
    81. WinMain endp
    82. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    83.     .IF uMsg==WM_DESTROY
    84.         invoke PostQuitMessage,NULL
    85.     .ELSEIF uMsg==WM_COMMAND
    86.         mov eax,wParam
    87.         .if ax==IDM_ABOUT
    88.             invoke CreateDialogParam,hInstance, addr DlgName,hWnd,OFFSET DlgProc,NULL
    89.             mov hwndDlg,eax
    90.         .else
    91.             invoke DestroyWindow, hWnd
    92.         .endif
    93.     .ELSE
    94.         invoke DefWindowProc,hWnd,uMsg,wParam,lParam
    95.         ret
    96.     .ENDIF
    97.     xor    eax,eax
    98.     ret
    99. WndProc endp
    100.  
    101. DlgProc PROC hWnd:HWND,iMsg:DWORD,wParam:WPARAM, lParam:LPARAM
    102.         .if iMsg==WM_INITDIALOG
    103.         invoke GetDlgItem,hWnd,IDC_EDIT
    104.         invoke SetFocus,eax
    105.         .elseif iMsg==WM_CLOSE
    106.         invoke EndDialog,hWnd,NULL
    107.         mov hwndDlg,0
    108.         .elseif iMsg==WM_COMMAND
    109.         mov eax,wParam
    110.         mov edx,eax
    111.         shr edx,16
    112.         .if dx==BN_CLICKED
    113.             .if eax==IDC_EXIT
    114.                 invoke SendMessage,hWnd,WM_CLOSE,NULL,NULL
    115.             .elseif eax==IDC_BUTTON
    116.                 invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString
    117.             .endif
    118.         .endif         
    119.         .else
    120.         mov eax,FALSE
    121.         ret
    122.         .endif
    123.         mov  eax,TRUE
    124.         ret
    125. DlgProc endp
    126. end start
    + DIALOG.rc:
    Код (Text):
    1. // constants for dialog box
    2. #define IDC_EDIT                    3000
    3. #define IDC_BUTTON                  3001
    4. #define IDC_EXIT                    3002
    5. #define DS_CENTER           0x0800L
    6. #define DS_CENTER           0x0800L
    7. #define WS_MINIMIZEBOX      0x00020000L
    8. #define WS_SYSMENU          0x00080000L
    9. #define WS_VISIBLE          0x10000000L
    10. #define WS_OVERLAPPED       0x00000000L
    11. #define DS_MODALFRAME       0x80L
    12. #define DS_3DLOOK           0x0004L
    13. #define WS_CAPTION          0xC00000L
    14. #define ES_AUTOHSCROLL      0x80L
    15. #define ES_LEFT             0
    16.  
    17. // Constants for menu
    18. #define IDM_EXIT 1
    19. #define IDM_ABOUT 2
    20.  
    21. FirstMenu MENU
    22. {
    23.  POPUP "&File"
    24.         {
    25.          MENUITEM "E&xit",IDM_EXIT
    26.         }
    27.  MENUITEM "About",IDM_ABOUT
    28. }
    29.  
    30. MyDialog DIALOG 10, 10, 205, 60
    31. STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
    32. WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
    33. CAPTION "Our Second Dialog Box"
    34. BEGIN
    35.     EDITTEXT         IDC_EDIT,   15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
    36.     DEFPUSHBUTTON   "Say Hello", IDC_BUTTON,    141,10,52,13
    37.     PUSHBUTTON      "E&xit", IDC_EXIT,  141,26,52,13
    38. END
    Итак, запускаем DIALOG.exe, дожидаемся окна "Our Main Window", жмем в нем меню "About", получаем диалог "Our Second Dialog Box", убеждаемся, что TAB в нем работает, а DEFPUSHBUTTON "Say Hello" - действительно DEFPUSHBUTTON, и, довольные, закрываем диалог
    (кнопка "Exit", или "X" кнопка заголовка, или пункт системного меню, или Alt+F4, - без разницы).

    Но погодите, разве диалог закрылся? Он же только спрятался! Причем при следующем нажатии About-а выскочит уже новый "Our Second Dialog Box"!

    (Проще всего это узреть через FrameRector, - единственный в природе спай "наизнанку", который не тычется вслепую, а преподносит все окна разом, предлагая не искать, а выбирать, рекомендую :)
    Ему на пару - WinTreeSnap, моментальный снимок в текстовый файл полного дерева окон с кучей параметров, мне не хватало этого в Spy++ . Кстати, WTS - пример использования wsprintf с максимальным числом параметров: 2+29=11111. Отмечу также, что FrameRector и WinTreeSnap - идейно безоконные, дабы не встревать в дерево окон, а последние версии FR и WTS согласованы c ExtraSpy, HTspy, ParentOwner).

    Вывод: завершать немодальный CreateDialog* "по Iczelion-у" (т.е. EndDialog вместо DestroyWindow) - ошибка.

    Исправляем.
    Но с другой стороны: "About" использует CreateDialogParam, диалоги получаются немодальные, и одновременно открыть их можно дофига.
    Понятно, куча эбаутов никому не нужна, хватит и одного модального, однако полезна возможность работы сразу с кучей панелей инструментов.
    Но тут хватит и двух, чтобы заметить: TAB у Iczelion-а работает только в последнем из созданных диалогов.
    И ясно, почему: Iczelion при создании очередного диалога засовывает его hWnd в одну и ту же переменную hwndDlg,
    так что только последний диалог и будет обслуживаться через IsDialogMessage:
    Код (Text):
    1.     .WHILE TRUE
    2.      INVOKE GetMessage, ADDR msg,NULL,0,0
    3.      .BREAK .IF (!eax)
    4.      .if hwndDlg!=0
    5.         invoke IsDialogMessage,hwndDlg,ADDR msg
    6.         .if eax==TRUE
    7.            .continue
    8.         .endif
    9.      .endif
    10.      INVOKE TranslateMessage, ADDR msg
    11.      INVOKE DispatchMessage, ADDR msg
    12.     .ENDW
    13.     mov     eax,msg.wParam
    Значит - опять правим Iczelion-а. Например, hwndDlg - нафиг, а message loop слегка автоматизируем:

    IsDialogMessage + (GetActiveWindow либо GetAncestor(msg.hwnd,GA_ROOT))

    Код (Text):
    1.     .WHILE TRUE
    2.      INVOKE GetMessage, ADDR msg,NULL,0,0
    3.      .BREAK .IF (!eax)
    4.       invoke GetActiveWindow
    5. ;      invoke GetAncestor,msg.hwnd,GA_ROOT
    6.       .if eax!=0
    7.         mov ecx,eax
    8.         invoke IsDialogMessage,ecx,ADDR msg
    9.         .if eax==TRUE
    10.            .continue
    11.         .endif
    12.      .endif
    13.      INVOKE TranslateMessage, ADDR msg
    14.      INVOKE DispatchMessage, ADDR msg
    15.     .ENDW
    16.     mov     eax,msg.wParam
    "Почувствуйте разницу" (и проверьте, нет ли подвоха в такой самонаводящейся петле): tut11-1-k.rar
    (ESCAPE и ENTER работают как "About" и "Exit" по воле Iczelion-а).

    ---
    Ну, так научил нас Iczelion, как "использовать диалоговое окно в качестве основного"?

    А это как посмотреть.
    На мой взгляд, подлинная роль Iczelion-а - роль отличного стартера. Он действительно помогает начинающему в WIN API побыстрее сориентироваться и начать осмысленное движение.
    На этом этапе - конечно, научил: брать окно готовеньким из ресурсов при помощи CreateDialog*, DialogBox* и шаблонов DIALOG(EX).

    Однако позже, при повторных "проходах" по Iczelion-у, вдруг доходит, что о диалоговом-то окне мы узнали ровно столько, сколько и о DefDlgProc: 0.
    И тем, кому охота разобраться в "действительно интересной теме", необходимы другие источники знаний, не Iczelion и даже не Petzold.

    Прежде всего вчитываемся в Dialog Box Programming Considerations, а потом идем на блог Raymond-а Chen-а (папаши ВинГуя):
    (цитата из The secret life of GetWindowText).
    Вот только несколько ссылок:
    A different type of dialog procedure
    Another different type of dialog procedure
    Modality, part 1: UI-modality vs code-modality
    Modality, part 2: Code-modality vs UI-modality
    Modality, part 3: The WM_QUIT message
    Modality, part 4: The importance of setting the correct owner for modal UI
    Modality, part 5: Setting the correct owner for modal UI
    Modality, part 6: Interacting with a program that has gone modal
    Modality, part 7: A timed MessageBox, the cheap version
    MsgWaitForMultipleObjects and the queue state
    Rescuing thread messages from modal loops via message filters
    Restating the obvious about the WM_COMMAND message
    The correct order for disabling and enabling windows
    The dialog manager, part 1: Warm-ups
    The dialog manager, part 2: Creating the frame window
    The dialog manager, part 3: Creating the controls
    The dialog manager, part 4: The dialog loop
    The dialog manager, part 5: Converting a non-modal dialog box to modal
    The dialog manager, part 6: Subtleties in message loops
    The dialog manager, part 7: More subtleties in message loops
    The dialog manager, part 8: Custom navigation in dialog boxes
    The dialog manager, part 9: Custom accelerators in dialog boxes
    The evolution of dialog templates - 32-bit Classic Templates
    The evolution of dialog templates - 32-bit Extended Templates
    The evolution of dialog templates - Summary
    The history of the Windows XP common controls
    What's so special about the desktop window?
    Why are dialog boxes initially created hidden?
    Waiting until the dialog box is displayed before doing something
    Why do we even have the DefWindowProc function?

    По материалам блога издана книга "Practical Development Throughout the Evolution of Windows" (2006):

    [​IMG]

    ---

    А от себя прилагаю

    Некоторые подробности о диалогах: учебный диалог ExtraSpy и другие примеры.

    [​IMG]

    ExtraSpy - это генератор и тестер диалогов. Одновременно можно задействовать несколько (много) экземпляров ExtraSpy.
    1) При пустом чекбоксе "lock F8" любое видимое окно можно зафиксировать, наведя на него курсор и нажав клавишу F8.
    Если при этом нажата и клавиша CTRL, то в зависимости от ситуации фиксируется либо (GetAncestor(GA_ROOT)), либо (GetWindow(GW_OWNER)), либо GUITHREADINFO.hwndMenuOwner.
    Кроме того, при отключенном таймере (т.е. при пустом чекбоксе "timer") можно в поле "hwnd" ввести 8-значную hex-запись hWnd.
    Например, на WinXP после ввода "00010014" зафиксируется старшее окно (системный класс #32769, константа HWND_DESKTOP), а после "00010016" - старшее message-only окно (системный класс "Message", константа HWND_MESSAGE, - см. WinTreeSnap).
    Отмена фиксации - снятием метки с чекбокса "lock F8".

    2) В ExtraSpy способ фиксации окна устанавливается вращением mouse wheel над статиком "hwnd.*" (id=105).
    В данной версии можно выбрать либо WindowFromPoint, либо - в сочетании с MapWindowPoints - RealChildWindowFromPoint или ChildWindowFromPointEx.
    В MSDN о различии между ChildWindowFromPoint и RealChildWindowFromPoint говорится следующее:
    Однако в случае mirrored windows вылезает куда более существенное различие, о котором MSDN молчит. Т.е. MSDN утверждает, что
    Но на самом деле в случае WS_EX_LAYOUTRTL у какого-либо оконного предка связка RealChildWindowFromPoint+MapWindowPoints попросту не работает! Вот попробуйте дурдом с окошком из layoutrtl.rar при "hwnd.1"!
    Тогда как с ChildWindowFromPoint тут OK.

    3) Кнопки "CreateWnd", "CreateDlg", "DlgBox" и "MsgBox" предназначены для создания top-level диалогов соответственно из CreateWindowEx, CreateDialog*, DialogBox* и MessageBox*.
    Эти диалоги полностью воспроизводят интерфейс и функционал исходного диалога ExtraSpy, принадлежа тому же потоку.
    Если на указанные кнопки нажимать при помеченных чекбоксах "owned" и "lock F8" - то у диалога будет owner: предварительно зафиксированное окно.
    По сценарию не-owned диалоги равноправны: можно оставить только один (любой), остальные закрыть (даже исходный), а ExtraSpy будет работать, как работал.

    4) WM_ENTERIDLE и DS_NOIDLEMSG.
    Модальный owned диалог, созданный без стиля DS_NOIDLEMSG, шлет своему owner-у WM_ENTERIDLE (см. MSDN).
    Проверим: фиксируем окно самого ExtraSpy, метим чекбокс "owned", жмем "MsgBox" (либо освобождаем чекбокс "ds_noidlemsg" и жмем "DlgBox") - и наблюдаем индикацию приема WM_ENTERIDLE прямо на чекбоксе "ds_noidlemsg".
    Почти то же видим и после создания menu loop (вызовом системного или иного меню либо нажатием ALT или F10).
    Кое-какие подробности - в нижнем многострочном эдите, находим вращением mouse wheel.

    Поскольку обычный MessageBox (можно вызвать R-кликом по кнопке "MsgBox") имеет предустановленный стиль DS_NOIDLEMSG, запрещающий посылку WM_ENTERIDLE (а последующая отмена этого стиля ничего не дает), - пришлось MessageBox хукнуть (единственное в ExtraSpy применение хука).
    Правда, все-таки можно было обойтись без хука, используя дополнительный поток, как в примере mb_ghost, см. ниже.
    Но зацепил промежуточный parent в момент HCBT_CREATEWND: его hWnd = 00010016h, а это - старшее message-only окно (системный класс "Message")!
    Хотелось бы понять, что происходит. Исходников винды не имею, и был бы признателен обладателям других Win OS за инфу, что тут выскакивает у них.
    Чтобы легче было откликнуться на эту просьбу - предусмотрен тест на "ранние" owner и parent: M-кликом по кнопке "MsgBox".
    Первоначальное намерение "не выдрючиваться" с MessageBox-ом и использовать хук только для отмены DS_NOIDLEMSG - было в конце концов похерено, и MessageBox стал-таки 4-ым вариантом ExtraSpy.
    А по ходу "выдрючивания" обнаружился глюк XP и Висты, см. ниже IsWindowMessageBox.

    5) После создания модальных owned диалогов при помощи кнопок "DlgBox" или "MsgBox" - окно owner-а, понятно, задизабливается.
    Однако функционал задизабленного ExtraSpy остается доступным, ибо кроме L-клика работает и R-клик по контролам (тут использована возможность "обходить" WS_DISABLED и EnableWindow в обработчике WM_SETCURSOR, подробности в HTSpy).

    6) R-клик в полях "id/hmenu", "userdata" или по строчкам комбобоксов "wndExtra" и "clsExtra" приводит к попытке xor (значение в поле),1, с целью узнать, доступно или нет данное поле.
    Внимание: "защиты от дурака" нет, так что бессознательные клики могут привести к падению ExtraSpy или приложения, окно которого зафиксировано в ExtraSpy (например - Explorer).
    (А UltraEdit подивил еще и таким сюрпризом: если его EditControl зафиксировать в ExtraSpy, поксорить поле "id/hmenu" и, забыв вернуться к исходному значению этого поля, закрыть UltraEdit - то, чтобы UltraEdit запустить снова, надо будет править его ini-файл!)

    7) M-клик по окну ExtraSpy включает/отключает индикацию счетчиков сообщений в message loop, WndProc и DlgProc, а также user/gdi хендлов.
    Прижимая без отжатия L/R кнопку мыши на заголовке окна, замечаем последствия модальности/немодальности. (Очевидны последствия и для TAB-навигации).

    8) При помеченном чекбоксе "template editor" можно менять WS/DS/WS_EX стили внутреннего DIALOGEX template (для "CreateDlg" и "DlgBox"):
    а) либо редактируя при отключенном таймере hex-значения в соответствующих полях с финальным R-кликом по их лейблу ("ws" или "ws_ex"),
    б) либо манипуляциями в нижнем многострочном эдите: вращением mouse wheel выбрать WS/DS/WS_EX, R-кликом переключиться на последовательность битов, и L-даблклик по названию нужного бита
    (Переключение по mouse wheel требует, чтобы многострочный эдит был НЕ в фокусе, тут сгодится шмяк по ближайшей кнопке "reposition").

    9) При пустом чекбоксе "template editor" и помеченном "lock F8" описанным выше образом модифицируются биты стилей предварительно зафиксированного окна.
    Например, зафиксированное окно делаем mirrored: вращением mouse wheel выбираем "ws_ex" стили, R-кликом переключаемся на последовательность битов, и L-даблклик по биту 400000 (т.е. WS_EX_LAYOUTRTL).

    10) Можно регистрировать собственные классы диалогов (перед "CreateWnd", "CreateDlg", "DlgBox"):
    при отключенном таймере заполняем поля "wndExtra", "clsExtra","style cs","class", и затем жмем кнопку "register".
    В ExtraSpy есть ограничение на регистрируемое имя класса диалога: должно состоять точно из 6 символов (как в "#32770").
    Ну, и в "wndExtra" надо вписать число не менее DLGWINDOWEXTRA (т.е. 30), подробности позже.
    После успешной регистрации имя класса диалога добавляется в комбобокс "class".

    Регистрация нужна, в частности, тогда, когда нужны другие объемы WndExtra и ClsExtra, - после регистрации они не изменяемы (однако см. ниже трюк с WndExtra).

    11) Кнопка "unregister" делает, что обещает, при условии, что закрыты все окна соответствующего класса.
    Соответствующее имя класса из комбобокса изымается.

    12) Выбор имени класса в вышеупомянутом комбобоксе одновременно вписывает это имя в DIALOGEX template.

    13) Если при отключенном таймере ввести в поле "class" уже зарегистрированное имя класса (не обязательно диалога, а, скажем, "scrollbar") и R-кликнуть по лейблу "class" -
    то в полях "wndExtra", "clsExtra" и "style cs" получим параметры этого класса, как результат GetClassInfoEx.
    Поскольку можно зарегистрировать свой, отличный от системного, класс с тем же названием (например - "#32770"), - то предусмотрена возможность посмотреть оба варианта: по лейблу "class" - WM_RBUTTONDOWN для одного и WM_RBUTTONUP для другого.

    14) Анализатор класса (поле с id=104):

    [​IMG]

    Здесь первое имя (CombolBox) - результат GetClassName, второе имя (ListBox) - результат RealGetWindowClass, а в скобках - результат WM_GETDLGCODE, который поясняется в нижнем многострочном эдите.
    15) Кнопка "reposition" - для посылки предварительно зафиксированному окну сообщения DM_REPOSITION, и это - какой-никакой тест окна на "диалоговость":
    если диалог хотя бы частично вне desktop area, то после посылки переместится в ближайшую часть desktop area.
    При отсутствии WS_EX_TOOLWINDOW диалог переберется даже не на desktop area, а на work area (desktop area минус system taskbar и application desktop toolbars).
    Но вот при пустом чекбоксе "ws_ex_controlparent" диалог и не пошевелится (в MSDN о такой особенности стиля WS_EX_CONTROLPARENT тишина).

    16) Для PUSHBUTTON-кнопок диалогов (только для них!) выдумано дополнительное качество DEFID, с соответствующим стилем BS_DEFPUSHBUTTON (о нем уже говорилось выше) и сообщениями DM_GETDEFID и DM_SETDEFID.
    При активном окне ExtraSpy и нажатой клавиши SHIFT вращение mouse wheel над произвольным контролом произвольного диалога определяет новое значение defid диалога как id этого контрола.
    17) Поскольку значения DM_GETDEFID и DM_SETDEFID - это соответственно WM_USER и WM_USER+1, то возможны конфликты с другими приложениями (см. MSDN).
    Например, попробуйте после запуска ExtraSpy драгдропить иконки файлов, или поработать с окном буфера обмена...
    Для временного отключения посылок DM_GETDEFID и введен чекбокс "def id".

    18) Еще одно отличие ресурсных диалогов от обычных окон: если в DIALOGEX template было задано WS_SYSMENU при отсутствии WS_THICKFRAME, WS_MAXIMIZEBOX и WS_MINIMIZEBOX, то даже если потом соотвествующие биты стиля установить - соответствующей функциональности диалогу не добавится.
    Ибо при указанных условиях диалог, в отличие от обычного окна, получает от MS куцее системное меню, а по воле MS состав системного меню "главнее" набора битов стиля.
    (Что исходный ExtraSpy и демонстрирует: попробуйте свернуть, развернуть или растянуть его окно. Хотя тут еще влияет, что за тема на винде: "классическая" или "Windows XP", с обычными для последней несуразностями).
    В случае custom-ного диалога успешно добавить эти стили все-таки можно и в оконной процедуре, но не позже, чем на WM_CREATE, т.е. до "скукоживания" системного меню. В исходнике ExtraSpy такой вариант в WndProc закомментирован.
    Однако в любой момент наготове грубая сила кнопки "re-sysmenu": GetSystemMenu(hWnd,FALSE) + GetSystemMenu(hWnd,TRUE), и сисменю в норме.

    19) Особенность модального диалога, созданного путем DialogBox*, - стартовать видимым, даже если в ресурсах нет WS_VISIBLE, и даже если в обработчик WM_INITDIALOG добавить ShowWindow(SW_HIDE).
    Но скрыть модальный диалог при его запуске все-таки можно, причем несколькими способами (см. здесь):
    #1: Ресурсный шаблон DIALOG(EX) - без WS_VISIBLE, а ShowWindow(SW_HIDE) - в обработчике первого по счету WM_NCPAINT.
    #2: В обработчике WM_WINDOWPOSCHANGING сбрасывать бит SWP_SHOWWINDOW (элемент .flags структуры WINDOWPOS по lParam).

    20) DLGWINDOWEXTRA

    В 10-ом уроке читаем:
    Но почему мы должны - Iczelion не сообщает, продолжая "утаивать" существование и роль функции DefDlgProc :)
    Да, именно DefDlgProc, задействованная в каждом настоящем диалоге, требует в свое распоряжение первые DLGWINDOWEXTRA (=30) байтов WndExtra, т.е. те, что соответствуют значениям x=0...28 для GetWindowLong(hWnd,x).
    (Тут следовало бы расписать, какую роль играет каждый из байтов WndExtra в DefDlgProc, но пока отмечу лишь, что первые 3 дворда отданы под параметры DWL_MSGRESULT, DWL_DLGPROC и DWL_USER, см. MSDN).

    Однако вот какое соображение: в обязанности DefDlgProc входит определение DEFPUSHBUTTON etc, но что, если НЕ входит - потрошение ресурсов?
    Тогда почему бы для окон вроде виндовского калькулятора, которые хоть и извлекаются из ресурсов, но потом никак не используют DefDlgProc, - не отказаться от DefDlgProc уже на стадии обработки ресурсов?
    Например - выкинуть из процедуры CreateDialog* всякое использование DefDlgProc, сводя к минимуму требуемый размер WndExtra?
    Ну, у кого хватит знаний на такой грязный хак ? :)

    (Кстати. Казалось бы, что мешает сэмулировать CreateDialog*/DialogBox* собственной процедурой обработки ресурсов с выдачей параметров для последующих CreateWindowEx без всяких требований к WndExtra. Но помехи есть. К примеру, вот: на XP после CreateWindowEx (0,"Edit",0,WS_CHILD|WS_VISIBLE|WS_BORDER,...) создается Edit с нарисованной рамкой, без WS_BORDER и без HTBORDER! Тогда как Static ведет себя нормально. Похоже, требуется скрупулезная инвентаризация, а это так скучно :)

    21) Диалоговое окно в качестве основного - варианты

    В 10-ом уроке приведены лишь два варианта, один с CreateDialogParam, другой с DialogBoxParam. На самом деле вариантов побольше, но обсуждать их пока не буду, кому понадобится - разберется: main_variants.rar.
    Впрочем, и в исходнике ExtraSpy не один такой вариант.

    22) IsWindowDialog

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

    Конечно, в списке кодов ошибок есть вроде бы подходящая формулировка:
    Но вот соответствующее
    По мне - это не то, мне бы выход на DefDlgProc :) Тем не менее и коды ошибок в поле зрения ExtraSpy: эдит c ID=125 показывает код, а нижний многострочный эдит - соответствующий текст ошибки. (Код в эдит c ID=125 можно и загружать вручную, а вращение mouse wheel при курсоре над этим эдитом творит inc/dec кода).

    Казалось бы, можно положиться на RealGetWindowClass. Но когда RealGetWindowClass сигналит о предопределенном системном классе "#32770", то это значит только то, что в соответствующем приложении хотя бы раз срабатывала DefDlgProc. Пример NonDialog для наглядности:
    Код (Text):
    1. .const
    2.   id   equ 100
    3.  .data
    4.   _app db "NonDialog",0
    5.   _btn db "Button",0
    6.   _txt db "Test  RealGetWindowClass",0
    7.  .data?
    8.  .code
    9.  
    10.  WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    11.    .if uMsg==WM_DESTROY
    12.      invoke PostQuitMessage,0
    13.      mov eax,FALSE
    14.    .elseif uMsg==WM_COMMAND && wParam==id
    15.      invoke DefDlgProc,hWnd,WM_NULL,0,0
    16.    .else
    17.      invoke DefWindowProc,hWnd,uMsg,wParam,lParam
    18.      .if uMsg==WM_NCHITTEST && eax==HTCLIENT
    19.        mov eax,HTCAPTION
    20.      .endif
    21.    .endif
    22.    ret
    23.  WndProc endp
    24.  
    25.  CreateWnd proc uses ebx hInst:HINSTANCE
    26.    local wc:WNDCLASSEX,msg:MSG
    27.    mov  wc.cbSize,sizeof WNDCLASSEX
    28.    mov wc.style,CS_DBLCLKS
    29.    mov wc.cbClsExtra,0
    30.    mov wc.cbWndExtra,DLGWINDOWEXTRA+6 ; (=36)
    31.    mov  eax,hInst
    32.    mov  wc.hInstance,eax  
    33.    mov wc.hbrBackground,COLOR_WINDOW
    34.    mov  wc.lpfnWndProc,offset WndProc
    35.    mov wc.lpszMenuName,0
    36.  ;  invoke LoadIcon,0,IDI_APPLICATION
    37.    xor eax,eax
    38.    mov wc.hIcon,eax
    39.    mov wc.hIconSm,eax
    40.  ;  invoke LoadCursor,0,IDC_HAND
    41.    mov wc.hCursor,eax
    42.    mov  wc.lpszClassName,offset _app
    43.    invoke RegisterClassEx,addr wc
    44.     invoke CreateWindowEx,WS_EX_TOPMOST OR WS_EX_CLIENTEDGE OR WS_EX_TOOLWINDOW,offset _app,offset _app,WS_SYSMENU,20,20,260,100,0,0,hInst,0
    45.    mov ebx,eax
    46.     invoke CreateWindowEx,0,offset _btn,offset _txt,WS_CHILD OR WS_VISIBLE OR BS_FLAT,20,20,210,30,ebx,id,hInst,0  
    47.    invoke ShowWindow,ebx,SW_SHOWNORMAL
    48.    invoke UpdateWindow,ebx
    49.    .while TRUE
    50.      invoke GetMessage,addr msg,0,0,0
    51.      .break .if (!eax)
    52.      invoke TranslateMessage,addr msg
    53.      invoke DispatchMessage,addr msg
    54.    .endw
    55.    mov  eax,msg.wParam
    56.    ret
    57.  CreateWnd endp
    58.  
    59.  start:
    60.     invoke GetModuleHandle,0
    61.     invoke CreateWnd,eax
    62.     invoke ExitProcess,eax
    63.  end start
    Запускаем этот пример и фиксируем в ExtraSpy его окно. В поле "анализатора класса" видим: "NonDialog (0000)". А после нажатия кнопки "Test RealGetWindowClass" там уже "NonDialog / #32770 (0000)".
    Т.е. первое же обращение к DefDlgProc привело к изменению состояния WndExtra (сравните результаты R-клика по строчкам комбобокса "wndExtra" до и после). Но достаточно ли этого, чтобы окно считать диалогом?..

    Может, подходящий критерий - DLGWINDOWEXTRA ? Но если окно имеет WndExtra длиной DLGWINDOWEXTRA, то это еще не значит, что окно - диалог. А если любимый спай (скажем, Spy++) показывает, что длина WndExtra меньше DLGWINDOWEXTRA, то это еще не значит, что окно - НЕ диалог.
    Ибо свою WndExtra диалог может "спрятать": после SetClassLong(GCL_CBWNDEXTRA,0) диалог остается диалогом, но его WndExtra становится невидимой для большинства спаев (Spy++ в том числе).

    Испытываем на ExtraSpy: отключаем таймер, вводим "0" в поле "wndExtra", R-кликаем по лейблу "wndExtra", и смотрим на WndExtra ExtraSpy-я через привычную нам тулзу.
    Конечно, ExtraSpy располагает и противоядием: если пометить чекбокс "wndExtra", то увидим первые 100 байтов спрятанной WndExtra
    (MSDN рекомендует задавать WndExtra и ClsExtra длиной не более 40 байтов, но посмотрите, сколько у системного контрола ScrollBar).

    Между прочим, этот трюк лишает ExtraSpy способности создавать диалоги "спрятанного" класса. В частности, если этот класс - системный #32770, то R-клик по "MsgBox" будет давать осечку. До тех пор, пока SetClassLong(GCL_CBWNDEXTRA) не отыграет назад.

    23) IsWindowMessageBox, или дополнение ко 2-му уроку Iczelion-а

    MessageBox во 2-ом уроке призван взбодрить начинающего тем, как, оказывается, просто получить полнофункциональную Windows-программу с окошком. Но MessageBox сам по себе совсем не тривиален...

    К тому, что о нем как о модальном диалоге уже сказано выше, прибавим, что ему не требуются ресурсы, как DialogBox-у, и его окно попросту вычисляется по параметру uType и длинам текстов заголовка и сообщения.

    [23.1] И вот первый "секрет" Message Box-а: он копируется в буфер обмена. Подробности получаем после R-клика по кнопке "MsgBox":

    ExtraSpy, построенный на Message Box-е, после R-клика по его верхнему левому полю (ID=131) переключается из "красного" состояния в "черное", в котором уже готов к копированию. Повторный R-клик отыграет назад в "красное", а M-клик - окончательно вернет Message Box в первобытное состояние:

    [​IMG]

    [23.2] Еще сюрприз: на XP PostMessage(WM_TIMER,0,0) закрывает окно Message Box-а, аки PostMessage(WM_CLOSE,0,0)!

    Зкспресс-проверка: активируйте окно ExtraSpy, наведите курсор на произвольный Message Box, прижмите CTRL и крутаните mouse wheel, - в результате Message Box получит PostMessage(WM_TIMER,0,0) и тут же даст дуба.

    Похоже, нововведенный на XP недокументированный MessageBoxTimeout, автоматически закрывающийся по истечении заданного периода (именно по WM_TIMER), затронул и обычный Message Box... (В исходнике ExtraSpy закомментирован пример c MessageBoxTimeout).

    [23.3] Еще одна особенность Message Box-а на XP: его USERDATA изначально занята (но какого черта? ведь USERDATA!).

    Если изменить значение USERDATA (например, xor единичкой правым кликом по соответствующему контролу ExtraSpy), то закрываться Message Box будет ой, как скверно...
    Что пример mb_ghost в итоге и показывает, а вместе с тем и еще кое-что:
    1) даже после изменения USERDATA Message Box вплоть до закрытия абсолютно работоспособен,
    2) FrameRector годится для слежения даже за гиблыми окошками,
    3) в виндах обитает жуткий плод мрачной фантазии кого-то из недр MS - окно класса Ghost.

    После запуска видим слегка навороченный Message Box. Кнопка "Ghost Off" (в девках "Help") - это триггер для USERDATA, "On"/"Off". Пытаем Message Box по всякому (для того и наворочен), видим, что OK.
    Потом напускаем FrameRector, наводим курсор на Message Box так, чтобы рамка по его периметру стала желтой (тогда он выбран), и включаем CAPS LOCK (т.е. фильтруем окна по процессу), после чего "лишние" рамки исчезают.
    Теперь уводим Message Box подальше от центра и нажимаем "X"-кнопку заголовка Message Box-а. Тут же в центр экрана сваливается окно dwwin.exe, но мы на него пока пилюем, и оттаскиваем в сторону то, что считаем Message Box-ом. И видим, что контуры Message Box-а остались на старом месте, так как он стал сплошным HTNOWHERE (HT 0), мы же оттаскиваем невесть что. Выключив CAPS LOCK и наведя курсор на это нечто, читаем вверху экрана: Ghost...
    Во как.

    [23.4] Мессаджбокс - оборотень

    В MSDN-овской статье "MessageBox Function" помимо прочего сообщается о 3-х особенных флагах:
     
  2. driver

    driver New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2008
    Сообщения:
    302
    у меня не компилится урок с тултипами и 35

     
  3. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
    2 driver
    MAKEFILE к 35: link /STACK:2000000,2000000 ... ?

    ---
    + скриншот (экономлю место :) )
     
  4. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  5. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  6. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  7. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  8. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  9. Ra_

    Ra_ New Member

    Публикаций:
    0
    Регистрация:
    4 мар 2007
    Сообщения:
    289
    58мег, но надо будет почитать :)
     
  10. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
    Ra_
    Думаю, не пожалеете :) Но неужели 58мег? Что-то и правда многовато.
    ---
    +
    ---
    +
    FrameRector v.2011-04-05:

    http://www.wasm.ru/forum/attachment.php?item=4543

    Кстати, FrameRector - еще и наглядный демонстратор удручающих тормозов Win7.
    Специально для этого теста - сведенный до минимума http://files.rsdn.ru/42164/min_framerector.rar .
    Сравните скорость на "классической" или "Windows 7 упрощенной", где отключена "композиция рабочего стола", - и на Aero или "стандартной Windows 7", где она включена.
     
  11. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  12. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  13. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
    +
    ---
    +
    WinTreeSnap v.2011-04-05
    :
    http://www.wasm.ru/forum/attachment.php?item=4544

    Теперь полное дерево окон - в нескольких order-ах:
    Enum-order (самопальная кликуха) - через EnumChildWindows(GetDesktopWindow),
    Z-order (стандартный) - a) через GetWindow и b) через FindWindowEx,
    сквозной Z-order (самопальный) - a) сверху вниз и b) снизу вверх, через GetWindow,
    ну, и еще несколько других.
    Причем каждый вариант - в 2х видах: разовый (как старый) и в цикле через Sleep.

    И еще кое-что:
    в последних FrameRector и WinTreeSnap учтен противнейший глюк, которым наградили GetWindowRect в Vista/Win7 при Aero и подобных color schemes.
    Нагуглился давний топик CyberManiac-а как раз насчет этого: Размер окна в Windows 7 - как определить ?.
    Наверняка участники того топика потом нашли решение, но все-таки приведу прямо здесь и мой вариант:
    везде в коде GetWindowRect заменяется на GetWindowRect_, примерно на такую -
    Код (Text):
    1. GetWindowRect_ proc uses ebx hWnd:HWND,lprect:DWORD
    2.   local var:DWORD
    3.  
    4. .const
    5.  DWMWA_EXTENDED_FRAME_BOUNDS equ 9
    6.  DWMWA_NCRENDERING_ENABLED   equ 1
    7. .data
    8.  _Dwmapi    db "Dwmapi",0
    9.  _DwmGWA    db "DwmGetWindowAttribute",0
    10.  _DwmICE    db "DwmIsCompositionEnabled",0
    11. .code
    12.  
    13.   invoke GetModuleHandle,offset _Dwmapi
    14.   test eax,eax
    15.   jz @f
    16.   mov ebx,eax
    17.   invoke GetProcAddress,ebx,offset _DwmICE
    18.   test eax,eax
    19.   jz @f
    20.   lea edx,var
    21.   push edx
    22.   call eax
    23.   cmp eax,S_OK
    24.   jnz @f
    25.   mov eax,var
    26.   test eax,eax
    27.   jz @f
    28.   invoke GetProcAddress,ebx,offset _DwmGWA
    29.   test eax,eax
    30.   jz @f
    31.   push sizeof RECT
    32.   push lprect
    33.   push DWMWA_EXTENDED_FRAME_BOUNDS
    34.   push hWnd
    35.   call eax
    36.   cmp eax,S_OK
    37.   jz @@@
    38. @@:
    39.   invoke GetWindowRect,hWnd,lprect
    40. @@@:
    41.   ret
    42. GetWindowRect_ endp
    (Понятно, в некоторых случаях понадобится и LoadLibrary).
     
  14. Aquila

    Aquila Самурай дзена

    Публикаций:
    0
    Регистрация:
    30 авг 2002
    Сообщения:
    1.467
    Адрес:
    Russia, Moscow
    Отлично, спасибо! Если ты не против, то как ты посчитаешь проект комментария более менее готовым, я его размещу в виде статьи, чтобы он не потерялся на форуме. И что такое ExtraSpy? :)
     
  15. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
    Aquila
    Кто ж в своем уме будет против :)
    Только хочу уточнить: я ведь не взял на себя комментарий по всему туториалу, меня на это не хватит, от меня - только "Спецокно Диалог" по урокам 10-11 :)
    Но наверняка и у других есть, что сказать по другим (а может, и по этим) урокам, да и были уже в разное время на васме существенные замечания по туториалу, вот и собрать бы все это воедино ?

    "А от себя прилагаю сборник подробностей о диалогах: учебную тулзу с исходником. Наверняка не безошибочна, но все же какой-никакой инструмент."
    "Учебный диалог ExtraSpy - это и подопытный диалог, и генератор диалогов разного толка, и тестер диалогов."

    Т.е. ExtraSpy - из разряда "образовательных программ" :)

    Кстати, сначала было так:
    Часть 1. По следам Iczelion-а.
    Часть 2. Куда не ступала нога Iczelion-а.
     
  16. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  17. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  18. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
  19. ALLeX

    ALLeX Member

    Публикаций:
    0
    Регистрация:
    21 окт 2004
    Сообщения:
    141
    Адрес:
    Ukraine
    Вот и я "сдул":
    Код (Text):
    1.     mov wc.lpfnWndProc, OFFSET MyDialogProc  ; First, you have to create a dialog box first.  Include a class name
    2.     mov wc.cbWndExtra, DLGWINDOWEXTRA      ; ...and add the constant DLGWINDOWEXTRA to the window extra byte component.
    3. <>
    4.     invoke  RegisterClassEx, ADDR wc                   ; Then, in your WinMain function, register a class using that class name
    5.     invoke  CreateDialogParam, hInst, MainDialogID, NULL, NULL, NULL      ; Then, use CreateDialog() to display the dialog, make sure the last 2 parameters are set to NULL
    6. <>
    7.  
    8. MyDialogProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    9.     .IF uMsg==WM_INITDIALOG
    10.         mov eax, FALSE      ; User can manual set focus by SetFocus (autoset if not use)
    11.     .ELSEIF uMsg==WM_CREATE
    12.         mov eax, TRUE
    13. <>
    14.     .ELSE
    15.         invoke DefDlgProc, hWnd, uMsg, wParam, lParam    ; In your WndProc function, instead of calling DefWindowProc, call DefDlgProc
    16.  
    17.     .ENDIF
    18.     ret
    19. MyDialogProc endp
    - в результате перестали приходить WM_INITDIALOG. Получаю только WM_CREATE.
     
  20. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
    ALLeX, у вас налицо путаница с WndProc/DlgProc.
    Проявите чуть больше внимания к main_variants.rar :)

    ---
    Собранных в main_variants примеров вполне достаточно, чтобы разобраться с вашей задачей.

    Но на всякий случай выкладываю "недостающий" вариант.

    Это - диалог, созданный через CreateDialogParam, с пользовательским классом окна и двумя процедурами: оконной (WndProc) и диалоговой (DlgProc).
    И заметьте: перераспределение между WndProc и DlgProc обработчиков сообщений путем закомментирований в исходнике - на ваше усмотрение.

    .asm
    Код (Text):
    1. .386
    2. .model flat,stdcall
    3. option casemap:none
    4.  
    5. include    \masm32\include\windows.inc
    6. include    \masm32\include\user32.inc
    7. include    \masm32\include\kernel32.inc
    8. include    \masm32\include\gdi32.inc
    9. include    \masm32\include\comctl32.inc
    10. includelib \masm32\lib\user32.lib
    11. includelib \masm32\lib\kernel32.lib
    12. includelib \masm32\lib\gdi32.lib
    13. includelib \masm32\lib\comctl32.lib
    14.  
    15. .const
    16.   id  equ 100
    17. .data
    18.   _app  db "CreateDialogParam+DlgProc+WndProc+Class",0
    19.   _dlg  db "#32770",0
    20.   _wnd  db "DlgClass",0
    21. .data?
    22. .code
    23.  
    24. DlgProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    25.  
    26.   .if uMsg==WM_INITDIALOG
    27.     invoke SetWindowText,hWnd,addr _app
    28. ;    invoke GetWindowLong,hWnd,GWL_STYLE
    29. ;    or eax,WS_THICKFRAME OR WS_MINIMIZEBOX OR WS_MAXIMIZEBOX
    30. ;    invoke SetWindowLong,hWnd,GWL_STYLE,eax
    31. ;    invoke SetWindowPos,hWnd,0,0,0,0,0,SWP_NOZORDER OR SWP_NOMOVE OR SWP_NOSIZE OR SWP_FRAMECHANGED OR SWP_NOACTIVATE  
    32.     mov eax,TRUE
    33.  
    34.   .elseif uMsg==WM_CLOSE
    35.     invoke DestroyWindow,hWnd
    36.     mov eax,TRUE
    37.  
    38.   .elseif uMsg==WM_DESTROY
    39.     invoke PostQuitMessage,0
    40.     mov eax,TRUE
    41.  
    42.   .elseif uMsg==WM_NCHITTEST
    43.     invoke DefWindowProc,hWnd,uMsg,wParam,lParam ; (NOT DefDlgProc, of course!)
    44.     .if eax==HTCLIENT
    45.       mov eax,HTCAPTION
    46.     .endif
    47.     invoke SetWindowLong,hWnd,DWL_MSGRESULT,eax
    48.     mov eax,TRUE  
    49.  
    50. ;  .elseif uMsg==WM_COMMAND
    51. ;    mov eax,wParam
    52. ;    and eax,0ffffh
    53. ;    invoke SetDlgItemInt,hWnd,id+5,eax,0
    54. ;    mov eax,TRUE
    55.  
    56. ;  .elseif uMsg==WM_CTLCOLORSTATIC || uMsg==WM_CTLCOLORDLG ; (ret.val != BOOL)
    57. ;    invoke GetStockObject,WHITE_BRUSH
    58.  
    59.   .else
    60.     mov eax,FALSE
    61.   .endif
    62.   ret
    63. DlgProc endp
    64.  
    65.  
    66. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    67.  
    68.   .if uMsg==WM_CREATE
    69. ;    invoke SetWindowText,hWnd,addr _app
    70.     invoke GetWindowLong,hWnd,GWL_STYLE
    71.     or eax,WS_THICKFRAME OR WS_MINIMIZEBOX OR WS_MAXIMIZEBOX
    72.     invoke SetWindowLong,hWnd,GWL_STYLE,eax
    73.     invoke SetWindowPos,hWnd,0,0,0,0,0,SWP_NOZORDER OR SWP_NOMOVE OR SWP_NOSIZE OR SWP_FRAMECHANGED OR SWP_NOACTIVATE
    74.     mov eax,FALSE
    75.  
    76. ;  .elseif uMsg==WM_CLOSE
    77. ;    invoke DestroyWindow,hWnd
    78. ;    mov eax,FALSE
    79.  
    80. ;  .elseif uMsg==WM_DESTROY
    81. ;    invoke PostQuitMessage,0
    82. ;    mov eax,FALSE
    83.  
    84. ;  .elseif uMsg==WM_NCHITTEST
    85. ;    invoke DefWindowProc,hWnd,uMsg,wParam,lParam ; (DefDlgProc also)
    86. ;    .if eax==HTCLIENT
    87. ;      mov eax,HTCAPTION
    88. ;    .endif
    89.  
    90.   .elseif uMsg==WM_COMMAND
    91.     mov eax,wParam
    92.     and eax,0ffffh
    93.     invoke SetDlgItemInt,hWnd,id+5,eax,0
    94.     mov eax,FALSE
    95.  
    96.   .elseif uMsg==WM_CTLCOLORSTATIC || uMsg==WM_CTLCOLORDLG ; (ret.val != BOOL)
    97.     invoke GetStockObject,WHITE_BRUSH
    98.  
    99.   .else
    100.     invoke DefDlgProc,hWnd,uMsg,wParam,lParam
    101.   .endif
    102.   ret
    103. WndProc endp
    104.  
    105. RegisterWndClass proc lpproc:WNDPROC,hInst:HINSTANCE,lpclass:LPCTSTR
    106.   local wc:WNDCLASSEX
    107.   mov   wc.cbSize,sizeof wc
    108.   invoke GetClassInfoEx,0,addr _dlg,addr wc
    109.   mov   eax,hInst
    110.   mov   wc.hInstance,eax  
    111.   mov eax,lpproc
    112.   mov   wc.lpfnWndProc,eax
    113.   mov eax,lpclass
    114.   mov   wc.lpszClassName,eax
    115.   invoke RegisterClassEx,addr wc
    116.   mov ecx,eax
    117.   mov eax,hInst
    118.   ret
    119. RegisterWndClass endp
    120.  
    121. DialogMessageLoop proc hWnd:HWND
    122.   local msg:MSG
    123.   .while TRUE
    124.     invoke GetMessage,addr msg,0,0,0
    125.     .break .if (!eax)
    126.     invoke IsDialogMessage,hWnd,addr msg
    127.     .if eax==FALSE
    128.       invoke TranslateMessage,addr msg
    129.       invoke DispatchMessage,addr msg
    130.     .endif
    131.   .endw
    132.   mov   eax,msg.wParam
    133.   ret
    134. DialogMessageLoop endp
    135.  
    136. start:
    137.   invoke GetModuleHandle,0
    138.   invoke RegisterWndClass,addr WndProc,eax,addr _wnd
    139.   invoke CreateDialogParam,eax,id,0,offset DlgProc,0
    140.   invoke DialogMessageLoop,eax
    141.   invoke ExitProcess,eax
    142.   invoke InitCommonControls
    143. end start
    .rc
    Код (Text):
    1. #include "\masm32\include\resource.h"
    2.  
    3. #define  id  100
    4. #define  bp  BUTTON, WS_TABSTOP | BS_DEFPUSHBUTTON
    5. #define  bt  BUTTON, WS_TABSTOP
    6. #define  ed  EDIT,   WS_TABSTOP | WS_BORDER
    7. #define  em  EDIT,   WS_TABSTOP | WS_BORDER | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_WANTRETURN
    8. #define  st  STATIC, SS_NOTIFY  | WS_BORDER
    9.  
    10. id DIALOGEX 20,5,200,150
    11. STYLE WS_POPUP | WS_SYSMENU | WS_VISIBLE
    12. EXSTYLE WS_EX_TOPMOST | WS_EX_CLIENTEDGE
    13. CAPTION ""
    14. CLASS "DlgClass"
    15. {
    16.  CONTROL "Button", id+1, bp, 10,  10, 80, 15
    17.  CONTROL "Button", id+2, bt, 10,  30, 80, 15
    18.  CONTROL "Edit",   id+3, ed, 10,  50, 80, 15
    19.  CONTROL "Edit",   id+4, em, 10,  70, 80, 35
    20.  CONTROL "Static", id+5, st, 10, 115, 80, 15
    21. }