Сказки дядюшки Римуса о x64

Тема в разделе "WASM.ARTICLES", создана пользователем Mikl___, 19 дек 2016.

Метки:
  1. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    Разбор полётов

    Сначала мы проанализируем код основной программы.
    Код (ASM):
    1.         invoke LoadLibrary,addr Libname
    2.            or eax,eax          ;.if eax!=NULL
    3.            jz @f
    4.            invoke FreeLibrary,eax
    5. @@:   . . .
    Мы вызовем LoadLibrary, чтобы загрузить DLL "splash.dll". После этого выгружаем ее из памяти функцией FreeLibrary. LoadLibrary не возвратится, пока DLL не закончит свою инициализацию.
    Это все, что делает основная программа. Интересующая нас часть находится в DLL.
    Код (ASM):
    1.       .if reason==DLL_PROCESS_ATTACH  ; When the dll is loaded
    2.          mov rax,hInst
    3.          mov hInstance,rax
    4.          invoke ShowBitMap
    После загрузки DLL в память, Windows вызывает ее входную функцию с флагом DLL_PROCESS_ATTACH. Мы пользуемся этой возможностью, чтобы отобразить сплэш-экран. Во-первых, мы сохраняем дескриптор DLL на будущее. Потом вызываем функцию ShowBitmap, которая выполняет главную работу. ShowBitmap регистрирует класс окна, создает окно и входит в цикл обработки сообщений. Следует обратить внимание на вызов CreateWindowEx:
    Код (ASM):
    1.            INVOKE CreateWindowEx,NULL,ADDR ClassName,NULL,\
    2.               WS_POPUP,CW_USEDEFAULT,\
    3.               CW_USEDEFAULT,250,250,NULL,NULL,\
    4.               hInstance,NULL
    Обратите внимание, что стиль окна WS_POPUP, что делает окно без бордюра и без заголовка. Мы также ограничиваем размер окна ― 250 x 250.
    Теперь, когда окно создано, в обработчике WM_CREATE мы передвигаем окно в центр экрана следующим кодом.
    Код (ASM):
    1.                    invoke GetWindowRect,hWnd,addr DlgRect
    2.                    invoke GetDesktopWindow
    3.                    invoke GetWindowRect,eax,addr DesktopRect
    4.                    and qword ptr [rsp+28h],0
    5.                    mov  eax,DlgRect.bottom
    6.                    sub  eax,DlgRect.top
    7.                    mov  DlgHeight,eax
    8.                    mov [rsp+20h],rax
    9.                    mov  r9d,DlgRect.right
    10.                    sub  r9d,DlgRect.left
    11.                    mov  DlgWidth,r9d
    12.                    mov  r8d,DesktopRect.bottom
    13.                    sub  r8d,DlgHeight
    14.                    shr  r8d,1
    15.                    mov  edx,DesktopRect.right
    16.                    sub  edx,DlgWidth
    17.                    shr  edx,1
    18.                    invoke MoveWindow,hWnd
    Мы получаем размеры рабочего стола и окна, а затем вычисляем координаты левого верхнего угла окна, чтобы оно было в центре.
    Код (ASM):
    1.                    invoke LoadBitmap,hInstance,addr BitmapName
    2.                    mov hBitMap,eax
    3.                    invoke SetTimer,hWnd,1,2000,NULL
    4.                    mov TimerID,eax
    Затем мы загружаем рисунок из ресурса функцией LoadBitmap и создаем таймеp, указывая в качестве его ID 1, а в качестве временного интервала 2 секунды. Таймеp будет посылать сообщения WM_TIMER окну каждый две секунды.
    Код (ASM):
    1. wmPAINT:    invoke BeginPaint,hWnd,addr ps
    2.                    mov hdc,eax
    3.                    invoke CreateCompatibleDC,hdc
    4.                    mov hMemoryDC,eax
    5.                    invoke SelectObject,eax,hBitMap
    6.                    mov hOldBmp,eax
    7.                    invoke GetObject,hBitMap,sizeof BITMAp,addr bitmap
    8.                    invoke StretchBlt,hdc,0,0,250,250,\
    9.                           hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOpY
    10.                    invoke SelectObject,hMemoryDC,hOldBmp
    11.                    invoke DeleteDC,hMemoryDC
    12.                    invoke EndPaint,hWnd,addr ps
    Когда окно получить сообщение WM_PAINT, она создаст DC в памяти, выберет в него рисунок, получит размер рисунка функцией GetObject, а затем поместит рисунок на окно, вызвав StretchBlt, которая действует как BitBlt, но адаптирует рисунок к желаемым размерам. В этом случае, нам нужно, чтобы рисунок поместился в окно, поэтому мы используем StrectchBlt вместо BitBlt. Мы удаляем созданный в памяти DC.
    Код (ASM):
    1. wmLBUTTONDOWN:       invoke DestroyWindow,hWnd
    Пользователя бы раздражало, если бы ему пришлось бы ждать, пока сплэш-экран не исчез. Мы можем предоставить пользователю выбор. Когда он кликнет на сплэш-экране, тот исчезнет. Вот почему нам нужно обрабатывать сообщение WM_LBUTTONDOWN. Когда мы получим это сообщение, окно будет уничтожено вызовом DestroyWindow.
    Код (ASM):
    1. wmTIMER:    invoke SendMessage,hWnd,WM_LBUTTONDOWN,NULL,NULL
    2.                    invoke KillTimer,hWnd,TimerID
    Если пользователь решит подождать, сплэш-экран исчезнет, когда пройдет заданный период времени (в нашем примере, это две секунды). Мы можем сделать это обработкой сообщения WM_TIMER. После получения этого сообщения, мы закрываем окно, послав ему сообщение WM_LBUTTONDOWN, чтобы избежать повторения кода. Таймер нам больше не нужен, поэтому мы уничтожаем его KillTimer.
    Когда окно будет закрыто, DLL возвращает контроль основной программе.
     
  2. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    Глава сорок пятая. Братец Кролик и подсказки (toolip control)

    [​IMG]
    Мы изучим контроль tooltip. Что это такое, как его создать и как им пользоваться.
    Скачайте пример здесь.

    Теория ― МАТЬ СКЛЕРОЗА

    Тултип ― это маленькая прямоугольное окно, которое отображается, когда курсор мыши находится над какой-то определенной областью. Окно тултипа содержит текст, заданный программистом. В этом отношении тултип играет ту же роль, что и окно статуса, но оно исчезает, когда пользователь кликает или убирает курсор мыши из заданной области. Вы, вероятно, знакомы с тултипами, ассоциированные с кнопками тулбара. Эти "тултипы" ― одно из удобств, предоставляемых тулбаром. Если вам нужны тултипы для других окон/контролов, вам необходимо создать собственный тултип контрол.
    Теперь, когда вы знаете, что такое тултип, давайте перейдем к тому, как мы можем создать и использовать его. Ниже pасписаны шаги:
    1. Создать тултип-контрол функцией CreateWindowEx.
    2. Определить регион, в котором он будет отслеживать передвижения мыши.
    3. Передать регион тултип-контролу.
    4. Передавать сообщения от мыши в указанном регионе тултип-контролу (этот шаг зависит от заданных флагов).
    Ниже мы детально проанализируем каждый шаг.

    Создание тултипа

    Тултип ― это common control. Поэтому вам необходимо где-нибудь в программе вызвать функцию InitCommonControls, чтобы MASM подлинковал к выходнуму экзешнику comctl32.dll. Вы создаете тултип с помощью CreateWindowEx. Это будет выглядеть примерно так:
    Код (ASM):
    1.        .data
    2.        TooltipClassName db "Tooltips_class32",0
    3.        .code
    4.        . . . . .
    5.        invoke InitCommonControls
    6.        invoke CreateWindowEx, NULL, addr TooltipClassName, NULL,
    7.        TIS_ALWAYSTIp, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    8.        CW_USEDEFAULT, NULL, NULL, hInstance, NULL
    Обратите внимание на стиль окна: TIS_ALWAYSTIP. Этот стиль указывает, что тултип будет показываться, когда курсор мыши будет находиться над заданной областью вне зависимости от статуса окна. То есть, если вы будете использовать этот флага, тултип будет появляться (когда курсор мыши будет находиться над определенной областью), даже если окно, с которым ассоциирован тултип, неактивно.
    Вам не нужно задавать слили WS_POPUP и WS_EX_TOOLWINDOW, потому что тултип определяет их автоматически. Вам также не нужно указывать координаты, ширину и высоту тултипа: он сам рассчитывает свои характеристики, поэтому в качестве всех четырех параметров мы указывает CW_USEDEFAULT. Оставшиеся параметры не играют роли.

    Определение tool'ов

    Тултип создается, но не отображается сразу. Нам нужно, чтобы он отображался только над определенной областью. Теперь пришло время задать ее. Мы называем такую область 'tool'. Tool ― это прямоугольная область клиентской части окна, в пределах которой тултип будет отслеживать передвижение мыши. Прямоугольная область может покрывать всю клиентскую часть окна или только некоторую долю от нее. Поэтому мы можем поделить 'tool' на два типа: один ― это, когда в качестве tool'а выступает целая клиентская область окна, а другой ― прямоугольная часть клиентской области окна. Оба типа находят свое применение. Например, наиболее часто тултипы первого типа используются вместе с кнопками, окнами ввода и так далее. Вам не нужно указывать координаты и размерность tool'а: предполагается, что будет задействована вся клиентская область. Tool'ы второго типа полезны, когда вы хотите поделить клиентскую часть окна на несколько регионов без использования дочерних окон. В этом случае вам будет необходимо задать координату верхнего левого угла, ширину и высоту tool'а.
     
    Последнее редактирование: 22 июн 2024
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Вы определяете характеристики tool'а в структуре TOOLINFO, которая имеет следующее определение:
    Код (ASM):
    1. TOOLINFO STRUCT
    2.        cbSize DWORD ? /* размер структуры TOOLINFO. Вы должны заполнить этот
    3.  параметр. Windows не будет отмечать ошибку, если это поле заполнено не правильно,
    4.  но вы получите странные, непредсказуемые результаты */
    5.        uFlags DWORD ? // битовые флаги определяют характеристики tool'а в таблице
    6.       hwnd  QWORD ?
    7.       uId      QWORD ?
    8.       rect RECT <>
    9.       hInst   QWORD ?
    10.       lpszText QWORD ?
    11. TOOLINFO ENDS
    константаназначение
    TTF_IDISHWNDЕсли вы укажете этот флаг, вы должны заполнить параметр uId дескриптором окна, который вы хотите использовать. Если вы не укажете этот флаг, это будет означать, что вы хотите использовать второй тип tool'а. В этом случае вам нужно заполнить параметр rect координатами прямоугольной области
    TTF_CENTERTIPобычно окно тултипа появляется справа и ниже курсора мыши. Если вы укажете этот флаг, окно тултипа появится ниже tool'а и отцентрируется независимо от позиции мышиного курсора
    TTF_RTLREADINGвы можете забыть об этом флаге, если ваша программа не предназначена специально для надписей на арабским или иврите. Этот флаг отображает текст тултипа справа налево. Не работает под другими системами
    TTF_SUBCLASSесли вы используете флаг, это означает, что вы указываете тултип-контролу сабклассировать окно tool'а, чтобы тултип мог интерпретировать сообщения от мыши, которые посылаются окну. Этот флаг очень удобен. Если вы не используете этот флаг, вам придется делать больше работы ― передавать сообщения от мыши тултипу
    hWnd ― дескриптор окна, который содержит tool. Если вы указали флаг TTF_IDISWND, это поле игнорируется, так как Windows будет использовать значение uId в качестве дескриптора окна. Вам нужно заполнить это поле, если:
    Вы не устанавливали флаг TTF_IDISHWND
    Вы указываете значение LPSTR_TEXTCALLBACK в параметре lpszText. Это значение указывает тултипу, что когда ему необходимо отобразить свое окно, он должен уведомить об этом окно, которое содержит tool. Это вид динамического обновления текста тултипа. Если вы хотите изменять динамически текст тултипа, вам следует LPSTR_TEXTCALLBACK в качестве значения LPSTR_TEXTCALLBACK. Тултип будет посылать уведомление TTN_NEEDTEXT окну, чей дескриптор содержится в поле hWnd.
    uId ― это поле может иметь одно из двух значений, в зависимости от того, содержит ли uFlags флаг IIF_IDISHWND.
    Определяемое приложением ID tool'а, если флаг TTF_IDISHWND. Так как это означает, что вы используете tool, покрывающее только часть клиентской области, то логично, что вы можете иметь несколько tool'ов на одной клиентской области (без пересечений). Тултип-контрол должен иметь какой-то путь отличать их друг от друга. В этом случае дескриптора окна hWnd не достаточно, так как все tool'ы находятся на одном и том же окне. Определяемые приложением ID служат именно для этой цели. ID может быть любым значением, главное, чтобы оно было уникально по отношению к другим ID.
    Дескриптор окна, чья клиентская область полностью используется в качестве tool'а, если указан флаг TTF_IDISHWND. Вы можете удивиться. почем это поле используется для хранения дескриптора окна, если есть hWnd? Ответ следующий: поле hWnd уже может быть заполнено, если в параметре lрszText указано значение LPSTR_TEXTCALLBACK. Окно, которое ответственно за предоставление текста тултипа, и окно, которое содержит tool, могут быть не одним и тем же.
    rect ― структура RECT, которая указывает размерность tool'а. Эта структура определяет прямоугольник относительного верхнего левого угла клиентской области окна, указанного в параметре hWnd. То есть, вы должны заполнить эту структуру, если вы хотите указать tool, который покрывает только часть клиентской области. Тултип-контрол проигнорирует это поле, если вы укажете флаг TTF_IDISHWND (вы хотите использовать в качестве tool'а целое окно).
    hInst ― это дескриптор процесса, содержащий ресурс строки, которая будет использована в качестве текста, если значение lpszText равно ID строкового ресурса. Это может вас несколько смутить. Прочтите сначала описание параметра lpszText, и вы поймете, для чего используется это поле. Тултип-контрол игнорирует это поле, если lpszText не содержит ID ресурса.
    lpszText ― это поле имеет несколько значений:
    Если вы укажете в этом поле значение LPSTR_TEXTCALLBACK, тултип будет посылать уведомительное сообщение TTN_NEEDTEXT окну, которое идентифицируется дескриптором поля hWnd, чтобы то предоставило тултипу текстовую строку. Это наиболее динамичный метод обновления текста тултипа: вы можете менять его каждый раз, когда отображается окно тултипа.
    Если вы укажете в этом поле ID строкового ресурса, тултип, когда ему потребуется отобразить текст в своем окне, будет искать строку в таблице строк процесса, заданного параметром hInst. Тултип-контрол идентифицирует ID ресурса следующим образом: так как ID ресурса ― это 16-битное значение, верхнее слово этого поля всегда будет pавно нулю. Этот метод полезен, если вы хотите портировать вашу программу на другие языки. Так как строковый ресурс определен в файле определения ресурсов, вам не нужно модифицировать исходный код. Вам только нужно изменить таблицу строк и текст тултипа изменится без риска внесения ошибок в программу.
    Если значение в этом поле не равно LPSTR_TEXTCALLBACK и верхнее слово не равно нулю, тултип-контрол интерпретирует значение как указатель на текстовую строку, которая будет использована в качестве текста тултипа. Этот метод самый простой, но наименее гибкий.
    резюме: вы должны заполнить структуру TOOLINFO и передать ее тултипу. Эта структура задаст характеристики tool'а.

    регистрация tool'а

    После того, как вы заполнили структуру TOOLINFO, вы должны передать ее тултипу. Тултип может обслуживать много tool'ов, поэтому обычно одно тултипа хватает на все окно. Чтобы зарегистрировать tool, вы посылаете тултипу сообщение TTM_ADDTOOL. wParam не используется, а lParam должен содержать адрес структуры TOOLINFO.
    Код (ASM):
    1. .data?
    2.        ti TOOLINFO <>
    3.        .......
    4.        .code
    5.        .......
    6.        .......
    7.        invoke SendMessage, hwndTooltip, TTM_ADDTOOL, NULL, addr ti
    SendMessage возвратит TRUE, если tool был успешно зарегистрирован тултипом или FALSE в обратном случае. Вы можете удалить tool сообщением TTM_DELTOOL.

    Передача сообщений от мыши тултипу

    Когда вышеописанные шаги выполнены, тултип имеет всю необходимую информацию о том, в какой области он должен отслеживать сообщения мыши и какой текст он должен отображать. Единственное, что отсутсвует ― это триггер. Подумайте: область, указанная в качестве tool'а находится на клиентской части другого окна. Как может тултип перехватить сообщения от мыши для этого окна? Необходимо, чтобы он мог измерить количество времени, которое курсор мыши находится над tool'ом, чтобы вовремя отобразить окно тултипа. Есть два метода, чтобы достичь этой цели, один требует помощи со стороны окна, которое tool, а другой этого не требует.
    Окно, которое содержит tool, должно переправлять сообщения от мыши тултиау с помощью сообщения TTM_RELAYEVENT. lParam должен содержать адрес структуры MSG, содержащую сообщение от мыши. Тултип обрабатывает только следующие сообщения от мыши:
    • WM_LBUTTONDOWN
    • WM_MOUSEMOVE
    • WM_LBUTTONUP
    • WM_RBUTTONDOWN
    • WM_MBUTTONDOWN
    • WM_RBUTTONUP
    • WM_MBUTTONUP
    Все другие сообщения игнорируются. Таким образом, в процедуре окна, содержащего tool, должен быть обработчик вроде следующего:
    Код (ASM):
    1. Wndproc proc hWnd:DWORD, uMsg:DWORD, wparam:DWORD, lparam:DWORD
    2.    .......
    3.       if uMsg==WM_CREATE
    4.            .............
    5.        elseif uMsg==WM_LBUTTONDOWN || uMsg==WM_MOUSEMOVE || \
    6.               uMsg==WM_LBUTTONUP || uMsg==WM_RBUTTONDOWN || \
    7.               uMsg==WM_MBUTTONDOWN || uMsg==WM_RBUTTONUP || \
    8.               uMsg==WM_MBUTTONUP
    9.            invoke SendMessage, hwndTooltip, TTM_RELAYEVENT, NULL, addr msg
    10.            ..........
    Вы можете указать флаг TTF_SUBCLASS в параметре uFlags структуры TOOLINFO. Этот флаг указывает тултипу сабклассировать окно, которое содержит tool, чтобы перехватывать сообщения от мыши без участия сабклассированного окна. Этот метод проще использовать и он требует меньше усилий, так как тултип берет всю обработку сообщений на себя.
    Вот и все. Теперь ваш тултип полностью функционален. Есть несколько полезных тултиповых сообщений, о которых вас следует знать.
    • TTM_ACTIVATE ― если вы хотите включать/выключать контрол динамически, это сообщение для вас. Если wparam равен TRUE, тултип включается, если wParam равен FALSE, тултип выключается. Тултип включается автоматически, как только он был создан, поэтому вам не надо посылать сообщение, чтобы активировать его.
    • TTM_GETTOOLINFO и TTM_SETTOOLINFO. Если вы хотите получить/изменить значения в структуре TOOLINFO после того, как она была отправлена тултипу, используйте данное сообщение. Вам потребуется указать tool, чьи характеристики вы хотите изменить, с помощью верных uId и hWnd. Если вы хотите изменить только параметр rect, используйте сообщение TTM_NEWTOOLRECT. Если вам нужно только изменить текст тултипа, используйте TTM_UPDATATIPTEXT.
    • TTM_SETDELAYTIME. С помощью этого сообщения вы можете задать временную задержку, которую будет использовать тултип.
     
    Последнее редактирование: 12 май 2019
  4. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    ПРАКТИКА ― СЕСТРА ШИЗОФРЕНИИ

    Следующий пример ― это простое диалоговое окно с двумя кнопками. Клиентская область диалогового окна поделена на 4 области: верхняя левая, верхняя правая, нижняя левая и нижняя правая. Каждая область указана как tool с собственным текстом. Две кнопки также имеют свои собственные тексты подсказок.

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE        equ 400000h
    3. IDD_MAINDIALOG equ 101
    4. .code
    5. WinMain proc
    6.     sub esp,38h
    7.     xor r8d,r8d
    8.     mov [rsp+20h],r8
    9.     mov r9d,offset dialog_procedure
    10.     invoke DialogBoxParam,IMAGE_BASE,IDD_MAINDIALOG
    11.     invoke ExitProcess,0
    12.     invoke InitCommonControls
    13. WinMain endp
    14. dialog_procedure proc hDlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD
    15. local ti:TOOLINFO
    16. local rect:RECT
    17.     mov hDlg,rcx
    18.  
    19.     cmp edx,WM_INITDIALOG
    20.     je wmINITDIALOG
    21.     cmp edx,WM_CLOSE
    22.     je wmCLOSE
    23.     xor eax,eax
    24.     jmp wmBYE
    25.  
    26. wmINITDIALOG:xor ecx,ecx
    27.     mov esi,IMAGE_BASE
    28.     push rcx
    29.     push rsi
    30.     push rcx
    31.     push rcx
    32.     shl esi,9
    33.     push rsi
    34.     push rsi
    35.     push rsi
    36.     push rsi
    37.     mov r9d,TTS_ALWAYSTIP
    38.     xor r8d,r8d
    39.     mov edx,offset ToolTipsClassName
    40.     sub esp,20h
    41.     call CreateWindowEx
    42.     mov hwndTool,rax
    43.     mov ti.cbSize,sizeof TOOLINFO
    44.     mov ti.uFlags,TTF_SUBCLASS
    45.     mov rcx,hDlg
    46.     mov ti.hwnd,rcx
    47.     lea edx,rect
    48.     call GetWindowRect
    49.     mov ebx,3
    50. @@:    lea ecx,rect
    51.     mov r9d,ebx
    52.     mov r8,handle[rbx*8]
    53.     lea edx,ti
    54.     call SetDlgToolArea
    55.     dec ebx
    56.     jns @b
    57.  
    58.     lea r8d,ti
    59.     mov edx,offset EnumChild
    60.     mov rcx,hDlg
    61.     call EnumChildWindows
    62.     jmp wmBYE
    63. wmCLOSE:xor edx,edx
    64.     ;mov rcx,hDlg
    65.     call EndDialog
    66. wmBYE:    leave
    67.     ret
    68. dialog_procedure endp
    69. EnumChild proc hwndChild:QWORD,lParam:QWORD
    70. local buffer[256]:BYTE
    71.     push rbp
    72.     mov ebp,esp
    73.     sub esp,(20h+256+15)and(-16)
    74.     mov lParam,rdx
    75.     mov [rdx+TOOLINFO.uId],rcx;rcx=hwndChild
    76.     or [rdx+TOOLINFO.uFlags],TTF_IDISHWND
    77.     mov r8d,255
    78.     lea edx,buffer
    79.     ;mov rcx,hwndChild
    80.     invoke GetWindowText
    81.     lea eax,buffer
    82.     mov r9,lParam
    83.     mov [r9+TOOLINFO.lpszText],rax
    84.     xor r8d,r8d
    85.     mov edx,TTM_ADDTOOL
    86.     mov rcx,hwndTool
    87.     call SendMessage
    88.     leave
    89.     retn
    90. EnumChild endp
    91. SetDlgToolArea proc lprect:QWORD,lpti:QWORD,lpText:QWORD,id:QWORD
    92.     push rbp
    93.     mov ebp,esp
    94.     sub esp,20h
    95.  
    96.     jmp [handels+r9*8]
    97. id_0:    mov [rdx+TOOLINFO.Rect.left],0
    98.         mov [rdx+TOOLINFO.Rect.top],0
    99.         mov eax,[rcx+RECT.right]
    100.         sub eax,[rcx+RECT.left]
    101.         shr eax,1
    102.         mov [rdx+TOOLINFO.Rect.right],eax
    103.         mov eax,[rcx+RECT.bottom]
    104.         sub eax,[rcx+RECT.top]
    105.         shr eax,1
    106.     jmp wmBYE
    107. id_1:    mov eax,[rcx+RECT.right]
    108.         sub eax,[rcx+RECT.left]
    109.         shr eax,1
    110.         inc eax
    111.         mov [rdx+TOOLINFO.Rect.left],eax
    112.         mov [rdx+TOOLINFO.Rect.top],0
    113.         mov eax,[rcx+RECT.right]
    114.         sub eax,[rcx+RECT.left]
    115.     jmp a1
    116. id_2:    mov [rdx+TOOLINFO.Rect.left],0
    117.         mov eax,[rcx+RECT.bottom]
    118.         sub eax,[rcx+RECT.top]
    119.         shr eax,1
    120.         inc eax
    121.         mov [rdx+TOOLINFO.Rect.top],eax
    122.         mov eax,[rcx+RECT.right]
    123.         sub eax,[rcx+RECT.left]
    124.         shr eax,1
    125.     jmp a1
    126. id_3:    mov eax,[rcx+RECT.right]
    127.         sub eax,[rcx+RECT.left]
    128.         shr eax,1
    129.         inc eax
    130.         mov [rdx+TOOLINFO.Rect.left],eax
    131.         mov eax,[rcx+RECT.bottom]
    132.         sub eax,[rcx+RECT.top]
    133.         shr eax,1
    134.         inc eax
    135.         mov [rdx+TOOLINFO.Rect.top],eax
    136.         mov eax,[rcx+RECT.right]
    137.         sub eax,[rcx+RECT.left]
    138. a1:        mov [rdx+TOOLINFO.Rect.right],eax
    139.         mov eax,[rcx+RECT.bottom]
    140.         sub eax,[rcx+RECT.top]
    141. wmBYE:    mov [rdx+TOOLINFO.Rect.bottom],eax
    142.     mov [rdx+TOOLINFO.lpszText],r8;lpText
    143.     mov r9d,edx;lpti
    144.     xor r8d,r8d;NULL
    145.     mov edx,TTM_ADDTOOL
    146.     mov rcx,hwndTool
    147.     call SendMessage
    148.     leave
    149.     ret
    150. handels dq id_0, id_1, id_2, id_3
    151. SetDlgToolArea endp
    152. ;---------------------------------------------------------
    153. ToolTipsClassName db "Tooltips_class32",0
    154. MainDialogText1 db "This is the upper left area of the dialog",0
    155. MainDialogText2 db "This is the upper right area of the dialog",0
    156. MainDialogText3 db "This is the lower left area of the dialog",0
    157. MainDialogText4 db "This is the lower right area of the dialog",0
    158. handle dq MainDialogText1,MainDialogText2,MainDialogText3,MainDialogText4
    159. hwndTool dq ?
    160. end

    rc-файл

    Код (C):
    1. #include "resource.h"
    2. #define IDD_MAINDIALOG                  101
    3. #define IDC_OK                          1000
    4. #define IDC_EXIT                        1001
    5. IDD_MAINDIALOG DIALOG DISCARDABLE  0, 0, 229, 96
    6. STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
    7.     WS_SYSMENU | DS_CENTER
    8. CAPTION "Tooltip Example"
    9. FONT 8, "MS Sans Serif"
    10. BEGIN
    11.     PUSHBUTTON      "",  -1,0,  -4,115,51,WS_VISIBLE+BS_GROUPBOX
    12.     PUSHBUTTON      "",  -1,114,43,115,53,WS_VISIBLE+BS_GROUPBOX
    13.     PUSHBUTTON      "OK",IDC_OK,55,68,50,14
    14.     PUSHBUTTON      "E&xit",IDC_EXIT,123,68,50,14
    15. END

    Разбор полётов


    После того, как создано основное диалоговое окно, мы создает тултип-контрол функцией CreateWindowsEx.
    Код (ASM):
    1.        invoke InitCommonControls
    2.        invoke CreateWindowEx,NULL,ADDR ToolTipsClassName,NULL,\
    3.               TTS_ALWAYSTIp,CW_USEDEFAULT,\
    4.               CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
    5.               hInstance,NULL
    6.        mov hwndTool,eax
    После этого мы переходим к определению четырех tool'ов для каждого угла диалогового окна.
    Код (ASM):
    1.        mov id,0        ; used as the tool ID
    2.        mov ti.cbSize,sizeof TOOLINFO
    3.        mov ti.uFlags,TTF_SUBCLASS   ; tell the tooltip control to subclass the dialog window.
    4.        push hDlg
    5.        pop ti.hWnd    ; handle to the window that contains the tool
    6.        invoke GetWindowRect,hDlg,addr rect   ; получаем размерность клиентской
    7.                                              ; области
    8.        invoke SetDlgToolArea,hDlg,addr ti,addr MainDialogText1,id,addr rect
    Мы инициализируем члены структуры TOOLINFO. Заметьте, что мы хотим поделить клиентскую область на 4 tool'а, поэтому нам нужно знать размерность клиентской области. Это то, для чего мы вызываем GetWindowsRect. Мы не хотим передавать сообщения мыши тултипу, поэтому мы указываем флага TIF_SUBCLASS.
    SetDlgToolArea ― это функция, которая высчитывает координаты прямоугольной области каждого tool'а и регистрирует tool в тултипе. Функция делит клиентскую область на 4 области с одними и теми же размерами. Затем она посылает сообщение TTN_ADDTOOL тултипу, передавая адрес структуры TOOLINFO в lParam.
     
    Последнее редактирование: 4 янв 2017
  5. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Код (ASM):
    1. invoke SendMessage,hwndTool,TTM_ADDTOOL,NULL,lpti
    После того, как все 4 tool'а зарегистрированы, мы можем перейти к кнопкам на диалоговом окне. Мы можем обрабатывать каждую кнопку с помошью ее ID, но это утомительно. Вместо этого мы используем EnumChildWindows API, чтобы перечислить все контролы на диалоговом окне и затем зарегистрировать для каждого из них подсказку. EnumChildWindows имеет следующий синтаксис:
    Код (C):
    1. BOOL WINAPI EnumChildWindows(
    2.   _In_opt_ HWND        hWndParent,// дескриптор родительского окна
    3.   _In_     WNDENUMPROC lpEnumFunc,
    4.   _In_     LPARAM      lParam
    5. );
    • lpEnumFunc ― адрес функции EnumChildProc, которая будет вызываться для каждого перечисленного контрола.
    • lParam ― заданное приложением значение, которое будет передано EnumChildProc.
    У этой функции следующее определение:
    Код (C):
    1. BOOL CALLBACK EnumChildProc(
    2.   _In_ HWND   hwnd,
    3.   _In_ LPARAM lParam
    4. );
    • hwndChild ― хэндл контрола, найденного EnumChildWindows.
    • lParam ― это тоже значение, что вы передали EnumChildWindow.
    В нашем примере мы вызываем EnumChildWindows следующим образом:
    Код (ASM):
    1.        invoke EnumChildWindows,hDlg,addr EnumChild,addr ti
    Мы передаем адрес структуры TOOLINFO в параметре lParam, потому что мы будем регистрировать подсказки для каждого дочерний контрол в функции EnumChild. Если мы не будем использовать данный метода, нам придется объявить глобальную переменную, чтобы предотвратить баги.
    Когда мы вызываем EnumChildWindows, Windows перечислит дочерние конролы нашего диалогового окна и вызовет для каждого из ни функцию EnumChild. То есть, если наше диалоговое окно имеет два контрола, EnumChild будет вызван дважды.
    Функция EnumChild заполнит соответствующие поля структуры TOOLINFO и зарегистрирует tool.
    Код (ASM):
    1. EnumChild proc uses edi hwndChild:DWORD,lparam:DWORD
    2.            LOCAL buffer[256]:BYTE
    3.  
    4.            mov edi,lParam
    5.            assume edi:ptr TOOLINFO
    6.            push hwndChild
    7.            pop [edi].uId   ; we use the whole client area of the control as the tool
    8.            or [edi].uFlags,TTF_IDISHWND
    9.            invoke GetWindowText,hwndChild,addr buffer,255
    10.            lea eax,buffer    ; use the window text as the tooltip text
    11.            mov [edi].lpszText,eax
    12.            invoke SendMessage,hwndTool,TTM_ADDTOOL,NULL,edi
    13.            assume edi:nothing
    14.            ret
    15.        EnumChild endp
    Заметьте, что в этом случае мы используем другой тип tool'ов, покрывающий всю клиентскую область окна. Поэтому нам нужно заполнить поле uID дескриптором окна, которое содержит tool. Также мы указываем флаг TTF_IDISHWND в параметре uFlags.
    [​IMG]
     

    Вложения:

    • tut_27.png
      tut_27.png
      Размер файла:
      20,3 КБ
      Просмотров:
      3.047
    Последнее редактирование: 5 янв 2017
  6. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    Глава сорок шестая. Братец Кролик и Контрол Listview


    [​IMG]
    В этой главе мы изучим как создать и использовать элемент управления listview.
    Скачайте пример здесь.

    Теория ― МАТЬ СКЛЕРОЗА

    Listview ― это один из common control'ов, таких как treeview, richedit и так далее. Вы знакомы с ними, даже если не знаете их имен. Например, правая панель Windows Explorer'а ― это элемент управления listview. Этот элемент управления подходит для отображения элементов. В этом отношении его можно рассматривать как усовершенствованный listbox.
    Вы можете создать listview двумя путями. Первый метод самый простой: создайте его с помощью редактора ресурсов, главное не забудьте поместить вызов InitCommonControls. Другой метод заключается в вызове CreateWindowsEx. Вы должны указать правильное имя класса окна, то есть SysListView32.
    Существует четыре метода отображения элементов в listview:
    • иконки
    • маленькие иконки
    • список
    • отчет
    Вы можете увидеть чем отличаются виды отображения друг от друга, выбрав View[math]\to[/math]Large Icons (иконки), Small Icons (маленькие иконки), List (список) и Details (отчет)
    Теперь, когда мы знаем, как создать listview, мы рассмотрим, как его можно применять. Сосредоточимся на режиме "отчет", как методе отображения, который может продемонстрировать многие свойства listview. Шаги использования listview следующие:
    1. Создаем listview с помощью CreateWindowEx, указав SysListView32 как имя класса. Вы должны указать начальный тип отображения.
    2. (если предусматривается) Создаем и инициализируем списки изображений, которые будут использованы при отображении item'ов listview.
    3. Вставляем колонки в listview. Этот шаг необходим, если listview будет использовать тип отображения 'отчет'.
    4. Вставьте item'ы и подitem'ы в listview.

    Колонки

    При отчете в listview может быть одна или более колонок. Вы можете считать тип организации данных в этом режиме таблицей: данные организованны в ряды и колонки. В режиме отчета в listview должна быть по крайней мере одна колонка. В других режимах вам не надо вставлять колонку, так как в контроле будет одна и только одна колонка.
    Вы можете вставить колонку, послав сообщение LVM_INSERTCOLUMN контролу listview.
    LVM_INSERTCOLUMN
    wParam = iCol
    lParam = указатель на структуру LV_COLUMN
    iCol ― это номеp колонки, начиная с нуля. Структура LV_COLUMN содержит информацию о колонке, которая должна быть вставлена. У нее следующее определение:
    Код (ASM):
    1. LV_COLUMN STRUCT
    2.   imask     DWORD ?
    3.   fmt     DWORD ?
    4.   lx     DWORD ?,?
    5.   pszText    QWORD ?
    6.   cchTextMax DWORD ?
    7.   iSubItem   DWORD ?
    8. LV_COLUMN ENDS
    • imask ― коллекция флагов, задающие, какие члены структуры верны. Этот параметр был введен, потому что не все члены этой структуры используются одновременно. Некоторые из них используются в особых ситуациях. Эта структура используются и для ввода и для вывода, поэтому важно, чтобы вы пометили, какие параметры верны. Существуют следующие флаги:
      LVCF_FMTПараметр fmt верен
      LVCF_SUBITEMПараметр isubItem верен
      LVCF_TEXTПараметр pszText верен
      LVCF_WIDTHПараметр lx верен
      Вы можете комбинировать вышеприведенные флаги. Например, если вы хотите указать текстовое имя колонки, вам нужно предоставить указатель на строку в параметре pszText. Также вы должны указать Windows, что параметр pszText содержит данные, указав флаг LVCF_TEXT в этом поле, иначе Windows будет игнорировать значение pszText.
    • fmt ― указывает выравнивание элементов/подэлементов в колонке. Доступны следующие значения:
      LVCFMT_CENTERтекст с выравниванием по центру
      LVCFMT_LEFTтекст с выравниванием влево
      LVCFMT_RIGHTтекст с выравниванием вправо
    • lx ― ширина колонки в пикселях. В дальнейшем вы можете изменить ширину колонки LVM_SETCOLUMNWIDTH.
    • pszText ― содержит указатель на имя колонки, если эта структура используется для установки свойств колонки. Если эта структура используется для получения свойств колонки, это поле содержит указатель на буфер, достаточно большой для получения имени колонки, которая будет возвращена. В этом случае вы должны указать размер буфера в поле cchTextMax. Вы должны игнорировать cchTextMax, если вы хотите установить имя колонки, потому что имя должно быть ASCIIZ-строкой, длину которой Windows сможет определить.
    • cchTextMax ― размер в байтах буфер, указанного в поле pszText. Этот параметр используется только когда вы используете структуру для получения информации о колонке. Если вы используете эту структуру, чтобы установить свойства колонки, это поле будет игнорироваться.
    • iSubItem ― указывает индекс подэлемента, ассоциированного с этой колонкой. Это значение используется в качестве маркера подэлемента, с которым ассоциирована эта колонка. Если хотите, вы можете указать бессмысленный номер и ваш listview будет прекрасно работать. Использование этого поля лучше всего демонстрируется, когда у вас есть номер колонки и вам нужно узнать с каким поэлементом ассоциирована эта колонка. Чтобы сделать это, вы можете послать сообщение LVM_GETCOLUMN, указав в параметре imask флаг LVCF_SUBITEM. Listview заполнит параметр iSubItem значением, которое вы укажете в этом поле, поэтому для работоспособности данного метода вам нужно указывать корректные подэлементы в этом поле.
    • iImage и iOrder ― используется начиная с Internet Explorer 3.0.
    Когда listview создан, вам нужно вставить в него одну или более колонок. Если не предполагается переключение в режим отчета, то это не нужно. Чтобы вставить колонку, вам нужно создать структуру LV_COLUMN, заполнить ее необходимой информацией, указать номер колонки, а затем послать структуру listview с помощью сообщения LVM_INSERTCOLUMN.
    Код (ASM):
    1.  LOCAL lvc:LV_COLUMN
    2.  
    3.           mov lvc.imask,LVCF_TEXT+LVCF_WIDTH
    4.           mov lvc.pszText,offset Heading1
    5.           mov lvc.lx,150
    6.           invoke SendMessage,hList, LVM_INSERTCOLUMN,0,addr lvc
    Вышеприведенный кусок кода демонстрирует процесс. Он указывает текста заголовка и его ширину, а затем посылает сообщение LVM_INSETCOLUMN listview. Это просто.

    Item'ы и под-item'ы

    Item'ы ― это основные элементы listview. В режимах отображения, отличных от отчета, вы будет видеть только item'ы. Под-item'ы ― это детали item'ов. Например, если item ― это имя файла, тогда вы можете считать атрибуты файла, его размер, дату создания файла как под-item'ы. В режиме отчета самая левая колонка содержит item'ы, а остальные ― под-item'ы. Вы можете думать о item'е и его под-item'ах как о записи базы данных. Item ― это основной ключ записи и его под-item'ы ― это поля записи.
    Минимум, что вам нужно иметь в listview ― это item'ы, под-item'ы необязательны. Тем не менее, если вы хотите дать пользователю больше информации об элементах, вы можете ассоциировать item'ы с под-item'ами, чтобы пользователь мог видеть детали в режиме отчета.
    Вставить item в listview можно послав сообщение LVM_INSERTITEM. Вам также нужно передать адрес структуры LV_ITEM в lParam. LV_ITEM имеет следующее определение:
    Код (ASM):
    1. LV_ITEM STRUCT
    2.   imask     DWORD ?
    3.   iItem      DWORD ?
    4.   iSubItem   DWORD ?
    5.   state      DWORD ?
    6.   stateMask  DWORD ?,?
    7.   pszText    QWORD ?
    8.   cchTextMax DWORD ?
    9.   iImage     DWORD ?
    10.   lParam     DWORD ?
    11.   iIndent    DWORD ?
    12. LV_ITEM ENDS
    • imask ― множество флагов, которые задают, какие из параметров данной структуры будут верны. В сущности, это поле идентично параметру imask LV_COLUMN. Чтобы получить детали относительно флагов, обратитесь к вашему справочнику по API.
    • iItem ― индекс item'а, на который ссылается эта структура. Индексы начинаются с нуля. Вы можете считать, что это поле содержит значение "ряда" таблицы.
    • iSubItem ― индекс под-item'а, ассоциированный с item'ом, заданном в iItem. Вы можете считать, что это поле содержит "колонку" таблицы. Например, если вы хотите вставить item в только что созданный listview, значение в iItem будет равно 0 (потому что этот item первый), а значение в iSubItem также будет равно нулю (нам нужно вставить item в первую колонку). Если вы хотите указать под-item, ассоциированный с этим item'ом, iItem будет являться индексом item'а, с которым будет происходить ассоциирование (в выше приведенном примере это 0). iSubItem будет равен 1 или более, в зависимости от того, в какую колонку вы хотите вставить под-item. Например, если у вашего listview 4 колонки, первая колонка будет содержать item'ы. Остальные 3 колонки предназначаются для под-item'ов. Если вы хотите вставить под-item в 4-ую колонку, вам нужно указать в iSubItem значение 3.
     

    Вложения:

    • 4.png
      4.png
      Размер файла:
      295,4 КБ
      Просмотров:
      3.038
    Последнее редактирование: 22 июн 2024
  7. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    • state ― параметр, содержащий флаги, отражающие состояние item'а. Оно может изменяться из-за действий пользователя или другой программы. Термин 'состояние' включает в себя, находится ли item в фокусе, подсвечен ли он, выделен для операции вырезания, выбран ли он. В добавление к флагам состояния он также содержит основанный на единице индекс изображения состояния данного item'а.
    • stateMask ― так как параметр state может содержать флаги состояния, индекс изображения, нам требуется сообщить Windows, какое значение мы хотим установить или получить. Это поле создано именно для этого.
    • pszText ― адрес ASCIIZ-строки, которая будет использоваться в качестве названия элемента в случае, если мы хотим установить или вставить элемент. Если мы используем эту структуру для того, чтобы получить свойства элемента, этот параметр должен содержать адрес буфера, который будет заполнен названием элемента.
    • cchTextMax ― это поле используется только тогда, когда вы используете данную структуру, чтобы получать информацию об элементе. В этом случае это поле содержит размер в байтах буфера, указанного параметром pszText.
    • iImage ― индекс image list'а, содержащего иконки для listview. Индекс указывает на иконку, которая будет использоваться для этого элемента.
    • lParam ― определяемое пользователем значение, которое будет использоваться, когда вы будете сортировать элементы в listview. Кратко говоря, когда вы будете указывать listview отсортировать item'ы, listview будет сравнивать item'ы попарно. Он будет посылать значение lParam обоих элементов вам, чтобы вы могли решить, какое из этих двух должно быть в списке идти раньше. Если вы пока не можете этого понять, не беспокойтесь. Вы изучите сортировку позже.
    Давайте кратко изложим шаги вставки элемента/подэлемента в listview.
    • Создаем переменную типа структуры LV_ITEM.
    • Заполняем ее необходимой информацией.
    • Посылаем сообщение LVM_INSERTITEM listview, если вам нужно вставить элемент. Или, если вы хотите вставить подэлемент, посылаем сообщение LVM_SETITEM. Это может смущать вас, если вы не понимаете взаимоотношений между элементом и его подэлементами. Подэлементы считаются свойствами элемента. Поэтому вы можете вставить item'ы, но не под-item'ы, а также у вас не может быть подэлемента без ассоциированного с ним элемента. Вот почему вам нужно послать сообщение LVM_SETITEM, чтобы добавить подэлемент вместо LVM_INSERTITEM.

    Сообщения/уведомления listview

    Теперь, когда вы знаете, как создавать и заполнять элементами listview, следующим шагом является общение с ним. Listview общается с родительским окном через сообщения и уведомления. Родительское окно может контролировать listview, посылая ему сообщения. Listview уведомляет родительское окно о важных/интересных сообщения через сообщение WM_NOTIFY, как и другие common control'ы.

    Сортировка элементов/подэлементов

    Вы можете указать порядок сортировки контрола listview по умолчанию указав стили LVS_SORTASCENDING или LVS_SORTDESCENDING в CreateWindowEx. Эти два стиля упорядочивают элементы только по элементам. Если вы хотите отсортировать элементы другим путем, вы должны послать сообщение LVM_SORTITEMS listview.
    LVM_SORTITEMS
    wParam = lParamSort
    lParam = pCompareFunction
    lParamSort
    ― это определяемое пользователем значение, которое будет передаваться функции сравнения. Вы можете использовать это значение любым путем, которым хотите.
    pCompareFunction ― это адрес задаваемой пользователем функции, которая будет определять результат сравнения item'ов в listview. Функция имеет следующий прототип:
    Код (ASM):
    1.    CompareFunc proto lParam1:QWORD, lParam2:QWORD, lParamSort:QWORD
    lParam1 или lParam2 ― это значения параметра lParam LV_ITEM, который вы указали, когда вставляли элементы в listview.
    lParamSort ― это значение wParam, посланное вместе с сообщением LVM_SORTITEMS.
    Когда listview получает сообщение LVM_SORTITEMS, она вызывает сортирующую функцию, указанную в параметре lParam, когда ей нужно узнать результат сравнения двух элементов. Кратко говоря, функция сравнения будет решать, какой из двух элементов, посланных ей, будет предшествовать другому. Правило простое: если функция возвращается отрицательное значение, тогда первый элемент (указанный в lParam1) будет предшествовать другому.
    Если функция возвращает положительное значение, второй элемент (заданный параметром lParam2) должен предшествовать первому. Если оба равны, тогда функция должна возвратить ноль.
    Что заставляет этот метод работать, так это значение lParam структуры LV_ITEM. Если вам нужно отсортировать item'ы (например, когда пользователь кликает по заголовку колонки), вам нужно подумать о схеме сортировки, в которой будет использоваться значения параметра lParam. В данном примере я помещаю это поле индекс элемента, чтобы получить другую информация о нем, послав сообщение LVM_GETITEM. Заметьте, что когда элементы перегруппированы, их индексы также меняется. Поэтому когда сортировка в моем примере выполнена, мне необходимо обновить значения в lParam, чтобы учесть новые значения индексов. Если вы хотите отсортировать элементы, когда пользователь кликает по заголовку колонки, вам нужно обработать уведомительное сообщение LVN_COLUMNCLICK в вашей оконной процедуре. LVN_COLUMNCLICK передается вашему окну через сообщение WM_NOTIFY.
     
    Последнее редактирование: 5 янв 2017
  8. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    ПРАКТИКА ― СЕСТРА ШИЗОФРЕНИИ

    Этот пример создает listview и заполняем его именами и размерами полей текущей папки. Режим отображения элементов по умолчанию поставлен в 'отчет'. В этом режиме вы можете кликать по заголовку колонок и элементы будут отсортированы согласно восходящему/нисходящему порядку. Вы можете выбрать режим отображения в меню. Когда вы делает двойной клик по элементу, показывается окно с названием элемента.

    Вариант #1

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2.  
    3. IMAGE_BASE        equ 400000h
    4. IDM_MAINMENU        equ 10000
    5. IDM_ICON        equ LVS_ICON
    6. IDM_SMALLICON        equ LVS_SMALLICON
    7. IDM_LIST        equ LVS_LIST
    8. IDM_REPORT        equ LVS_REPORT
    9.  
    10. .code
    11. WinMain proc
    12. local msg:MSG
    13.  
    14.         push rbp
    15.     mov ebp,esp
    16.     sub esp,sizeof MSG
    17.  
    18.     xor ebx,ebx
    19.     call InitCommonControls
    20.         mov eax,10029h
    21.     mov esi,IMAGE_BASE
    22.     mov edi,offset ClassName
    23.     push rax    ;hIconSm
    24.     push rdi    ;lpszClassName
    25.     push IDM_MAINMENU;lpszMenuName
    26.     push COLOR_WINDOW+1;hbrBackground
    27.     push 10005h    ;hCursor
    28.     push rax     ;hIcon  
    29.     push rsi    ;hInstance
    30.     push rbx    ;cbClsExtra & cbWndExtra
    31.     db 68h
    32.     dd WndProc;lpfnWndProc
    33.     push sizeof WNDCLASSEX;cbSize & style
    34.     mov rcx,rsp    ;addr WNDCLASSEX
    35.     call RegisterClassEx
    36.  
    37.     push rbx
    38.     push rsi    ;rsi=400000h
    39.     shl esi,9    ;rsi=CW_USEDEFAULT
    40.     push rbx
    41.     push rbx
    42.     push 200
    43.     push 400
    44.     push rsi
    45.     push rsi
    46.     mov r9d,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    47.     mov r8,rdi    ;offset ClassName
    48.     mov edx,edi    ;offset ClassName
    49.     mov ecx,WS_EX_CLIENTEDGE
    50.     sub esp,20h
    51.     call CreateWindowEx
    52.  
    53.     push rbx;<-- align 10h
    54.  
    55.     push IMAGE_BASE
    56.     push rbx;NULL
    57.     push rax;hWnd
    58.     push 240
    59.     push 400
    60.     push rbx;0
    61.     push rbx;0
    62.     mov r9d,LVS_REPORT+WS_CHILD+WS_VISIBLE
    63.     mov r8,rbx;NULL
    64.     mov edx,offset ListViewClassName
    65.     xor ecx,ecx;NULL
    66.     sub esp,20h
    67.     call CreateWindowEx
    68.     mov hList,rax
    69.  
    70.     call InsertColumn
    71.         call lv_col_info_fill
    72.     lea edi,msg
    73. @@:    mov ecx,edi
    74.     xor edx,edx
    75.     mov r8,rbx
    76.     mov r9,rbx
    77.     call GetMessage
    78.     mov ecx,edi
    79.     call DispatchMessage
    80.     jmp @b
    81. WinMain endp
    82.  
    83. InsertColumn proc
    84.  
    85. LOCAL lvc:LV_COLUMN
    86.  
    87.     push rbp
    88.     mov ebp,esp
    89.     sub esp,(20h+sizeof LV_COLUMN+15)and(-16)
    90.  
    91.     lea r9,lvc
    92.     mov [r9+LV_COLUMN.imask],LVCF_TEXT+LVCF_WIDTH
    93.     mov eax,offset Heading1
    94.     mov [r9+LV_COLUMN.pszText],rax
    95.     mov [r9+LV_COLUMN.lx],150
    96.     mov r8,rbx;0
    97.     mov edx,LVM_INSERTCOLUMN
    98.     mov rcx,hList
    99.     call SendMessage
    100.     lea r9,lvc
    101.     mov eax,offset Heading2
    102.     mov [r9+LV_COLUMN.pszText],rax
    103.     mov [r9+LV_COLUMN.lx],100
    104.     mov r8d,1
    105.     mov edx,LVM_INSERTCOLUMN
    106.     mov rcx,hList
    107.     call SendMessage
    108.     leave
    109.     retn
    110. InsertColumn endp
    111.  
    112. lv_col_info_fill proc
    113. LOCAL lvItem:LV_ITEM
    114. local old_rdi:QWORD
    115.  
    116.     push rbp
    117.     mov ebp,esp
    118.     sub esp,(28h+sizeof LV_ITEM+15)and(-16)
    119.  
    120.     mov old_rdi,rdi
    121.     xor edi,edi
    122. @@:    mov  lvItem.imask,LVIF_TEXT + LVIF_PARAM
    123.     mov  rax,[handle+rdi*8]
    124.     mov  lvItem.pszText,rax
    125.     mov  lvItem.iSubItem,0
    126.     mov  lvItem.iItem,edi
    127.     mov  lvItem.lParam,edi
    128.     lea  r9,lvItem
    129.     mov r8d,0
    130.     mov edx,LVM_INSERTITEM
    131.     mov rcx,hList
    132.     call SendMessage
    133.     mov  lvItem.imask,LVIF_TEXT
    134.     inc  lvItem.iSubItem
    135.     mov  rax,[handle+rdi*8+8*5]
    136.     mov  lvItem.pszText,rax
    137.     lea  r9,lvItem
    138.     mov r8d,0
    139.     mov edx,LVM_SETITEM
    140.     mov rcx,hList
    141.     call SendMessage
    142.     inc  edi
    143.     cmp  edi,5
    144.     jb   @b
    145. @0:    mov rdi,old_rdi
    146.     leave
    147.     retn
    148. lv_col_info_fill endp
    149. ;------------------------------------------------
    150. lv_param_update proc
    151. LOCAL lvi:LV_ITEM
    152. local old_rdi:QWORD
    153.  
    154.     push rbp
    155.     mov ebp,esp
    156.     sub esp,(28h+sizeof LV_ITEM+15)and(-16)
    157.     mov old_rdi,rdi
    158.     mov r9,rbx
    159.     mov r8,rbx
    160.     mov edx, LVM_GETITEMCOUNT
    161.     mov rcx,hList
    162.     call SendMessage
    163.     mov edi,eax
    164.     mov lvi.imask,LVIF_PARAM
    165.     mov lvi.iSubItem,0
    166.     mov lvi.iItem,0
    167. @@:    lea r9,lvi
    168.     mov eax,lvi.iItem
    169.     mov lvi.lParam,eax
    170.     mov r8,rbx;0
    171.     mov edx, LVM_SETITEM
    172.     mov rcx,hList
    173.     call SendMessage
    174.     inc lvi.iItem
    175.     dec edi
    176.     jnz @b;.endw
    177.     mov rdi,old_rdi
    178.     leave
    179.     retn
    180. lv_param_update endp
    181. ;------------------------------------------------
    182. lv_compare proc lParam1:QWORD,lParam2:QWORD,lParamSort:QWORD
    183. local buf1[100h]:BYTE
    184. local buf2[100h]:BYTE
    185. local lvi0:LV_ITEM
    186. local old_rdi:QWORD
    187. local old_rsi:QWORD
    188.     push rbp
    189.     mov ebp,esp
    190.     sub esp,(30h+sizeof LV_ITEM+200h+15)and(-16)
    191.     mov lParam2,rdx
    192.     mov lParamSort,r8
    193.  
    194.     mov  lvi0.imask,LVIF_TEXT
    195.     lea  eax,buf1
    196.     mov  lvi0.pszText,rax
    197.     mov  lvi0.cchTextMax,100h
    198.     test r8b,00000010b;[lParamSort],1;2
    199.     jnz compare_3
    200. compare_1:mov old_rdi,rdi
    201.     mov old_rsi,rsi
    202.     mov lvi0.iSubItem,1
    203.     lea  r9,lvi0
    204.     mov r8,rcx;[lParam1]
    205.     mov edx,LVM_GETITEMTEXT
    206.     mov rcx,hList
    207.     call SendMessage
    208.     lea  esi,buf1
    209.     call convert_strhval
    210.     mov  edi,eax
    211.     lea  r9,lvi0
    212.     mov r8,lParam2
    213.     mov edx,LVM_GETITEMTEXT
    214.     mov rcx,hList
    215.     call SendMessage
    216.     lea esi,buf1
    217.     call convert_strhval
    218.     test lParamSort,1
    219.     jne @f
    220.     xchg edi,eax
    221. @@:    sub eax,edi
    222. compare_1_exit: mov rdi,old_rdi
    223.     mov rsi,old_rsi
    224.     leave
    225.     retn
    226. compare_3:mov lvi0.iSubItem,0
    227.     lea r9,lvi0
    228.     mov r8,rcx;[lParam1]
    229.     mov edx,LVM_GETITEMTEXT
    230.     mov rcx,hList
    231.     call SendMessage
    232.     lea  ecx,buf2     ;destination
    233.     lea  edx,buf1     ;source
    234.     call lstrcpy
    235.     lea  r9,lvi0
    236.     mov r8,lParam2
    237.     mov edx,LVM_GETITEMTEXT
    238.     mov rcx,hList
    239.     call SendMessage
    240.     lea  edx,buf2
    241.     lea  ecx,buf1
    242.     test lParamSort,1
    243.     jne  @f
    244.     xchg edx,ecx
    245. @@:    call lstrcmpi
    246.     leave
    247.     retn
    248. lv_compare endp
    249. ;---------------------------------------
    250. convert_strhval proc
    251.     push rbp
    252.     mov ebp,esp
    253.     sub esp,20h
    254.  
    255.     mov ecx,esi
    256.     call lstrlen
    257.     mov ecx,eax
    258.     jrcxz b
    259.     xor eax,eax
    260.     cdq
    261. @@:    imul edx,10
    262.     lodsb
    263.     lea edx,[rdx+rax-'0']
    264.     loop @b
    265.     mov  eax,edx
    266. b:    leave
    267.     ret
    268. convert_strhval endp
    269. ;----------------------------------------
    270. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    271.     push rbp
    272.     mov ebp,esp
    273.     sub esp,30h
    274.  
    275.     mov hWnd,rcx
    276.     mov wParam,r8
    277.     mov lParam,r9
    278.  
    279.     cmp edx,WM_NOTIFY
    280.     je wmNOTIFY
    281.     cmp edx,WM_SIZE
    282.     je wmSIZE
    283.     cmp edx,WM_DESTROY
    284.     je wmDESTROY
    285. wmDEFAULT:leave
    286.     jmp DefWindowProc
    287. wmDESTROY:xor ecx,ecx
    288.     call ExitProcess
    289.  
    290. wmNOTIFY:mov  rdi,r9;[lParam]
    291.     mov  rax,[rdi + NMHDR.hwndFrom]
    292.     cmp  rax,hList
    293.     jne  wmDEFAULT
    294.     cmp  [rdi + NMHDR._code],LVN_COLUMNCLICK
    295.     jne wmDEFAULT
    296. wmNOTIFY_LVN_COLUMNCLICK:
    297.     cmp  [rdi + NM_LISTVIEW.iSubItem+4],1
    298.     jne   COLUMNCLICK_FILE
    299. COLUMNCLICK_SIZE:xor lvSortSize,1
    300.     mov r8d,lvSortSize
    301.     jmp  @f
    302. COLUMNCLICK_FILE:xor lvSortFile,1
    303.     mov r8d,lvSortFile
    304. @@:    mov r9d,lv_compare
    305.     mov edx,LVM_SORTITEMS
    306.     mov rcx,hList
    307.     call SendMessage
    308.     call lv_param_update
    309.     jmp wmBYE
    310. wmSIZE:    mov eax,r9d;lParam
    311.     and r9,0FFFFh
    312.         shr  eax,16
    313.     mov qword ptr [rsp+28h],TRUE
    314.     mov [rsp+20h],rax
    315.     xor edx,edx
    316.     mov r8,rdx
    317.     mov rcx,hList
    318.     call MoveWindow
    319. wmBYE:    leave
    320.     ret
    321. WndProc endp
    322. ;-------------------------------------------------------------
    323. ClassName         db 'Win64 Iczelion''s lesson #31a: ListView Control',0
    324. ListViewClassName     db 'SysListView32',0
    325. Heading1         db 'Filename',0
    326. Heading2         db 'Size',0
    327. hList            dq ?
    328. hMenu            dq ?
    329. lvSortSize    dd 0
    330. lvSortFile    dd 2
    331. lvI1a    db 'Durian',0
    332. lvI1b    db '34',0
    333. lvI2a    db 'Banana',0
    334. lvI2b    db '54',0
    335. lvI3a    db 'Watermelon',0
    336. lvI3b    db '44',0
    337. lvI4a    db 'Apple',0
    338. lvI4b    db '55',0
    339. lvI5a    db 'Papaya',0
    340. lvI5b    db '1',0
    341. handle    dq lvI1a,lvI2a,lvI3a,lvI4a,lvI5a,lvI1b,lvI2b,lvI3b,lvI4b,lvI5b
    342. end

    rc-файл

    Код (C):
    1. #include "resource.h"
    2. #define IDM_MAINMENU 10000
    3. #define IDM_ICON LVS_ICON
    4. #define IDM_SMALLICON LVS_SMALLICON
    5. #define IDM_LIST LVS_LIST
    6. #define IDM_REPORT LVS_REPORT
    7.  
    8. IDM_MAINMENU MENU
    9. {
    10.  POPUP "&View"
    11.         {
    12.          MENUITEM "&Icon View",IDM_ICON
    13.          MENUITEM "&Small Icon View",IDM_SMALLICON
    14.          MENUITEM "&List View", IDM_LIST
    15.          MENUITEM "&Report View",IDM_REPORT
    16.         }
    17. }

    Вариант #2

    rc-файл

    Код (C):
    1. #define MI_ICON  0
    2. #define MI_REPORT  1
    3. #define MI_SMALLICON  2
    4. #define MI_LIST  3
    5. #define M_MAIN 10000
    6.  
    7. M_MAIN MENU
    8. {
    9.        POPUP "&View"
    10.        {
    11.               MENUITEM "Lar&ge Icons",MI_ICON
    12.               MENUITEM "S&mall Icons",MI_SMALLICON
    13.               MENUITEM "&List",MI_LIST
    14.               MENUITEM "&Details",MI_REPORT
    15.        }
    16. }
     
    Последнее редактирование: 19 май 2019
    q2e74 нравится это.
  9. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE    equ 400000h
    3. M_MAIN        equ 10000
    4. MI_ICON     equ 0
    5. MI_SMALLICON    equ 2
    6. MI_LIST     equ 3
    7. MI_REPORT    equ 1
    8. .code
    9. WinMain proc
    10. local msg:MSG
    11. local rect:RECT
    12.  
    13.     xor ebx,ebx
    14.     invoke InitCommonControls
    15.     mov edi,offset ClassName
    16.     mov eax,10027h
    17.     mov esi,IMAGE_BASE
    18.  
    19.     push rax    ;hIconSm
    20.     push rdi    ;lpszClassName
    21.     push M_MAIN     ;lpszMenuName
    22.     push COLOR_WINDOW+1;hbrBackground
    23.         push 10005h    ;hCursor
    24.     push rax         ;hIcon  
    25.     push rsi    ;hInstance
    26.         push rbx        ;cbClsExtra & cbWndExtra
    27.     db 68h
    28.     dd WndProc;lpfnWndProc
    29.         push sizeof WNDCLASSEX;cbSize & style
    30.     mov rcx,rsp    ;addr WNDCLASSEX
    31.     call RegisterClassEx
    32.  
    33.     push rbx
    34.     push rsi
    35.     shl esi,9
    36.     push rbx
    37.     push rbx
    38.     push 240
    39.     push 400
    40.     push rsi
    41.     push rsi
    42.     mov r9d,WS_OVERLAPPEDWINDOW+WS_VISIBLE
    43.     mov r8d,edi
    44.     mov edx,edi
    45.     mov ecx,WS_EX_CLIENTEDGE
    46.     sub esp,20h
    47.     call CreateWindowEx
    48.         mov hwnd,rax
    49.  
    50.         lea edx,rect
    51.     mov ecx,eax
    52.     call GetClientRect
    53.  
    54.         push rbx;<-- align 10h
    55.     push IMAGE_BASE
    56.     push rbx
    57.     push hwnd
    58.     mov eax,rect.bottom
    59.     push rax
    60.     mov eax,rect.right
    61.     push rax
    62.     push rbx
    63.     push rbx
    64.     mov r9d,LVS_REPORT + WS_CHILD + WS_VISIBLE
    65.     mov r8,rbx
    66.     mov edx,offset ctlClsNameLv
    67.     xor ecx,ecx
    68.     sub esp,20h
    69.     call CreateWindowEx
    70.     mov hList,rax
    71.  
    72.     call lv_col_insert
    73.     call lv_col_info_fill
    74.  
    75.     mov r9d,0C9B5AFh
    76.     mov r8,rbx
    77.     mov edx,LVM_SETTEXTCOLOR
    78.     mov rcx,hList
    79.     call SendMessage
    80.     mov r9d,50352Eh
    81.     mov r8,rbx
    82.     mov edx,LVM_SETBKCOLOR
    83.     mov rcx,hList
    84.     call SendMessage
    85.     mov r9d,50352Eh
    86.     mov r8,rbx
    87.     mov edx,LVM_SETTEXTBKCOLOR
    88.     mov rcx,hList
    89.     call SendMessage
    90.     mov rcx,hwnd
    91.     call GetMenu
    92.     mov menuH,rax
    93.     mov qword ptr [rsp+20h],MF_CHECKED
    94.     mov r9d,MI_REPORT
    95.     mov r8d,MI_LIST
    96.     mov edx,MI_ICON
    97.     mov ecx,eax
    98.     call CheckMenuRadioItem
    99.  
    100.     lea edi,msg
    101. window_message_loop_start:
    102.     mov ecx,edi
    103.     xor edx,edx
    104.     mov r8,rbx
    105.     mov r9,rbx
    106.         call GetMessage
    107.     mov ecx,edi
    108.         call DispatchMessage
    109.     jmp window_message_loop_start
    110. WinMain endp
    111.  
    112. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    113.  
    114.     push rbp
    115.     mov ebp,esp
    116.     sub esp,30h
    117.     mov hWnd,rcx
    118.     mov wParam,r8
    119.     mov lParam,r9
    120.  
    121.     mov rdi,r9;lParam
    122.  
    123.     cmp edx,WM_DESTROY
    124.     je wmDESTROY
    125.     cmp edx,WM_SIZE
    126.     je wmSIZE
    127.     cmp edx,WM_NOTIFY
    128.     je wmNOTIFY
    129.     cmp edx,WM_COMMAND
    130.     je wmCOMMAND
    131. wmDEFAULT:leave
    132.     jmp DefWindowProc
    133.  
    134. wmDESTROY:xor ecx,ecx
    135.     call ExitProcess
    136. wmSIZE: mov eax,r9d;eax=[lParam]
    137.     shr eax,16;eax=[lParam+2]
    138.     and r9,0FFFFh
    139.     mov qword ptr [rsp+28h],TRUE
    140.     mov [rsp+20h],rax
    141.     xor edx,edx
    142.     mov r8,rdx
    143.     mov rcx,hList
    144.     call MoveWindow
    145.     jmp  wmBYE
    146. wmNOTIFY:mov rdi,r9;lParam
    147.     mov rax,[rdi + NMHDR.hwndFrom]
    148.     cmp rax,hList
    149.     jne wmDEFAULT
    150.     cmp [rdi + NMHDR._code],LVN_COLUMNCLICK
    151.     je wmNOTIFY_LVN_COLUMNCLICK
    152.     cmp [rdi + NMHDR._code],NM_DBLCLK
    153.     jne wmDEFAULT
    154. wmNOTIFY_NM_DBLCLK:call lv_item_focus
    155.     jmp  wmBYE
    156. wmNOTIFY_LVN_COLUMNCLICK:
    157.     cmp [rdi + NM_LISTVIEW.iSubItem+4],1
    158.     jne  COLUMNCLICK_FILE
    159. COLUMNCLICK_SIZE:xor lvSortSize,1
    160.     mov r8d,lvSortSize
    161.     jmp @f
    162. COLUMNCLICK_FILE:xor lvSortFile,1
    163.     mov r8d,lvSortFile
    164. @@:    mov r9d,offset lv_compare
    165.     mov edx,LVM_SORTITEMS
    166.     mov rcx,hList
    167.     call SendMessage
    168.     call lv_param_update
    169.     jmp wmBYE
    170. wmCOMMAND:or r9,r9;cmp  [lParam],0
    171.     jne  wmBYE
    172.     mov edx,GWL_STYLE
    173.     mov rcx,hList
    174.     call GetWindowLongPtr
    175.     and eax,not LVS_TYPEMASK
    176.     movzx edi,word ptr wParam
    177.     or eax,edi
    178.     mov r8,rax
    179.     mov edx,GWL_STYLE
    180.     mov rcx,hList
    181.     call SetWindowLongPtr
    182.     push MF_CHECKED
    183.     mov r9,rdi
    184.     mov r8d,MI_LIST
    185.     mov edx,MI_ICON
    186.     mov rcx,menuH
    187.     call CheckMenuRadioItem
    188. wmBYE:    leave
    189.     retn
    190. WndProc endp
    191.  
    192. lv_item_focus proc
    193. local lvi4:LV_ITEM
    194. local buf4[256]:BYTE
    195.  
    196.     mov r9d,LVNI_FOCUSED
    197.     or r8d,-1
    198.     mov edx,LVM_GETNEXTITEM
    199.     mov rcx,hList
    200.     call SendMessage
    201.     lea r9,lvi4
    202.     mov lvi4.iItem,eax
    203.     mov lvi4.iSubItem,0;ebx
    204.     mov lvi4.imask,LVIF_TEXT
    205.     lea eax,buf4
    206.     mov lvi4.pszText,rax
    207.     mov lvi4.cchTextMax,256
    208.     xor r8d,r8d
    209.     mov edx,LVM_GETITEM
    210.     mov rcx,hList
    211.     call SendMessage
    212.     xor r9d,r9d;MB_OK
    213.     mov r8d,offset ClassName
    214.     lea edx,buf4
    215.     xor ecx,ecx
    216.     call MessageBox
    217.     leave  
    218.     retn
    219. lv_item_focus endp
    220.  
    221. lv_param_update proc
    222. local lvi1:LV_ITEM
    223. local old_rdi:QWORD
    224.        
    225.     mov old_rdi,rdi
    226.     xor r9d,r9d
    227.     xor r8d,r8d
    228.     mov edx,LVM_GETITEMCOUNT
    229.     mov rcx,hList
    230.     call SendMessage
    231.     mov edi,eax
    232.     mov lvi1.imask,LVIF_PARAM
    233.     mov lvi1.iSubItem,0
    234.     mov lvi1.iItem,0
    235.     mov lvi1.lParam,0
    236.     or eax,eax
    237.     je wmBYE
    238. @@:    lea r9,lvi1
    239.     xor r8d,r8d
    240.     mov edx,LVM_SETITEM
    241.     mov rcx,hList
    242.     call SendMessage
    243.     inc lvi1.iItem
    244.         inc lvi1.lParam
    245.     dec  edi
    246.     jnz  @b
    247. wmBYE:    mov rdi,old_rdi
    248.     leave
    249.     retn
    250. lv_param_update endp
    251.  
    252. convert_strhval proc
    253.     sub esp,28h
    254.     mov ecx,esi
    255.     call lstrlen
    256.     or eax,eax
    257.     je f12          
    258.     mov ecx,eax
    259.     xor eax,eax
    260.     cqo
    261. @@:    imul edx,10        ;    edx=edx*10
    262.     lodsb            ;    mov al,[esi]/inc esi
    263.     lea edx,[rax+rdx-'0']    ;    edx=edx*10+eax
    264.     loop @b
    265.     mov eax,edx      
    266. f12:    add esp,28h
    267.     retn
    268. convert_strhval endp
    269.  
    270. lv_compare proc lParam1:QWORD,lParam2:QWORD,lParamSort:QWORD
    271. local lvi0:LV_ITEM
    272. local buf1[255]:BYTE
    273. local buf2[255]:BYTE
    274. local old_rdi:QWORD
    275. local old_rsi:QWORD    
    276.  
    277.     mov lParam1,rcx
    278.     mov lParam2,rdx
    279.     mov lParamSort,r8
    280.         mov old_rdi,rdi
    281.         mov old_rsi,rsi
    282.  
    283.     mov lvi0.imask,LVIF_TEXT
    284.     lea eax,buf1
    285.     mov lvi0.pszText,rax
    286.     mov lvi0.cchTextMax,255
    287.     cmp lParamSort,1
    288.     ja compare_3
    289.  
    290. compare_1:lea r9,lvi0
    291.     mov [r9+LV_ITEM.iSubItem],1
    292.     mov r8,lParam1
    293.     mov edx,LVM_GETITEMTEXT
    294.     mov rcx,hList
    295.     call SendMessage
    296.     lea esi,buf1
    297.     call convert_strhval
    298.     mov edi,eax
    299.         lea r9,lvi0
    300.     mov r8,lParam2
    301.     mov edx,LVM_GETITEMTEXT
    302.     mov rcx,hList
    303.     call SendMessage
    304.     lea esi,buf1
    305.     call convert_strhval
    306.     sub edi,eax
    307.     cmp lParamSort,1
    308.     jne compare_3_exit              
    309.     mov eax,edi
    310.     jmp compare_3_exit
    311.  
    312. compare_3:lea r9,lvi0
    313.     mov [r9+LV_ITEM.iSubItem],0
    314.     mov r8,lParam1
    315.     mov edx,LVM_GETITEMTEXT
    316.     mov rcx,hList
    317.     call SendMessage
    318.     lea ecx,buf2     ;destination
    319.     lea edx,buf1     ;source
    320.     call lstrcpy
    321.         lea r9,lvi0
    322.     mov r8,lParam2
    323.     mov edx,LVM_GETITEMTEXT
    324.     mov rcx,hList
    325.     call SendMessage
    326.     lea edx,buf2
    327.     lea ecx,buf1
    328.     cmp lParamSort,3
    329.     jne  @f
    330.     xchg edx,ecx
    331. @@:    call lstrcmpi
    332. compare_3_exit:mov rdi,old_rdi
    333.     mov rsi,old_rsi
    334.     leave
    335.     ret
    336. lv_compare endp
    337.  
    338. lv_col_insert proc
    339. local lvCol:LV_COLUMN  
    340.  
    341.     mov lvCol.imask,LVCF_TEXT + LVCF_WIDTH
    342.     mov eax,offset lvTxt1
    343.     mov lvCol.pszText,rax
    344.     mov lvCol.lx,150
    345.     lea r9,lvCol
    346.     mov r8,rbx
    347.     mov edx,LVM_INSERTCOLUMN
    348.     mov rcx,hList
    349.     call SendMessage
    350.     or lvCol.imask,LVCF_FMT
    351.     mov lvCol.fmt,LVCFMT_RIGHT
    352.     mov eax,offset lvTxt2
    353.     mov lvCol.pszText,rax
    354.     mov lvCol.lx,100
    355.     lea r9,lvCol
    356.     mov r8d,1
    357.     mov edx,LVM_INSERTCOLUMN
    358.     mov rcx,hList
    359.     call SendMessage
    360.     leave  
    361.     retn
    362. lv_col_insert endp
    363.  
    364. lv_col_info_fill proc
    365. local old_rdi:QWORD
    366.     push rbp
    367.     mov ebp,esp
    368.     sub esp,30h
    369.     mov old_rdi,rdi
    370.     mov edx,offset fData
    371.     mov ecx,offset fTxt1
    372.     call FindFirstFile
    373.     inc eax     ; eax = INVALID_HANDLE_VALUE ?
    374.     je fill_exit
    375.     dec eax
    376.     mov fFileH,rax
    377.     xor edi,edi
    378. fill_start:cmp fData.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY
    379.     je @f
    380.     call lv_col_info_insert
    381.     inc  edi
    382. @@:    mov edx,offset fData
    383.     mov rcx,fFileH
    384.     call FindNextFile
    385.     or eax,eax        ; eax <> 0 ?
    386.     jne fill_start
    387. fill_end:mov rcx,fFileH
    388.     call FindClose  
    389. fill_exit:mov rdi,old_rdi
    390.     leave
    391.     retn
    392. lv_col_info_fill endp
    393.  
    394. lv_col_info_insert proc
    395. local lvi2:LV_ITEM
    396. local buf3[20]:BYTE  
    397.  
    398.     lea r9,lvi2
    399.     mov [r9+LV_ITEM.imask],LVIF_TEXT + LVIF_PARAM
    400.     mov eax,offset fData.cFileName
    401.     mov [r9+LV_ITEM.pszText],rax
    402.     mov [r9+LV_ITEM.iSubItem],ebx
    403.     mov [r9+LV_ITEM.iItem],edi;?
    404.     mov [r9+LV_ITEM.lParam],edi      
    405.     mov r8,rbx
    406.     mov edx,LVM_INSERTITEM
    407.     mov rcx,hList
    408.     call SendMessage
    409.     mov lvi2.imask,LVIF_TEXT
    410.     inc lvi2.iSubItem
    411.     lea eax,buf3
    412.     mov lvi2.pszText,rax
    413.     mov r8d,fData.nFileSizeLow
    414.     mov edx,offset lvTxt3
    415.     mov ecx,eax
    416.     call wsprintf
    417.     lea r9,lvi2
    418.     mov r8,rbx
    419.     mov edx,LVM_SETITEM
    420.     mov rcx,hList
    421.     call SendMessage
    422.     leave
    423.     ret
    424. lv_col_info_insert endp
    425. ;data
    426. hwnd        dq ?
    427. ClassName    db 'Win64 Iczelion''s lesson #31b: listview',0
    428. ctlClsNameLv    db 'SysListView32',0
    429. hList        dq ?
    430. lvTxt1        db 'File Name',0
    431. lvTxt2        db 'Size',0
    432. lvTxt3        db '%lu',0
    433. lvSortSize    dd 0
    434. lvSortFile    dd 2;0
    435. menuH        dq ?
    436. fFileH        dq ?
    437. fData        WIN32_FIND_DATA <>
    438. fTxt1        db '*.*',0
    439. end

    Разбор полётов

    Первое, что должна сделать программа после того, как создано основное окно - это создать listview.
    Код (ASM):
    1. wmCREATE:
    2.        invoke CreateWindowEx, NULL, addr ListViewClassName, NULL, \
    3.               LVS_REPORT+WS_CHILD+WS_VISIBLE, 0,0,0,0,hWnd, NULL, hInstance, NULL
    4.        mov hList, eax
    Мы вызываем CreateWindowEx, передавая ей имя класса окна "SysListView32". Режим отображения по умолчанию задан стилем LVS_REPORT.
    Код (ASM):
    1.       invoke InsertColumn
    После того, как создан listview, мы вставляем в него колонку.
    Код (ASM):
    1. LOCAL lvc:LV_COLUMN
    2.  
    3.      mov lvc.imask,LVCF_TEXT+LVCF_WIDTH
    4.      mov lvc.pszText,offset Heading1
    5.      mov lvc.lx,150
    6.      invoke SendMessage,hList, LVM_INSERTCOLUMN, 0, addr lvc
     
    Последнее редактирование: 5 янв 2017
  10. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Мы указываем название и ширину первой колонки, в которой будут отображаться имена файлов, в структуре LV_COLUMN, поэтому нам нужно установить в imask флаги LVCF_TEXT и LVCF_WIDTH. Мы заполняем pszText адресом названия и lx ― шириной колонки в пикселях. Когда все сделано, мы посылаем сообщение LVM_INSERTCOLUMN listview, передавая ей структуру.
    Код (ASM):
    1.      or lvc.imask,LVCF_FMT
    2.      mov lvc.fmt,LVCFMT_RIGHT
    После вставки первой колонки, мы вставляем следующую, в которой будут отображаться размеры файлов. Так как нам нужно, чтобы размеры файлов выравнивались по правой стороне, нам необходимо указать флаг в параметре fmt, LVCFMT_RIGHT. Мы также указываем флаг LVCF_FMT в imask, в добавление к LVCF_TEXT и LVCF_WIDTH.
    Код (ASM):
    1.      mov lvc.pszText,offset Heading2
    2.      mov lvc.lx,100
    3.      invoke SendMessage,hList, LVM_INSERTCOLUMN, 1 ,addr lvc
    Оставшийся код прост. Помещаем адреса названия в pszText и ширину в lx. Затем посылаем сообщение LVM_INSERTCOLUMN listview, указывая номер колонки и адрес структуры.
    Когда колонки вставлены, мы можем заполнить listview элементами.
    Код (ASM):
    1.       invoke FillFileInfo
    В FillFileInfo содержится следующий код.
    Код (ASM):
    1.    FillFileInfo proc uses edi
    2.      LOCAL finddata:WIN32_FIND_DATA
    3.      LOCAL FHandle:DWORD
    4.  
    5.      invoke FindFirstFile,addr FileNamePattern,addr finddata
    Мы вызываем FindFirstFile, чтобы получить информацию о первом файле, который отвечает заданным условиям. У FindFirstFile следующий прототип:
    Код (C):
    1. HANDLE WINAPI FindFirstFile(
    2.   _In_ LPCTSTR  lpFileName,
    3.   _Out_ LPWIN32_FIND_DATA lpFindFileData
    4. );
    pFileName ― это адрес имени файла, который надо искать. Эта строка может содержать "дикие" символы. В нашем примере мы используем *.*, чтобы искать все файлы в данной папке.

    pWin32_Find_Data ― это адрес структуры WIN32_FIND_DATA, которая будет заполнена информацией о файле (если что-нибудь будет найдено).
    Эта функция возвращает INVALID_HANDLE_VALUE в rax, если не было найдено соответствующих заданным критериям файлов. Иначе она возвратит дескриптор поиска, который будет использован в последующих вызовах FindNextFile.
    Код (ASM):
    1.      .if eax!=INVALID_HANDLE_VALUE
    2.        mov FHandle,eax
    3.        xor edi,edi
    Если файл будет найден, мы сохраним дескриптор поиска в переменную, а потом обнулим rdi, который будет использован в качестве индекса элемента (номер ряда).
    Код (ASM):
    1.        .while eax!=0
    2.          test finddata.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY
    3.          .if ZERO?
    В этой главе мы не будем иметь дело с папками, поэтому отфильтровываем их проверяя параметр dwFileAttributes на предмет наличия установленного флага FILE_ATTRIBUTE_DIRECTORY. Если он есть ― сразу переходим к вызову FindNextFile.
    Код (ASM):
    1.              invoke ShowFileInfo,edi, addr finddata
    2.              inc edi
    3.          .endif
    4.          invoke FindNextFile,FHandle,addr finddata
    5.        .endw
    Вставляем имя и размер файла в listview вызывая функцию ShowFileInfo. Затем мы повышаем значение rdi (текущий номер столбца). И, наконец, делаем вызов FindNextFile, чтобы найти следующий файл в нашей папке, пока FindNextFile не возвратит 0, что означает то, что больше файлов найдено не было.
    Код (ASM):
    1.        invoke FindClose,FHandle
    2.      .endif
    3.      ret
    4.    FillFileInfo endp
    Когда все файлы в ткущей папке найдены, мы должны закрыть дескриптор поиска.
    Теперь давайте взглянем на функцию ShowFileInfo. Эта функция принимает два параметра, индекс элемента (номер ряда) и адрес структуры WIN32_FIND_DATA.
    Код (ASM):
    1. ShowFileInfo proc uses edi row:DWORD, lpFind:DWORD
    2.      LOCAL lvi:LV_ITEM
    3.      LOCAL buffer[20]:BYTE
    4.  
    5.      mov edi,lpFind
    6.      assume edi:ptr WIN32_FIND_DATA
    Сохраняем адрес структуры WIN32_FIND_DATA в rdi.
    Код (ASM):
    1.      mov lvi.imask,LVIF_TEXT+LVIF_PARAM
    2.      push row
    3.      pop lvi.iItem
    4.      mov lvi.iSubItem,0
    Мы предоставляем название элемента и значение lParam, поэтому мы помещаем флаги LVIF_TEXT и LVIF_PARAM в imask. Затем мы устанавливаем приравниваем iItem номер ряда, переданный функции и, так как это главный элемент, мы должны приравнять iSubItem нулю (колонка 0).
    Код (ASM):
    1.      lea eax,[edi].cFileName
    2.      mov lvi.pszText,eax
    3.      push row
    4.      pop lvi.lParam
    Затем мы помещаем адрес названия, в данном случая это имя файла в структуре WIN32_FIND_DATA, в pszText. Так как мы реализуем свою сортировку, мы должны заполнить lParam определенным значением. Помещаем номер ряда в это параметр, чтобы получать информацию об элементе по его индексу.
    Код (ASM):
    1.      invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi
    Когда все необходимые поля в LV_ITEM заполнены, мы посылаем сообщение LVM_INSERTITEM listview, чтобы вставить в него элемент.
    Код (ASM):
    1.      mov lvi.imask,LVIF_TEXT
    2.      inc lvi.iSubItem
    3.      invoke wsprintf,addr buffer, addr template,[edi].nFileSizeLow
    4.      lea eax,buffer
    5.      mov lvi.pszText,eax
    Мы установим подэлементы, ассоциированные с элементом. Подэлемент может иметь только название. Поэтому мы указываем в imask LVIF_TEXT. Затем указываем в iSubItem колонку, в которой должен находиться подэлемент. В этом случае мы устанавливаем его в 1. Названием этого элемента будет являться размер файла. Сначала нужно сконвертировать его значение в строку, вызвать wsprintf. Затем помещаем адрес строки в pszText.
    Код (ASM):
    1.      invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
    2.      assume edi:nothing
    3.      ret
    4.    ShowFileInfo endp
    Когда все требуемые поля в LV_ITEM заполнены, мы посылаем сообщение LVM_SETITEM listview, передавая ему адрес структуры LV_ITEM. Заметьте, что мы используем LVM_SETITEM, а не LVM_INSERTITEM, потому что подэлемент считается свойством элемента. Поэтому устанавливаем свойство элемента, а не вставляем новый элемент.
    Когда все элементы вставлены в listview, мы устанавливаем текст и цвет фона контрола listview.
    Код (ASM):
    1.        RGB 255,255,255
    2.        invoke SendMessage,hList,LVM_SETTEXTCOLOR,0,eax
    3.        RGB 0,0,0
    4.        invoke SendMessage,hList,LVM_SETBKCOLOR,0,eax
    5.        RGB 0,0,0
    6.        invoke SendMessage,hList,LVM_SETTEXTBKCOLOR,0,eax
    Мы устанавливаем цвет текста и цвет фона с помощью сообщений LVM_SETTEXTCOLOR и LVM_SETTEXTBKCOLOR. Мы устанавливаем цвет фона listview сообщением LVM_SETBKCOLOR.
    Код (ASM):
    1.        invoke GetMenu,hWnd
    2.        mov hMenu,eax
    3.        invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, IDM_REPORT,MF_CHECKED
    Мы позволим пользователю выбирать режимы отображения через меню. Поэтому мы должны получить сначала дескриптор меню. Чтобы помочь пользователю переключать режимы отображения, мы помещаем в меню систему radio button'ов. Для этого нам понадобится функция CheckMenuRadioItem. Эта функция поместит radio button перед пунктом меню.
    Заметьте, что мы создаем окно listview с шириной и высотой равной нулю. Оно будет менять размер каждый pаз, когда будет менять размер родительское окно. В этом случае мы можем быть уверены, что размер listview всегда будет соответствовать родительскому окну. В нашем примере нам требуется, чтобы listview занимал всю клиентскую область родительского окна.
    Код (ASM):
    1. wmSIZE:       mov eax,lParam
    2.        mov edx,eax
    3.        and eax,0ffffh
    4.        shr edx,16
    5.        invoke MoveWindow,hList, 0, 0, eax,edx,TRUE
    Когда родительское окно получает сообщение WM_SIZE, нижнее слово lParam содержит новую ширину клиентской области и верхнее словно новой высоты. Тогда мы вызываем MoveWindow, чтобы изменить размер listview, чтобы тот покрывал всю клиентскую область родительского окна.
    Когда пользователь выберет режим отображения в меню, мы должны соответственно отреагировать. Мы устанавливаем новый стиль контрола listview функцией SetWindowLong.
    Код (ASM):
    1. wmCOMMAND:
    2.        .if lParam==0
    3.          invoke GetWindowLong,hList,GWL_STYLE
    4.          and eax,not LVS_TYPEMASK
    Сначала мы получаем текущие стили listview. Затем мы стираем старый стиль отображения. LVS_TYPEMASK ― это комбинированное значение всех четырех стилей отображения. Поэтому когда мы выполняем логическое умножение текущих флагов стилей со значением "not LVS_TYPEMASK", стиль текущего отображения стирается.
    Во время проектирования меню я немного сжульничал. Я использовал в качестве ID пунктов меню константы стилей отображения.
    Код (ASM):
    1.    IDM_ICON equ LVS_ICON
    2.    IDM_SMALLICON equ LVS_SMALLICON
    3.    IDM_LIST equ LVS_LIST
    4.    IDM_REPORT equ LVS_REPORT
    Поэтому, когда родительское окно получает сообщение WM_COMMAND, нужный стиль отображения находится в нижнем слове wParam'а (как ID пункта меню).
    Код (ASM):
    1.          mov edx,wParam
    2.          and edx,0FFFFh
    Мы получили стиль отображения в нижнем слове wParam. Все, что нам теперь нужно, это обнулить верхнее слово.
    Код (ASM):
    1.          push edx
    2.          or eax,edx
    И добавить стиль отображения к уже существующим стилям (текущий стиль отображения мы ранее оттуда убрали).
    Код (ASM):
    1.          invoke SetWindowLong,hList,GWL_STYLE,eax
    И установить новые стили функцией SetWindowLong.
    Код (ASM):
    1.          pop edx
    2.          invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, edx,MF_CHECKED
    3.  
    4.       .endif
    Нам также требуется поместить radio button перед выбранным пунктом меню. Поэтому мы вызываем CheckMenuRadioItem, передавая ей текущий стиль отображения (а также ID пункта меню).
    Когда пользователь кликает по заголовку колонки в режиме отчета, нам нужно отсортировать элементы в listview. Мы должны отреагировать на сообщение WM_NOTIFY.
    Код (ASM):
    1. wmNOTIFY:        push edi
    2.        mov edi,lParam
    3.        assume edi:ptr NMHDR
    4.        mov eax,[edi].hwndFrom
    5.        .if eax==hList
     
    Последнее редактирование: 11 июн 2019
    q2e74 нравится это.
  11. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Когда мы получаем сообщение WM_NOTIFY, lParam содержит указатель на структуру NMHDR. Мы можем проверить, пришло ли это сообщение от listview, сравнив параметр hwndFrom структуры NMHDR с дескриптором контрола listview. Если они совпадают, мы можем заключить, что уведомление пришло от listview.
    Код (ASM):
    1.          .if [edi].code==LVN_COLUMNCLICK
    2.            assume edi:ptr NM_LISTVIEW
    Если уведомление пришло от listview, мы проверяем, равен ли код LVN_COLUMNCLICK. Если это так, это означает, что пользователь кликает на заголовке колонки. В случае, что код равен LVN_COLUMNCLICK, мы считаем, что lParam содержит указатель на структуру NM_LISTVIEW, которая является супермножеством по отношению к структуре NMHDR (то есть включает ее). Затем нам нужно узнать, по какому заголовку колонки кликнул пользователь. Эту информацию мы получаем из параметра iSubItem. Его значение можно считать номером колонки (отсчет начинается с нуля).
    Код (ASM):
    1.            .if [edi].iSubItem==1
    2.              .if SizeSortOrder==0 || SizeSortOrder==2
    Если iSubItem равен 1, это означает, что пользователь кликнул по второй колонке. Мы используем глобальные переменные, чтобы сохранять текущий статус порядка сортировки. 0 означает "еще не отсортировано", 1 значит "восходящая сортировка", а 2 ― "нисходящая сортировка". Если элементы/подэлементы в колонке ранее не были отсортированы или отсортированы по нисходящей, то мы устанавливаем сортировку по восходящей.
    Код (ASM):
    1.        invoke SendMessage,hList,LVM_SORTITEMS,1,addr CompareFunc
    Мы посылаем сообщение LVM_SORTITEMS listview, передавая 1 через wParam и адрес нашей сравнивающей функции через lParam. Заметьте, что значение в wParam задается пользователем, вы можете использовать его как хотите. Я использовал его в нашем примере как метод сортировки. Сначала мы взглянем на сравнивающую функцию.
    Код (ASM):
    1.    CompareFunc proc uses edi lParam1:DWORD, lParam2:DWORD, SortType:DWORD
    2.      LOCAL buffer[256]:BYTE
    3.      LOCAL buffer1[256]:BYTE
    4.      LOCAL lvi:LV_ITEM
    5.  
    6.      mov lvi.imask,LVIF_TEXT
    7.      lea eax,buffer
    8.      mov lvi.pszText,eax
    9.      mov lvi.cchTextMax,256
    В сравнивающей функции контрол listview будет передавать lParam'ы (через LV_ITEM) двух элементов, которые нужно сравнить, через lParam1 и lParam2. Вспомните, что мы помещаем индекс элемента в lParam. Таким образом мы можем получить информацию об элементах, используя эти индексы. Информация, которая нам нужна ― это названия сортирующихся элементов/подэлементов. Мы подготавливаем структуру LV_ITEM для этого, указывая в imask LVIF_TEXT и адрес буфера в pszText и размер буфера в cchTextMax.
    Код (ASM):
    1.      .if SortType==1
    2.        mov lvi.iSubItem,1
    3.        invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
    Если значение SortType равно 1 или 2, мы знаем, что кликнута колонка размера файла. 1 означает, что необходимо отсортировать элементы в нисходящем порядке. 2 значит обратное. Таким образом мы указываем iSubItem равным 1 (чтобы задать колонку размера) и посылаем сообщение LVM_GETITEMTEXT контролу listview, чтобы получить название (строку с размером файла) подэлемента.
    Код (ASM):
    1.        invoke String2Dword,addr buffer
    2.        mov edi,eax
    Конвертируем строку в двойное слово с помощью функции String2Dword. Она возвращает dword-значение в eax. Мы сохраняем ее в edi для последующего сравнения.
    Код (ASM):
    1.        invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
    2.        invoke String2Dword,addr buffer
    3.        sub edi,eax
    4.        mov eax,edi
    Тоже самое мы делаем и с lParam2. После получения размеров обоих файлов, мы можем сравнить их.
    Правила, которых придерживается функция сравнения, следующие:
    1. Если первый элемент должен предшествовать другому, вы должны возвратить отрицательное значение через eax.
    2. Если второй элемента должен предшествовать первому, вы должны возвратить через eax положительное значение.
    3. Если оба элемента равны, вы должны возвратить ноль.
    В нашем случае нам нужно отсортировать элементы согласно их размерам в восходящем порядке. Поэтому мы просто можем вычесть размер первого элемента из второго и возвратить результат в rax.
    Код (ASM):
    1.     .elseif SortType==3
    2.        mov lvi.iSubItem,0
    3.        invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
    4.        invoke lstrcpy,addr buffer1,addr buffer
    5.        invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
    6.        invoke lstrcmpi,addr buffer1,addr buffer
    В случае, если пользователь кликнет по колонке с именем файла, мы должны сравнивать имена файлов. Мы должны получить имена файлов, а затем сравнить их с помощью функции lstrcmpi. Мы можем возвратить значение, возвращаемое этой функцией, так как оно использует те же правила сравнения.
    После того, как элементы отсортированы, нам нужно обновить значения lParam'ов для всех элементов, чтобы учесть изменившиеся индексы элементов, поэтому мы вызываем функцию UpdatelParam.
    Код (ASM):
    1.                invoke UpdatelParam
    2.                mov SizeSortOrder,1
    Эта функция просто-напросто перечисляет все элементы в listview и обновляет значения lParam. Нам требуется это делать, иначе следующая сортировка не будет работать как ожидается, потому что мы исходим из того, что значение lParam ― это индекс элемента.
    Код (ASM):
    1.          .elseif [edi].code==NM_DBLCLK
    2.            invoke ShowCurrentFocus
    3.          .endif
    Когда пользователь делает двойной клик на элементе, нам нужно отобразить окно с сообщение с названием элемента. Мы должны проверить, равно ли поле code в NMHDR NM_DBLCLK. Если это так, мы можем перейти к получению названия и отображению его в окне с сообщением.
    Код (ASM):
    1. ShowCurrentFocus proc
    2.       LOCAL lvi:LV_ITEM
    3.       LOCAL buffer[256]:BYTE
    4.  
    5.       invoke SendMessage,hList,LVM_GETNEXTITEM,-1, LVNI_FOCUSED
    Как мы может узнать, по какому элементу кликнули два раза? Когда элемент кликнут (одинарным или двойным нажатием), он получает фокус. Даже если выбрано несколько элементов, фокус будет только у одного. Наши задача заключается в том, чтобы найти элемент у которого находится фокус. Мы делаем это, посылая сообщение LVM_GETNEXTITEM контролу listview, указав желаемое состояние элемента в lParam. -1 в wParam означает поиск по всем элементам. Индекс элемента возвращается в eax.
    Код (ASM):
    1.       mov lvi.iItem,eax
    2.       mov lvi.iSubItem,0
    3.       mov lvi.imask,LVIF_TEXT
    4.       lea eax,buffer
    5.       mov lvi.pszText,eax
    6.       mov lvi.cchTextMax,256
    7.       invoke SendMessage,hList,LVM_GETITEM,0,addr lvi
    Затем мы получаем название элемента с помощью сообщения LVM_GETITEM.
    Код (ASM):
    1.       invoke MessageBox,0, addr buffer,addr AppName,MB_OK
    И наконец, мы отображаем название элемента в окне сообщения.
    Если вы хотите узнать, как использовать в контроле listview иконки, вы можете прочитать об этом в главе о treeview. В случае с listview надо будет сделать примерно то же самое.
    [​IMG]
     

    Вложения:

    • tut_31a.png
      tut_31a.png
      Размер файла:
      31,7 КБ
      Просмотров:
      3.005
    Последнее редактирование: 5 янв 2017
    q2e74 нравится это.
  12. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    Глава сорок седьмая. Братец Кролик создает MDI-приложение

    [​IMG]
    Скачайте пример здесь.

    Теория ― МАТЬ СКЛЕРОЗА

    MDI-интерфейс (MDI Multiple Document Interface ― многодокументный интерфейс), позволяет пользователю работать одновременно с несколькими документами и не обязательно одного типа. При этом однодокументные приложения (SDISingle Document Interface ― однодокументный интерфейс) позволяют работать только с одним документом (пример, Notepad.exe). Notepad может обрабатывать только один документ за раз. Если вы хотите открыть другой документ, вам нужно закрыть предыдущий. Как вы можете себе представить, это довольно неудобно. Сравните его с Microsoft Word: тот может держать открытыми различные документы в одно и то же время и позволяет пользователю выбирать, какой документ использовать.
    У MDI-приложений есть несколько характеристик, присущих только им. Вот некоторые из них:
    • Внутри основного окна может быть несколько дочерних окон в пределах клиентской области.
    • Когда вы сворачиваете окно, он сворачивается к нижнему левому углу клиентской области основного окна.
    • Когда вы разворачиваете окно, его заголовок сливается с заголовком главного окна.
    • Вы можете закрыть дочернее окно, нажав Ctrl+F4 и переключатся между дочерними окнами, нажав на Ctrl+Tab.
    Главное окно, которое содержит дочерние окно, называется фреймовым окном. Его клиентская область ― это место, где находятся дочерние окна, поэтому оно и называется фреймовым (frame рамка, кадр). Его работа чуть более сложна, чем задачи обычного окна, так как оно обеспечивает работу MDI.
    Чтобы контролировать дочерние окна в клиентской области, вам нужно специальное окно, которое называется клиентским окном. Вы можете считать это клиентское окно прозрачным окном, покрывающим всю клиентскую область окном фрейма.
    00.png
    Рисунок 1. Иерархия MDI-приложения​

    Создание окна фрейма

    Теперь мы переключим наше внимание на детали. Прежде всего вам нужно создать окно фрема. Оно создается примерно таким же образом, как и обычное окно: с помощью вызова CreateWindowEx. Есть два основных отличия от создания обычного окна.
    Первое различие состоит в том, что вы ДОЛЖНЫ вызывать DefFramProc вместо NtdllDefWindowProc_ для обработки Windows-сообщение вашему окну, которые вы не хотите обрабатывать самостоятельно. Это единственный способ заставить Windows делать за вас грязную работу по управлению MDI-приложение. Если вы забудете использовать DefFramProc, ваше приложение не будет иметь MDI-свойств. DefFrameProc имеет следующий синтаксис:
    Код (C):
    1. LRESULT WINAPI DefFrameProc(
    2.   _In_ HWND  hWnd,
    3.   _In_ HWND  hWndMDIClient,
    4.   _In_ UINT  uMsg,
    5.   _In_ WPARAM wParam,
    6.   _In_ LPARAM lParam
    7. );
    Если вы сравните DefFramProc с NtdllDefWindowProc_, вы заметите, что разница между ними состоит в том, что у DefFrameProc пять параметров, в то время как у NtdllDefWindowProc_четыре. Дополнительный параметр ― это дескриптор клиентского окна. Это дескриптор необходим для того, чтобы Windows могла посылать MDI-сообщения клиентскому окну.
    Второе различие заключается в том, что вы должны вызывать TranslateMDISysAccel в цикле обработки сообщений вашего окна фрейма. Это необходим, если вы хотите, что Windows обрабатывала нажатия на комбинации клавиш, связанных с MDI, такие как Ctrl+F4, Ctrl+Tab. У этой функции следующий прототип:
    Код (C):
    1. BOOL WINAPI TranslateMDISysAccel(
    2.   _In_ HWND  hWndClient,
    3.   _In_ LPMSG lpMsg
    4. );
    Первый параметр ― это дескриптор клиентского окна. Для вас не должно быть сюрпризом, что клиентское окно будет родителем окном для все дочерних MDI-окон. Второй параметр ― это адрес MSG-структуры, которую вы заполните с помощью функции GetMessage. Идея состоит в том, чтобы передать MSG-структуру клиентскому окну, что оно могло проверить, содержит ли эта структура искомые комбинации клавиш. Если это так, она обрабатывает само сообщение и возвращает ненулевое значение, или, в противном случае, FALSE.
    Этапы создания окно фрейма:
    1. Заполняем структуру WNDCLASSEX как обычно.
    2. Регистрируем класс окна фрейма, вызвав RegisterClassEx.
    3. Создаем окно фрейма с помощью CreateWindowEx.
    4. Внутри цикла обработки сообщений вызываем TranslateMDISysAccel.
    5. Внутри процедуры окна передаем необрабатанные сообщения DefFrameProc вместо NtdllDefWindowProc_.

    Создание клиентского окна

    Теперь, когда у нас есть окно фрейма, мы можем создать клиентское окно. Класс клиентского окна перерегистрирован Windows. Имя этого класса ― "MDICLIENT". Вам также нужно передать адрес структуры CLIENTCREATESTRUCT функции CreateWindowEx. Эта структура имеет следующее определение:
    Код (ASM):
    1. CLIENTCREATESTRUCT STRUC
    2. hWindowMenu HANDLE ?
    3. idFirstChild UINT ?
    4. Reserved DWORD ?
    5. CLIENTCREATESTRUCT ENDS
    hWindowMenu ― это описатель подменю, к которому Windows присоединит список имен дочерних MDI-окон. Здесь требуется некоторое пояснение. Если вы когда-нибудь использовали раньше MDI-приложение вроде Microsoft Word, вы могли заметить, что у него есть подменю под названием "window", которое при активации отображает различные пункты меню, связанные с управлением дочерними окнами, а также список открытых дочерних окон. Этот список создается самими Windows: вам не нужно прилагать специальных усилий. Всего лишь передайте описатель подменю, к которому должен быть присоединен список, а Windows возьмет на себя все остальное. Обратите внимание, что подменю может быть любым: не обязательно тем, которое названо "window". Если вам не нужен список окон, просто передайте NULL в hWindowMenu. Получить дескриптор подменю можно с помощью GetSubMenu.
    idFirstChild ― ID первого дочернего MDI-окна. Windows увеличивает ID на 1 для каждого нового MDI-окна, которое создает приложение. Например, если вы передает 100 через это поле, первое MDI-окно будет иметь ID 100, второе - 101 и так далее. Это ID посылается фреймовому окну через WM_COMMAND, когда дочернее MDI-окно выбрано из списка окон. Обычно вы будете передавать эти "необрабатываемые" сообщения процедуре DefFrameProc. Я использую слово "необрабатываемые", потому что пункты меню списка окон не создаются вашим приложением, поэтому ваше приложение не знает их ID и не имеет обработчика для них. Поэтому для фреймового окна есть специальное правило: если у вас есть список окон, вы должны модифицировать ваш обработчик WM_COMMAND так:
    Код (ASM):
    1. wmCOMMAND:             .if lParam==0
    2.                  mov eax,wParam
    3.                  .if ax==IDM_CASCADE
    4.                         .....
    5.                  .elseif ax==IDM_TILEVERT
    6.                        .....
    7.                  .else
    8.                        invoke DefFrameProc, hwndFrame, hwndClient, uMsg,wParam,
    9.                        ret
    10.                  .endif
    Обычно вам следует игнорировать сообщения о необрабатываемых событиях, но в случае с MDI, если вы просто проигнорируете их, то когда пользователь кликнет на имени дочернего MDI-окна, это окно не станет активным. Вам следует передавать управление DefFrameProc, чтобы они были правильно обработаны.
    Я хочу предупредить вас относительно возможного значения idFirstChild: вам не следует использовать 0. Ваш список окон будет себя вести неправильно, то есть напротив пункта меню, обозначающего активное MDI-окно, не будет галочки. Лучше выберите какое-нибудь безопасное значение вроде 100 или выше.
    Заполнив структуру CLIENTCREATESTRUCT, вы можете создать клиентское окно, вызвав CreateWindowEx, указав предопределенный класс "MDICLIENT" и передав адрес структуры CLIENTCREATESTRUCT через lParam. Вы должны также указать дескриптор на окно фрейма в параметре hWndParent, чтобы Windows знала об отношениях родитель-ребенок между окном фрейма и клиентским окном. Вам следует использовать следующие стили окна: WS_CHILD, WS_VISIBLE и WS_CLIPCHILDREN. Если вы забудете указать стиль WS_VISIBLE, то не увидите дочерних MDI-окон, даже если они будут созданы успешно.

    Этапы создания клиентского окна следующие


    1. Получить дескриптор на подменю, к которому вы хотите присоединить список окон.
    2. Поместите значение дескриптора меню и значения, которое вы хотите использовать как ID первого дочернего MDI-окна в структуру CLIENCREATESTRUCT.
    3. Вызовите CreateWindosEx, передав имя класса "MDICLIENT" и адрес структуры CLIENTCREATESTRUCT, которую вы только что заполнили, через lParam.
     

    Вложения:

    • 9.png
      9.png
      Размер файла:
      169,6 КБ
      Просмотров:
      3.068
    Последнее редактирование: 13 янв 2021
    q2e74 нравится это.
  13. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    Создание дочернего MDI-окна

    Теперь у вас есть и окно фрейма, и клиентское окно. Теперь все готово для создания дочернего MDI-окна. Есть два пути сделать это.
    1. Вы можете послать сообщение WM_MDICREATE клиентскому окну, передав тому адрес структуры типа MDICREATESTRUCT через wParam. Это простейший и наиболее часто используемый способ создания дочерних MDI-окон.
      Код (ASM):
      1.  .data?
      2. mdicreate MDICREATESTRUCT <>
      3. ....
      4. .code
      5. .....
      6. ; заполняем поля структуры mdicreate
      7. ......
      8.                invoke SendMessage, hwndClient, WM_MDICREATE,addr mdicreate,0
      Функция SendMessage возвратит дескриптор только что созданного дочернего MDI-окна, если все пройдет успешно. Вам не нужно сохранять дескриптор. Вы можете получить его каким-либо другим образом, если хотите. У структуры MDICREATESTRUCT следующее определение:
      Код (ASM):
      1. MDICREATESTRUCTA STRUC
      2. szClass LPCTSTR ?
      3. szTitle LPCTSTR ?
      4. hOwner HANDLE ?
      5. x ULONG ?
      6. y ULONG ?
      7. lx ULONG ?
      8. ly ULONG ?
      9. style DWORD ?
      10. Reserved DWORD ?
      11. lParam LPARAM ?
      12. MDICREATESTRUCTA ENDS
      • szClass ― адрес класса окна, который вы хотите использовать в качестве шаблона для дочернего MDI-окна.
      • szTitle ― адрес строки, которая должна появиться в заголовке дочернего окна.
      • hOwner ― дескриптор приложения.
      • x, y, lx, ly ― верхняя левая координата, ширина и высота дочернего окна.
      • style ― стиль дочернего окна. Если вы создали клиентское окно со стилем MDIS_ALLCHILDSTYLES, то вы можете использовать все стили.
      • lParam ― определяемое программистом 32-х битное значение. Используется для передачи значение между MDI-окнами. Если у вас нет подобной нужны, просто поставьте их в NULL.
    2. Вы можете вызвать CreateMDIWindow. Эта функция имеет следующий синтаксис:
      Код (C):
      1. HWND WINAPI CreateMDIWindow(
      2.   _In_     LPCTSTR   lpClassName,
      3.   _In_     LPCTSTR   lpWindowName,
      4.   _In_     DWORD     dwStyle,
      5.   _In_     int       X,
      6.   _In_     int       Y,
      7.   _In_     int       nWidth,
      8.   _In_     int       nHeight,
      9.   _In_opt_ HWND      hWndParent,
      10.   _In_opt_ HINSTANCE hInstance,
      11.   _In_     LPARAM    lParam
      12. );
      Если вы внимательно посмотрите на параметры, вы увидите, что они идентичны параметрам структуры MDICREATESTRUCT не считая hWndParent. Очевидно, что точно такое количество параметров вы передавали вместе с сообщением WM_MDICREATE. У структуры MDICREATESTRUCT нет поля hWndParent, потому что вы все равно должны передавать структуру клиентскому окну с помощью функции SendMessage.
    Сейчас вы можете спросить: какой метод я должен выбрать? Какая разница между этими двумя методами? Вот ответ:
    Метод WM_MDCREATE создает дочернее MDI-окно в том же треде, что и вызывающий код. Это означает, что если у приложения есть только одна ветвь, все дочерние MDI-окна будут выполняться в контексте основной ветви. Это не слишком большая проблема, если одно или более из дочерних MDI-окон не выполняет какие-либо продолжительные операции, что может стать проблемой. Подумайте об этом, иначе в какой-то момент все ваше приложение внезапно зависнет, пока операция не будет выполнена.
    Эта проблема как раз то, что призвана решить функция CreateMDIWindow. Она создает отдельный тред для каждого из дочерних MDI-окон, поэтому если одно из них занято, оно не приводит к зависанию всего приложения.
    Необходимо сказать несколько слов относительно оконной процедуры дочернего MDI-окна. Как и в случае с окном фрейма, вы не должны вызывать DefWindowProc, чтобы обработать необрабатываемые вашим приложением сообщением. Вместо этого вы должны использовать DefMDIChildProc. У этой функции точно такие же параметры, как и у DefWindowProc.
    Кроме WM_MDICREATE, есть еще несколько сообщений, относящихся к MDI-окнам.
    Их описание:
    WM_MDIACTIVATE ― это сообщение может быть послано приложением клиентскому окну, чтобы последнее активировало выбранное дочернее MDI-окно. Когда клиентское окно получает сообщение, оно активирует выбранное дочернее MDI-окно, а также посылает это же сообщение окну, которое было активированы или дезактивировано. Таким образом, данное сообщение имеет двойное назначение: с его помощью приложение может активировать выбранное дочернее окно, а также оно может быть использовано дочерним MDI-приложением для определения того, активировано оно или нет. Например, если каждое дочернее MDI-окно имеет различно меню, оно может использовать эту возможность для изменения меню окна фрейма при активации/дезактивации.
    WM_MDICASCADE, WM_MDITILE, WM_MDICONARRANGE ― эти сообщения отвечаю за расположение дочерних MDI-окон. Например, если вы хотите, чтобы дочерние MDI-окна расположились каскадом, пошлите сообщение WM_MDICASCADE клиентскому окну.
    WM_MDIDESTROY ― пошлите это сообщение клиентскому окну, если хотите уничтожить дочернее MDI_окно. Вам следует использовать это сообщение вместо DestroyWindow, потому что если дочернее MDI-приложение максимизировано, это сообщение восстановить заголовок окна фрейма, что не будет сделано в случае применения функции DestroyWindow.
    WM_MDIGETACTIVE ― используйте это сообщение, чтобы получить хэндл активного в настоящий момент дочернего MDI-окна.
    WM_MDIMAXIMIZE, WM_MDIRESTORE ― используйте WM_MDIMAXIZE для разворачивания дочернего MDI-окна и WM_MDIRESTORE для его восстановления. Всегда используйте эти сообщения для данных операций. Если вы используете ShowWindow с SW_MAXIMIZE, дочернее MDI-окно будет развернуто, но у вас появятся проблемы, когда вы захотите восстановить его до прежнего размера. Вы можете минимизировать окно с помощью ShowWindow без всяких проблем.
    WM_MDINEXT ― посылайте это сообщение клиентскому окну, чтобы активировать следующее или предыдущее дочернее MDI-окно, согласно значению wParam и lParam.
    WM_MDIREFRESHMENU ― это сообщение посылается клиентскому окну, чтобы обновить меню окна фрейма. Обратите внимание, что вы должны вызвать DrawMenuBar для обновления меню баp после отсылки данного сообщения.
    WM_MDISETMENU ― посылайте это сообщение клиентскому окну, что полностью заменить меню окна фрейма или только подменю окон. Вы должны использовать данное сообщение вместо SetMenu. После того, как вы отослали данное сообщение, вы должны вызвать DrawMenuBar. Обычно вы будете использовать это сообщение, когда у активного дочернего MDI-окна есть свое меню и вы хотите, чтобы оно заменяло меню окна фрейма, пока это дочернее MDI-окно активно.
    Небольшое обозрение создания MDI-приложения еще раз:
    1. Регистрируем классы окна, класса фрейма и дочернего MDI-окна.
    2. Создаем окно фрейма с помощью CreateWindowEx.
    3. Внутри цикла обработки сообщений вызываем TranslateMDISysAccel, чтобы обработать "горячие клавиши", относящиеся к MDI.
    4. Внутри оконной процедуры фреймового окна вызываем DefFramProc, чтобы обрабатывать все сообщения, необрабатываемые приложением.
    5. Создаем клиентское окно, вызвав CreateWindowEx, которой передаем имя предопределенного класса окна, "MDICLIENT", передавая адрес структуры CLIENTCREATESTRUCT через lParam. Обычно вы будете создавать клиентское окно внутри обработчика сообщения WM_CREATE окна фрейма.
    6. Вы можете создать дочернее MDI-окно, послав клиентскому окну сообщение WM_MDICREATE или вызвав функцию CreateMDIWindow.
    7. Внутри оконной процедуры дочернего MDI-ОКНА, передаем все необработанные сообщения DefMDIChildProc.
    8. Используем MDI-версии сообщений, если таковые существуют. Например, вместо WM_MDIDESTROY вместо DestroyWindow.
     
    Последнее редактирование: 5 янв 2017
  14. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    ПРАКТИКА ― СЕСТРА ШИЗОФРЕНИИ

    asm-файл

    Код (ASM):
    1. include win64.inc
    2. IMAGE_BASE        equ 400000h
    3. IDM_MENU        equ 1000
    4. IDM_FILE_EXIT        equ 0
    5. IDM_FILE_NEW        equ 1
    6. IDM_WINDOW_NEXT     equ 2
    7. IDM_WINDOW_PREVIOUS    equ 3
    8. MI_CLOSE        equ 4
    9. MI_TILEHOR        equ 5
    10. MI_TILEVER        equ 6
    11. MI_CASCADE        equ 7
    12. IDM_HELP_ABOUT        equ 8
    13. MI_CLOSE_ALL        equ 9
    14. .code
    15. WinMain proc
    16. local msg:MSG
    17.         push rbp
    18.     mov ebp,esp
    19.     sub esp,sizeof MSG
    20.  
    21.     xor ebx,ebx
    22.     mov edi,offset ClassName
    23.     mov eax,10029h
    24.     mov esi,IMAGE_BASE
    25.     push rax    ;hIconSm
    26.     push rdi    ;lpszClassName
    27.     push IDM_MENU    ;lpszMenuName
    28.     push COLOR_APPWORKSPACE;hbrBackground
    29.     push 10005h    ;hCursor
    30.     push rax        ;hIcon
    31.     push rsi    ;hInstance
    32.     push DLGWINDOWEXTRA;cbClsExtra & cbWndExtra
    33.     lea rax,WndProc
    34.     push rax    ;lpfnWndProc
    35.     push sizeof WNDCLASSEX;cbSize & style
    36.     mov ecx,esp    ;addr WNDCLASSEX
    37.     mov edi,ecx
    38.     sub esp,20h
    39.         call RegisterClassEx
    40.  
    41.     mov eax,offset MdiChildProc
    42.         mov [rdi+WNDCLASSEX.lpfnWndProc],rax
    43.     mov [rdi+WNDCLASSEX.cbWndExtra],ebx
    44.     mov [rdi+WNDCLASSEX.lpszMenuName],rbx
    45.     mov eax,offset MdiChildClassName
    46.     mov [rdi+WNDCLASSEX.lpszClassName],rax
    47.     mov ecx,edi
    48.     call RegisterClassEx
    49.  
    50.     push rbx
    51.     push rsi    ;rsi=400000h
    52.     shl esi,9    ;rsi=CW_USEDEFAULT
    53.     push rbx
    54.     push rbx
    55.     push 600
    56.     push 800
    57.     push rsi
    58.     push rsi
    59.     mov r9d,WS_OVERLAPPEDWINDOW or WS_VISIBLE or WS_CLIPCHILDREN
    60.     mov edx,offset ClassName
    61.     mov r8,rdx    ;offset ClassName
    62.     mov ecx,WS_EX_CLIENTEDGE
    63.     sub esp,20h
    64.         call CreateWindowEx
    65.         mov hWnd,rax
    66.         mov ecx,eax
    67.         call GetMenu
    68.         mov hMenu,rax
    69.         mov edx,1
    70.         mov ecx,eax
    71.         call GetSubMenu
    72.  
    73.         mov cc.hWindowMenu,rax
    74.         mov edi,offset rect
    75.         mov edx,edi
    76.         mov rcx,hWnd
    77.         call GetClientRect
    78.         mov eax,offset cc
    79.         push rax
    80.         push rsi
    81.         push rbx
    82.         push hWnd
    83.         mov eax,[rdi+RECT.bottom]
    84.         push rax
    85.         mov eax,[rdi+RECT.right]
    86.         push rax
    87.         push rbx
    88.         push rbx
    89.         mov r9d,WS_CHILD + WS_VISIBLE + WS_VSCROLL + WS_HSCROLL + WS_CLIPCHILDREN
    90.         mov r8,rbx
    91.         mov edx,offset MdiClientClassName
    92.         mov ecx,WS_EX_CLIENTEDGE
    93.         sub esp,20h
    94.         call CreateWindowEx
    95.         mov hClient,rax
    96.         lea edi,msg
    97. @@:     mov ecx,edi
    98.     xor edx,edx
    99.     mov r8,rbx
    100.     mov r9,rbx
    101.         call GetMessage
    102.         mov edx,edi
    103.         mov rcx,hClient
    104.         call TranslateMDISysAccel
    105.         test eax,eax
    106.         jnz @b
    107.     mov ecx,edi
    108.         call DispatchMessage
    109.         jmp @b
    110. WinMain endp
    111. WndProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    112.         push rbp
    113.     mov ebp,esp
    114.     sub esp,20h
    115.  
    116.     mov hWin,rcx
    117.     mov wParam,r8
    118.     mov lParam,r9
    119.     xor ebx,ebx
    120.  
    121.         cmp  edx,WM_DESTROY
    122.         je   wmDESTROY
    123.         cmp  edx,WM_COMMAND
    124.         je   wmCOMMAND
    125.         cmp  edx,WM_SIZE
    126.         je wmSIZE
    127.         leave
    128.         jmp DefWindowProc
    129. wmDESTROY: xor ecx,ecx
    130.         call ExitProcess
    131. wmCOMMAND:mov rax,r8;[wParam]
    132.         cmp eax,MI_CLOSE_ALL;5
    133.         ja wmBYE
    134.         jmp [menu_handlers+rax*8]
    135. New:    mov esi,IMAGE_BASE
    136.         push rbx;NULL
    137.         push rsi;hInstance
    138.         shl esi,9
    139.         push rbx
    140.         push hClient
    141.         push rsi
    142.         push rsi
    143.         push rsi
    144.         push rsi
    145.         mov r9d,MDIS_ALLCHILDSTYLES
    146.         mov edx,offset MdiChildClassName
    147.         mov r8,rdx
    148.         mov ecx,WS_EX_MDICHILD + WS_EX_CLIENTEDGE
    149.         sub esp,20h
    150.         call CreateWindowEx
    151.         jmp wmBYE
    152. Exit:   mov rax,hWin
    153.         jmp a1
    154. Next:   mov r9d,TRUE
    155.         jmp a3
    156. Prev:   mov r9,rbx
    157. a3:     mov r8,rbx
    158.         mov edx,WM_MDINEXT
    159. a4:     mov rcx,hClient
    160.         jmp a2
    161. Horiz:  mov r9,rbx
    162.         mov r8d,MDITILE_HORIZONTAL
    163. a5:     mov edx,WM_MDITILE
    164.         jmp a4
    165. Vert:   mov r9,rbx
    166.         mov r8,rbx
    167.         jmp a5
    168. Cascade:mov r9,rbx
    169.         mov r8d,MDITILE_SKIPDISABLED
    170.         mov edx,WM_MDICASCADE
    171.         jmp a4
    172. Close:  mov r9,rbx
    173.         mov r8,rbx
    174.         mov edx,WM_MDIGETACTIVE
    175.         mov rcx,hClient
    176.         call SendMessage
    177. a1:     mov r9,rbx
    178.         mov r8,rbx
    179.         mov edx,WM_CLOSE
    180.         mov rcx,rax
    181. a2:     call SendMessage
    182.         jmp wmBYE
    183. About:  mov r9,rbx
    184.         mov r8d,offset AboutMsg
    185.         mov edx,offset ClassName
    186.         mov rcx,hWin
    187.         call ShellAbout
    188.         jmp wmBYE
    189. CloseAll:mov r9,rbx
    190.         mov r8,rbx
    191.         mov edx,WM_MDIGETACTIVE
    192.         mov rcx,hClient
    193.         call SendMessage
    194.         test eax,eax
    195.         je wmBYE
    196.         mov r9,rbx
    197.         mov r8,rbx
    198.         mov edx,WM_CLOSE
    199.         mov ecx,eax
    200.         call SendMessage
    201.         jmp CloseAll
    202. wmSIZE: mov edi,offset rect
    203.         mov edx,edi
    204.         mov rcx,hWin
    205.         call GetClientRect
    206.         push TRUE
    207.         mov eax,[rdi+RECT.bottom]
    208.         push rax
    209.         mov r9d,[rdi+RECT.right]
    210.         mov r8,rbx
    211.         xor edx,edx
    212.         mov rcx,hClient
    213.         sub esp,20h
    214.         call MoveWindow
    215. wmBYE:    leave
    216.     retn
    217. menu_handlers dq Exit,New,Next,Prev,Close,Horiz,Vert,Cascade,About,CloseAll
    218. WndProc endp
    219. MdiChildProc proc
    220.  
    221.         push rbp
    222.         mov ebp,esp
    223.         sub esp,20h
    224.  
    225.         cmp edx,WM_MDIACTIVATE
    226.         jz wmMDIACTIVATE
    227.         leave
    228.         jmp DefMDIChildProc
    229. wmMDIACTIVATE: mov rax,r9;[lParam2]
    230.         cmp rax,rcx;[hChild]
    231.         setne bl;MF_ENABLED=0 MF_GRAYED=1
    232.         mov r8,rbx
    233.         mov edx,MI_CLOSE
    234.         mov rcx,hMenu
    235.         call EnableMenuItem
    236.         mov r8,rbx
    237.         mov edx,MI_TILEHOR
    238.         mov rcx,hMenu
    239.         call EnableMenuItem
    240.         mov r8,rbx
    241.         mov edx,MI_TILEVER
    242.         mov rcx,hMenu
    243.         call EnableMenuItem
    244.         mov r8,rbx
    245.         mov edx,MI_CASCADE
    246.         mov rcx,hMenu
    247.         call EnableMenuItem
    248.         mov r8,rbx
    249.         mov edx,IDM_WINDOW_NEXT
    250.         mov rcx,hMenu
    251.         call EnableMenuItem
    252.         mov r8,rbx
    253.         mov edx,IDM_WINDOW_PREVIOUS
    254.         mov rcx,hMenu
    255.         call EnableMenuItem
    256.         mov r8,rbx
    257.         mov edx,MI_CLOSE_ALL
    258.         mov rcx,hMenu
    259.         call EnableMenuItem
    260.         xor ebx,ebx
    261.         leave
    262.     retn
    263.  
    264. MdiChildProc endp
    265. ;data
    266. ClassName db 'Win64 Iczelion''s lesson #32: Multiple Document Interface (MDI)',0
    267. MdiChildClassName db 'MDICHILD',0
    268. MdiClientClassName db 'MDICLIENT',0
    269. cc      CLIENTCREATESTRUCT <0,2000>
    270. hWnd         dq ?
    271. hClient      dq ?
    272. hMenu        dq ?
    273. rect      RECT <>
    274. AboutMsg    db 'Mikl__ 2015',0
    275. end

    rc-файл

    Код (C):
    1. #define IDM_MENU        1000
    2. #define IDM_FILE_EXIT           0
    3. #define IDM_FILE_NEW        1
    4. #define IDM_WINDOW_NEXT         2
    5. #define IDM_WINDOW_PREVIOUS    3
    6. #define MI_CLOSE        4
    7. #define MI_TILEHOR                 5
    8. #define MI_TILEVER                 6
    9. #define MI_CASCADE                 7
    10. #define IDM_HELP_ABOUT             8
    11. #define MI_CLOSE_ALL               9
    12.  
    13. IDM_MENU MENU DISCARDABLE
    14. BEGIN
    15.     POPUP "&File"
    16.     BEGIN
    17.                MENUITEM "&New", IDM_FILE_NEW
    18.                 MENUITEM "&Close\tCtrl+F4",MI_CLOSE,GRAYED
    19.                 MENUITEM SEPARATOR
    20.         MENUITEM "&Exit",IDM_FILE_EXIT
    21.     END
    22.         POPUP "&Window"
    23.         BEGIN
    24.             MENUITEM "Tile Horizontal",MI_TILEHOR,GRAYED
    25.             MENUITEM "Tile Vertical",MI_TILEVER,GRAYED
    26.             MENUITEM "Cascade",MI_CASCADE,GRAYED
    27.             MENUITEM "&Next\tCtrl+F6",IDM_WINDOW_NEXT,GRAYED
    28.         MENUITEM "&Previous\tShift+Ctrl+F6",IDM_WINDOW_PREVIOUS,GRAYED
    29.         MENUITEM "&Close All",MI_CLOSE_ALL,GRAYED
    30.         END
    31.         POPUP "&Help"
    32.         BEGIN
    33.             MENUITEM "&About",IDM_HELP_ABOUT
    34.         END
    35. END

    Разбор полётов

    Первое, что должна сделать программа ― это зарегистрировать классы фреймового и дочернего MDI-окна. После этого она вызывает функцию CreateWindowEx, чтобы создать окно фрейма. Внутри обработчика WM_CREATE окна фрейма создаем клиентское окно:
    Код (ASM):
    1. LOCAL ClientStruct:CLIENTCREATESTRUCT
    2.            .if uMsg==WM_CREATE
    3.                    invoke GetMenu,hWnd
    4.                    mov hMainMenu,eax
    5.                    invoke GetSubMenu,hMainMenu,1
    6.                    mov ClientStruct.hWindowMenu,rax
    7.                    mov ClientStruct.idFirstChild,100
    8.                    invoke CreateWindowEx,NULL,ADDR MDIClientName,NULL,\
    9.                            WS_CHILD or WS_VISIBLE or WS_CLIPCHILDREN,CW_USEDEFAU
    10.                            CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hWnd,NULL,\
    11.                            hInstance,addr ClientStruct
    12.                    mov hwndClient,rax
    Здесь мы вызываем GetMenu, чтобы получить дескриптор меню окна фрейма, который будем использовать в GetSubMenu. Обратите внимание, что мы передаем 1 функции GetSubMenu, потому что подменю, к которому мы будем присоединять список окон, является вторым подменю. Затем мы заполняем параметры структуры CLIENTCREATESTRUCT.
    Затем мы инициализируем структуру MDCLIENTSTRUCT. Обратите внимание, что мы не обязаны делать это здесь. Просто это удобнее осуществлять в обработчике WM_CREATE.
    Код (ASM):
    1.            mov mdicreate.szClass,offset MDIChildClassName
    2.            mov mdicreate.szTitle,offset MDIChildTitle
    3.            push hInstance
    4.            pop mdicreate.hOwner
    5.            mov mdicreate.x,CW_USEDEFAULT
    6.            mov mdicreate.y,CW_USEDEFAULT
    7.            mov mdicreate.lx,CW_USEDEFAULT
    8.            mov mdicreate.ly,CW_USEDEFAULT
     
    Последнее редактирование: 5 янв 2017
    q2e74 нравится это.
  15. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    После того, как окно фрейма создано (так же как клиентское окно), мы вызывает LoadMenu, чтобы загрузить меню дочернего окна из ресурса. Нам нужно получить дескриптор этого меню, чтобы мы могли заменить меню окна фрейма, когда дочернее MDI-окно становится активным. Не забудьте вызвать DestroyMenu, прежде чем приложение завершит работу. Обычно Windows сама освобождает память, занятую меню, но в данном случае этого не произойдет, так как меню дочернего окна не ассоциировано ни с каким окном, поэтому оно все еще будет занимать ценную память, хотя приложение уже прекратило свое выполнение.
    Код (ASM):
    1.            invoke LoadMenu,hInstance, IDR_CHILDMENU
    2.            mov hChildMenu,eax
    3.            ........
    4.            invoke DestroyMenu, hChildMenu
    Внутри цикла обработки сообщений, мы вызываем TranslateMDISysAccel.
    Код (ASM):
    1.            .while TRUE
    2.                    invoke GetMessage,ADDR msg,NULL,0,0
    3.                    .break .if (!eax)
    4.                       invoke TranslateMDISysAccel,hwndClient,addr msg
    5.                    .if !eax
    6.                            invoke TranslateMessage, ADDR msg
    7.                            invoke DispatchMessage, ADDR msg
    8.                    .endif
    9.            .endw
    Если TranslateMDISysAccel возвращает ненулевое значение, это означает, что сообщение уже было обработано Windows, поэтому вам не нужно делать что-либо с ним. Если был возвращен 0, сообщение не относится к MDI и поэтому должно обрабатываться как обычно.
    Код (ASM):
    1.    WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    2.            .....
    3.            .else
    4.                    invoke DefFrameProc,hWnd,hwndClient,uMsg,wParam,lParam
    5.                    ret
    6.            .endif
    7.            xor eax,eax
    8.            ret
    9.    WndProc endp
    Обратите внимание, что внутри оконной процедуры фреймового окна мы вызываем DefFrameProc для обработки сообщений, которые не представляют для нас интереса.
    Основной часть процедуры окна является обработчик сообщения WM_COMMAND. Когда пользователь выбирает в меню пункт "New", мы создает новое дочернее MDI-окно.
    Код (ASM):
    1.            .elseif ax==IDM_NEW
    2.                    invoke SendMessage,hwndClient,WM_MDICREATE,0,addr mdicreate
    В нашем примере мы создаем дочернее MDI-окно, посылая WM_MDIREATE клиентскому окну, передавая адрес структуры MDICREATESTRUCT через lParam.
    Код (ASM):
    1.    ChildProc proc hChild:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
    2.            .if uMsg==WM_MDIACTIVATE
    3.                    mov eax,lParam
    4.                    .if eax==hChild
    5.                            invoke GetSubMenu,hChildMenu,1
    6.                            mov edx,eax
    7.                            invoke SendMessage,hwndClient,WM_MDISETMENU,hChildMen
    8.                    .else
    9.                            invoke GetSubMenu,hMainMenu,1
    10.                            mov edx,eax
    11.                            invoke SendMessage,hwndClient,WM_MDISETMENU,hMainMenu
    12.                    .endif
    13.                    invoke DrawMenuBar,hwndFrame
    Когда создано дочернее MDI-окно, оно отслеживает сообщение WM_MDIACTIVATE, чтобы определить, является ли оно в данный момент активным. Оно делает это сравнивая значение lParam, которое содержит дескриптор активного дочернего окна со своим собственным дескриптором.
    Если они совпадают, значит оно является активным и следующим шагом будет замена меню окна фрейма на свое собственное. Так как изначально меню будет заменено, вам надо будет указать Windows снова в каком подменю должен появиться список окон. Поэтому мы должны снова вызвать функцию GetSubMenu, чтобы получить дескриптор подменю. Мы посылаем сообщение WM_MDISETMENU клиентскому окну, достигая, таким образом, желаемого результата. Параметр wParam сообщения WM_MDISETMENU содержит дескриптор меню, которое заменит оригинальное. lParam содержит дескриптор подменю, к которому будет присоединен список окон. Сразу после отсылки сообщения WM_MDISETMENU, мы вызываем DrawMenuBar, чтобы обновить меню, иначе произойдет большая путаница.
    Код (ASM):
    1.            .else
    2.                    invoke DefMDIChildProc,hChild,uMsg,wParam,lParam
    3.                    ret
    4.            .endif
    Внутри оконной процедуры дочернего MDI-окна вы должны передать все необработанные сообщения функции DefMDIChildProc.
    Код (ASM):
    1.            .elseif ax==IDM_TILEHORZ
    2.                    invoke SendMessage,hwndClient,WM_MDITILE,MDITILE_HORIZONTAL,0
    3.            .elseif ax==IDM_TILEVERT
    4.                    invoke SendMessage,hwndClient,WM_MDITILE,MDITILE_VERTICAL,0
    5.            .elseif ax==IDM_CASCADE
    6.                    invoke SendMessage,hwndClient,WM_MDICASCADE,MDITILE_SKIPDISAB
    Когда пользователь выбирает один из пунктов меню в подменю окон, мы посылаем соответствующее сообщение клиентскому окну. Если пользователь выбирает один из методов расположения окон, мы посылаем WM_MDITILE или WM_CASCADE.
    Код (ASM):
    1.            .elseif ax==IDM_CLOSE
    2.                    invoke SendMessage,hwndClient,WM_MDIGETACTIVE,0,0
    3.                    invoke SendMessage,eax,WM_CLOSE,0,0
    Если пользователь выбирает пункт меню "Close", мы должны получить дескриптор текущего активного MDI-окна.
    Код (ASM):
    1. wmCLOSE:
    2.                    invoke MessageBox,hChild,addr ClosePromptMessage,addr AppName
    3.                    .if eax==IDYES
    4.                            invoke SendMessage,hwndClient,WM_MDIDESTROY,hChild,0
    5.                    .endif
    Когда процедура дочернего MDI-окна получает сообщение WM_CLOSE, его обработчик отображает окно, которое спрашивает пользователя, действительно ли он хочет закрыть окно. Если ответ - "Да", то мы посылаем клиентскому окну сообщение WM_MDIDESTROY, которое закрывает дочернее MDI-окно и восстанавливает заголовок окна фрейма.
    [​IMG]
     

    Вложения:

    • Win64.png
      Win64.png
      Размер файла:
      45,2 КБ
      Просмотров:
      3.074
    Последнее редактирование: 19 май 2019
    rococo795 нравится это.
  16. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    Глава тридцать седьмая. Братец Кролик и элементы управления

    [​IMG]
    Мы узнаем, что такое элементы управления и как их использовать. Эта глава является не более, чем поверхностным введением в данную тему.
    Скачайте код примера здесь.

    ТЕОРИЯ ― МАТЬ СКЛЕРОЗА

    Windows 95 принесла несколько новых элементов пользовательского интерфейса, сделавших GUI более разнообразным. Некоторые из них широко использовались и в Windows 3.1, но программисты должны были программировать их самостоятельно. Теперь Microsoft включил их в Windows 9x и NT. Мы изучим их в этой главе.
    Вот список элементов управления:
    КатегорияЭлемент управленияОписание
    Элементы управления, обычно используемые в главном окне
    Элементы управления главного окнаToolbar (панель инструментов)Состоит из кнопок быстрого доступа
    Tooltip (окно подсказки)Обеспечивает пользователя быстрой подсказкой, отображая текст во всплывающем окне
    Status bar (строка состояния)Информационная строка, обычно размещаемая в нижней части окна приложения.
    Элементы управления для списков свойств и мастеров (wizards)
    Составные диалоговые элементы управленияProperty page (страница свойств)Немодальное диалоговое окно, используемое как одна страница в списке свойств или мастере.
    Property sheet (набор страниц свойств)Набор из множества окон страниц свойств
    Элементы управления для построения приложений, похожих на Windows Explorer
    Элементы управления Windows ExplorerTree view (дерево просмотра)Отображает иерархически элементизированный список (левая панель окна программы Windows Explorer)
    List view (список просмотра)Отображает список элементов, идентифицируемых битовым образом и текстовыми данными (правая панель окна программы Windows Explorer)
    Другие элементы управления
    Animation (анимационное изображение)Проигрывает анимационную последовательность для индикации длительной операции
    Drag list (список, поддерживающий операции типа drag/drop)Окно списка, поддерживающее простые операции drag/drop по отношению к себе и другим окнам типа Drag list. (Не drag/drop OLE-контейнер)
    Header (заголовок списка просмотра)Отображает горизонтальные заголовки для столбцов (используется совместно со списком просмотра)
    Hot-Key (горячая клавиша)Отображает результат операции определения клавиш активизации (горячих клавиш)
    Image list (список изображений)Элемент управления для хранения набора растровых изображений (битовых образов, курсоров, значков), не являющийся окном
    Progress bar (индикатор процесса)Отображает динамику длительной операции как процент от выполненной задачи
    Rich edit (усовершенствованный редактор)Редактор, поддерживающий множество шрифтов и базовые возможности OLE-контейнера
    Tabs control (набор закладок для выбора)Отображает список закладок для выбора. Tabs используются в окне набора страниц свойств для выбора страницы свойств. Панель задач (task bar) Windows — элемент управления Tabs control, использующий кнопки вместо закладок
    Trackbar (окно с движком для выбора значения из диапазона)Тип полосы прокрутки для выбора значения в заданном диапазоне
    Up-Down (полоса прокрутки, связанная с окном редактирования для увеличения или уменьшения на 1 целочисленного значения)Тип полосы прокрутки, состоящий из двух стрелок (но собственно без полосы) для увеличения или уменьшения на 1 величины, находящейся в связанном поле редактирования
    Так как новых элементов управления довольно много, их загрузка в память и регистрация была бы бессмысленной тратой ресурсов. Все эти элементы управления, за исключением rich edit'а, находятся в comctl32.dll, чтобы приложения могли загружать их, когда они им нужны. Rich edit находится в своей собственной dll, richedXX.dll, так как он слишком сложен и поэтому больше, чем остальные.
    Вы можете вызвать comctl32.dll, поместив вызов функции IntiCommonControls в вашу программу. InitCommonControls ― это функция в comctl32.dll, поэтому ее вызов в любом месте вашего кода заставит PE-загрузчик загрузить comctl32.dll, когда ваша программ запустится. Вам не нужно выполнять эту функцию, просто поместите ее где-нибудь. Эта функция ничего не делает! Ее единственной инструкцией является "ret". Ее главная цель ― это создание ссылки на comctl32.dll в секции импорта, чтобы PE-загрузчик загружал ее всегда, когда будет загружаться программа. Главным следствием будет являться то, что стартовая функция DLL зарегистрирует все классы элементов управления при загрузке dll. Элементы управления создаются на основе этих классов, как и другие дочерние элементы окон, например, окна ввода, listbox и так далее.
    С rich edit'ом дел обстоит совершенно по другому. Если вы хотите использовать его, вы должны вызвать LoadLibrary, чтобы загрузить его и FreeLibrary, чтобы выгрузить.
    Теперь давайте научимся создавать элементы управления. Вы можете использовать редактор ресурсов, чтобы внедрить их в диалоговое окно, или создать их самостоятельно. Почти все элементы управления создаются с помощью вызова CreateWindowEx или CreateWindow, путем передачи имени класса элемента управления. У некоторых элементов управления есть специальные функции для создания, хотя, на самом деле, они являются функциями-обертками вокруг CreateWindowEx, чтобы сделать создание элемента управления легче. Такие функции перечислены ниже:
    • CreateToolbarEx
    • CreateStatusWindow
    • CreatePropertySheetPage
    • PropertySheet
    • ImageList_Create
    Чтобы создавать элементы управления, вы должны знать их имена. Они перечислены ниже:
    Категория/Элемент управленияКласс элемента управленияФункция создания
    Элементы управления главного окна
    Панель инструментовToolbarWindow32CreateToolbarEx
    Окно подсказкиtooltips_class32
    Строка состоянияmsctls_statusbar32CreateStatusWindow
    Анимационное изображениеSysAnimate32
    Индикатор процессаmsctls_progress32
    Составные диалоговые элементы управления
    Страница свойствCreatePropertySheetPage
    Набор страниц свойствPropertySheet
    Элементы управления Windows Explorer
    Дерево просмотраSysTreeView32
    Список просмотраSysListView32
    Список изображенийImageList_Create
    Другие элементы управления
    Список, поддерживающий операции типа drag/drop"listbox"MakeDragList
    Заголовок списка просмотраSysHeader32
    Горячая клавишаmsctls_hotkey32
    Усовершенствованный редакторRICHEDIT
    Набор закладок для выбораSysTabControl32
    Окно с движком для выбора значения из диапазонаTRACKBAR_CLASS
    Полоса прокрутки, связанная с окном редактирования для изменения значенияmsctls_updown32CreateUpDownControl
    Property sheet'ы и Property Page'ы и список изображений имеют собственные функции создания. Drag list control ― это усовершенствованный listbox, поэтому у него нет своего собственного класса.
    Эти элементы управления могут использовать общие стили окна, такие как WS_CHILD и тому подобное. У них также есть специальные стили, такие как TVS_XXX для окон просмотра деревьев, LVS_XXX для list view control'а и так далее.
    Теперь, когда вы знаете, как создать элементы управления, можно перейти к взаимодействию элементов управления и окон, на которых эти элементы расположены. В отличие от дочерних элементов управления, элементы управления не взаимодействую с родительским окном через WM_COMMAND. Вместо этого они используют сообщение WM_NOTIFY, посылаемое родительскому окну, когда происходит какое-то событие. "Родительское окно" может контролировать "детей", посылая им определенные сообщения.
    Давайте посмотрим, как создать progress bar и status bar.
     

    Вложения:

    • tut_34.zip
      Размер файла:
      3,8 КБ
      Просмотров:
      939
    Последнее редактирование: 13 янв 2021
    rococo795 нравится это.
  17. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792

    ПРАКТИКА ― СЕСТРА ШИЗОФРЕНИИ

    Код (ASM):
    1. iinclude win64a.inc
    2. IMAGE_BASE equ 400000h
    3. IDC_PROGRESS  equ 1; control IDs
    4. IDC_STATUS  equ 2
    5. IDC_TIMER  equ 3
    6. btn1   equ 4
    7. .code
    8. WinMain proc
    9. local msg:MSG
    10.  
    11.        invoke InitCommonControls
    12.  
    13.        xor ebx,ebx
    14.        mov eax,10027h
    15.        mov edi,offset ClassName
    16.        mov esi,IMAGE_BASE
    17.        push rax ;hIconSm
    18.        push rdi ;lpszClassName
    19.        push rbx ;lpszMenuName
    20.        push COLOR_APPWORKSPACE;hbrBackground
    21.        push 10005h ;hCursor
    22.        push rax        ;hIcon
    23.        push rsi ;hInstance
    24.        push rbx        ;cbClsExtra & cbWndExtra
    25.        pushaddr WndProc ;lpfnWndProc
    26.        push sizeof WNDCLASSEX;cbSize & style
    27.        invoke RegisterClassEx,esp ;addr WNDCLASSEX
    28.        push rbx
    29.        push rsi ;rsi=400000h
    30.        shl esi,9 ;rsi=CW_USEDEFAULT
    31.        push rbx
    32.        push rbx
    33.        push 240
    34.        push 400
    35.        push rsi
    36.        push rsi
    37.        sub esp,20h
    38.        invoke CreateWindowEx,0,edi,edi,WS_OVERLAPPEDWINDOW+WS_VISIBLE
    39.  
    40.        lea edi,msg
    41. @@:invoke GetMessage,edi,0,0,0
    42.        invoke DispatchMessage,edi
    43.        jmp @b
    44.  
    45. WinMain endp
    46.  
    47. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    48. local oldBrush:QWORD
    49. local ps:PAINTSTRUCT
    50.  
    51.        mov hWnd,rcx
    52.        mov wParam,r8
    53.        mov lParam,r9
    54.  
    55.        cmp  edx,WM_DESTROY
    56.        je   wmDESTROY
    57.        cmp  edx,WM_PAINT
    58.        je   wmPAINT
    59.        cmp  edx,WM_CREATE
    60.        je   wmCREATE
    61.        cmp  edx,WM_COMMAND
    62.        je   wmCOMMAND
    63.        leave
    64.        jmp DefWindowProc
    65. wmPAINT:lea edx,ps
    66.        invoke BeginPaint
    67.        invoke SelectObject,ps.hdc,blueBrush
    68.        mov oldBrush,rax
    69.        finit
    70.        fld angle
    71.        fsincos
    72.        fmul const_80
    73.        fistp tempX
    74.        fmul const_80
    75.        fchs
    76.        fist tempY
    77.        mov eax,tempY
    78.        add rax,130
    79.        push rax ;nYRadial2
    80.        mov eax,tempX
    81.        add rax,190
    82.        push rax ;nXRadial2
    83.        push 80       ;nYRadial1
    84.        push 190      ;nXRadial1
    85.        push 180      ;nBottomRect
    86.        sub esp,20h
    87.        invoke Pie,ps.hdc,110,80,270
    88.        invoke SelectObject,ps.hdc,oldBrush
    89.        lea edx,ps
    90.        invoke EndPaint,hWnd
    91.        jmp wmBYE
    92. wmDESTROY:mov rdx,TimerID
    93.        or edx,edx
    94.        jne  @f
    95.        invoke KillTimer;,hWnd
    96. @@: invoke ExitProcess,0
    97.  
    98. wmCREATE:mov edi,3
    99. @@: push rbx ;lpParam
    100.        push IMAGE_BASE ;hInstance
    101.        push IDC_PROGRESS
    102.        push hWnd ;hWndParent
    103.        push nHeight[rdi*8]
    104.        push nWidth[rdi*8]
    105.        push y[rdi*8]
    106.        push x[rdi*8]
    107.        mov r9d,dwStyle[rdi*4]
    108.        ;mov edx,offset progressClass;lpClassName
    109.        mov ecx,dwExStyle[rdi*4]
    110.        sub esp,20h
    111.        invoke CreateWindowEx,,&progressClass,0;lpWindowName
    112.        mov pb1H[rdi*8],rax
    113.        invoke SendMessage,eax,PBM_SETSTEP,2,0
    114.        dec edi
    115.        jns @b
    116.  
    117.        push rbx;NULL
    118.        push IMAGE_BASE
    119.        push btn1
    120.        push hWnd
    121.        push 30
    122.        push 100
    123.        push 90
    124.        push 10
    125.        mov r8d,offset btn1Txt
    126.        mov edx,offset ctlClsNameBtn
    127.        sub esp,20h
    128.        invoke CreateWindowEx,NULL,,,WS_CHILD + WS_VISIBLE + BS_PUSHBUTTON
    129.        mov btn1H,rax
    130.        invoke CreateStatusWindow,WS_CHILD + WS_VISIBLE,NULL,hWnd,IDC_STATUS
    131.        mov hwndStatus,rax
    132.        invoke CreateSolidBrush,0FF9933h; light blue color
    133.        mov blueBrush,rax
    134.        jmp wmBYE
    135. wmCOMMAND:cmp wParam,BN_CLICKED shl 16 or 4
    136.        jne wmBYE
    137. wmCOMMAND_btn1:;create a timer
    138.        mov r9d,offset TimerProc
    139.        invoke SetTimer,hWnd,IDC_TIMER,100
    140.        mov TimerID,rax
    141.        invoke EnableWindow,btn1H,FALSE
    142. wmBYE: leave
    143. retn
    144. WndProc endp
    145.  
    146. TimerProc proc hWnd:HWND
    147. local buffer[16]:BYTE
    148.  
    149.        mov hWnd,rcx
    150.        mov edi,3
    151. @@:    invoke SendMessage,pb1H[rdi*8],PBM_STEPIT,0,0; step up the progress in
    152.        invoke SendMessage,pb1H[rdi*8],PBM_GETPOS,0,0
    153.        dec edi
    154.        jns @b
    155.        lea ecx,buffer
    156.        mov edx,offset format
    157.        invoke wsprintf,,,rax
    158.        lea r9,buffer
    159.        invoke SendMessage,hwndStatus,SB_SETTEXT,0
    160.        invoke InvalidateRect,hWnd,0,0
    161.        finit
    162.        fld delta
    163.        fadd angle
    164.        fst angle
    165.        sub CurrentStep,2
    166.        jne wmBYE
    167.        invoke KillTimer,hWnd,TimerID
    168.        mov TimerID,0
    169.        mov r9d,offset Message
    170.        invoke SendMessage,hwndStatus,SB_SETTEXT,0
    171. wmBYE: leave
    172.        retn
    173. TimerProc endp
    174. ;data
    175. ClassName db 'Win64 Iczelion''s lesson #18: Common Controls',0
    176. progressClass db 'msctls_progress32',0
    177. TimerID dq ?
    178. pb1H dq ?;hwndprogress
    179. pb2H dq ?
    180. pb3H dq ?
    181. pb4H dq ?
    182. sb1H dq ?
    183. btn1H dq ?
    184. hwndStatus dq ?
    185. ctlClsNameBtn db 'BUTTON',0
    186. btn1Txt  db 'Click To Start',0
    187. CurrentStep dq 100 ;increase step value
    188. blueBrush dq ?
    189. const_80 dd 80.0
    190. tempX dd ?
    191. tempY dd ?
    192. angle dd 1.57952297305486826711594014548    ;90.5*pi/180
    193. delta dd 0.12548917321839229658081336625    ;7.19*pi/180
    194. dwStyle dd WS_CHILD + WS_VISIBLE,WS_CHILD + WS_VISIBLE + PBS_SMOOTH,\
    195.                         WS_CHILD + WS_VISIBLE + PBS_VERTICAL,WS_CHILD + WS_VISIBLE + PBS_VERTICAL + PBS_SMOOTH
    196. x               dq 10,10,305,350
    197. y               dq 10,40,80,80
    198. nHeight         dq 22,22,100,100
    199. nWidth          dq 367,367,25,25
    200. dwExStyle       dd WS_EX_DLGMODALFRAME,WS_EX_DLGMODALFRAME + WS_EX_STATICEDGE,0,0
    201. format db ' Process : %i %%',0
    202. Message db ' 100% Completed',0
    203. end

    Разбор полетов

    Код (ASM):
    1. invoke InitCommonControls
    эта функция необходима только для создания ссылки на comctl32.dll в секции импорта. Как вы можете видеть, элементы управления работают, даже если функцию InitCommonControls поместить после вызова ExitProcess.
    Код (ASM):
    1. wmCREATE:mov edi,3
    2. @@: push rbx ;lpParam
    3.        push IMAGE_BASE ;hInstance
    4.        push IDC_PROGRESS
    5.        push hWnd ;hWndParent
    6.        push nHeight[rdi*8]
    7.        push nWidth[rdi*8]
    8.        push y[rdi*8]
    9.        push x[rdi*8]
    10.        mov r9d,dwStyle[rdi*4]
    11.        mov edx,offset progressClass;lpClassName
    12.        mov ecx,dwExStyle[rdi*4]
    13.         sub esp,20h
    14.        invoke CreateWindowEx,,,0;lpWindowName
    15.        mov pb1H[rdi*8],rax
    16.        invoke SendMessage,eax,PBM_SETSTEP,2,0
    17.        dec edi
    18.        jns @b
    19.  
    20.        push rbx;NULL
    21.        push IMAGE_BASE
    22.        push btn1
    23.        push hWnd
    24.        push 30
    25.        push 100
    26.        push 90
    27.        push 10
    28.        mov r8d,offset btn1Txt
    29.        mov edx,offset ctlClsNameBtn
    30.         sub esp,20h
    31.         invoke CreateWindowEx,NULL,,,WS_CHILD + WS_VISIBLE + BS_PUSHBUTTON
    32.        mov btn1H,rax
    33.        invoke CreateStatusWindow,WS_CHILD + WS_VISIBLE,NULL,hWnd,IDC_STATUS
    34.        mov hwndStatus,rax
    35.         invoke CreateSolidBrush,0FF9933h; light blue color
    36.        mov blueBrush,rax
    Здесь мы создаем элементы управления. Заметьте, что вызов CreateWindowEx содержит hWnd в качестве дескриптора родительского окна. Он также задает ID элемента управления, для идентификации последнего. Тем не менее, так как у нас есть дескриптор окна элемента управления, этот ID не используется. Все дочерние окна должны иметь стиль WS_CHILD.
    Код (ASM):
    1.                mov eax,1000
    2.                mov CurrentStep,eax
    3.                shl eax,16
    4.                invoke SendMessage,hwndprogress,pBM_SETRANGE,0,eax
    5.                invoke SendMessage,hwndprogress,pBM_SETSTEp,10,0
    После того, как создан progress bar, мы можем установить его диапазон. Диапазон по умолчанию равен от 0 до 100. Если это вас не устраивает, вы можете указать ваш собственный диапазон с помощью сообщения PBM_SETRANGE. lParam этого сообщения содержит диапазон, максимальное значение в верхнем слове и минимальное в нижнем. Вы также можете указать шаг, используя сообщение PBM_SETSTEP. Этот пример устанавливает его в 10, что означает то, что когда вы посылаете сообщение PBM_STEPIT прогресс бару, индикатор прогресса будет повышаться на 10. Вы также можете установить положение индикатора, послав сообщение PBM_SETPOS. Это сообщение дает вам полный контроль над progress bar'ом.
    Код (ASM):
    1.               invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
    2.                mov hwndStatus,eax
    3.                invoke SetTimer,hWnd,IDC_TIMER,100,NULL        ; create a timer
    4.                mov TimerID,eax
    Затем мы создаем status bar, вызывая CreateStatusWindow. После того, как status window создан, мы создаем таймер. В этом примере мы будем обновлять progress bar каждые 100 ms, поэтому нам нужно создать таймеp.
    Код (C):
    1. UINT_PTR WINAPI SetTimer(
    2.   _In_opt_ HWND  hWnd,//дескриптор родительского окна
    3.   _In_ UINT_PTR  nIDEvent,/* не равный нулю идентификатор таймера. Вы можете
    4.  создать свой собственный идентификатор */
    5.   _In_ UINT  uElapse,/* временной интервал в миллисекундах, который должен пройти,
    6.  прежде чем таймер вызовет процедуру таймер или пошлет сообщение WM_TIMER */
    7.   _In_opt_ TIMERPROC lpTimerFunc /* адрес функции таймера, которая будет
    8. вызываться при истечении временного интервала. Если параметр равен нулю, таймер
    9. вместо этого будет посылать родительскому окну сообщение WM_TIMER */
    10. );
    Если вызов прошел успешно, функция возвратит TimerID. В противном случае, будет возвращен ноль. Вот почему не желательно, чтобы идентификатор таймера был равен нулю.
    Когда истекает указанный временной интервал, таймер посылает сообщение WM_TIMER. Вы можете поместить здесь свой код, который будет выполнен.
    В данном пример, мы обновляем progress bar, а затем проверяем, было ли достигнуто максимальное значение. Если это так, мы убиваем таймеp, после чего устанавливаем текст статус-окна с помощью сообщения SB_SETTEXT. Вызывается MessageBox, и когда пользователь нажмет на OK, мы очищаем текст в status bar'е и progress bar'е.
     
    Последнее редактирование: 7 янв 2017
  18. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Глава вторая. Прототип нашей IDE

    03.gif
    В качестве прототипа IDE Братец Кролик взял пакет MASM32 Стива Хатчессона (BP, , CBuilder, Delphi, Visual Studio организованы примерно так же).
    сам пакет MASM32 распространяется в виде одного исполняемого модуля, в котором в упакованном виде содержатся все его компоненты. При инсталяции выбирается один из локальных дисков, в корне которого создается каталог masm32.
    00.png
    IDE состоит из следующих блоков:
    1. Инструментальный блок (masm32\bin). Здесь располагаются компилятор ml.exe, компоновщик link.exe, компилятор ресурсов rc.exe, редактор для написания ассемблерных программ qeditor.exe и так далее. В общем всё что Вам понадобится для превращения исходных файлов в программу.
    2. Справочный блок (masm32\help). Содержит chm- и hlp-файлы и другую документацию чтобы описать возможности пакета, собственные директивы, макросредства и так далее, и тому подобное...
    3. Блок примеров (masm32\examples, masm32\tutorial и так далее). Огромное количество примеров программирования на диалекте masm под 32-разрядную Windows.
    4. Блок описаний (masm32\include). Cодержит inc-файлы с прототипами WinAPI функций, а также константы и структуры, используемые для написания программ под Windows.
    5. Блок библиотек (masm32\lib). Cодержит lib-файлы, которые являются шлюзами для вызова WinAPI-функций, код которых расположен в системных динамических библиотеках, которые размещены в каталоге Windows\system32
    6. Библиотека макросов (masm32\macros). Содержит всевозможные макроопределения, различные дополнения и заготовки, чтобы облегчить рутинный труд программиста.
    Создание inc- и lib-файлов из системных dll

    Для самой первой программы мы использовали файлы temphls.inc, win64.inc, user32.inc, kernel32.inc, user32.lib, kernel32lib взятые с сайта dsmhelp.narod.ru. Но проект заброшен автором, поэтому inc- и lib-файлы мы будем создавать самостоятельно :)
    Назначение inc и lib файлов

    inc-файлы ― это текстовые файлы содержащие описания структур данных и констант Windows, а также макроопределения.
    inc-файлы формируются программистом по мере расширения используемых им средств операционной системы. Аналогичны заголовочным h/hpp-файлам используемых при программировании на C/C++, иногда можно сформировать inc-файлы из h-файлов используя утилиту h2inc.exe (ее можно найти в старых пакетах MASM).
    Назначение lib-файлов ― предоставление программе link.exe информации о внешних ссылках на WinAPI-функции внутри системных dll-файлов. lib-файл ― это архив, который хранит набор сопоставлений "внешний символ" ― ссылка на объектный (COFF или PE) файл. Этот "символ" на стадии линковки либо добавляется в исполняемый образ (в случае с COFF ― из прекомпилированного объектного файла), либо ― прописывается в таблице импорта (в случае с PE). То есть некоторый объем внешних ссылок транслируется в ваш exe или dll.
    link.exe обрабатывает стандартные библиотеки COFF и библиотеки импорта COFF, которые имеют расширение LIB. Стандартные библиотеки содержат объекты и создаются с помощью утилиты lib.exe. Библиотеки импорта содержат сведения об экспорте в другие программы и создаются либо компилятором link.exe при построении программы, содержащей экспорт, либо утилитой lib.exe.
    Для получения содержимого системного dll-файла использую следующий bat-файл
    Код (DOS):
    1. @echo off
    2. ::стираем с экрана
    3. cls
    4. ::устанавливаем путь к каталогу masm64
    5. set masm64_path=\masm64\
    6. ::имя "препарируемой dll", начнем с user32
    7. set FileName=user32
    8. if exist %FileName%.inc del %FileName%.inc
    9. if exist %FileName%.def del %FileName%.def
    10. ::обрабатываем user32.dll и получаем user32.txt файл
    11. %masm64_path%bin\dumpbin.exe /EXPORTS %windir%\System32\%FileName%.dll /OUT:%FileName%.txt
    12. @echo EXPORTS >> %FileName%.def
    13. for /f "skip=16 tokens=1-4" %%a in (%FileName%.txt) do ( if "%%a"=="Summary" goto :exit
    14. if "%%d"=="" ( @echo extern __imp_%FileName%_ordinal%%a:qword >> %FileName%.inc
    15. @echo %FileName%_ordinal%%a TEXTEQU ^<__imp_%FileName%_ordinal%%a^> >> %FileName%.inc
    16. @echo %FileName%_ordinal%%a=ordinal%%a @%%a NONAME >> %FileName%.def
    17. ) else ( if not "%%d"=="(forwarded" ( @echo extern __imp_%%d:qword >> %FileName%.inc
    18. @echo %%d TEXTEQU ^<__imp_%%d^> >> %FileName%.inc
    19. @echo %%d=__imp_%%d >> %FileName%.def )))
    20. :exit
    21. %masm64_path%bin\link -lib /DEF:%FileName%.def /OUT:%FileName%.lib /MACHINE:X64
    Разбор bat-файла

    • предварительная настройка bat-файла
      Код (DOS):
      1. ::стираем с экрана
      2. cls
      3. ::устанавливаем путь к каталогу masm64
      4. set masm64_path=\masm64\
      5. ::имя "препарируемой dll", начнем с user32
      6. set FileName=user32
      7. ::обрабатываем user32.dll и получаем user32.txt файл
      8. %masm64_path%bin\dumpbin.exe /EXPORTS %windir%\System32\%FileName%.dll /OUT:%FileName%.txt
    • содержимое user32.txt
      Код (DOS):
      1. Dump of file C:\Windows\System32\user32.dll
      2. File Type: DLL
      3.   Section contains the following exports for USER32.dll
      4.     00000000 characteristics
      5.     4CE799CD time date stamp Sat Nov 20 17:50:05 2010
      6.         0.00 version
      7.         1500 ordinal base
      8.         1003 number of functions
      9.          830 number of names
      10.     ordinal hint RVA      name
      11.        1502    0 000083C0 ActivateKeyboardLayout
      12.        1503    1 0002AD40 AddClipboardFormatListener
      13.        1504    2 000235B8 AdjustWindowRect
      14.        1505    3 00017CE4 AdjustWindowRectEx
      15.  ....
      16.        2341  33C 0007B430 wvsprintfA
      17.        2342  33D 00020BFC wvsprintfW
      18.        1500      0002B260 [NONAME]
      19.        1501      0002AE80 [NONAME]
      20. ....
      21.   Summary
      22.         2000 .data
      23.         A000 .pdata
      24.        10000 .rdata
      25.         1000 .reloc
      26.        5B000 .rsrc
      27.        81000 .text
    • после просмотра user32.txt видно, что из user32.dll импортируется 846 функции, из них 826 функций импортируются по именам, 16 ― по ординалам, а функции DefDlgProcA, DefDlgProcW, DefWindowProcA, DefWindowProcW портируются в user32.dll из системной библиотеки NTDLL.dll
      Код (DOS):
      1. Dump of file C:\Windows\System32\user32.dll
      2. File Type: DLL
      3.   Section contains the following exports for USER32.dll
      4.     00000000 characteristics
      5.     4CE799CD time date stamp Sat Nov 20 17:50:05 2010
      6.         0.00 version
      7.         1500 ordinal base
      8.         1003 number of functions
      9.          830 number of names
      10.     ordinal hint RVA      name
      11.        1502    0 000083C0 ActivateKeyboardLayout <-- полезная информация начинается здесь
    • если перед началом обработки в каталоге уже существуют файлы user32.inc, user32.def, user32.lib оставшиеся от предыдущей обработки dll-файлов ― удаляем их.
      Код (DOS):
      1. if exist %FileName%.inc del %FileName%.inc
      2. if exist %FileName%.def del %FileName%.def
      создаем файл user32.def, который должен начинаться со строки "EXPORTS"
      Код (DOS):
      1. @echo EXPORTS >> %FileName%.def
    • полезная информация начинается в user32.txt с 16 строки, поэтому skip=16 означает ― пропускаем первые 16 строк в user32.txt
    • при построчном разборе файла user32.txt используем четыре первых слова в строке, которым присвоим имена %%a, %%b, %%c, %%d
      Код (DOS):
      1. for /f "skip=16 tokens=1-4" %%a in (%FileName%.txt) do
      если первый параметр равен "Summary" ― значит обработаны все функции, входящие в dll, мы прекращаем обработку, выходим из файла user32.txt и переходим на метку :exit
      Код (DOS):
      1. if "%%a"=="Summary" goto :exit
    • если четвертый параметр в файле user32.txt пустой ― перед нами импорт по ординалам
      Код (DOS):
      1.        %%a         %%b      %%c          %%d
      2.        1500      0002B260 [NONAME]    
      сохраняем первое слово (ординал WinAPI-функции) в строке user32.txt в переменной %%a, обрамляем ее и помещаем в две новых строки в файл user32.inc
      Код (DOS):
      1. extern __imp_user32_ordinal1500:qword
      2. user32_ordinal1500 TEXTEQU <__imp_user32_ordinal1500>
      и user32.def
      Код (DOS):
      1. user32_ordinal1500=ordinal1500 @1500 NONAME
    • если четвертый параметр непустой ― перед нами импорт по именам функций
    • в очередной строке файла user32.txt
      Код (DOS):
      1. %%a   %%b  %%c      %%d
      2. 1502    0 000083C0 ActivateKeyboardLayout
      четвертое слово в строке (имя WinAPI-функции), сохраняем в переменной %%d, создаем две новых строки в файле user32.inc, предваряем %%d "extern __imp_" строку завершаем ":qword", добавляем "TEXTEQU", "__imp_", экранируем управляющие символы "<" и ">" (^<__imp_%%d^>) чтобы bat-файл воспринимал их, как обычные символы.
      Код (DOS):
      1. extern __imp_ActivateKeyboardLayout:qword
      2. ActivateKeyboardLayout TEXTEQU <__imp_ActivateKeyboardLayout>
      и user32.def
      Код (DOS):
      1. ActivateKeyboardLayout=__imp_ActivateKeyboardLayout
    • если четвертый параметр равен "(forwarded", значит WinAPI-функция берется из другой dll и мы пропускаем такую строку.
      Код (DOS):
      1. %%a    %%b         %%c           %%d
      2. 1657   94          DefDlgProcA (forwarded to NTDLL.NtdllDialogWndProc_A)
    • из содержимого файлов user32.def и user32.inc создаем файл user32.lib
      Код (DOS):
      1. :exit
      2. %masm64_path%bin\link -lib /DEF:%FileName%.def /OUT:%FileName%.lib /MACHINE:X64
      того же результата можно добиться строкой
      Код (DOS):
      1. %masm64_path%bin\lib /DEF:%FileName%.def /OUT:%FileName%.lib /MACHINE:X64
    • переносим файл user32.inc в каталог masm64\include, а файл user32.lib в каталог masm64\lib
    • удаляем программный мусор
    Код (DOS):
    1. if exist %FileName%.def del %FileName%.def
    2. if exist %FileName%.exp del %FileName%.exp
    3. if exist %FileName%.txt del %FileName%.txt
    С удивлением Братец Кролик обнаружил, что в kernel32.dll нет ExitProcess, а в user32.dll нет DefWindowProcA, обе функции портируются из ntdll.dll (RtlExitUserProcess и NtdllDefWindowProc_A соответственно) :)
    Аналогично user32.dll препарируем kernel32.dll, ntdll.dll, gdi32.dll, comctrl32.dll и далее, по мере необходимости :)


    © Mikl___ 2019
     

    Вложения:

    • 07.png
      07.png
      Размер файла:
      440,6 КБ
      Просмотров:
      2.276
    • 06.png
      06.png
      Размер файла:
      460 КБ
      Просмотров:
      2.406
    • MB_00.png
      MB_00.png
      Размер файла:
      6,4 КБ
      Просмотров:
      2.205
    • MB_01.png
      MB_01.png
      Размер файла:
      8,2 КБ
      Просмотров:
      2.207
    • MB_03.png
      MB_03.png
      Размер файла:
      8,7 КБ
      Просмотров:
      2.234
    • MB_02.png
      MB_02.png
      Размер файла:
      8,7 КБ
      Просмотров:
      2.172
    • MB_04.png
      MB_04.png
      Размер файла:
      7,7 КБ
      Просмотров:
      2.176
    • MB_05.png
      MB_05.png
      Размер файла:
      8,3 КБ
      Просмотров:
      2.199
    • MB_06.png
      MB_06.png
      Размер файла:
      9,1 КБ
      Просмотров:
      2.161
    • MB_07.png
      MB_07.png
      Размер файла:
      9,8 КБ
      Просмотров:
      2.102
    • MB_08.png
      MB_08.png
      Размер файла:
      10,3 КБ
      Просмотров:
      844
    Последнее редактирование: 12 май 2019
    kero, Коцит, TermoSINteZ и ещё 1-му нравится это.
  19. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Глава четвертая. Как Братец Кролик создал универсальный bat-файл
    Превращение текстового asm-файла в двоичный com/exe/sys/dll-файл происходит под управлением с командной строки
    • либо (если asm-файл единственный) в один этап (компилятору передается ключ /link)
    • либо (если asm-файлов несколько) в два этапа с раздельной компиляцией каждого asm-файла (компилятору передается ключ /c) и (если компиляция проходит без ошибок) запускается линковка всех obj-файлов в один файл
    Как это сделано в FASM
    С целью управления форматом сгенерированного кода FASM использует директиву "format". Директива "format", за которой следует идентификатор, позволяет выбрать формат выходного файла. Директива помещается в начало asm-файла. Формат выходного файла "по-умолчанию" ― простой двоичный файл (flat binary file), его также можно выбрать с помощью директивы "format binary". За этой директивой может следовать ключевое слово "as" и строка в кавычках, указывающая расширение для выходного файла. Если имя выходного файла не было указано в командной строке, ассемблер будет использовать это расширение при создании выходного файла с именем asm-файла.
    Директивы "use16" и "use32" заставляют ассемблер генерировать 16-разрядный или 32-разрядный код, опуская настройку по умолчанию для выбранного выходного формата. "use64" позволяет генерировать код для long mode режима x64.
    Ниже описаны различные выходные форматы с указаниями, специфичными для этих форматов:
    • Для выбора формата MZ-COM для выходного файла используйте
      Код (ASM):
      1. use16
      2. org 100h
    • Для выбора формата MZ-EXE для выходного файла используйте диррективу "format MZ". По умолчанию код собирается в 16-разрядном формате.
    • Для выбора формата Portable Executable для выходного файла используйте диррективу "format PE", за ней могут следовать дополнительные настройки формата:
      Код (ASM):
      1.     format PE GUI 4.0 DLL at 7000000h on 'stub.exe'
      • сперва ключевое слово для настройки целевой подсистемы, ими могут быть:
        • для Windows приложений:
          • console
          • GUI
        • для Windows kernel mode драйверов:
          • "native"
        • для UEFI:
          • "EFI"
          • "EFIboot"
          • "EFIruntime"
    Windows SubsystemValue
    (hex)
    DescriptionПример
    IMAGE_SUBSYSTEM_NATIVE
    1​
    Device drivers and native Windows processesformat PE native
    IMAGE_SUBSYSTEM_WINDOWS_GUI
    2​
    The Windows graphical user interface subsystemformat PE GUI
    IMAGE_SUBSYSTEM_WINDOWS_CUI
    3​
    The Windows character subsystemformat PE console
    IMAGE_SUBSYSTEM_EFI_APPLICATION
    0xA​
    An Extensible Firmware Interface applicationformat EFI
    IMAGE_SUBSYSTEM_EFI_BOOT_ SERVICE_DRIVER
    0xB​
    An EFI driver with boot servicesformat EFIboot
    IMAGE_SUBSYSTEM_EFI_RUNTIME_ DRIVER
    0xC​
    An EFI driver with run-time servicesformat EFIruntime
      • далее могут следовать минимальная версия системы, под которую написано приложение и ее подверсия (версия и подверсией будут разделены точкой)
      • ключевое слово "DLL" создает файл вывода как динамически подключаемую библиотеку (Dinamic Link Library)
      • ключевое слово "WDM" создает файл вывода как Windows Driver Model (WDM-драйвер)
      • ключевое слово "large" отмечает, что приложение может работать с адресами, размер которых свыше 2 гигабайт
      • ключевое слово "NX" сигнализирует, что у исполняемого файла есть ограничению на исполнение кода, находящегося в неисполняемых разделах (сегменты данных, стека, ресурсах и т.п.), запрет исполнения, добавленный в таблицы страниц для предотвращения выполнения данных как кода. Используется для предотвращения уязвимости типа «переполнение буфера», позволяющей выполнять вредоносный код.
      • Далее может следовать оператор "at" и числовое выражение, указывающее адрес базового образа PE файла
      • Далее опционально может следовать оператор "on" со следующей за ним строкой в кавычках, содержащей имя файла, являющимся MZ-stub для PE программы (если указанный файл не в формате MZ, то он трактуется как простой двоичный исполняемый файл и конвертируется в формат MZ). По умолчанию код для этого формата 32-разрядный
    • Для создания PE-файла для архитектуры x64 используйте ключевое слово "PE64" вместо директивы PE в декларации формата, в таком случае, по-умолчанию будет сгенерирован код для long mode.
    • Для выбора формата Common Object File Format используйте:
      • директиву "format COFF или "format MS COFF", зависит от того, хотите ли вы создать классический (DJGPP) или Microsoft-ский вариант файла COFF. Настройка кода по умолчанию для этого формата — 32-разрядная.
      • Для создания файла в формате Microsoft COFF для архитектуры x64 используйте "format MS64 COFF" в таком случае по умолчанию будет сгенерирован код для long mode.
    • Для выбора формата ELF используйте директиву
      • "format ELF". По умолчанию в этом формате будет сгенерирован 32-разрядный код.
      • Для создания ELF файла для архитектуры x64 используйте директиву "format ELF64", в таком случае по умолчанию будет сгенерирован код для long mode.
    Код (DOS):
    1. :: стираем с экрана
    2. cls
    3. :: запоминаем путь к нашей IDE
    4. set masm64_path=\masm64\
    5. :: получаем имя обрабатываемого asm-файла
    6. set filename=%~n1
    7. :: вызываем процедуру, которая по первому слову, составит метку,
    8. на которую передаст управление внутри bat-файла
    9. call :read_settings %filename%
    10. @echo %kind_of_file%
    11. goto %kind_of_file%
    12. :: При сборке приложения используется ключ /SUBSYSTEM:WINDOWS
    13. Создается приложение основанное на графическом интерфейсе (Grafical User Interface)
    14. Приложение не требует консоли, создает собственное окно, имеют меню, взаимодействуют
    15. с пользователем через диалоговые окна.
    16. :GUI
    17. if exist %filename%.exe del %filename%.exe
    18. if exist %filename%.obj del %filename%.obj
    19. if exist errors.txt del errors.txt
    20. %masm64_path%bin\ml64 /Cp /c /I"%masm64_path%Include" %filename%.asm >> errors.txt
    21. if errorlevel 1 exit
    22. if exist %1.rc (
    23. %masm64_path%bin\RC /r /i"%masm64_path%\Include" %filename%.rc >> errors.txt
    24. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:"%masm64_path%Lib" ^
    25. /LARGEADDRESSAWARE:NO /BASE:0x400000 /STUB:%masm64_path%bin\stubby.exe ^
    26. /SECTION:.text,W /ALIGN:16 /entry:WinMain /MERGE:.rdata=.text ^
    27. /fixed /nocoffgrpinfo %filename%.obj %filename%.res >> errors.txt
    28. if exist %1.res del %1.res
    29. ) else (
    30. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:"%masm64_path%Lib" ^
    31. /LARGEADDRESSAWARE:NO /BASE:0x400000 /STUB:%masm64_path%bin\stubby.exe ^
    32. /SECTION:.text,W /ALIGN:16 /entry:WinMain /MERGE:.rdata=.text ^
    33. /fixed /nocoffgrpinfo %filename%.obj >> errors.txt
    34. )
    35. if errorlevel 1 exit
    36. goto exit1
    37. :: При сборке приложения используется ключ /SUBSYSTEM:CONSOLE
    38. Приложения работают с символьным экраном, не формируют окна, не обрабатывают сообщения,
    39. не являются Windows-программами в обычном смысле. Операционная система предоставляет консоль
    40. :CONSOLE
    41. :: .... Какие-то команды для создания консольного приложения
    42. goto exit1
    43. :: При сборке приложения используется ключ /DRIVER Создается драйвер режима ядра
    44. :DRIVER
    45. :: .... Какие-то команды для создания драйвера режима ядра
    46. goto exit1
    47. :: При сборке приложения используется ключ /DLL Выполняет сборка DLL-библиотеки
    48. :DLL
    49. if exist %filename%.dll del %filename%.dll
    50. %masm64_path%bin\ml64 /c /Cp /I %masm64_path%include %filename%.asm >> errors.txt
    51. if errorlevel 1 exit
    52. if exist %1.rc (
    53. %masm64_path%bin\RC /r  %filename%.rc >> errors.txt
    54. if errorlevel 1 exit
    55. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:%masm64_path%lib ^
    56. /ENTRY:DllMain /DLL /DLL /section:.bss,S /stub:%masm64_path%bin\stubby.exe  ^
    57. %filename%.obj %filename%.res /DEF:%filename%.def >> errors.txt
    58. if exist %1.res del %1.res
    59. ) else (
    60. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:%masm64_path%lib ^
    61. /ENTRY:DllMain /DLL /DLL /section:.bss,S /stub:%masm64_path%bin\stubby.exe  ^
    62. %filename%.obj /DEF:%filename%.def >> errors.txt
    63. )
    64. if errorlevel 1 exit
    65. del %filename%.exp
    66. :: удаление «программного мусора»
    67. :exit1
    68. del %filename%.obj
    69. del errors.txt
    70. :: выход из bat-файла
    71. exit
    72. :: процедура, которая разбирает первую строку asm-файла
    73. :read_settings
    74. for /f "eol=# tokens=2-3" %%A in (%filename%.asm) do (
    75. set kind_of_file=%%A
    76. if %%B == # exit /b )
    77. :: выход из процедуры
    78. exit /b
    • В первой строке cls стираем с экрана
    • в переменной masm64_path запоминаем путь к нашей IDE
    • «set filename=» bat-файл через командную строку получает имя нашего asm-файла
    • читаем первую строку выбранного asm-файла, которая начинается со знака «точка с запятой», которую компилятор посчитает «комментарием» и проигнорирует, но эта строка будет восприниматься командным процессором, который, в свою очередь, игнорирует символ «точка с запятой».
    • Внутри нашего asm.bat вставим процедуру, которая по первому слову, составит метку, на которую передаст управление внутри asm.bat и там уже из нашего asm-файла сделают необходимый нам файл-результат (exe-gui, exe-consoe, dll, sys)
    • Разделим наш asm-bat на несколько частей, допустим, в первой части asm.bat строки, которые собирают из asm-файла EXE-файлы для Windows (причем, когда нам нужен GUI ― подставляется параметр /SUBSYSTEM:WINDOWS, когда нужна консоль ― переходим во вторую часть и подставляем параметр /SUBSYSTEM:CONSOLE), когда требуется DLL-файлы ― переходим в третью часть, в четвертой части ― создаем SYS-файлы и так далее, часть строк asm-bat будет общая и служит для удаления «программного мусора» в виде obj-файлов и тому подобное.
    Братец Кролик нашел в книге Джеффри Рихтера «Windows. Создание эффективных Win32-приложений с учетом специфики 64-разрядных версий Windows» следующие строки
    Тип приложенияВходная функцияключ
    компоновщика​
    GUI-
    приложение​
    работает с ANSI-символами и строками
    WinMain
    /SUBSYSTEM:
    WINDOWS​
    работает с Unicode-символами и строками
    wWinMain
    консольное
    приложение​
    работает с ANSI-символами и строками
    main
    /SUBSYSTEM:
    CONSOLE​
    работает с Unicode-символами и строками
    wmain


    © Mikl___ 2019
     

    Вложения:

    • MB_21.png
      MB_21.png
      Размер файла:
      932 байт
      Просмотров:
      7.535
    • MB_22.png
      MB_22.png
      Размер файла:
      590 байт
      Просмотров:
      2.390
    • MB_23.png
      MB_23.png
      Размер файла:
      731 байт
      Просмотров:
      3.190
    • MB_31.png
      MB_31.png
      Размер файла:
      724 байт
      Просмотров:
      2.381
    • MB_30.png
      MB_30.png
      Размер файла:
      560 байт
      Просмотров:
      2.373
    • MB_25.png
      MB_25.png
      Размер файла:
      638 байт
      Просмотров:
      2.384
    • MB_26.png
      MB_26.png
      Размер файла:
      662 байт
      Просмотров:
      3.707
    • MB_27.png
      MB_27.png
      Размер файла:
      564 байт
      Просмотров:
      2.331
    • MB_12.png
      MB_12.png
      Размер файла:
      8 КБ
      Просмотров:
      2.109
    • MB_20.png
      MB_20.png
      Размер файла:
      8,3 КБ
      Просмотров:
      851
    • MB_13.png
      MB_13.png
      Размер файла:
      8,6 КБ
      Просмотров:
      2.243
    • MB_14.png
      MB_14.png
      Размер файла:
      8,7 КБ
      Просмотров:
      2.218
    • MB_15.png
      MB_15.png
      Размер файла:
      7,9 КБ
      Просмотров:
      2.099
    • MB_16.png
      MB_16.png
      Размер файла:
      8,6 КБ
      Просмотров:
      2.145
    • MB_17.png
      MB_17.png
      Размер файла:
      6 КБ
      Просмотров:
      2.256
    • MB_18.png
      MB_18.png
      Размер файла:
      9 КБ
      Просмотров:
      790
    • MB_19.png
      MB_19.png
      Размер файла:
      9,4 КБ
      Просмотров:
      806
    • MB_24.png
      MB_24.png
      Размер файла:
      759 байт
      Просмотров:
      2.334
    • MB_09.png
      MB_09.png
      Размер файла:
      10,3 КБ
      Просмотров:
      812
    • MB_10.png
      MB_10.png
      Размер файла:
      10,3 КБ
      Просмотров:
      822
    • MB_11.png
      MB_11.png
      Размер файла:
      10,3 КБ
      Просмотров:
      834
    mantissa, kero и Aiks нравится это.
  20. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Глава пятая. Братец Кролик разбирается с потрохами MessageBox

    MessageBox относится к диалоговому окну, специальному виду окон, позволяющих вести диалог с программой. Обмен информацией с программой и задание режимов ее работы осуществляется с помощью кнопок разного вида и с разными свойствами. Все кнопки, так же как сама диалоговая панель, являются окнами и их можно создавать в программе при помощи соответствующих функций Windows. В отличии от оконных приложений, при вызове диалогового окна значительную часть работы по созданию и поддержке таких окон берет на себя операционная система, что упрощает логику программы.
    Диалог, представляет собой окно со специальными свойствами, может существовать и без главного окна, образуя специфический тип "безоконного" приложения. Такие приложения, состоящие из одного диалогового окна, оказываются значительно проще программ с "главным окном".
    Функция MessageBox создает на экране диалоговую панель с текстом, заданным параметром lpszText и заголовком, заданным параметром lpszTitle.
    Код (C):
    1. int WINAPI MessageBox(HWND hwndParent, LPCSTR lpszText, LPCSTR lpszTitle, UINT fuStyle);
    Параметры
    fuStyleHexBin
    1MB_OK
    0​
    0000.0000.0000.0000.0000.0000
    2MB_OKCANCEL
    1​
    0000.0000.0000.0000.0000.0001
    3MB_ABORTRETRYIGNORE
    2​
    0000.0000.0000.0000.0000.0010
    4MB_YESNOCANCEL
    3​
    0000.0000.0000.0000.0000.0011
    5MB_YESNO
    4​
    0000.0000.0000.0000.0000.0100
    6MB_RETRYCANCEL
    5​
    0000.0000.0000.0000.0000.0101
    7MB_CANCELTRYCONTINUE
    6​
    0000.0000.0000.0000.0000.0110
    8MB_TYPEMASK
    F​
    0000.0000.0000.0000.0000.1111
    9MB_NOICON
    00​
    0000.0000.0000.0000.0000.0000
    10MB_ICONHAND,
    MB_ICONERROR,
    MB_ICONSTOP
    10​
    0000.0000.0000.0000.0001.0000
    11MB_ICONQUESTION
    20​
    0000.0000.0000.0000.0010.0000
    12MB_ICONEXCLAMATION,
    MB_ICONWARNING
    30​
    0000.0000.0000.0000.0011.0000
    13MB_ICONASTERISK,
    MB_ICONINFORMATION
    40​
    0000.0000.0000.0000.0100.0000
    14MB_USERICON
    80​
    0000.0000.0000.0000.1000.0000
    15MB_ICONMASK
    F0​
    0000.0000.0000.0000.1111.0000
    16MB_DEFBUTTON1
    000​
    0000.0000.0000.0000.0000.0000
    17MB_DEFBUTTON2
    100​
    0000.0000.0000.0001.0000.0000
    18MB_DEFBUTTON3
    200​
    0000.0000.0000.0010.0000.0000
    19MB_DEFBUTTON4
    300​
    0000.0000.0000.0011.0000.0000
    20MB_DEFMASK
    F00​
    0000.0000.0000.1111.0000.0000
    21MB_APPLMODAL
    0000​
    0000.0000.0000.0000.0000.0000
    22MB_SYSTEMMODAL
    1000​
    0000.0000.0001.0000.0000.0000
    23MB_TASKMODAL
    2000​
    0000.0000.0010.0000.0000.0000
    24MB_MODEMASK
    3000​
    0000.0000.0011.0000.0000.0000
    26MB_HELP
    4000​
    0000.0000.0100.0000.0000.0000
    27MB_NOFOCUS
    8000​
    0000.0000.1000.0000.0000.0000
    28MB_MISCMASK
    C000​
    0000.0000.1100.0000.0000.0000
    29MB_SETFOREGROUND
    10000​
    0000.0001.0000.0000.0000.0000
    30MB_DEFAULT_DESKTOP_ONLY
    20000​
    0000.0010.0000.0000.0000.0000
    31MB_TOPMOST
    40000​
    0000.0100.0000.0000.0000.0000
    31MB_NOTIFICATION_NT3X
    40000​
    0000.0100.0000.0000.0000.0000
    32MB_RIGHT
    80000​
    0000.1000.0000.0000.0000.0000
    33MB_RTLREADING1000000001.0000.0000.0000.0000.0000
    34MB_SERVICE_NOTIFICATION2000000010.0000.0000.0000.0000.0000
    88.png
    Внимательно посмотрев на диапазон значений (от 0 до 200000h), мы увидим, что используется только 19 флагов (битов) из 32 возможных ― что оставляет 13 флагов, которые ещё не определены.
    А теперь более подробно.
    MessageBox не самостоятельная функция, а может быть вызвана только, чтобы передать сообщение от программы-владельца. Владелец MessageBox'а у нас идентифицируется по параметру hwndParent. Если, как в данном примере, "программы-владельца" нет, тогда hwndParent = 0, что означает "программой-владельцем" данного MessageBox является "заданный по умолчанию рабочий стол" ― первая запущенная прикладная программа, после того, как пользователь вошел в систему.
    Сообщение через MessageBox пользователю, который еще не ввел логин/пароль может послать служба операционной системы (например, CSRSS.EXE), но как быть, ведь пользователь еще не вошел в систему и у него нет активного рабочего стола? Для этого нужно в функцию MessageBox помимо прочих флагов передать MB_SERVICE_NOTIFICATION Во время запуска, используйте функцию GetVersionEx, чтобы проверить системную версию. Тогда при продолжении запуска Windows NT 3.x, используйте MB_SERVICE_NOTIFICATION_NT3X = 40000h, а для Windows NT 4.0, используйте MB_SERVICE_NOTIFICATION = 200000h
    Код (C):
    1. #ifdef _WIN32_WINNT
    2. #if (_WIN32_WINNT >= 0x0400)
    3. #define MB_SERVICE_NOTIFICATION 0x00200000L
    4. #else
    5. #define MB_SERVICE_NOTIFICATION 0x00040000L
    6. #endif
    7. #define MB_SERVICE_NOTIFICATION_NT3X 0x00040000L
    8. #endif
    Код (ASM):
    1. OSVERSIONINFO struct
    2.  dwOSVersionInfoSize DWORD ?
    3.  dwMajorVersion DWORD ?
    4.  dwMinorVersion DWORD ?
    5.  dwBuildNumber DWORD ?
    6.  dwPlatformId DWORD ?
    7.  szCSDVersion BYTE 128 dup(?)
    8. OSVERSIONINFO ends
    9.  
    10. local VersionInfo:OSVERSIONINFO
    11.  ....
    12.  lea ecx,VersionInfo
    13.  mov [rcx].OSVERSIONINFO.dwOSVersionInfoSize,sizeof OSVERSIONINFO
    14.  invoke GetVersionEx
    15.  mov eax,VersionInfo.dwMajorVersion;6
    16.  mov eax,VersionInfo.dwMinorVersion;1
    Количество и тип кнопок

    Первые два бита (2-0) и 14-ый бит определяют количество кнопок и надписи на них
    fuStyle
    hexbin
    MB_OK0000000000000000000000000[​IMG]
    MB_OKCANCEL1000000000000000000000001[​IMG]
    MB_ABORTRETRYIGNORE2000000000000000000000010[​IMG]
    MB_YESNOCANCEL3000000000000000000000011[​IMG]
    MB_YESNO4000000000000000000000100[​IMG]
    MB_RETRYCANCEL5000000000000000000000101[​IMG]
    MB_CANCELTRYCONTINUE6000000000000000000000110[​IMG]
    MB_TYPEMASK0Fh000000000000000000001111используется для программного определения количества кнопок и надписей на них. По количеству битов можно предположить, что сочетаний кнопок может быть и больше (от 0 до 0Eh)
    Код (C):
    1. int _CustomMessageBoxShowButtons ( HWND hwndDlg, UINT uType)
    2.  { int nVisibleButtons = 0;
    3.  switch(MB_TYPEMASK & uType)
    4.  {case MB_OKCANCEL:
    5.  ::ShowWindow(::GetDlgItem(hwndDlg, IDCANCEL), SW_SHOW);
    6.  ++nVisibleButtons; // Fallsthrough
    7.  case MB_OK:
    8.  ::ShowWindow(::GetDlgItem(hwndDlg, IDOK), SW_SHOW);
    9.  ++nVisibleButtons;
    10.  break;
    11.  case MB_YESNOCANCEL:
    12.  ::ShowWindow(::GetDlgItem(hwndDlg, IDCANCEL), SW_SHOW);
    13.  ++nVisibleButtons; // Fallsthrough
    14.  case MB_YESNO:
    15.  ::ShowWindow(::GetDlgItem(hwndDlg, IDYES), SW_SHOW);
    16.  ::ShowWindow(::GetDlgItem(hwndDlg, IDNO), SW_SHOW);
    17.  nVisibleButtons += 2;
    18.  break;
    19.  case MB_ABORTRETRYIGNORE:
    20.  ::ShowWindow(::GetDlgItem(hwndDlg, IDABORT), SW_SHOW);
    21.  ::ShowWindow(::GetDlgItem(hwndDlg, IDRETRY), SW_SHOW);
    22.  ::ShowWindow(::GetDlgItem(hwndDlg, IDIGNORE), SW_SHOW);
    23.  nVisibleButtons = 3;
    24.  break;
    25.  case MB_RETRYCANCEL:
    26.  ::ShowWindow(::GetDlgItem(hwndDlg, IDRETRY), SW_SHOW);
    27.  ::ShowWindow(::GetDlgItem(hwndDlg, IDCANCEL), SW_SHOW);
    28.  nVisibleButtons = 2;
    29.  break; #if(WINVER >= 0x0500)
    30.  case MB_CANCELTRYCONTINUE:
    31.  ::ShowWindow(::GetDlgItem(hwndDlg, IDCANCEL), SW_SHOW);
    32.  ::ShowWindow(::GetDlgItem(hwndDlg, IDTRYAGAIN), SW_SHOW);
    33.  ::ShowWindow(::GetDlgItem(hwndDlg, IDCONTINUE), SW_SHOW);
    34.  nVisibleButtons = 3;
    35.  break;
    36.  #endif
    37.  default: // Not implemented yet
    38.  _ASSERTE(!"Not implemented");
    39.  break;
    40.  }
    41.  if (MB_HELP & uType)
    42.  {
    43.  ::ShowWindow(::GetDlgItem(hwndDlg, IDHELP), SW_SHOW);
    44.  ++nVisibleButtons; }
    45.  return nVisibleButtons; }
    MB_HELP4000h00000000010000000000XXXдополнительную кнопку "Справка" можно добавить к любому сочетанию клавиш
    [​IMG]
    • Нажатие на кнопку [​IMG], или нажатие клавиши ESC, или выбор кнопки [​IMG] возвращает значение IDCANCEL=2.
    • Если у MessageBox стиль MB_OK, то нажатие клавиши ESC, или выбор кнопки [​IMG]возвращает значение IDOK=1.
    • Если у окна сообщений нет кнопки [​IMG], (стили MB_YESNO, MB_ABORTRETRYIGNORE), тогда кнопка [​IMG] не активизирована и нажатие на ESC не имеет никакого эффекта.
    • Выбор кнопки [​IMG] или нажатие F1 генерирует событие "появления Справки".
    • Остальные кнопки реагируют на mouse-click или на нажатие Enter если кнопка имеет фокус.
    Иконки
    4 следующих бита (8-4) определяют иконку
    fuStylehexbin
    MB_NOICON00h000000000000000000000000[​IMG]
    MB_ICONHAND,
    MB_ICONERROR,
    MB_ICONSTOP
    10h000000000000000000010000[​IMG]
    MB_ICONQUESTION20h000000000000000000100000[​IMG]
    MB_ICONEXCLAMATION,
    MB_ICONWARNING
    30h000000000000000000110000[​IMG]
    MB_ICONASTERISK,
    MB_ICONINFORMATION
    40h000000000000000001000000[​IMG]
    По количеству битов можно предположить, что иконок может быть и больше (от 0 до 7), но реально значениям с 50h по 70h не соответствуют никакие иконки.
    MB_USERICON80h000000000000000010000000иконка, выбранная пользователем
    [​IMG]
    MB_ICONMASKF0h000000000000000011110000для программного определения иконки
    Пример программного определения иконки
    Код (C):
    1. void _CustomMessageBoxSetIcon( HWND hwndDlg, UINT uType)
    2. {       switch(MB_ICONMASK & uType)
    3.        {case MB_ICONEXCLAMATION:
    4.         ::SendDlgItemMessage(hwndDlg, ID_MSGBOXICON,
    5. STM_SETICON, (WPARAM)::LoadIcon(NULL, IDI_EXCLAMATION), 0);
    6.         break;
    7.     case MB_ICONQUESTION:
    8.         ::SendDlgItemMessage(hwndDlg, ID_MSGBOXICON,
    9. STM_SETICON, (WPARAM)::LoadIcon(NULL, IDI_QUESTION), 0);
    10.         break;
    11.     case MB_ICONASTERISK:
    12.         ::SendDlgItemMessage(hwndDlg, ID_MSGBOXICON,
    13. STM_SETICON, (WPARAM)::LoadIcon(NULL, IDI_ASTERISK), 0);
    14.         break;
    15.     case MB_ICONHAND:
    16.         ::SendDlgItemMessage(hwndDlg, ID_MSGBOXICON,
    17. STM_SETICON, (WPARAM)::LoadIcon(NULL, IDI_HAND), 0);
    18.         break;
    19.     default: // Turn off the icon
    20.         ::ShowWindow(::GetDlgItem(hwndDlg, ID_MSGBOXICON), SW_HIDE);
    21.         break;    }
    22. }
     
    mantissa и rasstriga нравится это.