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

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

Метки:
  1. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709
    • hItem ― это дескриптор окна просмотра деревьев. Каждый элемент имеет дескриптор, как и в случае с окнами. Если вы хотите сделать что-нибудь с элементом, вы должны выбрать его с помощью его дескриптора.
    • pszText ― это указатель на строку, оканчивающуюся NULL, которая является названием окна просмотра деревьев.
    • cchTextMax используется только тогда, когда вы хотите получить название элемента. Windows надо будет знать размер предоставленного вами буфера (pszText), поэтому этот элемент используется именно для этого.
    • iImage и iSelectedImage содержат индекс из списка изображений, который содержит изображения, показывающиеся когда элемент выбран и не выбран. Если вспомните левую панель Windows Explorer'а, то изображения директорий задаются именно этими двумя параметрами.
    • Чтобы вставить элемент в окно просмотра деревьев, вы должны заполнить, по крайней мере, hParent, hInsertAfter, а также вам следует заполнить imask и pszText.
    • поле stateMask определяет, какое состояние элемента должно быть установлено или получено. Оно может принимать одно из значений или комбинацию значений из таблицы описанной в поле state
    • поле state содержит информацию о состоянии элемента окна просмотра деревьев. Некоторые наиболее часто используемые значения
      Содержание поляназначение
      TVIS_DISABLEDэлемент запрещен
      TVIS_DROPHILITEDэлемент выбран как объект операции перетаскивания (drag-and-drop)
      TVIS_EXPANDEDветвь, порожденная элементом, полностью развернута (применимо только к порождающим узлам)
      TVIS_EXPANDEDDONCEветвь, порожденная элементом, развернута на один уровень или более (применимо только к порождающим узлам)
      TVIS_FOCUSEDэлемент имеет фокус ввода
      TVIS_SELECTEDэлемент выбран
    Когда элемент вставляется в древовидную структуру, поле pszText должно содержать указатель на строку, которая будет отображаться рядом с этим элементом. Если информация об элементе записывается в структуру из окна просмотра деревьев, поле pszText должно содержать указатель на символьный массив, имеющий достаточно большой размер для того, чтобы записать в него текст. Поле cchTextMax будет содержать количество символов, записываемых в буфер pszText.

    Списки изображений

    Каждая запись в окно просмотра деревьев может иметь две ассоциированных с ней пиктограммы. Первая пиктограмма выводится на экран, когда запись развернута, вторая ― когда запись свернута (например, пиктограммы, изображающие открытый и закрытый каталог).
    Кроме того, приложение может задать список изображений для обозначения состояний записи. В этом случае окно просмотра деревьев резервирует место слева от записи для размещения данного изображения.
    Для работы с изображением предназначены функции GetImageList и SetImageList
    Код (C):
    1. GetImageList ( UINT nImage   );
    Функция GetImageList возвращает указатель на список изображений, связанных с окно просмотра деревьев или 0 ― в случае неудачи. Параметр nImage и может принимать одно из следующих значений:
    • TVSIL_NORMAL ― предписывает возвращать список обычных изображений, который содержит изображение для выбранных и невыбранных записей;
    • TVSIL_STATE ― предписывает возвращать список изображений, который содержит изображение для определяемых пользователем состояний.
    Код (C):
    1. SetImageList ( CImageList* pImageList,
    2.        int nImageListType    );
    Функция SetImageList устанавливает новый список изображений для просмотра дерева, тип списка задается параметром nImageListType

    Добавление изображений в окно просмотра деревьев

    Если вы хотите поместить изображение слева от названия элемента, вам следует создать список изображений и ассоциировать его с окном просмотра деревьев.
    Код (C):
    1. HIMAGELIST ImageList_Create(
    2.    int  cx,//ширина любого изображения в этом списке изображений в пикселях
    3.    int  cy,/* высота любого изображения в этом списке изображений в пикселях.
    4. Все изображения в списке изображений должно быть равны друг другу по размеру.
    5. Если вы укажете большой рисунок, Windows разрежет его на несколько кусков
    6. согласно значению в cx и cy. Поэтому вам следует тщательно подготовить
    7. необходимые изображения */
    8.    UINT flags,/* задает тип изображения: является ли оно цветным или
    9. монохромным и их глубину */
    10.    int  cInitial, /* количество изображений, которое будет изначально
    11. содержать список изображений. Windows использует эту информацию
    12. для резервирования памяти для изображений*/
    13.    int  cGrow /* количество изображений, на которое должен увеличиваться
    14. список изображений, когда системе необходимо изменить размер списка,
    15. чтобы выделить место для новых изображений. Этот параметр представляет
    16. количество новых изображений, которое может содержать список
    17. изображений, изменивший размер*/
    18. );
    Если вызов пройдет успешно, функция возвратит дескриптор на пустой список изображений.
    Список изображений ― это не окно! Это только хранилище изображений, которые будут использоваться другими окнами.
    После того, как список изображений создан, вы можете добавить изображения с помощью вызова ImageList_Add.
    Код (C):
    1. int ImageList_Add(
    2.   _In_ HIMAGELIST himl, /* дескриптор списка изображений, в который
    3. вы хотите добавить изображения. Это значение возвращается ImageList_Create*/
    4.   _In_ HBITMAP  hbmImage, /* дескриптор рисунка, который должен быть
    5. добавлен в список изображений. Обычно изображения задаются в ресурсах
    6. и вызываются с помощью LoadBitmap. Заметьте, что вам не надо указывать
    7. количество изображений, содержащихся в этом рисунке, потому что это
    8. вытекает из параметров cx и cy, переданных ImageList_Create */
    9.   _In_opt_ HBITMAP  hbmMask /* дескриптор рисунка, в котором содержится
    10. маска. Если маска в списке изображений не используется, этот параметр
    11. игнорируется */
    12. );
    Если во время вызова произойдет какая-либо ошибка, будет возвращен -1.
    Обычно мы будем добавлять только два изображения в список изображений, который будет использоваться окном просмотра деревьев: одно для невыбранного элемента, а другое ― для выбранного.
    Когда список изображений готов, мы ассоциируем его с окном просмотра деревьев, посылая тому сообщение TVM_SETIMAGELIST:
    • wParam ― тип списка изображений. Есть две возможности:
      • TMSIL_NORMAL ― задает обычный список изображений, который содержит изображения выбранного и невыбранного элементов.
      • TVSIL_STATE ― устанавливает список изображений, содержащий изображения элементов для состояний, определяемых пользователем.
    • lParam ― дескриптор списка изображений.

    Функции для работы с Tree view в целом

    Когда ветка дочерних записей развернута, то они изображаются с некоторым сдвигом относительно родительской записи. Для определения и изменения сдвига дочерней записи относительно родительской предназначены функции: GetIndent() и SetIndent (UINT nIndent)
    • GetIndent() ― возвращает сдвиг в пикселях дочерней записи относительно родительской.
    • SetIndent (UINT nIndent) ― задает сдвиг в пикселях дочерней записи относительно родительской. Если задается значение меньшее, чем определяемое системой, то функция игнорируется.

    Изменение содержимого дерева

    Функции предназначенные для работы с конкретными записями, а также для добавления, изменения и удаления записей.
    • GetCount ― возвращает число записей, вставленных в окно просмотра деревьев
    • GetVisibleCount ― возвращает текущее количество видимых записей в окно просмотра деревьев
    • DeleteAllItems ― удаление всех записей из окна просмотра деревьев
    • InsertItem ― функция осуществляет вставку новых записей и определяет начальное положение записи при ее добавлении в окно просмотра деревьев. При вызове этой функции задаются дескриптор родительской записи и дескриптор записи, после которой должна быть вставлена новая запись. Второй дескриптор должен быть дескриптором дочерней записи или принимать одно из следующих значений: TVI_FIRST, TVI_LAST или TVI_SORT. Значения TVI_FIRST и TVI_LAST определяют, что запись должна быть помещена первой или последней в списке дочерних записей. Если задано значение TVI_SORT, то после вставки записи дочерние записи сортируются по алфавиту.

    Получение информации об окне просмотра деревьев

    Вы можете получить информацию об окне просмотра деревьев, послав ей сообщение TVM_GETITEM:
    • wParam=0
    • lParam=указатель на структуру TV_ITEM заполненную информацией
    Прежде, чем вы пошлете это сообщение, вы должны заполнить параметр imask флагами, которые укажут, какие из полей TV_ITEM должны быть заполнены Windows. А самое главное, вы должны заполнить hItem дескриптором элемента, о котором вы хотите получить информацию. И это порождает следующую проблему: где взять этот дескриптор? Надо ли вам сохранять все дескрипторы окон просмотра деревьев?
    Ответ достаточно прост: вам не надо этого делать. Вы можете послать сообщение TVM_GETNEXTITEM окну просмотра деревьев, чтобы получить дескриптор окна просмотра деревьев, который имеет указанные вами атрибуты. Например, вы можете получить дескриптор первого дочернего элемента, корневого элемента, выбранного элемента и так далее.
     
    Последнее редактирование: 21 июн 2019
  2. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709
    TVM_GETNEXTITEM:
    • wParam=флаг
    • lParam ― дескриптор окна просмотра деревьев (не всегда необходим)
    Значение wParam очень важно, поэтому ниже приведены все возможные флаги:
    • TVGN_CARET ― получение дескриптора выбранного элемента.
    • TVGN_CHILD ― получение дескриптора первого дочернего элемента по отношению к элементу, чей дескриптор указан в параметре hitem.
    • TVGN_DROPHILITE ― получение дескриптора элемента, который является целью операции drag-and-drop.
    • TVGN_FIRSTVISIBLE ― получение дескриптора первого видимого элемента.
    • TVGN_NEXT ― получение дескриптора следующего родственного элемента.
    • TVGN_NEXTVISIBLE ― получение дескриптора следующего видимого элемента, который следует за указанным элементом. Указанный элемент должен быть видимым. Используйте сообщение TVM_GETITEMRECT, чтобы определить, является ли элемент видимым.
    • TVGN_PARENT ― получение дескриптора указанного родительского элемента по отношению к указанному.
    • TVGN_PREVIOUS ― получение дескриптора предыдущего родственного элемента.
    • TVGN_PREVIOUSVISIBLE ― получение дескриптора первого видимого элемента, который предшествует указанному элементу, который должен быть видимым. Используйте сообщение TVM_GETITEMRECT, чтобы определить, является ли элемент видимым.
    • TVGN_ROOT ― получает дескриптор самого первого из корневых элементов окна просмотра деревьев.
    Вы можете видеть, что вы можете получить дескриптор интересующего вас сообщения с помощью этого сообщения. SendMessage возвратит дескриптор окна просмотра деревьев в случае успешного вызова. Затем вы можете заполнить поле hItem структуры TV_ITEM возвращенным дескриптором, чтобы передать структуру TVM_GETITEM.

    Операции Drag-and-Drop над окном просмотра деревьев

    Когда пользователь пытается перетащить элемент, окно просмотра деревьев посылает уведомление TVN_BEGINDRAG родительскому окну. Вы можете использовать эту возможность для создания специального изображения, которое будет представлять элемент, когда его тащат. Вы можете послать окну просмотра деревьев сообщение TVM_CREATEDRAGIMAGE, чтобы сказать тому создать такое изображение по умолчанию из изображения, использующееся в настоящее время элементом, который будет перетащен. Окно просмотра деревьев создаст список изображений с одним drag-изображением и возвратит дескриптор этого списка изображений.
    После того, как drag-изображение создано, вы указываете его "горячую точку", вызывая ImageList_BeginDrag.
    Код (C):
    1. BOOL ImageList_BeginDrag(
    2.    HIMAGELIST himlTrack, // это дескриптор списка изображений, который содержит drag-изображение
    3.    int  iTrack, // индекс элемента списка изображений, который будет являться drag-изображением
    4.    int  dxHotspot, /* относительная горизонтальная координата "горячей точки" (которая нам
    5. необходима, так как мы будем использовать drag-изображение вместо курсора мыши. У стандартного
    6. курсора "горячая точка" находится на кончике стрелки). */
    7.    int  dyHotspot // относительная вертикальная координата "горячей точки"
    8. );
    Как правило, iTrack будет равен 0, если вам нужно сказать окну просмотра деревьев, чтобы то создало для вас drag-изображение. dxHotspot и dyHotspot могут быть равными 0, если вы хотите, чтобы левый верхний угол drag-изображения был "горячей точкой".
    Когда drag-изображение готово, мы вызываем ImageList_DragEnter, чтобы отобразить его в окне.
    Код (C):
    1. BOOL ImageList_DragEnter(
    2.    HWND hwndLock,/* дескриптор окна, которому принадлежит drag-изображение.
    3. Drag-изображение нельзя будет двигать за пределы этого окна */
    4.    int  x, /* координаты места, где drag-изображение должно быть отображено сначала.
    5.  Заметьте, что эти значения задаются по отношению к левому верхнему углу окна,
    6. а не клиентской области. */
    7.    int  y
    8. );
    Теперь, когда drag-изображение отображено в окне, вам следует поддерживать операцию перетаскивания в окно просмотра деревьев. Тем не менее, здесь появляется небольшая проблема. Мы должны отслеживать путь перетаскивания с помощью WM_MOUSEMOVE и позицию сброса (drop) с помощью WM_LBUTTONUP. Однако, если drag-изображение находится над каким-нибудь дочерним окном, родительское окно никогда не получит никаких сообщений от мыши. Решение состоит в том, чтобы взять контроль на сообщениями от мыши с помощью SetCapture. Эта функция позволяет направить мышиные сообщения напрямую определенному окну, вне зависимости от того, где находится курсор мыши.
    Внутри обработчика WM_MOUSEMOVE, вы будете отслеживать drag-путь с помощью вызова ImageList_DragMove. Эта функция передвигает изображение относительно пути переноса. Более того, если вы захотите, вы можете подсвечивать элемент, над которым находится drag-изображение, посылая сообщение TVM_HITTEST, проверяя, находится ли изображение над каким-нибудь элементом. Если это так, вы можете послать TVM_SELECTITEM с флагом TVGN_DROPHILITE, чтобы подсветить элемент. Заметьте, что прежде, чем послать сообщение TVM_SELECTITEM, вы должны спрятать drag-изображение или оно будет оставлять уродливый след. Это можно сделать, вызвав ImageList_DragShowNolock, а после того, как элемент будет подсвечен, необходимо вызвать ImageList_DragShowNolock, чтобы снова отобразить drag-изображение.
    Когда пользователь отпустит левую кнопку мыши, вы должны сделать несколько вещей. Если вы подсветили элемент, вам нужно перевести его в обычное состояние, снова послав TVM_SELECTITEM с флагом TVGN_DROPHILITE, но в этот pаз lparam должен быть равен нулю. Затем вы должны вызвать ImageList_DragLeave, за которым должен следовать вызов ImageList_EndDrag. Вы должны освободить мышь с помощью ReleaseCapture. Если вы создадите список изображений, вам следует уничтожить его функцией ImageList_Destroy. После этого вы можете сделать все, что нужно, когда операция drag-and-drop завершена.

    Нотификационные сообщения окну просмотра деревьев

    При воздействии на окно просмотра деревьев генерируется сообщение WM_NOTIFY. В этом сообщении может содержаться несколько нотификационных кодов.
    нотификационный кодназначение
    TVN_DELETEITEMэлемент удален
    TVN_ITEMEXPANDINGэлемент должен быть развернут или свернут
    TVN_ITEMEXPANDEDэлемент развернут или свернут
    TVN_SELCHANGINGэлемент должен быть выбран
    TVN_SELCHANGEDэлемент выбран

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

    rc-файл

    Код (C):
    1. #define IDB_TREE 4006
    2. #define IDM_EXPANDALL 0
    3. #define IDM_COLLAPSEALL 1
    4. #define IDM_EXIT 2
    5. #define ZZZ_Menu 30
    6. IDB_TREE BITMAP "Images\\list.bmp"
    7. ZZZ_Menu MENU
    8. {
    9.                POPUP "Treeview"
    10.                {  
    11.                               MENUITEM "Expand all",IDM_EXPANDALL
    12.                               MENUITEM "Collapse all",IDM_COLLAPSEALL
    13.                               MENUITEM SEPARATOR
    14.                               MENUITEM "&Exit",IDM_EXIT
    15.                }
    16. }
     
    Последнее редактирование: 3 янв 2017
  3. Mikl___

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

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

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2. include gdi32.inc
    3. includelib gdi32.lib
    4. IMAGE_BASE equ 400000h
    5. IDB_TREE equ 4006
    6. ZZZ_Menu  equ 30
    7. IDM_EXPANDALL equ 0
    8. IDM_COLLAPSEALL equ 1
    9. IDM_EXIT equ 2
    10. ID_TREEVIEW equ 44
    11. .code
    12. WinMain proc
    13. local msg:MSG    
    14.  
    15. xor ebx,ebx
    16. invoke InitCommonControls
    17. mov eax,10027h
    18. mov edi,offset ClassName
    19. mov esi,IMAGE_BASE
    20. push rax ;hIconSm
    21. push rdi ;lpszClassName
    22. push ZZZ_Menu ;lpszMenuName
    23. push COLOR_APPWORKSPACE;hbrBackground
    24. push 10005h ;hCursor
    25. push rax        ;hIcon
    26. push rsi ;hInstance
    27. push rbx        ;cbClsExtra & cbWndExtra
    28. db 68h
    29. dd WndProc;lpfnWndProc
    30. push sizeof WNDCLASSEX;cbSize & style
    31. invoke RegisterClassEx,esp ;addr WNDCLASSEX
    32.  
    33. push rbx
    34. push rsi ;rsi=400000h
    35. shl esi,9 ;rsi=CW_USEDEFAULT
    36. push rbx
    37. push rbx
    38. push 260;rsi
    39. push 385;rsi
    40. push rsi
    41. push rsi
    42. sub esp,20h
    43.     invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    44. WS_OVERLAPPEDWINDOW+WS_VISIBLE
    45. lea edi,msg
    46. @@:invoke GetMessage,edi,0,0,0
    47. invoke DispatchMessage,edi
    48.         jmp @b
    49. WinMain endp
    50.  
    51. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    52. local tvis:TV_INSERTSTRUCT
    53. local old_rdi:QWORD
    54. local ps:PAINTSTRUCT
    55. local tvhit:TV_HITTESTINFO
    56.  
    57.         mov hWnd,rcx
    58. mov lParam,r9
    59. mov old_rdi,rdi
    60.  
    61.         cmp edx,WM_DESTROY
    62.         je wmDESTROY
    63. cmp edx,WM_NOTIFY
    64. je wmNOTIFY
    65. cmp edx,WM_LBUTTONUP
    66. je wmLBUTTONUP
    67. cmp edx,WM_CREATE
    68. je wmCREATE
    69. cmp edx,WM_COMMAND
    70. je wmCOMMAND
    71. cmp edx,WM_SIZE
    72. je wmSIZE
    73.         cmp edx,WM_MOUSEMOVE
    74. je wmMOUSEMOVE
    75. leave
    76.         jmp DefWindowProc
    77. wmMOUSEMOVE:cmp DragMode,TRUE
    78. jnz wmBYE
    79. movzx ecx,r9w;word ptr lParam
    80. movzx edx,word ptr lParam+2
    81. mov tvhit.pt.x,ecx ; eax is the horizontal position of the drag image
    82. mov tvhit.pt.y,edx ; ecx is the vertical position
    83. invoke ImageList_DragMove
    84.  
    85. invoke ImageList_DragShowNolock,FALSE
    86.  
    87. lea r9,tvhit ; check if an item is hit
    88. invoke SendMessage,hWndTree,TVM_HITTEST,NULL
    89. or eax,eax;.if eax!=NULL
    90. jz @f
    91. invoke SendMessage,hWndTree,TVM_SELECTITEM,TVGN_DROPHILITE,rax
    92. @@: invoke ImageList_DragShowNolock,TRUE
    93. jmp wmBYE
    94. wmCOMMAND:cmp r8d,IDM_EXIT
    95.         ja wmBYE
    96. jmp handle[r8*8]
    97. handle dq wmEXPANDALL,wmCOLLAPSEALL,wmDESTROY
    98. wmEXPANDALL:invoke SendMessage,hWndTree,TVM_EXPAND,TVE_EXPAND,hParent0
    99. invoke SendMessage,hWndTree,TVM_EXPAND,TVE_EXPAND,hParent1
    100.         invoke SendMessage,hWndTree,TVM_EXPAND,TVE_EXPAND,hParent2
    101.         invoke SendMessage,hWndTree,TVM_EXPAND,TVE_EXPAND,hChild3
    102. jmp wmBYE
    103. wmCOLLAPSEALL:invoke SendMessage,hWndTree,TVM_EXPAND,TVE_COLLAPSE,hParent0
    104. jmp wmBYE
    105. wmDESTROY:invoke ExitProcess,0
    106. wmSIZE: lea edx,ps
    107. invoke BeginPaint
    108. mov r8d,GreenBrush
    109.         lea edx,ps.rcPaint
    110. invoke FillRect,eax
    111. lea edx,ps
    112. invoke EndPaint,hWnd
    113. jmp wmBYE
    114. wmCREATE:push rbx;NULL
    115. push IMAGE_BASE;hInstance
    116. push ID_TREEVIEW
    117. push rcx;hWnd
    118. push 200
    119. push 200
    120. push rbx
    121. push rbx
    122. mov edx,offset TreeViewClass
    123. sub esp,20h
    124. invoke CreateWindowEx,0,,NULL,WS_CHILD or WS_VISIBLE or TVS_HASLINES or \
    125. TVS_HASBUTTONS or TVS_LINESATROOT or WS_CLIPSIBLINGS
    126.         mov hWndTree,rax
    127.         invoke SendMessage,hWndTree,TVM_SETBKCOLOR,0,0FF00h
    128. ;---------- [Get the Imagelist] ----------
    129. mov qword ptr [rsp+20h],10
    130. invoke ImageList_Create,16,16,ILC_COLOR16,5
    131. mov hImageList,rax
    132. invoke LoadBitmap,IMAGE_BASE,IDB_TREE
    133. mov edi,eax;mov hBitmap,rax
    134. invoke ImageList_Add,hImageList,eax,NULL
    135. invoke DeleteObject,edi;hBitmap
    136. invoke SendMessage,hWndTree,TVM_SETIMAGELIST,0,hImageList
    137. ;---------- [Fill the tree] ----------
    138.         invoke SendMessage,hWndTree,TVM_DELETEITEM,0,TVI_ROOT
    139.  
    140. mov tvis.hParent,rbx;NULL
    141. mov tvis.hInsertAfter,TVI_ROOT
    142. mov tvis.item.imask,TVIF_TEXT or TVIF_IMAGE or TVIF_SELECTEDIMAGE
    143. mov eax,offset Parent0
    144. mov tvis.item.pszText,rax
    145. mov tvis.item.iImage,ebx;0
    146. mov tvis.item.iSelectedImage,1
    147.  
    148.         lea r9,tvis
    149. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    150.         mov hParent0,rax
    151.  
    152.         mov tvis.hParent,rax
    153.         mov eax,offset Parent1
    154. mov tvis.item.pszText,rax
    155.  
    156.         lea r9,tvis
    157. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    158.         mov hParent1,rax
    159.  
    160.  
    161. mov tvis.hParent,rax
    162. mov tvis.hInsertAfter,TVI_LAST
    163.  
    164. mov eax,offset Child1
    165. mov tvis.item.pszText,rax
    166.  
    167. lea r9,tvis
    168. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    169.         mov hChild1,rax
    170.  
    171. mov eax,offset Child2
    172. mov tvis.item.pszText,rax
    173.  
    174. lea r9,tvis
    175. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    176.         mov hChild2,rax
    177.  
    178. mov eax,offset Child3
    179. mov tvis.item.pszText,rax
    180.  
    181. lea r9,tvis
    182. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    183.         mov hChild3,rax
    184.  
    185.         mov tvis.hParent,rax
    186. mov eax,offset Child4
    187. mov tvis.item.pszText,rax
    188.  
    189. lea r9,tvis
    190. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    191.         mov hChild4,rax
    192.  
    193. mov eax,offset Child5
    194. mov tvis.item.pszText,rax
    195.  
    196. lea r9,tvis
    197. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    198.         mov hChild5,rax
    199.  
    200.         mov rax,hParent1
    201.         mov tvis.hParent,rax
    202.  
    203. mov eax,offset Child6
    204. mov tvis.item.pszText,rax
    205.  
    206. lea r9,tvis
    207. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    208.         mov hChild6,rax
    209.  
    210.         mov rax,hParent0
    211.         mov tvis.hParent,rax
    212.         mov tvis.hInsertAfter,TVI_LAST
    213. mov tvis.item.imask,TVIF_TEXT or TVIF_IMAGE or TVIF_SELECTEDIMAGE
    214. mov eax,offset Parent2
    215. mov tvis.item.pszText,rax
    216.  
    217.         lea r9,tvis
    218. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    219.         mov hParent2,rax
    220. mov tvis.hParent,rax
    221.  
    222.         mov eax,offset Child7
    223. mov tvis.item.pszText,rax
    224.  
    225. lea r9,tvis
    226. invoke SendMessage,hWndTree,TVM_INSERTITEM,0
    227.         mov hChild7,rax
    228.  
    229.        invoke CreateSolidBrush,000FF00h;green=00FF00h
    230. mov GreenBrush,eax
    231. jmp wmBYE
    232. wmLBUTTONUP:cmp DragMode,TRUE
    233. jnz wmBYE
    234. invoke ImageList_DragLeave,hWndTree
    235. invoke ImageList_EndDrag
    236. invoke ImageList_Destroy,hDragImageList
    237.  
    238. invoke SendMessage,hWndTree,TVM_GETNEXTITEM,TVGN_DROPHILITE,0; Get the currently hilited item
    239. invoke SendMessage,hWndTree,TVM_SELECTITEM,TVGN_CARET,rax
    240. invoke SendMessage,hWndTree,TVM_SELECTITEM,TVGN_DROPHILITE,0
    241. invoke ReleaseCapture
    242. mov DragMode,FALSE
    243. jmp wmBYE
    244. wmNOTIFY:cmp r8d,ID_TREEVIEW ; Treeview?
    245.         jne wmBYE
    246.         cmp [r9+NM_TREEVIEW.hdr._code],TVN_ITEMEXPANDING
    247.         jne @f
    248.         cmp [r9+NM_TREEVIEW.action],TVE_COLLAPSE
    249.         jne wmBYE
    250.         invoke SendMessage,hWndTree,TVM_SELECTITEM,TVGN_CARET,0
    251. jmp wmBYE
    252. @@: cmp [r9+NM_TREEVIEW.hdr._code],TVN_BEGINDRAG
    253. jnz wmBYE
    254.         mov rdi,r9;lParam
    255. mov r9,[rdi+NM_TREEVIEW.itemNew.hItem]
    256. invoke SendMessage,hWndTree,TVM_CREATEDRAGIMAGE,0
    257. mov hDragImageList,rax
    258. invoke ImageList_BeginDrag,hDragImageList,0,0,0
    259. mov r8d,[rdi+NM_TREEVIEW.ptDrag.y]
    260. mov edx,[rdi+NM_TREEVIEW.ptDrag.x]
    261. invoke ImageList_DragEnter,hWndTree
    262. invoke SetCapture,hWnd
    263. mov DragMode,TRUE
    264. wmBYE: mov rdi,old_rdi
    265. leave
    266. xor eax,eax
    267. retn
    268. WndProc endp
    269. ;data
    270. ClassName db 'Win64 Iczelion''s lesson #19: Tree View Demo',0
    271. TreeViewClass  db "SysTreeView32",0
    272. Parent0 db 'Zero',0
    273. Parent1 db 'One',0
    274. Parent2 db 'Two',0
    275. Child1 db 'Eight',0
    276. Child2 db 'Four',0
    277. Child3 db 'Five',0
    278. Child4 db 'Six',0
    279. Child5 db 'Seven',0
    280. Child6 db 'Three',0
    281. Child7 db 'Nine',0
    282. DragMode db FALSE
    283. GreenBrush dd ?
    284. hWndTree dq ?
    285. hParent0 dq ?
    286. hParent1 dq ?
    287. hParent2 dq ?
    288. hChild1 dq ?
    289. hChild2 dq ?
    290. hChild3 dq ?
    291. hChild4 dq ?
    292. hChild5 dq ?
    293. hChild6 dq ?
    294. hChild7 dq ?
    295. hImageList dq ?
    296. hDragImageList dq ?
    297. end

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

    Внутри обработчика WM_CREATE вы создаете окно просмотра деревьев.
    Код (ASM):
    1. invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\
    2.                       WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+\
    3.                       TVS_LINESATROOT,0,0,200,400,hWnd,NULL,hInstance,NULL
    Обратите внимание на стили. TVS_xxxx ― это стили, присущие окну просмотра деревьев.
    Код (ASM):
    1.             invoke ImageList_Create,16,16,ILC_COLOR16,2,10
    2.                 mov hImageList,eax
    3.                 invoke LoadBitmap,hInstance,IDB_TREE
    4.                 mov hBitmap,eax
    5.                 invoke ImageList_Add,hImageList,hBitmap,NULL
    6.                 invoke DeleteObject,hBitmap
    7.                 invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList
    Затем вы создаете пустой список изображений, который будет принимать изображения размером 16x16 пикселей и с глубиной цвета 16 бит. Вначале он будет содержать 2 изображения, но будет расширен до 10, если это потребуется. Далее мы загружаем рисунок из ресурса и добавляем его в только что созданный список изображений. После этого мы удаляем дескриптор рисунка, так как он больше нам не нужен. Как только список изображений готов, мы ассоциируем его с окном просмотра деревьев, посылая ему TVM_SETIMAGELIST.
    Код (ASM):
    1.                mov tvinsert.hparent,NULL
    2.                mov tvinsert.hInsertAfter,TVI_ROOT
    3.                mov tvinsert.u.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
    4.                mov tvinsert.u.item.pszText,offset parent
    5.                mov tvinsert.u.item.iImage,0
    6.                mov tvinsert.u.item.iSelectedImage,1
    7.                invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
     
    Последнее редактирование: 3 янв 2017
  4. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709
    Мы вставляем элементы в окно просмотра деревьев, начиная с корневого элемента. Так как это будет корневой элемент, параметр hParent равен NULL, а hInsertAfter TVI_ROOT. imask указывает, что pszText, iImage и iSelectedImage структуры TV_ITEM верны. Мы заполняем эти три параметра соответствующими значениями. pszText содержит название корневого элемента, iImage ― это индекс изображения в списке изображений, который будет отображаться слева от невыбранного элемента, а iSelectedImage ― индекс изображения выбранного элемента. Когда все требуемые параметры заполнены, мы посылаем сообщение TVM_INSERTITEM окну просмотра деревьев, чтобы добавить в него корневой элемент.
    Код (ASM):
    1.               mov hParent,eax
    2.                mov tvinsert.hParent,eax
    3.                mov tvinsert.hInsertAfter,TVI_LAST
    4.                mov tvinsert.u.item.pszText,offset Child1
    5.                invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
    6.                mov tvinsert.u.item.pszText,offset Child2
    7.                invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
    После этого мы добавляем дочерние элементы. hParent теперь заполнен дескриптором родительского элемента. Мы будем использовать те же изображения, поэтому не меняем iImage и iSelectedImage.
    Код (ASM):
    1. wmNOTIFY:
    2.                mov rdi,lParam
    3.                assume rdi:ptr NM_TREEVIEW
    4.                .if [rdi].hdr.code==TVN_BEGINDRAG
    5.                    invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,\
    6.                           0,[edi].itemNew.hItem
    7.                    mov hDragImageList,rax
    8.                    invoke ImageList_BeginDrag,hDragImageList,0,0,0
    9.                    invoke ImageList_DragEnter,hwndTreeView,[rdi].ptDrag.x,\
    10.                           [rdi].ptDrag.y
    11.                    invoke SetCapture,hWnd
    12.                    mov DragMode,TRUE
    13.                .endif
    14.                assume rdi:nothing
    Теперь, когда пользователь попытается перетащить элемент, окно просмотра деревьев пошлет сообщение WM_NOTIFY с кодом TVN_BEGINDRAG. lParam ― это указатель на структуру NM_TREEVIEW, которая содержит некоторую информацию, которая необходима нам, поэтому мы помещаем значение lParam в rdi и используем rdi как указатель на структуру NM_TREEVIEW. 'assume edi:ptr NM_TREEVIEW' указывает MASM'у, что rdi ― это указатель на структуру NM_TREEVIEW. Затем мы создаем drag-изображение, посылая [TVM_CREATEDRAGIMAGE окну просмотра деревьев. Сообщение возвращает дескриптор на созданный список изображений, внутри которого содержится drag-изображение. Мы вызываем ImageList_BeginDrag, чтобы установить его "горячую точку". После этого начинаем операцию переноса с помощью ImageList_DragEnter. Эта функция отображает drag-изображение в указанном месте заданного окна.
    Мы используем структуру ptDrag, которая является полем структуры NM_TREEVIEW в качестве точки, в которой должно быть показано drag-изображение. Затем перехватываем мышь и устанавливаем флаг, который показывает, что мы находимся в drag-режиме.
    Код (ASM):
    1. wmMOUSEMOVE:
    2.                .if DragMode==TRUE
    3.                    mov eax,lParam
    4.                    and eax,0ffffh
    5.                    mov ecx,lParam
    6.                    shr ecx,16
    7.                    mov tvhit.pt.x,eax
    8.                    mov tvhit.pt.y,ecx
    9.                    invoke ImageList_DragMove,eax,ecx
    10.                    invoke ImageList_DragShowNolock,FALSE
    11.                    invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit
    12.                    .if eax!=NULL
    13.                      invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,eax
    14.                    .endif
    15.                    invoke ImageList_DragShowNolock,TRUE
    Теперь мы концентрируемся на WM_MOUSEMOVE. Когда пользователь перетаскивает drag-изображение, наше родительское окно получает сообщения WM_MOUSEMOVE. В ответ на них мы обновляем позицию drag-изображения функцией ImageList_DragMove, после чего проверяем, не находится ли оно над каким-нибудь элементом с помощью сообщения TVM_HITTEST с указанием координаты проверяемой точки. Если drag-изображение находится над каким-либо элементом, тот подсвечивается сообщением TVM_SELECTITEM с флагом TVGN_DROPHILITE. Во время операции подсветки мы прячем drag-изображение, чтобы не было лишних глюков.
    Код (ASM):
    1. wmLBUTTONUP:
    2.                .if DragMode==TRUE
    3.                    invoke ImageList_DragLeave,hwndTreeView
    4.                    invoke ImageList_EndDrag
    5.                    invoke ImageList_Destroy,hDragImageList
    6.                    invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROPHILITE,0
    7.                    invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax
    8.                    invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROPHILITE,0
    9.                    invoke ReleaseCapture
    10.                    mov DragMode,FALSE
    11.                .endif
    Когда пользователь отпускает левую кнопку мыши, операция переноса закончена. Мы выходим из drag-режима, последовательно вызывая функции ImageList_DragLeave, ImageList_EndDrag и ImageList_Destroy. Также мы проверяем последний подсвеченный элемент и выбираем его. Мы также должны убрать его подсветку, иначе другие элементы не будут подсвечиваться, когда их будут выбирать. И наконец, мы убираем перехват сообщений от мыши.
     
    Последнее редактирование: 26 июн 2019
  5. Mikl___

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

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

    Глава тридцать девятая. Братец Кролик изучает сабклассинг окна

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

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

    Если вы уже некоторое время программируете в Windows, вы уже могли столкнуться с ситуацией, когда окно имеет почти все атрибуты, которые вам нужны, но не все. Сталкивались ли вы с ситуацией, когда вам требуется специальный вид окна ввода, который бы отфильтровывал ненужный текст? Первое, что может придти в голову, это написать свое собственное окно. Hо это действительно тяжелая работа, требующая значительного времени. Выходом является сабклассинг окна.
    Вкратце, сабклассинг окна позволяет получить контроль над сабклассированным окном. У вас будет абсолютный контроль над ним. Давайте рассмотрим пример, что прояснить данное утверждение. Предположите, что вам нужен text box, в котором можно вводить только шестнадцатиричные числа. Если вы будете использовать обычное окне ввода, максимум, что вы сможете сделать, если пользователь введет неверную букву, это стереть исходную строку и вывести ее снова в отредактированном виде. По меньшей мере, это непрофессионально. Фактически вам требуется получить возможность проверять каждый символ, который пользователь набирает в text box'е, как pаз в тот момент, когда он делает это.
    Теперь мы изучим как это сделать. Когда пользователь печатает что-то в text box'е, Windows посылает сообщение WM_CHAR процедуре окна ввода. Эта процедура окна находится внутри Windows, поэтому мы не можем модифицировать ее. Hо мы можем перенаправить поток сообщений к нашей оконной процедуре. Поэтому наша процедура окна первой получит возможность обработать сообщение, которое Windows пошлет окну ввода. Если наша процедура решит обработать сообщение, она так и сделает. Hо если она не захочет его обрабатывать, она может передать его оригинальной оконной процедуре. Таким образом, наша функция будет стоять между Windows и окном ввода. Посмотрите на условную схему внизу.
    До сабклассинга: Windows [math]\to[/math] процедура окна ввода
    После сабклассинга: Windows [math]\to[/math] наша оконная процедура [math]\to[/math] процедура окна ввода
    Теперь мы можем рассмотреть то, каким образом происходит сабклассинг окна. Заметьте, что сабклассинг не ограничивается элементами управления, он может использоваться с любым окном. Давайте подумаем о том, как Windows узнает, где находится процедура edit box'а. Hу?.. Поле lpfnWndProc в структуре WNDCLASSEX. Если мы сможем поменять значение этого поля на адрес собственной структуры, Windows пошлет сообщение нашей процедуре окна вместо этого. Мы можем сделать это, вызвав SetWindowLong.
    Код (C):
    1. LONG WINAPI SetWindowLong(
    2.   _In_ HWND hWnd, //дескриптор окна, чьи свойства мы хотим поменять
    3.   _In_ int  nIndex, // значение, которое нужно изменить
    4.   _In_ LONG dwNewLong
    5. );
    • dwNewLong ― новое значение. У каждого окна есть ассоциированное с ним 32-битное значение, предназначенное для использования приложением в своих целях.
    GWL_EXSTYLEУстановка нового расширенного стиля окна.
    GWL_STYLEУстановка нового стиля окна.
    GWL_WNDPROCУстановка нового адреса для процедуры окна.
    GWL_HINSTANCEУстановка нового дескриптора приложения.
    GWL_IDУстановка нового идентификатора окна.
    GWL_USERDATAУстановка 32-битного значения, ассоциирующегося с окном.
    Таким образом, наша работа проста: мы создаем процедуру окна, которая будет обрабатывать сообщения для окна ввода и затем вызывать SetWindowLong с флагом GWL_WNDPROC, которому передается адрес нашего окна в качестве третьего параметра. В случае, если вызов функции прошел нормально, возвращаемым значением является прежнее значение замещаемого параметра, в нашем случае ― это адрес оригинальной процедуры окна. Нам нужно сохранить это значение, чтобы использовать его внутри нашей процедуры.
    Помните, что есть сообщения, которые нам не нужно будет обрабатывать. Их мы будем передавать оригинальной процедуре. Мы можем сделать это с помощью вызова функции CallWindowProc.
    Код (C):
    1. LRESULT WINAPI CallWindowProc(
    2.   _In_ WNDPROC lpPrevWndFunc,// адрес оригинальной процедуры окна
    3.   _In_ HWND  hWnd,
    4.   _In_ UINT  Msg,
    5.   _In_ WPARAM  wParam,
    6.   _In_ LPARAM  lParam
    7. );
    • Остальные четыре значения ― это те, что передаются нашей процедуре окна. Мы передаем их CallWindowProc.

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

    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE equ 400000h
    3. .code
    4. WinMain proc
    5. local msg:MSG
    6.         xor ebx,ebx
    7.        mov eax,10029h
    8.        mov esi,IMAGE_BASE
    9.        push rax ;hIconSm
    10.        mov edi,offset ClassName
    11.        push rdi ;lpszClassName
    12.        push rbx ;lpszMenuName
    13.        push COLOR_WINDOW;hbrBackground
    14.        push 10005h ;hCursor
    15.        push rax     ;hIcon
    16.        push rsi ;hInstance
    17.        push rbx ;cbClsExtra & cbWndExtra
    18.        db 68h
    19.        dd WndProc;lpfnWndProc
    20.        push sizeof WNDCLASSEX;cbSize & style
    21.        invoke RegisterClassEx,esp ;addr WNDCLASSEX
    22.         push rbx
    23.        push rsi ;rsi=400000h
    24.        shl esi,9 ;rsi=CW_USEDEFAULT
    25.        push rbx
    26.        push rbx
    27.        push 240
    28.        push 410
    29.        push rsi
    30.        push rsi
    31.        sub esp,20h
    32.        invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    33.        WS_OVERLAPPEDWINDOW or WS_VISIBLE
    34.        push rbx
    35.        push IMAGE_BASE
    36.        push rbx
    37.        push rax;hWnd
    38.        push 24
    39.        push 300
    40.        push 20
    41.        push 20
    42.        mov edx,offset ctlClsNameEdit
    43.        sub esp,20h
    44.        invoke CreateWindowEx,WS_EX_CLIENTEDGE,,0,\
    45.        WS_CHILD + WS_VISIBLE + WS_BORDER
    46.        mov  edit1H,rax
    47.        invoke SetFocus,eax
    48.        mov r8d,offset edit1_procedure
    49.        invoke SetWindowLongPtr,edit1H,GWL_WNDPROC
    50.        mov wndProcAddr,rax
    51.        lea edi,msg
    52. @@: invoke GetMessage,edi,0,0,0
    53.        invoke TranslateMessage,edi
    54.        invoke DispatchMessage,edi
    55.        jmp @b
    56. WinMain endp
    57. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    58.        cmp  edx,WM_DESTROY
    59.        je   wmDESTROY
    60. wmDEFAULT:jmp DefWindowProc
    61. wmDESTROY:invoke ExitProcess,0
    62. WndProc endp
    63. edit1_procedure proc hWnd:HWND, uMsg:QWORD, wParam:WPARAM, lParam:LPARAM
    64.        push rbp
    65.        mov ebp,esp
    66.        sub esp,30h
    67.         mov hWnd,rcx
    68.        mov uMsg,rdx
    69.        mov wParam,r8
    70.        mov lParam,r9
    71.  
    72.         cmp edx,WM_KEYDOWN
    73.        je   edit1_wmKEYDOWN
    74.        cmp edx,WM_CHAR
    75.        jne  a1
    76.        edit1_wmCHAR: cmp r8b,VK_BACK  ;compare with virtual key BACKSPACE
    77.        e a1
    78.        cmp r8b,30h ;compare with ascii 0
    79.        jb edit1_wmBYE
    80.        cmp r8b,39h ;compare with ascii 9
    81.        jbe a1
    82.         and r8b,-33 ;so our DL become big letter
    83.        cmp r8b,41h ;compare with ascii A
    84.        jb edit1_wmBYE
    85.        cmp r8b,46h ;compare with ascii F
    86.        ja edit1_wmBYE     ;something else  
    87. a1:  mov [esp+20h],r9;lParam
    88.        mov r9,r8;wParam
    89.         mov r8,rdx;uMsg
    90.         mov rdx,rcx;hWnd
    91.         invoke CallWindowProc,wndProcAddr
    92.        jmp  edit1_wmBYE
    93. edit1_wmKEYDOWN: cmp r8b,VK_RETURN ;compare with virtual key RETURN
    94.        jne  a1
    95.        mov r8d,offset ClassName
    96.        mov edx,offset edit1Txt1
    97.        invoke MessageBox,,,,MB_OK
    98. invoke SetFocus,hWnd
    99. edit1_wmBYE:
    100.        leave
    101.        ret
    102. edit1_procedure endp
    103. ClassName db 'Win64 Iczelion''s lesson #20: Window Subclassing',0
    104. ctlClsNameEdit db 'EDIT',0
    105. edit1Txt1 db 'A simple HEX edit control!',0
    106. wndProcAddr dq ?
    107. edit1H dq ?
    108. end

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

    Код (ASM):
    1. invoke SetWindowLong, hwndEdit, GWL_WNDPROC, addr EditWndproc
    2.                mov OldWndproc,eax
    После того, как окно ввода создано, мы сабклассим его, вызывая SetWindowLong и замещая адрес оригинальной процедуры окна нашим собственным адресом. Заметьте, что мы сохраняем значение адреса оригинальной процедуры, чтобы впоследствии использовать его при вызове CallWindowProc. Заметьте, что EditWndProc ― это обычная оконная процедура.
    Код (ASM):
    1. wmCHAR:         mov rax,wParam
    2.                .if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
    3.                    .if al>="a" && al<="f"
    4.                        sub al,20h
    5.                    .endif
    6.                    invoke CallWindowProc,OldWndproc,hEdit,uMsg,eax,lparam
    7.                    ret
    8.                .endif
    Внутри EditWndProc, мы фильтруем сообщения WM_CHAR. Если введен символ в диапазоне 0-9 или a-f, мы передаем его оригинальной процедуре окна. Если это символ нижнего регистра, мы конвертируем его в верхний, добавляя 20h. Заметьте, что если символ не тот, который мы ожидали, мы пропускаем его. Мы не передаем его оригинальной процедуре окна. Поэтому, когда пользователь набирает что-нибудь отличное от 0-9 или A-F, символ не появляется в окне ввода.
    Код (ASM):
    1. wmKEYDOWN:   mov rax,wParam
    2.                .if al==VK_RETURN
    3.                    invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
    4.                    invoke SetFocus,hEdit
    5.                .else
    6.                    invoke CallWindowproc,OldWndproc,hEdit,uMsg,wparam,lparam
    7.                    ret
    8.                .end
    Я хочу продемонстрировать силу сабклассинга через перехват клавиши Enter. EditWndProc проверяет сообщение WM_KEYDOWN, не равно ли оно VK_RETURN (клавиша Enter). Если это так, она отображает окно с сообщением "You pressed the Enter key in the text box!". Если это не клавиша Enter, она передает сообщение оригинальной процедуре.
    Вы можете использовать сабклассинг окна, чтобы получить контроль над другими окнами. Эту мощную технику вам следует иметь в своем арсенале.
     

    Вложения:

    • 8.png
      8.png
      Размер файла:
      196 КБ
      Просмотров:
      1.949
    Последнее редактирование: 13 янв 2021
  6. Mikl___

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

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

    Глава сороковая. Братец Кролик и пайп


    [​IMG]
    В этой главе мы исследуем пайп (pipe ―труба, трубопровод), что это такое и для чего мы можем использовать его. Чтобы сделать этот процесс более интересным, покажем, как можно изменить фон и цвет текста у окна ввода.
    Скачайте пример здесь.

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

    Пайп ― канал или дорога с двумя концами. Вы можете использовать пайп, чтобы обмениваться данными между двумя различными процессами или внутри одного процесса. Это что-то вроде переговорного устройства. Вы даете другому участнику конец канала и он может использовать его для того, чтобы взаимодействовать с вами.
    Есть два типа пайпов: анонимные и именованные. Анонимный пайп анонимен ― вы можете использовать его не зная его имени. Для того, чтобы использовать именованный пайп, вам обязательно нужно знать его имя.
    Вы можете разделить пайп по их свойствам: однонаправленные и двунаправленные. В однонаправленном пайпе данные могут течь только в одном направлении: от одного конца к другому, в то время как в двунаправленном данные могут передаваться между обоими концами.
    Анонимный пайп всегда однонаправленный. Именованный может быть и таким, и таким. Именованные пайпы обычно используются в сетевом окружении, где сервер может коннектиться к нескольким клиентам.
    В этой главе мы подробно рассмотрим анонимные пайпы. Главная цель таких пайпов ― служить каналом между родительским и дочерним процессом или между дочерними процессами.
    Анонимный пайп действительно полезен, когда вы взаимодействуете с консольным приложением. Консольное приложение ― это вид windows-программ, которые используют консоль для своего ввода и вывода. Консоль ― это вроде DOS-box'а. Тем не менее, консольное приложение ― это полноценное 64-битное приложение. Оно может использовать любую GUI-функцию, так же как и другие GUI-программы. Она отличается только тем, что у нее есть консоль.
    У консольного приложения есть три дескриптора, которые оно может использовать для ввода и вывода. Они называются стандартными дескрипторами: стандартный ввод, стандартный вывод и стандартный вывод ошибок. Стандартный дескриптор ввода используется для того, чтобы читать/получать информации из консоли и стандартный дескриптор вывода используется для вывода/распечатки информации на консоль. Стандартный дескриптор вывода ошибок используется для сообщения об ошибках.
    Консольное приложение может получить эти три стандартных значения, вызвав функцию GetStdHandle, указав дескриптор, который она хочет получить. GUI-приложение не имеет консоли. Если вы вызывает GetStdHandle, она возвратит ошибку. Если вы действительно хотите использовать консоль, вы можете вызвать AllocConsole, чтобы зарезервировать новую консоль. Тем не менее, не забудьте вызвать FreeConsole, когда вы уже не будете в ней нуждаться.
    Анонимный пайп очень часто используется для перенаправления ввода и/или вывода дочернего консольного приложения. родительский процесс может быть консоль или GUI-приложение, но дочернее приложение должно быть консольным, чтобы это сработало. Как вы знаете, консольное приложение использует стандартные дескрипторы для ввода и вывода. Если мы хотите перенаправить ввод/вывод консольного приложения, мы можем заменить один дескриптор другим дескриптором одного конца пайпа. Консольное приложение не будет знать, что оно использует один конец пайпа. Оно будет считать, что это стандартный дескриптор. Это вид полиморфизма на ООП-жаргоне. Это мощный подход, так как нам не нужно модифицировать родительский процесс никаким образом.
    Другая вещь, которую вы должны знать о консольном приложение ― это откуда оно берет стандартный дескриптор. Когда консольное приложение создано, у родительского приложения есть следующий выбор: оно может создать новую консоль для дочернего приложения или позволить тому наследовать собственную консоль. Чтобы второй метод работал, родительский процесс должен быть консольным, либо, если он GUI'евый, создать консоль с помощью AllocConsole.
    Давайте начнем работу. Чтобы создать анонимный пайп, вам требуется вызывать CreatePipe. Эта функция имеет следующий прототип:
    Код (C):
    1. BOOL WINAPI CreatePipe(
    2.   _Out_ PHANDLE  hReadPipe,/* указатель на переменную типа qword,
    3. которая получит дескриптор конца чтения пайпа*/
    4.   _Out_ PHANDLE  hWritePipe,/*указатель на переменную типа qword,
    5. которая получить дескриптор на конец записи пайпа*/
    6.   _In_opt_ LPSECURITY_ATTRIBUTES lpPipeAttributes,/*указывает на структуру
    7. SECURITY_ATTRIBUTES, которая определяет, наследуется ли каждый из концов
    8. дочерним процессом */
    9.   _In_ DWORD  nSize /* предполагаемый размер буфера, который пайп
    10. зарезервирует для использования. Это всего лишь предполагаемый размер.
    11. Вы можете передать NULL, чтобы указать функции использовать размер по
    12. умолчанию */
    13. );
    Если вызов прошел успешно, возвращаемое значение не равно нулю, иначе оно будет нулевым. После успешного вызова CreatePipe вы получите два дескриптора, один к концу чтения, а другой к концу записи.
    Теперь изложим шаги, необходимые для перенаправления стандартного вывода дочерней консольной программы в ваш процесс.
    1. Создаем анонимный пайп с помощью CreatePipe. Не забудьте установить параметр bInheritable структуры SECURITY_ATTRIBUTES в TRUE, чтобы дескрипторы могли наследоваться.
    2. Теперь мы должны подготовить параметры, которые передадим CreateProcess (мы используем эту функцию для загрузки консольного приложения). Среди аргументов этой функции есть важная структура STARTUPINFO. Эта структура определяет появление основного окна дочернего процесса, когда он запускается. Эта структура жизненно важна для нас. Вы можете спрятать основное окно и передать дескриптор пайпа дочерней консоли вместе с этой структурой.
    3. Hиже находятся поля, которые вы должны заполнить:
      • cb : размер структуры STARTUPINFO
      • dwFlags : двоичные битовые флаги, которые определяют, какие члены структуры будут использоваться, также она управляет состоянием основного окна. Нам нужно указать комбинацию STARTF_USESHOWWINDOW and STARTF_USESTDHANDLES.
      • hStdOutput и hStdError : дескрипторы, которые будут использоваться в дочернем процессе в качестве дескрипторов стандартного ввода/вывода. Для наших целей мы передадим дескриптор пайпа в качестве стандартного вывода и вывода ошибок. Поэтому когда дочерний процесс выведет что-нибудь туда, он фактически передаст информацию через пайп родительскому процессу.
      • wShowWindow управляет тем, как будет отображаться основное окно. Нам не нужно, что окно консоли отображалось на экран, поэтому мы приравняем этот параметр к SW_HIDE.
    4. Вызов CreateProcess, чтобы загрузить дочернее приложение. После того, как вызов прошел успешно, дочерний процесс все еще находится в спящем состоянии. Он загружается в память, но не запускается немедленно.
    5. Закройте конец дескриптор конца записи пайпа. Это необходимо, так как родительский процессу нет нужды использовать этот дескриптор, а пайп не будет работать, если открыть более чем один конец записи. Следовательно, мы должны закрыть его прежде, чем считывать данные из пайпа. тем не менее, не закрывайте этот конец до вызова CreateProcess, иначе ваш пайп будет сломан. Вам следует закрыть конец записи после того, как будет и вызвана функция CreateProcess, и до того, как вы считаете данные из конца чтения пайпа.
    6. Теперь вы можете читать данные из конца чтения с помощью ReadFile. С ее помощью вы запускаете дочерний процесс, который начнет выполняться, а когда он запишет что-нибудь в стандартный дескриптор вывода, данные будут посланы на конец чтения пайпа. Вы должны последовательно вызывать ReadFile, пока она не возвратит ноль, что будет означать, что больше данных нет. С полученной информацией вы можете делать все, что хотите, в нашем случае я вывожу их в окно ввода.
    7. Закроем дескриптор чтения пайпа.
     

    Вложения:

    • 8.png
      8.png
      Размер файла:
      805 КБ
      Просмотров:
      1.945
    Последнее редактирование: 13 янв 2021
  7. Mikl___

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

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

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

    rc-файл

    Код (C):
    1. #define MI_CSCRIPT 100
    2. #define IDC_MENU   30
    3.  
    4. IDC_MENU  MENU
    5. {
    6.     POPUP "&Action"
    7.     {       MENUITEM "&Assemble",MI_CSCRIPT
    8.     }
    9. }

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE  equ 400000h
    3. MI_CSCRIPT equ 100
    4. IDC_MENU    equ 30
    5. .code
    6. WinMain proc
    7. local msg:MSG
    8.  
    9.         xor ebx,ebx
    10.        mov eax,10027h
    11.        mov esi,IMAGE_BASE
    12.        mov edi,offset ClassName
    13.  
    14.        push rax ;hIconSm
    15.        push rdi ;lpszClassName
    16.        push IDC_MENU ;lpszMenuName
    17.        push COLOR_WINDOW;hbrBackground
    18.        push 10005h ;hCursor
    19.        push rax      ;hIcon
    20.        push rsi ;hInstance
    21.        push rbx        ;cbClsExtra & cbWndExtra
    22.        db 68h
    23.        dd WndProc ;lpfnWndProc
    24.        push sizeof WNDCLASSEX;cbSize & style
    25.        invoke RegisterClassEx,esp ;addr WNDCLASSEX
    26.        push rbx
    27.        push rsi ;rsi=400000h
    28.        shl esi,9 ;rsi=CW_USEDEFAULT
    29.        push rbx
    30.        push rbx
    31.        push 350
    32.        push 600
    33.        push rsi
    34.        push rsi
    35.        sub rsp,20h
    36.        invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    37.        WS_OVERLAPPEDWINDOW or WS_VISIBLE
    38.         push rbx
    39.        push IMAGE_BASE
    40.        push rbx
    41.        push rax
    42.        push 280
    43.        push 575
    44.        push rbx
    45.        push rbx
    46.        mov edx,offset ctlClsNameEdit
    47.         sub rsp,20h
    48.        invoke CreateWindowEx,WS_EX_CLIENTEDGE,,0,\
    49.        WS_CHILD + WS_VISIBLE + WS_HSCROLL + WS_VSCROLL +\
    50.        ES_MULTILINE + ES_AUTOHSCROLL + ES_AUTOVSCROLL
    51.        mov edit1H,rax
    52.        lea edi,msg
    53. @@:invoke GetMessage,edi,0,0,0
    54.        invoke TranslateMessage,edi
    55.        invoke DispatchMessage,edi
    56.         jmp @b
    57. WinMain endp
    58. ;----------------------------------------
    59. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    60. local pipeReadH:QWORD
    61. local pipeWriteH:QWORD
    62. local pro1StartInfo:STARTUPINFO
    63. local pro1Info:PROCESS_INFORMATION
    64. local pipeRead:QWORD
    65. local pipeBuffer[400h]:BYTE
    66.  
    67.        mov hWnd,rcx
    68.        mov wParam,r8
    69.        mov lParam,r9
    70.  
    71.        cmp edx,WM_DESTROY
    72.        je wmDESTROY
    73.        cmp edx,WM_COMMAND
    74.         je wmCOMMAND
    75.         cmp edx,WM_SIZE
    76.         je wmSIZE
    77.         cmp edx,WM_CTLCOLOREDIT
    78.         je wmCTLCOLOREDIT
    79.         leave
    80.         jmp DefWindowProc
    81. wmDESTROY: invoke ExitProcess,0
    82. wmSIZE: mov qword ptr [esp+28h],TRUE
    83.        mov rax,r9
    84.        movzx r9,ax
    85.        shr eax,16
    86.        mov [esp+20h],rax
    87.        invoke MoveWindow,edit1H,0,0
    88.        jmp wmBYE
    89. wmCTLCOLOREDIT:invoke SetTextColor,r8,0C9B5AFh
    90.        invoke SetBkColor,wParam,0
    91.        invoke GetStockObject,BLACK_BRUSH
    92.        jmp wmBYE
    93. wmCOMMAND:cmp r8,0FFFFh and MI_CSCRIPT
    94.        jne wmBYE
    95. wmCOMMAND_MI_CSCRIPT:mov edi,offset pipeSecAttr
    96.         mov  [rdi+SECURITY_ATTRIBUTES.nLength], sizeof SECURITY_ATTRIBUTES
    97.        mov  [edi+SECURITY_ATTRIBUTES.lpSecurityDescriptor],rbx
    98.         mov  [edi+SECURITY_ATTRIBUTES.bInheritHandle],TRUE
    99.        lea edx,pipeWriteH
    100.        lea ecx,pipeReadH
    101.        invoke CreatePipe,,,rdi,0
    102.        or eax,eax;NULL
    103.        je   MI_CSCRIPT_error_pipe
    104.        lea edi,pro1StartInfo
    105.        mov  [edi+STARTUPINFO.cb],sizeof STARTUPINFO
    106.        invoke GetStartupInfo,edi
    107.        mov  rax,pipeWriteH
    108.        mov  [rdi+STARTUPINFO.hStdOutput],rax
    109.        mov  [rdi+STARTUPINFO.hStdError],rax
    110.        mov  [rdi+STARTUPINFO.dwFlags],STARTF_USESHOWWINDOW +        STARTF_USESTDHANDLES
    111.        mov  [rdi+STARTUPINFO.wShowWindow],SW_HIDE
    112. ;+----------------+
    113. ;| create process |
    114. ;+----------------+
    115.        lea eax,pro1Info
    116.        mov [rsp+48h],rax
    117.        mov [rsp+40h],rdi
    118.        mov [rsp+38h],rbx
    119.         mov [rsp+30h],rbx
    120.         mov [rsp+28h],rbx
    121.        mov qword ptr [rsp+20h],TRUE
    122.        mov edx,offset pro1CmdLine
    123.        invoke CreateProcess,0,,0,0
    124.        or eax,eax;cmp  eax,NULL
    125.        je MI_CSCRIPT_error_process
    126. @@:     invoke CloseHandle,pipeWriteH
    127.        cmp  eax,TRUE
    128.        jne  @b
    129.        lea ecx,pipeBuffer
    130.        invoke RtlZeroMemory,,400h
    131.        mov [rsp+20h],rbx
    132.        lea r9,pipeRead
    133.        lea edx,pipeBuffer
    134.        invoke ReadFile,pipeReadH,,1023
    135.        or eax,eax;cmp  eax,NULL
    136.        je   @f
    137.        invoke SendMessage,edit1H,EM_SETSEL,-1,0
    138.        lea r9,pipeBuffer
    139.        invoke SendMessage,edit1H,EM_REPLACESEL,0
    140.        jmp  @f
    141. MI_CSCRIPT_error_pipe:mov r8d,offset ClassName
    142.        mov edx,offset errStr1
    143.        invoke MessageBox,hWnd,,,,MB_OK
    144.        jmp  wmBYE
    145. MI_CSCRIPT_error_process:mov r8d,offset ClassName
    146.        mov edx,offset errStr2
    147.        invoke MessageBox,hWnd,,,MB_OK
    148. @@:     invoke CloseHandle,pipeReadH
    149.        invoke CloseHandle,pro1Info.hProcess
    150.        invoke CloseHandle,pro1Info.hThread
    151. wmBYE:  leave
    152.         retn
    153. WndProc endp
    154. ;-----------------------------------------
    155. ClassName db 'Win64 Iczelion''s lesson #21: Pipe',0
    156. ctlClsNameEdit db 'EDIT',0
    157. edit1H dq ?
    158. pipeSecAttr  SECURITY_ATTRIBUTES <>
    159. errStr1  db 'Error - Pipe Creation Failed!',0
    160. errStr2  db 'Error - Process Creation Failed!',0
    161. pro1CmdLine db 'ml64 /Cp  msgbox.asm /link /subsystem:windows /entry:WinMain',0
    162. end
     
    Последнее редактирование: 3 янв 2017
  8. Mikl___

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

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

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

    Пример вызовет ml.exe, чтобы скомпилировать файл под названием test.asm, и перенаправит вывод в окно ввода. Когда программа загружена, она регистрирует класс окна и создает, как обычно, основное окно.
    Теперь наступает самая интересная часть. Мы изменим цвет текста и фон окна ввода. Когда окно ввода подойдет к моменту отрисовки его клиентской области, он пошлет сообщение WM_CTLCOLOREDIT родительскому окну.
    wParam содержит дескриптор device context'а, который окно ввода будет использовать для отрисовки его клиентской области. Мы можем использовать эту возможность для изменения характеристик HDC.
    Код (ASM):
    1. wmCTLCOLOREDIT:invoke SetTextColor, wParam, Yellow
    2.                invoke SetTextColor,wParam,Black
    3.                invoke GetStockObject,BLACK_BRUSH
    4.                ret
    SetTextColor изменяет цвет текста на желтый. SetTextColor изменяет цвет фона текста на черный. И, наконец, мы получаем дескриптор черной кисти, которую мы возвратим Windows. Обрабатывая сообщение WM_CTLCOLOREDIT, мы должны возвратить дескриптор кисти, которую Windows использует для отрисовки фона окна ввода. В нашем пример, я хочу, чтобы фон был черным, поэтому я возвращаю дескриптор черной кисти Windows.
    Когда пользователь выберет пункт меню 'Assemble', программа создаст анонимный пайп.
    Код (ASM):
    1. .if ax==IDM_ASSEMBLE
    2.                    mov sat.niLength,sizeof SECURITY_ATTRIBUTES
    3.                    mov sat.lpSecurityDescriptor,NULL
    4.                    mov sat.bInheritHandle,TRUE
    Перед вызовом CreatePipe мы должны заполнить структуру SECURITY_ATTRIBUTES. Заметьте, что мы можем передать NULL, если нас не интересуют настройки безопасности. И параметр bInheritHandle должен быть равен нулю, поэтому дескриптор пайпа наследуется дочерним процессом.
    Код (ASM):
    1. invoke CreatePipe,addr hRead,addr hWrite,addr sat,NULL
    После этого мы вызываем CreatePipe, которая заполнить переменные hRead и hWrite дескрипторами концов чтения и записи.
    Код (ASM):
    1.               mov startupinfo.cb,sizeof STARTUPINFO
    2.                        invoke GetStartupInfo,addr startupinfo
    3.                        mov eax, hWrite
    4.                        mov startupinfo.hStdOutput,eax
    5.                        mov startupinfo.hStdError,eax
    6.                        mov startupinfo.dwFlags, STARTF_USESHOWWINDOW+STARTF_USESTDHANDLES
    7.                        mov startupinfo.wShowWindow,SW_HIDE
    Затем мы заполним структуру STARTUPINFO. Мы вызовем GetStartupInfo, чтобы заполнить ее значениями родительского процесса. После вы модифицирует члены структуры. Мы копируем дескриптор конца записи в hStdOutput и hStdError, так как мы хотим, чтобы дочерний процесс использовал их вместо соответствующих стандартных дескрипторов. Мы также хотим спрятать консольное окно дочернего процесса, поэтому в wShowWindow мы помещаем значение SW_HIDE. И, наконец, мы должны подтвердить, что модифицированные нами поля нужно использовать, поэтому мы указываем флаги STARTF_USESHOWWINDOW и STARTF_USESTDHANDLES.
    Код (ASM):
    1.        invoke CreateProcess, NULL, addr CommandLine, NULL, NULL, TRUE, NULL, NULL, NULL,\
    2.            addr startupinfo, addr pinfo
    Теперь мы создаем дочерний процесс функцией CreateProcess. Заметьте, что параметр bInheritHandles должен быть установлен в TRUE, чтобы дескриптор пайпа работал.
    Код (ASM):
    1.      invoke CloseHandle,hWrite
    После успешного создания дочернего процесса мы закрываем конец записи пайпа. Помните, что мы передали дескриптор записи дочернему процессу через структуру STURTUPINFO. Если мы не закроем конец записи с нашей стороны, будет два конца записи, и тогда пайп не будет работать. Мы должны закрыть конец записи после Createprocess, но до того, как начнем считывание данных.
    Код (ASM):
    1. .while TRUE
    2.                           invoke RtlZeroMemory,addr buffer,1024
    3.                           invoke ReadFile,hRead,addr buffer,1023,addr bytesRead,NULL
    4.                             .if eax==NULL
    5.                               .break
    6.                               .endif
    7.                               invoke SendMessage,hwndEdit,EM_SETSEL,-1,0
    8.                               invoke SendMessage,hwndEdit,EM_REpLACESEL,FALSE,addr buffer
    9.                        .endw
    Теперь мы готовы читать данные. Мы входим в бесконечный цикл, пока все данные не будут считаны. Мы вызываем RtlZeroMemory, чтобы заполнить буфер нулями, потом вызываем ReadFile и вместо дескриптора файла передаем дескриптор пайпа. Заметьте, что мы считываем максимум 1023 байта, так данные, которые мы получим, должны быть ASCIIZ-строкой, которую можно будет передать окну ввода.
    Когда ReadFile вернет данные в буфере, мы выведем их в окно ввода. Тем не менее, здесь есть несколько проблем. Если мы используем SetWindowText, чтобы поместить данные в окно ввода, новые данные перезапишут уже считанные! Нам нужно, чтобы новые данные присоединялись к старым.
    Для достижения цели мы сначала двигаем курсор к концу текста окна ввода, послав сообщение EM_SETSEL с wParam'ом равным -1. Затем мы присоединяем данные с помощью сообщения EM_REPLACESEL.
    Код (ASM):
    1.         invoke CloseHandle, hRead
    Когда ReadFile возвращает NULL, мы выходим из цикла и закрываем конец чтения.
     
  9. Mikl___

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

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

    Глава сорок первая. Братец Кролик и суперклассинг

    [​IMG]
    В этой главе мы изучим суперклассинг, что это такое и для чего он служит. Вы также узнаете, как pеализовать навигацию с помощью клавиши 'Tab' в вашем окне.
    Скачайте пример здесь.

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

    Во время вашей программной карьеры, вы наверняка встретитесь с ситуацией, когда вам потребуется несколько контролов с *несколько* отличным поведением. Например, вам могут потребоваться 10 окон ввода, которые принимают только число. Есть несколько путей достигнуть цели:
    1. Создать собственный класс и написать контролы с нуля
    2. Создать эти edit control'ы и сабклассировать каждый из них
    3. Суперклассировать окно ввода
    Первый метод слишком сложен. Вам придется с нуля воплощать всю функциональность окна ввода. Слишком трудоемкая задача, чтобы ее можно было быстро выполнить. Второй метод лучше, чем первый, но, тем не менее, также требует немало работы. Все нормально, пока вам надо сабклассировать несколько контролов, но сабклассинг дюжины или еще большего количества контролов может превратиться в ад. Суперклассинг ― это техника, которой вы должны владеть.
    Суперклассинг ― это метод, с помощью которого вы сможете взять контроль над определенным классом окна. По взятием контроля я подразумеваю, что вы сможете изменить свойства класса, так чтобы они соответствовали вашим целям, после чего вы можете создать сколько угодно таких контролов.
    Hиже приведены шаги для суперклассинга:
    1. вызвать функцию GetClassInfoEx, чтобы получить информацию о классе окна, который вы хотите суперклассировать. GetClassInfoEx требует указатель на структуру WNDCLASSEX, которая будет заполнена информацией, если вызов пройдет успешно.
    2. Изменяйте требуемые параметры WNDCLASSEX. Тем не менее, если два члена, которые вы должны обязательно изменить:
      • hInstance ― Вы должны поместить в это поле дескриптор программы.
      • lpszClassName ― вы должны поместить сюда указатель на новое имя класса.
      Вы не обязаны изменять параметр lpfnWndProc, но обычно вам будет это нужно делать. Главное не забудьте сохранить старое значение lpfnWndproc, если вам надо будет его вызывать с помощью CallWindowProc.
    3. Зарегистрирует измененную структуру WNDCLASSEX. У вас будет новый класс окна, который будет обладать некоторыми характеристиками старого класса.
    4. Создайте окна с помощью нового класса.
    Суперклассинг лучше, чем сабклассинг, если вы хотите создать много контролов с одинаковыми характеристиками.

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


    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE equ 400000h
    3. .code
    4. WinMain proc
    5. local msg:MSG
    6.  
    7.         xor ebx,ebx
    8.        mov eax,10029h
    9.        mov esi,IMAGE_BASE
    10.        push rax ;hIconSm
    11.        mov edi,offset ClassName
    12.        push rdi ;lpszClassName
    13.        push rbx ;lpszMenuName
    14.        push COLOR_WINDOW;hbrBackground
    15.        push 10005h ;hCursor
    16.        push rax     ;hIcon
    17.        push rsi ;hInstance
    18.        push rbx ;cbClsExtra & cbWndExtra
    19.        db 68h
    20.        dd WndProc;lpfnWndProc
    21.        push sizeof WNDCLASSEX;cbSize & style
    22.        invoke RegisterClassEx,esp ;addr WNDCLASSEX
    23.         push rbx
    24.        push rsi ;rsi=400000h
    25.        shl esi,9 ;rsi=CW_USEDEFAULT
    26.        push rbx
    27.        push rbx
    28.        push 240
    29.        push 410
    30.        push rsi
    31.        push rsi
    32.        sub esp,20h
    33.        invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    34.        WS_OVERLAPPEDWINDOW or WS_VISIBLE
    35.        lea edi,msg
    36. @@: invoke GetMessage,edi,0,0,0
    37.        invoke TranslateMessage,edi
    38.        invoke DispatchMessage,edi
    39.        jmp @b
    40. WinMain endp
    41. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    42. local editCls:WNDCLASSEX
    43.        mov hWnd,rcx
    44.  
    45.        cmp edx,WM_CREATE
    46.        je wmCREATE
    47.        cmp  edx,WM_DESTROY
    48.        je   wmDESTROY
    49. wmDEFAULT:leave
    50.        jmp DefWindowProc
    51. wmDESTROY:invoke ExitProcess,0
    52. wmCREATE:lea edi,editCls
    53.         mov dword ptr [rdi+WNDCLASSEX.cbSize],sizeof WNDCLASSEX
    54.         mov esi,offset ctlClsNameEdit+3;ClsName="EDIT"
    55.         invoke GetClassInfoEx,0,esi,rdi
    56.         sub esi,3
    57.         mov rax,[rdi+WNDCLASSEX.lpfnWndProc]
    58.         mov wndProcAddr,rax
    59.         mov dword ptr [rdi+WNDCLASSEX.lpfnWndProc],offset edit1_procedure
    60.         mov dword ptr [rdi+WNDCLASSEX.hInstance],IMAGE_BASE
    61.         mov [rdi+WNDCLASSEX.lpszClassName],rsi;OFFSET ctlClsNameEdit ClsName="HEXEDIT"
    62.        invoke RegisterClassEx,edi
    63.        mov ebx,5
    64.        mov edi,20
    65. @@: push 0;rbx
    66.        push IMAGE_BASE
    67.        push rbx
    68.        push hWnd
    69.        push 24
    70.        push 300
    71.        push rdi
    72.        push 20
    73.        sub esp,20h
    74.        invoke CreateWindowEx,WS_EX_CLIENTEDGE,esi,0,\
    75.        WS_CHILD + WS_VISIBLE + WS_BORDER
    76.        mov [edit1H+8*rbx],rax      
    77.        invoke SendMessage,eax,EM_LIMITTEXT,15,0  ;limit to 15 chars
    78.        add edi,30
    79.        dec ebx
    80.        jns @b
    81.        inc ebx
    82.        invoke SetFocus,[edit1H+8*5]
    83. wmBYE: leave
    84.        retn
    85. WndProc endp
    86. edit1_procedure proc hWnd:HWND, uMsg:QWORD, wParam:WPARAM, lParam:LPARAM
    87.        push rbp
    88.        mov ebp,esp
    89.        sub esp,30h
    90.         mov hWnd,rcx
    91.        mov uMsg,rdx
    92.        mov wParam,r8
    93.        mov lParam,r9
    94.  
    95.         cmp edx,WM_KEYDOWN
    96.        je   edit1_wmKEYDOWN
    97.        cmp edx,WM_CHAR
    98.        jne  @f
    99.  
    100. edit1_wmCHAR: cmp r8b,VK_BACK  ;compare with virtual key BACKSPACE
    101.        je @f
    102.        cmp r8b,'0' ;compare with ascii 0
    103.        jb edit1_wmBYE
    104.        cmp r8b,'9' ;compare with ascii 9
    105.        jbe @f
    106.         and r8b,-33 ;so our DL become big letter
    107.        cmp r8b,'A' ;compare with ascii A
    108.        jb edit1_wmBYE
    109.        cmp r8b,'F' ;compare with ascii F
    110.        ja edit1_wmBYE     ;something else    
    111. @@: mov [esp+20h],r9;lParam
    112.        mov r9,r8;wParam
    113.         mov r8,rdx;uMsg
    114.         mov rdx,rcx;hWnd
    115.         invoke CallWindowProc,wndProcAddr
    116.        jmp  edit1_wmBYE
    117. edit1_wmKEYDOWN: cmp r8b,VK_RETURN ;compare with virtual key RETURN
    118.         je wmKEYDOWN_VK_RETURN
    119.         cmp r8b,VK_TAB
    120. jne @b
    121. ;-----------------------------------------------------
    122. wmKEYDOWN_VK_TAB:invoke GetKeyState,VK_SHIFT
    123.        or eax,eax;test eax,0x80000000
    124.        js VK_TAB_PREV;jne   VK_TAB_PREV
    125. VK_TAB_NEXT:invoke GetWindow,hWnd,GW_HWNDNEXT;=2
    126.        or eax,eax
    127.        jne VK_TAB_BYE
    128.        xor edx,edx;GW_HWNDFIRST=0
    129.        jmp @f
    130. VK_TAB_PREV:invoke GetWindow,hWnd,GW_HWNDPREV;=3
    131.        test eax,eax
    132.        jne VK_TAB_BYE
    133.        mov edx,GW_HWNDLAST;=1
    134. @@: invoke GetWindow,hWnd
    135. VK_TAB_BYE:mov ecx,eax
    136.        jmp @f
    137. wmKEYDOWN_VK_RETURN:mov r8d,offset ClassName
    138.        mov edx,offset edit1Txt1
    139.        invoke MessageBox,,,,MB_OK
    140.        mov rcx,hWnd
    141. @@: invoke SetFocus
    142. edit1_wmBYE:leave
    143.        ret
    144. edit1_procedure endp
    145. ClassName db 'Win64 Iczelion''s lesson #22: Superclassing Demo',0
    146. ctlClsNameEdit db 'HEXEDIT',0
    147. edit1Txt1 db 'A simple HEX edit control!',0
    148. wndProcAddr dq ?
    149. edit1H dq 6 dup(?)
    150. end
     

    Вложения:

    • 0.png
      0.png
      Размер файла:
      22,2 КБ
      Просмотров:
      1.968
    Последнее редактирование: 13 янв 2021
  10. Mikl___

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

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

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

    Программа создаст простое окно с "измененными" окнами ввода в своей клиентской области. Окна ввода будут принимать только шестнадцатиричные числа. Фактически, это адаптированный пример с сабклассингом. Программа стартует как обычно, а самое интересное происходит, когда создается основное окно:
    Код (ASM):
    1. wmCREATE:
    2.             mov wc.cbSize,sizeof WNDCLASSEX
    3.            invoke GetClassInfoEx,NULL,addr EditClass,addr wc
    Сначала мы заполним данными класса, который мы хотим суперклассировать, в нашем случае это класс edit'а. Помните, что вы должны установить параметр структуры WNDCLASSEX, перед тем, как вызвать GetClassInfoEx, в противном случае она будет заполнена неверно. После вызова GetClassInfoEx у нас будет иметься вся необходимая для создания нового класса информация.
    Код (ASM):
    1.           push wc.lpfnWndproc
    2.            pop OldWndproc
    3.            mov wc.lpfnWndproc, OFFSET EditWndproc
    4.            push hInstance
    5.            pop wc.hInstance
    6.            mov wc.lpszClassName,OFFSET OurClass
    Теперь мы можем изменить некоторые члены wc. Первый из них ― это указатель на процедуру окна. Так как нам нужно будет соединить вызовы новой и старой процедуры в цепь, нам необходимо сохранить старое значение в переменную, чтобы потом воспользоваться функцией CallWindowProc. Эта техника идентична с сабклассингом, не считая того, что вы напрямую изменяете структуру WNDCLASSEX не вызывая SetWindowLong. Следующие два поля должны быть изменены, иначе вам не удастся зарегистрировать ваш новый класс окна, hInstance и lpszClassName. Вы должны заменить старое значение hInstance на дескриптор вашей программы, а также выбрать имя для нового класса.
    Код (ASM):
    1.      invoke RegisterClassEx, addr wc
    Когда все готово, регистрируйте новый класс. Вы получите новый класс, обладающий некоторыми характеристиками старого.
    Код (ASM):
    1.            xor ebx,ebx
    2.            mov edi,20
    3.            .while ebx<6
    4.                invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,WS_CHILD+\
    5.                     WS_VISIBLE+WS_BORDER,20,edi,300,25,hWnd,ebx,hInstance,NULL
    6.                mov dword ptr [hwndEdit+4*ebx],eax
    7.                add edi,25
    8.                inc ebx
    9.            .endw
    10.            invoke SetFocus,hwndEdit
    Теперь, когда мы зарегистрировали класс, мы можем создать основанные на нем окна. Вы вышеприведенном куске кода, я использовал ebx в качестве счетчика созданных окон. edi используется как y-координата левого верхнего угла окна. Когда окно создано, его дескриптор сохраняется в массиве dword'ов. Когда все окна созданы, устанавливаем фокус на первое окно. К этому моменту у вас есть 6 edit control'ов, которые принимают только шестнадцатиричные числа. Hовая процедура окна, заменившая старую, выполняет роль фильтра. Фактически, это работает точно также, как и в примере с сабклассингом, только вам не нужно выполнять лишнюю работу.
    Вставлен кусок кода, который обрабатывает нажатия на Tab, чтобы сделать пример более полезным для вас. Обычно, если вы помещаете контролы на диалоговое окно, его внутренний менеджер сам обрабатывает нажатия на клавиши навигации. Увы, но это недоступно, когда вы помещаете контролы на обычное окно. Вам следует сабклассировать их, чтобы нажатия на Tab обрабатывались. В нашем примере нам нет нужны сабклассировать контролы по одному, так как мы уже суперклассировали, поэтому можем реализовать "центральный менеджер навигации контролов".
    Код (ASM):
    1.            .elseif al==VK_TAB
    2.                invoke GetKeyState,VK_SHIFT
    3.                test eax,80000000
    4.                .if ZERO?
    5.                    invoke GetWindow,hEdit,GW_HWNDNEXT
    6.                    .if eax==NULL
    7.                        invoke GetWindow,hEdit,GW_HWNDFIRST
    8.                    .endif
    9.                .else
    10.                    invoke GetWindow,hEdit,GW_HWNDpREV
    11.                    .if eax==NULL
    12.                        invoke GetWindow,hEdit,GW_HWNDLAST
    13.                    .endif
    14.                .endif
    15.                invoke SetFocus,eax
    16.                xor eax,eax
    17.                ret
    Вышеприведенный код взят из процедуры EditWndClass. Он проверяет, нажал ли пользователь клавишу tab, если да, он вызывает GetKeyStat, чтобы узнать, нажата ли также клавиша Shift. GetKeyState возвращает значение в eax, которое определяет, нажата ли указанная клавиша или нет. Если клавиша нажата, верхний бит eax будет установлен. Если нет, он будет очищен. Поэтому мы тестируем полученное значение 80000000h. Если верхний бит установлен, это будет означать, что пользователь нажал shift и tab одновременно, и должны обработать это отдельно.
    Если пользователь нажал клавишу Tab, мы вызываем GetWindow, чтобы получить дескриптор следующего контрола. Мы используем флаг GW_HWNDNEXT, чтобы указать GetWindow получить дескриптор следующего окна относительно текущего hEdit. Если эта функция возвращает NULL, то такого окна нет и мы устанавливаем фокус на первое окно, вызвав GetWindow с флагом GW_HWNDFIRST. Shift-Tab работает так же, как и обычно нажатие на Tab, только передвигает фокус окна назад.
     
    Последнее редактирование: 4 янв 2017
  11. Mikl___

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

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

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

    [​IMG]
    В этой главе мы узнаем, как помещать иконки в system tray и как создавать/использовать всплывающее меню.
    Скачайте пример здесь.

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

    System tray ― это прямоугольная область панели задач, в которой располагаются несколько иконок. Скорее всего, вы обнаружите там как минимум цифровые часы. Вы можете самостоятельно помещать иконки в system tray. Далее приводятся шаги, которые нужно для этого выполнить:
    1. Заполните структуру NOTIFYICONDATA,
      Код (ASM):
      1. NOTIFYICONDATA STRUCT
      2.   cbSize   DWORD ?,?
      3.   hWnd   QWORD ?
      4.   uID   DWORD ?
      5.   uFlags   DWORD ?
      6.   uCallbackMessage DWORD ?,?
      7.   hIcon   QWORD ?
      8.   szTip   BYTE 64 dup (?)
      9. NOTIFYICONDATA ENDS
      содержащую следующие поля:
      • cbSize ― размер данной структуры.
      • hwnd ― дескриптор окна, которое будет получать уведомление, когда над иконкой в tray'e произойдёт событие мыши.
      • uID ― константа, используемая в качестве идентификатора иконки. Вы сами выбираете значение этой константе. В случае, если вы поместили в system tray несколько иконок, вы сможете узнать, над какой именно из них произошло событие мыши.
      • uFlags ― указывает, какие поля данной структуры заполнены
        • NIF_ICON Поле hIcon заполнено.
        • NIF_MESSAGE Поле uCallbackMessage заполнено.
        • NIF_TIP Поле szTip заполнено.
      • uCallbackMessage ― пользовательское сообщение, которое Windows отошлёт указанному в поле hwnd окну, в случае, когда над иконкой произойдёт событие мыши. Сообщение вы создаете сами.
      • hIcon ― дескриптор иконки, которую вы хотите поместить в system tray.
      • szTip ― 64-байтовый массив, содержащий строку для использования в качестве всплывающей подсказки к иконке.
    2. Вызовите Shell_NotifyIcon, определённую в shell32.inc. Данная функция имеет следующий прототип:
      Код (C):
      1. BOOL Shell_NotifyIcon(
      2.   _In_ DWORD  dwMessage,
      3.   _In_ PNOTIFYICONDATA lpdata
      4. );
      • dwMessage ― это тип сообщения, которое нужно отправить оболочке.
        • NIM_ADD Добавляет иконку в system tray.
        • NIM_DELETE Удаляет иконку из system tray.
        • NIM_MODIFY Изменяет иконку в system tray.
      • lpdata ― это указатель на корректно заполненную структуру NOTIFYICONDATA.
    3. Если вы хотите добавить иконку в system tray, используйте сообщение NIM_ADD, если хотите удалить иконку, применяйте NIM_DELETE.
    Вот, собственно, и всё. Но чаще всего просто поместить иконку в system tray недостаточно. Вам нужно как-то реагировать на событий мыши, происходящие над этой иконкой. Это можно сделать, обрабатывая сообщение, указанное в поле uCallbackMessage структуры NOTIFYICONDATA. Это сообщение содержит следующие значения в wParam и lParam:
    • wParam содержит ID иконки. Это то же самое значение, что вы поместили в поле uID структуры NOTIFYICONDATA.
    • lParam Младшее слово содержит сообщение мыши. Например, если пользователь сделал правый щелчок по иконке, то lParam будет содержать WM_RBUTTONDOWN.
    Обычно иконка в system tray показывает всплывающее меню при правом щелчке по ней. Этого можно добиться, если сначала создать само всплывающее меню, а затем вызывать TrackPopupMenu для его отображения. Шаги приведены ниже:
    1. Создайте всплывающее меню, вызвав CreatePopupMenu. Эта функция создаёт пустое меню, и при успешном создании возвращает его дескриптор в eax.
    2. Добавьте пункты в меню с помощью AppendMenu, InsertMenu или InsertMenuItem.
    3. Когда вам будет нужно отобразить всплывающее меню на месте курсора мыши, вызовите GetCursorPos, чтобы узнать текущие координаты курсора, а затем вызовите TrackPopupMenu, чтобы вывести меню на экран.
    4. Когда пользователь щёлкнет на одном из пунктов меню, Windows отправит сообщение WM_COMMAND вашей оконной процедуре, точно так же, как и при работе с обычным меню.
    Внимание: остерегайтесь следующих проблем, часто возникающих при работе со всплывающими меню.
    • Когда меню отображено на экране, щелчок вне меню не приводит к его немедленному исчезновению. Это происходит потому, что окно, которое будет получать уведомления от меню, ДОЛЖНО быть на переднем плане. Просто вызовите SetForegroundWindow, чтобы исправить эту проблему.
    • После вызова SetForegroundWindow вы обнаружите, что в первый раз всплывающее меню сработает нормально, но при последующем появлении оно будет отображаться, а затем тут же исчезать. Как написано в MSDN, это сделано "намеренно". Необходимо переключить задачу на программу, являющуюся владельцем иконки в system tray. Этого можно добиться, отправив любое сообщение окну вашей программы. Но только используйте PostMessage, а не SendMessage!
     

    Вложения:

    • 5.png
      5.png
      Размер файла:
      202,5 КБ
      Просмотров:
      1.975
    Последнее редактирование: 13 янв 2021
  12. Mikl___

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

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

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

    Вариант #1

    Код (ASM):
    1. include win64a.inc
    2. include shell32.inc
    3. includelib shell32.lib
    4. IMAGE_BASE    equ 400000h
    5. WM_SHELLNOTIFY    equ WM_USER+5
    6. IDI_TRAY    equ 0
    7. IDM_RESTORE    equ 1000
    8. IDM_EXIT    equ 1010
    9. .code
    10. WinMain proc
    11. local msg:MSG
    12.     push rbp
    13.     mov ebp,esp
    14.     sub esp,sizeof MSG
    15.  
    16.     xor ebx,ebx
    17.     mov esi,IMAGE_BASE
    18.     mov ecx,offset FileName
    19.     call LoadCursorFromFile
    20.         mov note.hIcon,rax
    21.     mov edi,offset ClassName
    22.     push rax    ;hIconSm
    23.     push rdi    ;lpszClassName
    24.     push rbx    ;lpszMenuName
    25.     push COLOR_WINDOW;hbrBackground
    26.     push 10005h    ;hCursor
    27.     push rax        ;hIcon
    28.     push rsi    ;hInstance
    29.     push rbx        ;cbClsExtra & cbWndExtra
    30.     db 68h
    31.     dd WndProc      ;lpfnWndProc
    32.     push sizeof WNDCLASSEX;cbSize & style
    33.     mov ecx,esp    ;addr WNDCLASSEX
    34.         call RegisterClassEx  
    35.     push rbx
    36.     push rsi    ;rsi=400000h
    37.     shl esi,9    ;rsi=CW_USEDEFAULT
    38.     push rbx
    39.     push rbx
    40.     push 200
    41.     push 350
    42.     push rsi
    43.     push rsi
    44.     mov r9d,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    45.     mov r8,rdi    ;offset ClassName
    46.     mov edx,edi    ;offset ClassName
    47.     xor ecx,ecx
    48.     sub esp,20h
    49.         call CreateWindowEx
    50.         lea edi,msg
    51. @@:     mov ecx,edi
    52.     xor edx,edx
    53.     xor r8d,r8d
    54.     xor r9d,r9d
    55.         call GetMessage
    56.     mov ecx,edi
    57.         call DispatchMessage
    58.         jmp @b
    59. WinMain endp
    60. WndProc proc hWnd:QWORD, uMsg:QWORD, wParam:QWORD, lParam:QWORD
    61. local pt:POINT
    62.     push rbp
    63.     mov ebp,esp
    64.     sub esp,(40h+sizeof POINT+15)and(-16)
    65.     mov hWnd,rcx
    66.     mov wParam,r8
    67.  
    68.     cmp edx,WM_DESTROY
    69.     je wmDESTROY
    70.         cmp edx,WM_CREATE
    71.     je wmCREATE
    72.     cmp edx,WM_SHELLNOTIFY
    73.     je wmSHELLNOTIFY
    74.     cmp edx,WM_COMMAND
    75.     je wmCOMMAND
    76.     cmp edx,WM_SIZE
    77.     je wmSIZE
    78.     leave
    79.         jmp DefWindowProc
    80. wmDESTROY:mov rcx,hPopupMenu
    81.     call DestroyMenu
    82.     xor ecx,ecx
    83.         call ExitProcess
    84. wmCREATE:mov note.hWnd,rcx;rcx=hWnd
    85.     call CreatePopupMenu
    86.     mov hPopupMenu,rax
    87.     mov r9d,offset RestoreString
    88.     mov r8d,IDM_RESTORE
    89.     xor edx,edx;mov edx,MF_STRING
    90.     mov ecx,eax;hPopupMenu
    91.     call AppendMenu
    92.     mov r9d,offset ExitString
    93.     mov r8d,IDM_EXIT
    94.     xor edx,edx;mov edx,MF_STRING
    95.     mov rcx,hPopupMenu
    96.     call AppendMenu
    97.     jmp wmBYE
    98. wmSHELLNOTIFY:or r8,r8;.if wParam==IDI_TRAY
    99.     jne wmBYE
    100.     cmp r9,WM_RBUTTONDOWN
    101.     jne @f
    102. wmSHELLNOTIFY_WM_RBUTTONDOWN:
    103.     lea ecx,pt
    104.     call GetCursorPos
    105.     mov rcx,hWnd
    106.         call SetForegroundWindow
    107.         mov [rsp+30h],rbx;0
    108.     mov rax,hWnd
    109.         mov [rsp+28h],rax
    110.     mov [rsp+20h],rbx;0
    111.     mov r9d,pt.y
    112.     mov r8d,pt.x
    113.     mov edx,TPM_RIGHTALIGN
    114.     mov rcx,hPopupMenu
    115.     call TrackPopupMenu
    116.     xor r9d,r9d
    117.     xor r8d,r8d
    118.     mov edx,WM_NULL
    119.     mov rcx,hWnd
    120.         call PostMessage
    121.     jmp wmBYE
    122. @@:    cmp r9,WM_LBUTTONDBLCLK
    123.     jne wmBYE
    124. wmSHELLNOTIFY_WM_LBUTTONDBLCLK:
    125.     xor r9d,r9d;0
    126.     mov r8d,IDM_RESTORE
    127.     mov edx,WM_COMMAND
    128.     mov rcx,hWnd
    129.     call SendMessage
    130.     jmp wmBYE
    131. wmCOMMAND:or r9,r9;.if lParam==0 message is not from control
    132.     jnz wmBYE
    133.     mov edx,offset note
    134.     mov ecx,NIM_DELETE
    135.     call Shell_NotifyIcon
    136.         mov rcx,hWnd
    137.     cmp word ptr wParam,IDM_RESTORE
    138.     jne @f
    139. wmCOMMAND_IDM_RESTORE:
    140.     mov edx,SW_RESTORE  
    141.     call ShowWindow
    142.     jmp wmBYE
    143. @@:    call DestroyWindow
    144.     jmp wmBYE
    145. wmSIZE: cmp r8d,SIZE_MINIMIZED;.if wParam==SIZE_MINIMIZED
    146.         jnz wmBYE
    147.     xor edx,edx;mov edx,SW_HIDE
    148.     call ShowWindow
    149.     mov edx,offset note
    150.     xor ecx,ecx;mov ecx,NIM_ADD
    151.     call Shell_NotifyIcon
    152. wmBYE:    leave
    153.     retn
    154. WndProc endp
    155. ;---------------------------------------
    156. RestoreString    db "&Restore",0
    157. ExitString     db "E&xit Program",0
    158. FileName    db "..\Images\Cursor.cur",0
    159. note label NOTIFYICONDATA
    160. dd sizeof NOTIFYICONDATA,0
    161. dq ?
    162. dd IDI_TRAY
    163. dd NIF_ICON+NIF_MESSAGE+NIF_TIP
    164. dd WM_SHELLNOTIFY,0
    165. dq ?
    166. ClassName db 'Win64 Iczelion''s lesson #23-1: TrayIcon Demo'
    167. db 64-(lengthof ClassName) dup(0)
    168. hPopupMenu    dq ?
    169. end
    [​IMG]

    Вариант #2

    Код (ASM):
    1. include win64a.inc
    2. include shell32.inc
    3. includelib shell32.lib
    4. IMAGE_BASE equ 400000h
    5. WM_SHELLNOTIFY equ WM_USER+5
    6. IDI_TRAY equ 0
    7. IDM_SHOWHIDE equ 100
    8. IDM_EXIT equ 101
    9. .code
    10. WinMain proc
    11. local msg:MSG
    12. push rbp
    13. mov ebp,esp
    14. sub esp,sizeof MSG
    15.  
    16. xor ebx,ebx
    17. mov esi,IMAGE_BASE
    18. mov ecx,offset FileName
    19. call LoadCursorFromFile
    20.         mov note.hIcon,rax
    21. mov edi,offset ClassName
    22. push rax ;hIconSm
    23. push rdi ;lpszClassName
    24. push rbx ;lpszMenuName
    25. push COLOR_WINDOW;hbrBackground
    26. push 10005h ;hCursor
    27. push rax        ;hIcon
    28. push rsi ;hInstance
    29. push rbx        ;cbClsExtra & cbWndExtra
    30. db 68h
    31. dd WndProc      ;lpfnWndProc
    32. push sizeof WNDCLASSEX;cbSize & style
    33. mov ecx,esp ;addr WNDCLASSEX
    34.     call RegisterClassEx
    35. push rbx
    36. push rsi ;rsi=400000h
    37. shl esi,9 ;rsi=CW_USEDEFAULT
    38. push rbx
    39. push rbx
    40. push 200
    41. push 350
    42. push rsi
    43. push rsi
    44. mov r9d,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    45. mov r8,rdi ;offset ClassName
    46. mov edx,edi ;offset ClassName
    47. xor ecx,ecx
    48. sub esp,20h
    49.     call CreateWindowEx
    50.     lea edi,msg
    51. @@:     mov ecx,edi
    52. xor edx,edx
    53. xor r8d,r8d
    54. xor r9d,r9d
    55.         call GetMessage
    56. mov ecx,edi
    57.         call DispatchMessage
    58.         jmp @b
    59. WinMain endp
    60. WndProc proc hWnd:QWORD, uMsg:QWORD, wParam:QWORD, lParam:QWORD
    61. local pt:POINT
    62. push rbp
    63. mov ebp,esp
    64. sub esp,(40h+sizeof POINT+15)and(-16)
    65. mov hWnd,rcx
    66. mov wParam,r8
    67.  
    68. cmp edx,WM_DESTROY
    69. je wmDESTROY
    70.         cmp edx,WM_CREATE
    71. je wmCREATE
    72. cmp edx,WM_SHELLNOTIFY
    73. je wmSHELLNOTIFY
    74. cmp edx,WM_COMMAND
    75. je wmCOMMAND
    76.         cmp edx,WM_SYSCOMMAND
    77. je wmSYSCOMMAND
    78. defwndproc:leave
    79.         jmp DefWindowProc
    80. wmDESTROY:mov edx,offset note
    81. mov ecx,NIM_DELETE
    82. call Shell_NotifyIcon
    83. mov rcx,hPopupMenu
    84. call DestroyMenu
    85. xor ecx,ecx
    86.         call ExitProcess
    87. wmCOMMAND:or r9,r9;.if lParam==0 message is not from control
    88. jnz wmBYE
    89. cmp word ptr wParam,IDM_SHOWHIDE
    90. je showhide
    91.         cmp word ptr wParam,IDM_EXIT
    92.         jne wmBYE
    93. @@: call DestroyWindow
    94. jmp wmBYE
    95. wmSYSCOMMAND:cmp r8,SC_MINIMIZE; when user presses "minimize" button, main window
    96. jne defwndproc; should be hidden
    97. showhide:xor showflag,1
    98. mov edx,showflag
    99. lea edx,[rdx+rdx*4]
    100. call ShowWindow
    101. jmp wmBYE
    102. wmSHELLNOTIFY:or r8,r8;.if wParam==IDI_TRAY WM_SHELLNOTIFY handler - here we handle actions
    103. jne wmBYE; like clicking on our icon
    104. cmp r9,WM_LBUTTONDOWN
    105. je showhide
    106. cmp r9,WM_RBUTTONDOWN
    107. jne wmBYE
    108. wmSHELLNOTIFY_WM_RBUTTONDOWN:
    109. lea ecx,pt
    110. call GetCursorPos
    111. mov rcx,hWnd
    112.         call SetForegroundWindow
    113.         mov [rsp+30h],rbx;0
    114. mov rax,hWnd
    115.         mov [rsp+28h],rax
    116. mov [rsp+20h],rbx;0
    117. mov r9d,pt.y
    118. mov r8d,pt.x
    119. mov edx,TPM_RIGHTALIGN
    120. mov rcx,hPopupMenu
    121. call TrackPopupMenu
    122. xor r9d,r9d
    123. xor r8d,r8d
    124. mov edx,WM_NULL
    125. mov rcx,hWnd
    126.         call PostMessage
    127. jmp wmBYE
    128.  
    129. wmCREATE:mov note.hWnd,rcx;rcx=hWnd
    130. mov edx,offset note
    131. xor ecx,ecx;NIM_ADD=0
    132. call Shell_NotifyIcon
    133. call CreatePopupMenu
    134. mov hPopupMenu,rax
    135. mov r9d,offset szShowHide
    136. mov r8d,IDM_SHOWHIDE
    137. xor edx,edx;MF_STRING=0
    138. mov ecx,eax;hPopupMenu
    139. call AppendMenu
    140. mov r9d,offset szExit
    141. mov r8d,IDM_EXIT
    142. xor edx,edx;MF_STRING=0
    143. mov rcx,hPopupMenu
    144. call AppendMenu
    145. wmBYE: leave
    146. retn
    147. WndProc endp
    148. ;---------------------------------------
    149. szShowHide db "&Show/Hide",0
    150. szExit  db "E&xit",0
    151. FileName db "..\Images\Cursor.cur",0
    152. showflag  dd 1   ;if main window is visible showflag=1
    153. note label NOTIFYICONDATA
    154. dd sizeof NOTIFYICONDATA,0
    155. dq ?
    156. dd IDI_TRAY
    157. dd NIF_ICON+NIF_MESSAGE+NIF_TIP
    158. dd WM_SHELLNOTIFY,0
    159. dq ?
    160. ClassName db 'Win64 Iczelion''s lesson #23-2: TrayIcon Demo'
    161. db 64-(lengthof ClassName) dup(0)
    162. hPopupMenu dq ?
    163. end

    Вариант #3. Анимация в трее

    rc-файл
    Код (C):
    1. #define     icon1  1
    2. #define     icon2  2
    3. #define     icon3  3
    4. #define     icon4  4
    5. #define     icon5  5
    6. #define     icon6  6
    7. #define     icon7  7
    8. #define     icon8  8
    9. #define     icon9  9
    10. #define     icon10  10
    11. #define     icon11  11
    12.  
    13. icon1 ICON "..\\Images\\01.ico"
    14. icon2 ICON "..\\Images\\02.ico"
    15. icon3 ICON "..\\Images\\03.ico"
    16. icon4 ICON "..\\Images\\04.ico"
    17. icon5 ICON "..\\Images\\05.ico"
    18. icon6 ICON "..\\Images\\06.ico"
    19. icon7 ICON "..\\Images\\07.ico"
    20. icon8 ICON "..\\Images\\08.ico"
    21. icon9 ICON "..\\Images\\09.ico"
    22. icon10 ICON "..\\Images\\10.ico"
    23. icon11 ICON "..\\Images\\11.ico"
     
    Последнее редактирование: 4 янв 2017
  13. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709
    asm-файл
    Код (ASM):
    1. include win64a.inc
    2. include shell32.inc
    3. includelib shell32.lib
    4. IMAGE_BASE  equ 400000h
    5. WM_SHELLNOTIFY  equ WM_USER+5
    6. IDI_TRAY    equ 0
    7. IDM_SHOWHIDE    equ 100
    8. IDM_EXIT    equ 101
    9. .code
    10. WinMain proc
    11. local msg:MSG
    12.     push rbp
    13.     mov ebp,esp
    14.     sub esp,sizeof MSG
    15.  
    16.     xor ebx,ebx
    17.     mov esi,IMAGE_BASE
    18.     mov ecx,offset FileName
    19.     call LoadCursorFromFile
    20.         mov note.hIcon,rax
    21.     mov edi,offset ClassName
    22.     push rax    ;hIconSm
    23.     push rdi    ;lpszClassName
    24.     push rbx    ;lpszMenuName
    25.     push COLOR_WINDOW;hbrBackground
    26.     push 10005h ;hCursor
    27.     push rax        ;hIcon
    28.     push rsi    ;hInstance
    29.     push rbx        ;cbClsExtra & cbWndExtra
    30.     db 68h
    31.     dd WndProc      ;lpfnWndProc
    32.     push sizeof WNDCLASSEX;cbSize & style
    33.     mov ecx,esp ;addr WNDCLASSEX
    34.         call RegisterClassEx   
    35.     push rbx
    36.     push rsi    ;rsi=400000h
    37.     shl esi,9   ;rsi=CW_USEDEFAULT
    38.     push rbx
    39.     push rbx
    40.     push 200
    41.     push 408
    42.     push rsi
    43.     push rsi
    44.     mov r9d,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    45.     mov r8,rdi  ;offset ClassName
    46.     mov edx,edi ;offset ClassName
    47.     xor ecx,ecx
    48.     sub esp,20h
    49.         call CreateWindowEx
    50.         lea edi,msg
    51. @@:     mov ecx,edi
    52.     xor edx,edx
    53.     xor r8d,r8d
    54.     xor r9d,r9d
    55.         call GetMessage
    56.     mov ecx,edi
    57.         call DispatchMessage
    58.         jmp @b
    59. WinMain endp
    60. WndProc proc hWnd:QWORD, uMsg:QWORD, wParam:QWORD, lParam:QWORD
    61. local ps:PAINTSTRUCT
    62.     push rbp
    63.     mov ebp,esp
    64.     sub esp,(40h+sizeof PAINTSTRUCT+20h+15)and(-16)
    65.     mov hWnd,rcx
    66.  
    67.     cmp edx,WM_DESTROY
    68.     je wmDESTROY
    69.         cmp edx,WM_CREATE
    70.     je wmCREATE
    71.     cmp edx,WM_TIMER
    72.     je wmTIMER
    73.     cmp edx,WM_PAINT
    74.     je wmPAINT
    75. defwndproc:leave
    76.         jmp DefWindowProc
    77. wmDESTROY:xor edx,edx
    78.     call KillTimer
    79.     mov edx,offset note
    80.     mov ecx,NIM_DELETE
    81.     call Shell_NotifyIcon
    82.     xor ecx,ecx
    83.         call ExitProcess
    84. wmPAINT:lea edx,ps
    85.     call BeginPaint
    86.         mov r9,note.hIcon
    87.     mov r8d,100
    88.     mov edx,330
    89.     mov ecx,eax
    90.     call DrawIcon
    91.         lea edx,ps
    92.     mov rcx,hWnd
    93.     call EndPaint
    94.     jmp wmBYE
    95. wmTIMER:mov rdx,index
    96.     inc edx
    97.     cmp dl,11
    98.     jb @f
    99.     xor edx,edx
    100. @@: mov index,rdx
    101.     mov rdx,[hIcon2+rdx*8]
    102.     mov note.hIcon,rdx
    103.     mov edx,offset note
    104.     mov ecx,NIM_MODIFY
    105.     call Shell_NotifyIcon
    106.     mov r8d,TRUE
    107.         xor edx,edx
    108.     mov rcx,hWnd
    109.         call InvalidateRect;send WM_PAINT
    110.     jmp wmBYE
    111. wmCREATE:mov note.hWnd,rcx;rcx=hWnd
    112.     mov edx,offset note
    113.     xor ecx,ecx;NIM_ADD=0
    114.     call Shell_NotifyIcon
    115.     mov ebx,11
    116. @@: mov qword ptr [rsp+28h],0
    117.         mov r9d,16
    118.     mov [rsp+20h],r9
    119.         mov r8d,IMAGE_ICON
    120.         mov edx,ebx
    121.         mov ecx,IMAGE_BASE
    122.     call LoadImage
    123.     mov hIcon2[rbx*8-8],rax
    124.     dec ebx
    125.     jnz @b
    126.     xor r9d,r9d
    127.     mov r8d,500
    128.     xor edx,edx
    129.     mov rcx,hWnd
    130.     call SetTimer
    131. wmBYE:  leave
    132.     retn
    133. WndProc endp
    134. ;---------------------------------------
    135. FileName db "..\Images\Cursor.cur",0
    136. note label NOTIFYICONDATA
    137. dd sizeof NOTIFYICONDATA,0
    138. dq ?
    139. dd IDI_TRAY
    140. dd NIF_ICON+NIF_MESSAGE+NIF_TIP
    141. dd WM_SHELLNOTIFY,0
    142. dq ?
    143. ClassName db 'Win64 Iczelion''s lesson #23-2: TrayIcon Animation'
    144. db 64-(lengthof ClassName) dup(0)
    145. index dq 0
    146. hIcon2 dq 12 dup(?)
    147. end
     
  14. Mikl___

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

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

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

    Программа отобразит на экране обычное окно. По нажатию кнопки "Свернуть" оно свернётся до иконки в system tray. По двойному щелчку по иконке программа восстановит своё окно и удалит иконку из system tray. По правому щелчку будет выведено всплывающее меню, из которого можно восстановить программу или выйти из неё.
    Код (ASM):
    1. wmCREATE:           invoke CreatePopupMenu
    2.            mov hPopupMenu,rax
    3.            invoke AppendMenu,hPopupMenu,MF_STRING,IDM_RESTORE,addr RestoreString
    4.            invoke AppendMenu,hPopupMenu,MF_STRING,IDM_EXIT,addr ExitString
    Когда будет создано главное окно, также создастся всплывающее меню, к которому затем будут добавлены два пункта. Функция AppendMenu имеет следующий синтаксис:
    Код (C):
    1. BOOL WINAPI AppendMenu(
    2.   _In_ HMENU  hMenu,//дескриптор меню, к которому вы хотите добавить пункт
    3.   _In_ UINT  uFlags,/* информирует Windows о добавляемом пункте меню - изображение
    4.  ли это, строка или отрисовываемый владельцем объект; включен ли он, неопределён
    5.  или отключен, и так далее. В нашем случае мы используем флаг MF_STRING, который
    6.  означает, что пункт меню - это строка */
    7.   _In_ UINT_PTR uIDNewItem,/* ID пункта меню. Это значение определяется
    8. пользователем, и используется для обращения к пункту меню */
    9.   _In_opt_ LPCTSTR  lpNewItem /* хранит содержание пункта меню, в зависимости от
    10.  значения поля uFlags. Так как мы указали MF_STRING в поле uFlags, то lpNewItem
    11.  должен содержать указатель на строку для отображения в пункте меню */
    12. );
    После того, как всплывающее меню создано, главное окно будет терпеливо ждать до тех пор, пока пользователь не нажмет на кнопку "Свернуть".
    Когда окно сворачивается, оно получает сообщение WM_SIZE со значением SIZE_MINIMIZED в wParam.
    Код (ASM):
    1. wmSIZE:
    2.            .if wParam==SIZE_MINIMIZED
    3.                mov note.cbSize,sizeof NOTIFYICONDATA
    4.                push hWnd
    5.                pop note.hwnd
    6.                mov note.uID,IDI_TRAY
    7.                mov note.uFlags,NIF_ICON+NIF_MESSAGE+NIF_TIP
    8.                mov note.uCallbackMessage,WM_SHELLNOTIFY
    9.                invoke LoadIcon,NULL,IDI_WINLOGO
    10.                mov note.hIcon,eax
    11.                invoke lstrcpy,addr note.szTip,addr AppName
    12.                invoke ShowWindow,hWnd,SW_HIDE
    13.                invoke Shell_NotifyIcon,NIM_ADD,addr note
    14.            .endif
    Мы используем этот момент, чтобы заполнить структуру NOTIFYICONDATA. IDI_TRAY это просто константа, определённая в начале исходного кода. Ей можно задать любое значение. Это не очень важно, так как у нас только одна иконка в system tray. Но если вы захотите поместить туда сразу несколько иконок, то вам потребуется задать уникальный ID для каждой из них. Мы выставляем сразу все флаги в поле uFlags, так как мы указываем иконку (NIF_ICON), мы указываем пользовательское сообщение (NIF_MESSAGE), а также текст всплывающей подсказки (NIF_TIP). WM_SHELLNOTIFY это просто пользовательское сообщение, определённое как WM_USER+5. Само значение не так важно, пока оно сохраняет свою уникальность. Я использовал логотип Windows в качестве иконки для этой программы, но вы можете использовать и любую другую иконку :) Просто загрузите её из файла ресурсов вызовом LoadIcon и сохраните возвращаемое значение в поле hIcon. После всего этого поместим в поле szTip текст, который мы хотим видеть в качестве всплывающей подсказки к иконке.
    Мы скрываем главное окно, чтобы создать эффект "сворачивания в иконку". Затем мы вызываем Shell_NotifyIcon с сообщением NIM_ADD, чтобы добавить иконку в system tray.
    Теперь наше главное окно скрыто, а иконка успешно помещена в system tray. Если вы наведёте на неё курсор, то увидите подсказку с текстом, который вы поместили в поле szTip. Далее, если вы дважды щелкните по иконке, восстановится главное окно, а сама иконка исчезнет.
    Код (ASM):
    1. wmSHELLNOTIFY:
    2.            .if wParam==IDI_TRAY
    3.                .if lParam==WM_RBUTTONDOWN
    4.                    invoke GetCursorPos,addr pt
    5.                    invoke SetForegroundWindow,hWnd
    6.                    invoke TrackPopupMenu,hPopupMenu,TPM_RIGHTALIGN,pt.x,pt.y,NULL,hWnd,NULL
    7.                    invoke PostMessage,hWnd,WM_NULL,0,0
    8.                .elseif lParam==WM_LBUTTONDBLCLK
    9.                    invoke SendMessage,hWnd,WM_COMMAND,IDM_RESTORE,0
    10.                .endif
    11.            .endif
    Когда над иконкой происходит событие мыши, ваше окно получает сообщение WM_SHELLNOTIFY, то есть пользовательское сообщение, указанное в поле uCallbackMessage. Напомню, что по приёму этого сообщения wParam содержит ID иконки, а lParam содержит событие мыши. В вышеприведенном коде сначала проверяется, пришло ли сообщение от интересующей нас иконки. Если да, то тогда мы смотрим на событие мыши. Так как нам нужны только правый щелчок и левый двойной щелчок, то мы обрабатываем лишь сообщения WM_RBUTTONDOWN и WM_LBUTTONDBLCLK.
    Если сообщение от мыши это WM_RBUTTONDOWN, мы вызываем GetCursorPos, чтобы узнать текущие координаты курсора мыши. После возврата из функции, структура POINT содержит абсолютные координаты курсора. Под абсолютными координатами подразумеваются координаты, привязанные ко всему экрану, не берущие во внимание границы окна. Например, если разрешение экрана 640*480, то правый нижний угол это x==639, y==479. Если вы желаете перевести абсолютные координаты в оконные, используйте функцию ScreenToClient.
    Однако мы хотим отобразить всплывающее меню в точке, где сейчас расположен курсор мыши, с помощью функции TrackPopupMenu, которой требуются именно абсолютные координаты. Поэтому мы просто используем координаты, полученные от GetCursorPos.
    TrackPopupMenu имеет следующий синтаксис:
    Код (C):
    1. BOOL WINAPI TrackPopupMenu(
    2.   _In_ HMENU hMenu, // дескриптор всплывающего меню, которое нужно отобразить
    3.   _In_ UINT  uFlags, /* указывает опции отображения. Например, как располагать меню
    4.  относительно указанных ниже координат, и какая из кнопок мыши используется для
    5.  отслеживания меню. В нашем примере мы используем флаг TPM_RIGHTALIGN, чтобы
    6.  разместить меню слева от указанной точки */
    7.   _In_ int  x, // указывают местоположение меню в абсолютных координатах
    8.   _In_ int  y,
    9.   _In_ int  nReserved, // должно содержать NULL
    10.   _In_ HWND  hWnd, // дескриптор окна, которое будет получать сообщения от меню
    11.   _In_opt_ const RECT  *prcRect /* прямоугольная область экрана, щелчки в пределах
    12.  которой НЕ будут приводить к исчезновению меню. Обычно сюда помещается NULL,
    13.  чтобы меню исчезало при любом щелчке вне его */
    14. );
    Когда пользователь дважды щёлкнет по иконке, мы отправим нашему окну сообщение WM_COMMAND с указанием IDM_RESTORE, чтобы создать иллюзию выбора пользователем пункта "Восстановить" в меню, и таким образом восстановить окно, а также удалить иконку из system tray. Чтобы иметь возможность получать сообщения двойного щелчка, главное окно должно иметь стиль CS_DBLCLKS.
    Код (ASM):
    1. invoke Shell_NotifyIcon,NIM_DELETE,addr note
    2.                mov eax,wParam
    3.                .if ax==IDM_RESTORE
    4.                    invoke ShowWindow,hWnd,SW_RESTORE
    5.                .else
    6.                    invoke DestroyWindow,hWnd
    7.                .endif
    Когда пользователь выберет пункт "Восстановить" в меню, мы удаляем иконку повторным вызовом Shell_NotifyIcon, только на этот раз указывая NIM_DELETE в качестве сообщения. Затем мы возвращаем первозданный вид главному окну. Если пользователь выберет пункт "Закрыть", мы тоже удаляем иконку из system tray и уничтожаем главное окно вызовом DestroyWindow
     
    Последнее редактирование: 4 янв 2017
  15. Mikl___

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

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

    Глава сорок третья. Братец Кролик и Windows-хуки

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

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

    Хуки Windows можно считать одной из самых мощных техник. С их помощью вы можете перехватывать события, которые случатся внутри созданного вами или кем-то другим процесса. Перехватывая что-либо, вы сообщаете Windows о фильтрующей функции, также называющейся функцией перехвата, которая будет вызываться каждый раз, когда будет происходить интересующее вас событие. Есть два вида хуков: локальные и удаленные.
    • Локальные хуки перехватывают события, которые случаются в процессе, созданном вам.
    • Удаленные хуки перехватывают события, которые случаются в других процессах. Есть два вида удаленных хуков:
      • тредоспециализированные перехватывают события, которые случатся в определенном треде другого процесса. То есть, такой хук нужен вам, когда необходимо наблюдать за процессами, происходящими в определенном треде какого-то процесса.
      • системные перехватывают все события, предназначенные для всех тредов всех процессов в системе.
    При установке хуков, помните, что они оказывают отрицательное воздействие на быстродействие системы. Особенно в этом отличаются системные. Так как все требуемые события будут проходить через вашу функцию, ваша система может значительно потерять в быстродействии.
    Поэтому, если вы используете системный хук, вам следует использовать их только тогда, когда вам это действительно нужно. Также, существует высокая вероятность того, что другие процессы могут зависнуть, если что-нибудь неправильно в вашей функции. Помните: вместе с силой приходит ответственность.
    Вы должны понимать, как работают хуки, чтобы использовать их эффективно. Когда вы создаете хук, Windows создает в памяти структуры данных, которая содержит информацию о хуке, и добавляет ее в связанный список уже существующих хуков. Новый хук добавляется перед всеми старыми хуками. Когда случается событие, то если вы установили локальный хук, вызывается фильтрующая функция в вашем процессе, поэтому тут все просто. Hо если вы установили удаленный ху, система должна вставить код хук-процедуры в адресное пространство другого процесса. Система может сделать это только, если функция находится в DLL. Таким образом, если вы хотите использовать удаленный хук, ваша хук-процедура должна находиться в DLL. Из этого правила есть два исключения:
    журнально-записывающие и журнально-проигрывающие хуки. Хук-процедуры для этих типов хуков должны находиться в треде, который инсталлировал хуки. Причина этого кроется в том, что оба хука имеют дело с низкоуровневым перехватом хардварных входных событий. Эти события должны быть записаны/проиграны в том порядке, в котором они произошли. Если код такого хука находится в DLL, входные события могут быть "разбросаны" по нескольким тредам, что делает невозможным установления точной их последовательности. решение: процедуры таких хуков должна быть в одном треде, то есть в том треде, который устанавливает хуки.
    Существует 14 типов хуков:
    • WH_CALLWNDPROC ― хук вызывается при вызове SendMessage.
    • WH_CALLWNDPROCRET ― хук вызывается, когда возвращается SendMessage.
    • WH_GETMESSAGE ― хук вызывается, когда вызывается GetMessage или PeekMessage.
    • WH_KEYBOARD ― хук вызывается, когда GetMessage или PeekMessage получают WM_KEYUP или WM_KEYDOWN из очереди сообщений.
    • WH_MOUSE ― хук вызывается, когда GetMessage или PeekMessage получают сообщение от мыши из очереди сообщений.
    • WH_HADRWARE ― хук вызывается, когда GetMessage или PeekMessage получают хардварное сообщение, не относящееся к клавиатуре или мыши.
    • WH_MSGFILTER ― хук вызывается, когда диалоговое окно, меню или скролбар готовятся к обработке сообщения. Этот хук ― локальный. Он создан специально для тех объектов, у которых свой внутренний цикл сообщений.
    • WH_SYSMSGFILTER ― то же самое WH_MSGFILTER, но системный.
    • WH_JOURNALRECORD ― хук вызывается, когда Windows получает сообщение из очереди хардварных сообщений.
    • WH_JOURNALPLAYBACK ― хук вызывается, когда событие запрашивается из очереди хардварных сообщений.
    • WH_SHELL ― хук вызывается, когда происходит что-то интересное и связанное с оболочкой, например, когда таскбару нужно перерисовать кнопку.
    • WH_CBN ― хук используется специально для CBT.
    • WH_FOREGROUND ― такие хуки используются Windows. Обычным приложениям от них пользы немного.
    • WH_DEBUG ― хук используется для отладки хук-процедуры.
    Теперь, когда мы немного подучили теорию, мы можем перейти к тому, как, собственно, устанавливать/снимать хуки.
    Чтобы установить хук, вам нужно вызвать функцию SetWindowsHookEx, имеющую следующий синтаксис:
    Код (C):
    1. HHOOK WINAPI SetWindowsHookEx(
    2.   _In_ int  idHook, /* одно из значений, перечисленных выше
    3. (WH_MOUSE, WH_KEYBOARD и тому подобное) */
    4.   _In_ HOOKPROC  lpfn, /* адрес хук-процедуры, которая будет вызвана для обработки
    5.  сообщений от хука. Если хук является удаленным, он должен находиться в DLL. Если
    6.  нет, то он должен быть внутри процесса */
    7.   _In_ HINSTANCE hMod, /* дескриптор DLL, в которой находится хук-процедура.
    8. Если хук локальный, тогда это значения должно быть равно NULL  */
    9.   _In_ DWORD  dwThreadId /* ID треда, на который вы хотите поставить хук. Этот
    10. параметр определяет является ли хук локальным или удаленным. Если этот параметр
    11.  равен NULL, Windows будет считать хук системным и удаленным, который затрагивает
    12.  все треды в системе. Если вы укажете ID одного из тредов вашего собственного
    13. процесса, хук будет локальным. Если вы укажете ID треда из другого процесса, то хук
    14.  будет тредоспециализированным и удаленным. Из этого правила есть два
    15.  исключения: WH_JOURNALRECORD и WH_JOURNALPLAYBACK ― это всегда
    16.  локальные системные хуки, которым не нужно быть в DLL. Также WH_SYSMSGFILTER ― это всегда системный удаленный хук. Фактически он
    17.  идентичен хуку WH_MSGFILTER при ThreadID равным 0 */
    18. );
     

    Вложения:

    • 6.png
      6.png
      Размер файла:
      146,3 КБ
      Просмотров:
      1.940
    Последнее редактирование: 13 янв 2021
  16. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.709
    Если вызов успешен, он возвращает хэндл хука в eax. Если нет, возвращается NULL. Вы должны сохранить хэндл хука, чтобы снять его в дальнейшем.
    Вы можете деинсталлировать хук, вызвав UnhookWindowsHookEx, которая принимает только один параметр ― дескриптор хука, который нужно деинсталлировать. Если вызов успешен, он возвращает ненулевое значение в rax. Иначе он возвратит NULL.
    Хук-процедура будет вызываться каждый раз, когда будет происходить событие, ассоциированное с инсталлированным хуком. Например, если вы инсталлируете хук WH_MOUSE, когда происходит событие, связанное с мышью, ваша хук-процедура будет вызвана. Вне зависимости от типа установленного хука, хук-процедура всегда будет иметь один и тот же прототип:
    Код (ASM):
    1.        Hookproc proto nCode:DWORD, wparam:DWORD, lparam:DWORD
    • nCode задает код хука.
    • wParam и lParam содержат дополнительную информацию о событие.
    Вместо HookProc будет имя вашей хук-процедуры. Вы можете назвать ее как угодно, главное чтобы ее прототип совпадал с вышеприведенным. Интерпретация nCode, wParam и lParam зависит от типа установленного хука, так же, как и возвращаемое хук-процедурой значение. Например:
    • WH_CALLWNDPROC
      • nCode может иметь значение HC_ACTION ― это означает, что окну было послано сообщение.
      • wParam содержит посланное сообщение, если он не равен нулю, lрaram указывает на структуру CWPSTRUCT.
      • возвращаемое значение: не используется, возвращайте ноль.
      WH_MOUSE
      • nCode может быть равно HC_ACTION или HC_NOREMOVE.
      • wParam содержит сообщение от мыши.
      • lParam указывает на структуру MOUSEHOOKSTRUCT.
      • возвращаемое значение: ноль, если сообщение должно быть обработано. 1, если сообщение должно быть пропущено.
    Вы должны обратиться к вашему справочнику по Win32 API за подробным описанием значение параметров и возвращаемых значений хука, который вы хотите установить.
    Теперь еще один нюанс относительно хук-процедуры. Помните, что хуки соединены в связанный список, причем в его начале стоит хук, установленный последним. Когда происходит событие, Windows вызовет только первый хук в цепи. Вызов следующего в цепи хука остается на вашей ответственности. Вы можете и не вызывать его, но вам лучше знать, что вы делаете. Как правило, стоит вызвать следующую процедуру, чтобы другие хуки также могли обработать событие. Вы можете вызвать следующий хук с помощью функции CallNextHookEx:
    Код (C):
    1. LRESULT WINAPI CallNextHookEx(
    2.   _In_opt_ HHOOK  hhk, /* дескриптор вашего хука. Функция использует этот
    3.  хук для того, чтобы определить, какой хук надо вызвать следующим */
    4.   _In_ int  nCode,
    5.   _In_ WPARAM wParam,
    6.   _In_ LPARAM lParam
    7. );
    • nCode, wParam и lParam - передают соответствующие параметры, полученные от Windows.
    Важная деталь относительно удаленных хуков: хук-процедура должна находиться в DLL, которая будет промэппирована в другой процесс. Когда Windows мэппирует DLL в другой процесс, секция данных мэппироваться не будет. То есть, все процессы разделяют одну копию секции кода, но у них будет своя личная копия секции кода DLL! Это может стать большим сюрпризом для непредупрежденного человека. Вы можете подумать, что при сохранении значения в переменную в секции данных DLL, это значение получать все процессы, загрузившие DLL в свое адресное пространство. Hа самом деле, это не так. В обычной ситуации, такое поведение правильно, потому что это создает иллюзию, что у каждого процесса есть отдельная копия DLL. Hо не тогда, когда это касается хуков Windows. Нам нужно, чтобы DLL была идентична во всех процессах, включая данные. решение: вы должны пометить секцию данных как разделяемую. Это можно сделать, указав атрибуты секции линкеру. Если речь идет о MASM'е, это делается так:
    Код (Text):
    1.        /SECTION:, S
    Имя секции инициализированных данных '.data', а неинициализированных ― '.bss'. Например, если вы хотите скомпилировать DLL, которая содержит хук-процедуру, и вам нужно, что секция неинициализированных данных разделялась между процессами, вы должны использовать следующую команду:
    Код (Text):
    1.        link /section:.bss,S  /DLL  /SUBSYSTEM:WINDOWS
    атрибут 'S' отмечает, что секция разделяемая.

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

    Есть два модуля: один ― это основная программа с GUI'ем, а другая ― это DLL, которая устанавливает/снимает хук
     
    Последнее редактирование: 4 янв 2017
  17. Mikl___

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

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

    файл makeDLL64.bat

    Код (Text):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=tut_24a
    4. if exist %filename%.dll del %filename%.dll
    5. %masm64_path%bin\ml64.exe /c /Cp /I %masm64_path%include %filename%.asm || exit
    6. %masm64_path%bin\Link.exe %filename%.obj /LIBPATH:%masm64_path%lib ^
    7. /SUBSYSTEM:WINDOWS /ENTRY:DllEntry /DLL /section:.bss,S ^
    8. /stub:%masm64_path%bin\stubby.exe /DEF:%filename%.def || exit
    9. del %filename%.obj
    10. del %filename%.exp
    tut_24a.asm
    Код (ASM):
    1. include win64a.inc
    2. WM_MOUSEHOOK equ WM_USER+6
    3. MOUSEHOOKSTRUCT STRUCT
    4.   pt            POINT      <>
    5.   hwnd          QWORD      ?
    6.   wHitTestCode  QWORD      ?
    7.   dwExtraInfo   QWORD      ?
    8. MOUSEHOOKSTRUCT ENDS
    9.  
    10. .data?
    11. hInstance dq ?
    12. hHook dq ?
    13. hWnd dq ?
    14. .code
    15. DllEntry proc hInstDLL:QWORD, reason:QWORD, unused:QWORD
    16. mov hInstance,rcx
    17. mov eax,TRUE
    18.         ret
    19. DllEntry Endp
    20. MouseProc proc nCode:QWORD,wParam:QWORD,lParam:QWORD
    21. push rbp
    22. mov ebp,esp
    23. sub rsp,20h
    24. mov lParam,r8
    25.  
    26.         mov r9,r8;lParam
    27. mov r8,rdx;wParam
    28. mov edx,ecx;nCode
    29. mov rcx,hHook
    30. call CallNextHookEx
    31. mov rax,lParam
    32.         mov rcx,[rax]
    33. call WindowFromPoint
    34. mov rcx,hWnd
    35. mov edx,WM_MOUSEHOOK
    36. mov r8,rax
    37. mov r9d,0
    38. call PostMessage;,hWnd,WM_MOUSEHOOK,eax,0
    39. xor eax,eax
    40. leave
    41. ret
    42. MouseProc endp
    43.  
    44. InstallHook proc hwnd:QWORD
    45. push rbp
    46. mov ebp,esp
    47.         sub esp,20h
    48.  
    49. mov hWnd,rcx
    50. mov ecx,WH_MOUSE
    51. mov rdx,offset MouseProc
    52. mov r8,hInstance
    53. mov r9d,0
    54. call SetWindowsHookEx;,WH_MOUSE,addr MouseProc,hInstance,NULL
    55. mov hHook,rax
    56. leave
    57. ret
    58. InstallHook endp
    59.  
    60. UninstallHook proc
    61. push rbp
    62. mov ebp,esp
    63. sub esp,20h
    64. mov rcx,hHook
    65. call UnhookWindowsHookEx
    66. leave
    67. ret
    68. UninstallHook endp
    69. end

    tut_24a.def

    Код (Text):
    1. LIBRARY tut_24a
    2. EXPORTS
    3. MouseProc
    4. InstallHook
    5. UninstallHook
    asm2.bat
    Код (Text):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=tut_24b
    4. if exist %filename%.exe del %filename%.exe
    5. %masm64_path%bin\RC /r  %filename%.rc || exit
    6. %masm64_path%bin\ml64 /Cp /c /I"%masm64_path%Include" %filename%.asm || exit
    7. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:"%masm64_path%Lib" ^
    8. /entry:WinMain %filename%.obj %filename%.res /LARGEADDRESSAWARE:NO ^
    9. /ALIGN:16 /SECTION:.text,W ^
    10. /BASE:0x400000 /STUB:%masm64_path%bin\stubby.exe || exit
    11. del %filename%.res
    12. del %filename%.obj

    tut_24b.asm

    Код (ASM):
    1. include win64a.inc
    2. include mymacros.asm
    3. includelib tut_24a.lib
    4. IDD_MAINDLG equ 101
    5. IDC_CLASSNAME equ 1000
    6. IDC_HANDLE equ 1001
    7. IDC_WNDPROC equ 1002
    8. IDC_HOOK equ 1004
    9. IDC_EXIT equ 1005
    10. WM_MOUSEHOOK equ WM_USER+6
    11. IMAGE_BASE  equ 400000h
    12. InstallHook proto :QWORD
    13. UninstallHook proto
    14. MouseProc proto :QWORD,:QWORD,:QWORD
    15. DlgFunc proto :QWORD,:QWORD,:QWORD,:QWORD
    16.  
    17.  
    18. .data
    19. HookFlag dq FALSE
    20. HookText db "&Hook",0
    21. UnhookText db "&Unhook",0
    22. template db "%lx",0
    23. hHook dq ?
    24.  
    25. .code
    26. WinMain proc
    27. sub esp,38h
    28. xor ebx,ebx
    29. mov [rsp+20h],rbx
    30. mov r9d,offset DlgFunc
    31. mov r8,rbx
    32. mov edx,IDD_MAINDLG
    33. mov ecx,IMAGE_BASE
    34.         call DialogBoxParam;,hInstance,IDD_MAINDLG,NULL,addr DlgFunc,NULL
    35. xor ecx,ecx
    36.         call ExitProcess
    37. WinMain endp
    38.  
    39. DlgFunc proc hDlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD
    40.  
    41. LOCAL hLib:QWORD
    42. LOCAL buffer[128]:byte
    43. LOCAL buffer1[128]:byte
    44. LOCAL rect:RECT
    45.  
    46. push rbp
    47. mov ebp,esp
    48. sub esp,(38h+8+2*128+sizeof RECT+15)and(-16)
    49. mov hDlg,rcx
    50. mov wParam,r8
    51. mov lParam,r9
    52.  
    53. cmp edx,WM_INITDIALOG
    54. je wmINITDIALOG
    55.         cmp edx,WM_MOUSEHOOK
    56. je wmMOUSEHOOK
    57.         cmp edx,WM_COMMAND
    58. je wmCOMMAND
    59.         cmp edx,WM_COMMAND
    60. je wmCOMMAND
    61. cmp edx,WM_CLOSE
    62. je wmCLOSE
    63.         xor eax,eax;FALSE
    64. leave
    65.         retn
    66. wmCLOSE:cmp HookFlag,TRUE
    67. jnz @f
    68.         call UninstallHook
    69. @@: xor edx,edx
    70.         mov rcx,hDlg
    71.         call EndDialog
    72. jmp wmBYE
    73. wmINITDIALOG:lea edi,rect
    74. mov edx,edi
    75. mov rcx,hDlg
    76.         call GetWindowRect
    77. mov qword ptr [rsp+30h],SWP_SHOWWINDOW
    78. mov eax,[rdi+RECT.bottom]
    79. mov [rsp+28h],rax
    80. mov eax,[rdi+RECT.right]
    81. mov [rsp+20h],rax
    82.         mov r9d,[rdi+RECT.top]
    83. mov r8d,[rdi+RECT.left]
    84. or edx,HWND_TOPMOST
    85. mov rcx,hDlg
    86.         call SetWindowPos
    87. jmp wmBYE
    88. wmMOUSEHOOK:mov r9d,128
    89. lea r8,buffer1
    90. mov edx,IDC_HANDLE
    91. ;mov rcx,hDlg
    92.         call GetDlgItemText
    93.         mov r8,wParam
    94. mov edx,offset template
    95. lea ecx,buffer
    96.         call wsprintf
    97. lea edx,buffer1
    98. lea ecx,buffer
    99.         call lstrcmpi
    100.         or eax,eax;.if eax!=0
    101. jz @f
    102. lea r8,buffer
    103. mov edx,IDC_HANDLE
    104. mov rcx,hDlg
    105.         call SetDlgItemText
    106.  
    107. @@:     mov r9d,128
    108. lea r8,buffer1
    109. mov edx,IDC_CLASSNAME
    110. mov rcx,hDlg
    111.         call GetDlgItemText
    112. mov r8d,128
    113. lea edx,buffer
    114. mov rcx,wParam
    115. call GetClassName
    116.         lea edx,buffer1
    117. lea ecx,buffer
    118.         call lstrcmpi
    119. or eax,eax;.if eax!=0
    120. jz @f
    121. lea r8,buffer
    122. mov edx,IDC_CLASSNAME
    123. mov rcx,hDlg
    124.         call SetDlgItemText
    125. @@:     mov r9d,128
    126. lea r8,buffer1
    127. mov edx,IDC_WNDPROC
    128. mov rcx,hDlg
    129.         call GetDlgItemText
    130. mov edx,GCL_WNDPROC
    131. mov rcx,wParam
    132.         call GetClassLong
    133. mov r8,rax
    134. mov edx,offset template
    135. lea ecx,buffer
    136.         call wsprintf
    137. lea edx,buffer1
    138. lea ecx,buffer
    139.         call lstrcmpi
    140.         or eax,eax;.if eax!=0
    141. jz wmBYE
    142. lea r8,buffer
    143. mov edx,IDC_WNDPROC
    144. mov rcx,hDlg
    145.         call SetDlgItemText
    146. jmp wmBYE
    147. wmCOMMAND:cmp r9,rbx;cmp lParam,rbx;.if lParam!=0
    148. jz wmBYE          
    149.         mov rax,r8;wParam
    150.         mov edx,eax
    151.         shr edx,16
    152.         or edx,edx;.if dx==BN_CLICKED
    153.         jnz wmBYE
    154. and eax,0FFFFh
    155. cmp eax,IDC_EXIT
    156. jnz @f
    157.         mov r9,rbx
    158.         mov r8,rbx
    159. mov edx,WM_CLOSE
    160. mov rcx,hDlg
    161.         call SendMessage
    162. jmp wmBYE
    163. @@:     cmp HookFlag,rbx;FALSE
    164.         jnz @f
    165. mov rcx,hDlg
    166.         call InstallHook
    167.         or eax,eax;     .if eax!=NULL
    168.         jz wmBYE
    169.         mov HookFlag,TRUE
    170. mov r8d,offset UnhookText
    171. mov edx,IDC_HOOK
    172. mov rcx,hDlg
    173.         call SetDlgItemText
    174.         jmp wmBYE
    175. @@:     call UninstallHook
    176.         mov r8d,offset HookText
    177. mov edx,IDC_HOOK
    178. mov rcx,hDlg
    179.         call SetDlgItemText
    180.         mov HookFlag,rbx;FALSE
    181. mov r8,rbx
    182. mov edx,IDC_CLASSNAME
    183. mov rcx,hDlg
    184.         call SetDlgItemText
    185. mov r8,rbx
    186. mov edx,IDC_HANDLE
    187. mov rcx,hDlg
    188.         call SetDlgItemText
    189.         mov r8,rbx
    190. mov edx,IDC_WNDPROC
    191. mov rcx,hDlg
    192.         call SetDlgItemText
    193. wmBYE: leave
    194.        mov eax,TRUE
    195.        retn
    196. DlgFunc endp
    197. end

    tut_24b.rc

    Код (C):
    1. #define IDD_MAINDLG                     101
    2. #define IDC_CLASSNAME                   1000
    3. #define IDC_HANDLE                      1001
    4. #define IDC_WNDPROC                     1002
    5. #define IDC_HOOK                        1004
    6. #define IDC_EXIT                        1005
    7. #define IDC_STATIC                      -1
    8. #define DS_MODALFRAME                   0x80
    9. #define WS_POPUP                        0x80000000
    10. #define WS_CAPTION                      0xC00000
    11. #define WS_SYSMENU                      0x80000
    12. #define ES_AUTOHSCROLL                  0x80
    13. #define ES_READONLY                     0x800
    14.  
    15. IDD_MAINDLG DIALOG DISCARDABLE  0, 0, 229, 85
    16. STYLE DS_MODALFRAME |  WS_POPUP | WS_CAPTION | WS_SYSMENU
    17. CAPTION "Iczelion Tutorial #24: Mouse Hook Demo"
    18. FONT 8, "MS Sans Serif"
    19. BEGIN
    20.     GROUPBOX        "Window Information",IDC_STATIC,7,7,214,67
    21.     LTEXT           "Class name:",IDC_STATIC,21,22,39,8
    22.     EDITTEXT        IDC_CLASSNAME,69,20,139,12,ES_AUTOHSCROLL | ES_READONLY
    23.     LTEXT           "Handle:",IDC_STATIC,33,37,26,8
    24.     EDITTEXT        IDC_HANDLE,69,36,77,12,ES_AUTOHSCROLL | ES_READONLY
    25.     LTEXT           "Window Proc:",IDC_STATIC,13,52,46,8
    26.     EDITTEXT        IDC_WNDPROC,69,51,77,12,ES_AUTOHSCROLL | ES_READONLY
    27.     DEFPUSHBUTTON   "&Hook",IDC_HOOK,159,35,50,14
    28.     PUSHBUTTON      "E&xit",IDC_EXIT,159,50,50,14
    29. END
     
  18. Mikl___

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

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

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

    Пример отобразит диалоговое окно с тремя окнами ввода, которые будут заполнены именем класса, дескриптором окна и адресом процедуры окна, ассоциированное с окном под курсором мыши. Есть две кнопки - Hook и Exit. Когда вы нажимаете кнопку Hook, программа перехватывает сообщения от мыши и текст на кнопке меняется на Unhook. Когда вы двигаете курсор мыши над каким-либо окном, информация о нем отобразится в окне программы. Когда вы нажмете кнопку Unhook, программа уберет установленный hook.
    Основная программа использует диалоговое окно в качестве основного. Она определяет специальное сообщение - WM_MOUSEHOOK, которая будет использоваться между основной программой и DLL с хуком. Когда основная программа получает это сообщение, wParam содержит дескриптор окна, над которым находится курсор мыши. Конечно, это было сделано произвольно. Я решил слать дескриптор в wParam, чтобы было проще. Вы можете выбрать другой метод взаимодействия между основной программой и DLL с хуком.
    Код (ASM):
    1.                        .if HookFlag==FALSE
    2.                            invoke InstallHook,hDlg
    3.                            .if eax!=NULL
    4.                                mov HookFlag,TRUE
    5.                                invoke SetDlgItemText,hDlg,IDC_HOOK,addr UnhookText
    6.                            .endif
    Программа пользуется флагом, HookFlag, чтобы отслеживать состояние хука.
    Он равен FALSE, если хук не установлен, и TRUE, если установлен.
    Когда пользователь нажмет кнопку hook, программа проверяет, установлен ли уже хук. Если это так, она вызывает функцию InstallHook из DLL. Заметьте, что мы передаем дескриптор основного диалогового окна в качестве параметра функции, чтобы хук-DLL могла посылать сообщения WM_MOUSEHOOK верному окну, то есть нашему.
    Когда программа загружена, DLL с хуком также загружается. Фактически, DLL загружаются сразу после того, как программа оказывается в памяти. Входная функция DLL вызывается прежде, чем будет исполнена первая инструкция основной программы. Поэтому, когда основная программа запускается DLL и инициализируются. Помещаем следующий код во входную функцию хук-DLL:
    Код (ASM):
    1.        .if reason==DLL_PROCESS_ATTACH
    2.            push hInst
    3.  
    4.            pop hInstance
    5.        .endif
    Данный код всего лишь сохраняет дескриптор процесса DLL в глобальную переменную, названную hInstance для использования внутри функции InstallHook. Так как входная функция вызывается прежде, чем будут вызваны другие функции в DLL, hInstance будет всегда верен. Помещаем hInstance в секцию .data, поэтому это значение будет различаться от процесса к процессу. Когда курсор мыши проходит над окном, хук-DLL проецируется в память процесса. Представьте, что уже есть DLL, которая занимает предполагаемый загрузочный адрес хук-DLL. Значение hInstance будет обновлено. Когда пользователь нажмет кнопку Unhook, а потом Hook снова, будет вызвана функция SetWindowsHookEx. Тем не менее, в этот pаз, она будет использовать новое значение hInstance, которое будет неверным, потому что в данном процессе загрузочный адрес DLL не измениться. Хук будет локальным, что нам не нужно.
    Код (ASM):
    1.    InstallHook proc hwnd:DWORD
    2.        push hwnd
    3.        pop hWnd
    4.        invoke SetWindowsHookEx,WH_MOUSE,addr Mouseproc,hInstance,NULL
    5.        mov hHook,eax
    6.        ret
    7.    InstallHook endp
    Функция InstallHook сама по себе очень проста. Она сохраняет хэндл окна, переданный ей в качестве параметра, в глобальную переменную hWnd. Затем она вызывает SetWindowsHookEx, чтобы установить хук на мышь. Возвращенное значение сохраняется в глобальную переменную hHook, чтобы в будущем передать ее UnhookWindowsHookEx.
    После того, как вызван SetWindowsHookEx, хук начинает работать. Всякий pаз, когда в системе случается мышиное событие, вызывается MouseProc (ваша хук-процедура).
    Код (ASM):
    1.    Mouseproc proc nCode:DWORD,wparam:DWORD,lparam:DWORD
    2.        invoke CallNextHookEx,hHook,nCode,wparam,lparam
    3.        mov edx,lparam
    4.        assume edx:pTR MOUSEHOOKSTRUCT
    5.        invoke WindowFrompoint,[edx].pt.x,[edx].pt.y
    6.        invoke postMessage,hWnd,WM_MOUSEHOOK,eax,0
    7.        assume edx:nothing
    8.        xor eax,eax
    9.        ret
    10.    Mouseproc endp
    Сначала вызывается CallNextHookEx, чтобы другие хуки также могли обработать событие мыши. После этого, она вызывает функцию WindowFromPoint, чтобы получить дескриптор окна, находящегося в указанной координате экрана. Заметьте, что мы используем структуру POINT, являющуюся членом структуры MOUSEHOOKSTRUCT, на которую указывает lParam, то есть координату текущего местонахождения курсора. После этого, мы посылаем дескриптор окна основной программы через сообщение WM_MOUSEHOOK. Вы должны помнить: вам не следует использовать SendMessage в хук-процедуре, так как это может вызвать зависание программы, поэтому рекомендуется использовать PostMessage. Структура MOUSEHOOKSTRUCT определена ниже:
    Код (ASM):
    1. MOUSEHOOKSTRUCT STRUCT DWORD
    2.      pt            pOINT <> //текущая координата курсора мыши
    3.      hwnd          DWORD      ? /* дескриптор окна, которое получает сообщения от мыши.
    4.  Это обычно окно под курсором мыши, но не всегда. Если окно вызывает
    5. SetCapture, сообщения от мыши будут перенаправлены этому окну. По этой причине
    6.  не используют параметр hwnd этой структуры, а вызывают WindowFromPoint*/
    7.      wHitTestCode  DWORD      ? /* дополнительная информация о том, где находится
    8.  курсор мыши */
    9.      dwExtraInfo   DWORD      ? /* дополнительная информация, ассоциированная с
    10.  сообщением. Обычно это значение устанавливается с помощью вызова mouse_event
    11.  и получаем его функцией GetMessageExtraInfo */
    12.    MOUSEHOOKSTRUCT ENDS
    Когда основное окно получает сообщение WM_MOUSEHOOK, оно использует дескриптор окна в wParam, чтобы получить информацию об окне.
    Код (ASM):
    1. wmMOUSEHOOK:
    2.  
    3.            invoke GetDlgItemText,hDlg,IDC_HANDLE,addr buffer1,128
    4.            invoke wsprintf,addr buffer,addr template,wparam
    5.            invoke lstrcmpi,addr buffer,addr buffer1
    6.            .if eax!=0
    7.  
    8.                invoke SetDlgItemText,hDlg,IDC_HANDLE,addr buffer
    9.            .endif
    10.            invoke GetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer1,128
    11.            invoke GetClassName,wparam,addr buffer,128
    12.  
    13.            invoke lstrcmpi,addr buffer,addr buffer1
    14.            .if eax!=0
    15.                invoke SetDlgItemText,hDlg,IDC_CLASSNAME,addr buffer
    16.            .endif
    17.  
    18.            invoke GetDlgItemText,hDlg,IDC_WNDpROC,addr buffer1,128
    19.            invoke GetClassLong,wparam,GCL_WNDpROC
    20.            invoke wsprintf,addr buffer,addr template,eax
    21.            invoke lstrcmpi,addr buffer,addr buffer1
    22.  
    23.            .if eax!=0
    24.                invoke SetDlgItemText,hDlg,IDC_WNDpROC,addr buffer
    25.            .endif
    Чтобы избежать мерцания, мы проверяем, не идентичны ли текст в окнах ввода с текстом, который мы собираемся ввести. Если это так, то мы пропускаем этот этап.
    Мы получаем имя класса с помощью вызова GetClassName, адрес процедуры с помощью вызова GetClassLong со значением GCL_WNDPROC, а затем форматируем их в строки и помещаем в соответствующие окна ввода.
    Код (ASM):
    1.                            invoke UninstallHook
    2.                            invoke SetDlgItemText,hDlg,IDC_HOOK,addr HookText
    3.                            mov HookFlag,FALSE
    4.  
    5.                            invoke SetDlgItemText,hDlg,IDC_CLASSNAME,NULL
    6.                            invoke SetDlgItemText,hDlg,IDC_HANDLE,NULL
    7.                            invoke SetDlgItemText,hDlg,IDC_WNDpROC,NULL
    Когда пользователь нажмет кнопку Unhook, программа вызовет функцию UninstallHook в хук-DLL. UninstallHook всего лишь вызывает UnhookWindowsHookEx. После этого, она меняет текст кнопки обратно на "Hook", HookFlag на FALSE и очищает содержимое окон ввода.
    Обратите внимание на опции линкера в makefile.
    Код (Text):
    1.            Link /SECTION:.bss,S /DLL /DEF:$(NAME).def /SUBSYSTEM:WINDOWS
    Секции .bss помечается как разделяемая, чтобы все процессы разделяли секцию неинициализируемых данных хук-DLL. Без этой опции, ваша DLL функционировала бы неправильно.
     
    Последнее редактирование: 4 янв 2017
  19. Mikl___

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

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

    Глава сорок четвертая. Братец Кролик и сплэш-экран

    [​IMG]
    Теперь, когда мы знаем, как использовать рисунок, мы можем применить его более творчески.
    Скачайте пример здесь.

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

    Сплэш-экран ― это окно, у которого нет заголовка, нет системных кнопок, нет рамки, которое отображает рисунок на некоторое время и затем исчезает. Обычно сплэш-экран используется во время загрузки программы, чтобы отображать логотип программы или отвлечь внимание пользователя, пока программа делает длительную инициализацию. В этой главе мы создадим сплэш-экран.
    Первый шаг ― это прописать рисунок в файле ресурсов. Тем не менее, если это важно для вас, то загружать рисунок , который будет использоваться только один раз, и держать его в памяти, пока программа не будет закрыта, пустая трата ресурсов. Лучшим решением является ресурсовую DLL, которая будет содержать рисунок, и чьей целью является отображение сплэш-экрана. В этом случае вы сможете загрузить DLL, когда вам нужно отобразить сплэш-экран, и выгрузить ее, как только нужда в ней отпадает. Поэтому у нас будет два модуля: основная программа и сплэш-экран. Мы поместим рисунок в файл ресурсов DLL.
    Общая схема такова:
    1. Поместить рисунок в DLL как ресурс.
    2. Основная программа вызывает LoadLibrary, чтобы загрузить dll в память.
    3. Запускается входная функция DLL. Она создаст таймеp и установит время, в течении которого будет отображаться сплэш-экран. Затем она зарегистрирует и создаст окно без заголовка и бордера, после чего отобразит битмап в клиенсткой области.
    4. Когда закончится указанный период времени, сплэш-экран будет убран с экрана и контроль будет передан главной программе.
    5. Основная программа вызовет FreeLibrary, чтобы выгрузить DLL из памяти, а затем перейдет к выполнению того, к чему она предназначена.
    Мы детально проанализируем описанную последовательность действий.

    Загрузка/выгрузка DLL

    Вы можете динамически загрузить DLL с помощью функции LoadLibrary, которая имеет следующий синтаксис:
    Код (C):
    1. HMODULE WINAPI LoadLibrary(
    2.   _In_ LPCTSTR lpFileName
    3. );
    Она принимает только один параметр: адрес имени DLL, который вы хотите загрузить в память. Если вызов пройдет успешно, он возвратит хэндл модуля DLL, в противном случае NULL.
    Чтобы выгрузить DLL, вызовите FreeLibrary:
    Код (C):
    1. BOOL WINAPI FreeLibrary(
    2.   _In_ HMODULE hModule
    3. );
    Она получает один параметр: дескриптор модуля DLL, которую вы хотите выгрузить.

    Как использовать таймеp

    Во-первых, мы должны создать таймер с помощью функции SetTimer:
    Код (C):
    1. UINT_PTR WINAPI SetTimer(
    2.   _In_opt_ HWND  hWnd, /* дескриптор окна, которое будет получать уведомительные
    3.  сообщения от таймера. Этот параметр может быть равным NULL, если никакое окно
    4.  не ассоциируется с таймером */
    5.   _In_ UINT_PTR  nIDEvent, /* заданное пользователем значение, которое будет
    6.  использоваться в качестве ID таймера */
    7.   _In_ UINT  uElapse, // временной интервал в миллисекундах
    8.   _In_opt_ TIMERPROC lpTimerFunc /* адрес функции, которая будет обрабатывать
    9.  уведомительные сообщения от таймера. Если вы передает NULL, сообщения от
    10.  таймера будут посылаться окну, указанному в параметре hWnd */
    11. );
    SetTimer возвращает ID таймера, если вызов прошел успешно, иначе она возвратит NULL. Поэтому лучше не использовать ноль в качестве ID таймера.
    Вы можете создать таймеp двумя путями:
    1. Если у вас есть окно и вы хотите, чтобы сообщения от таймера посылались окну, вы должны передать все четыре параметра SetTimer (lpTimerFunc должен быть равен NULL).
    2. Если у вас нет окна или вы не хотите обрабатывать сообщения таймера в процедуре окна, вы должны передать NULL функции вместо дескриптора окна. Вы также должны указать адрес функции таймера, которая будет обрабатывать его сообщения.
    В этой главе мы используем первый подход.
    Каждый раз за указанный вами временной интервал окну, ассоциированному с таймером, будет посылаться сообщение WM_TIMER. Например, если вы укажете 1000: ваше окно будет получать WM_TIMER каждую секунду.
    Когда вам больше не нужен таймеp, уничтожьте его с помощью KillTimer:
    Код (C):
    1.   BOOL WINAPI KillTimer(
    2.   _In_opt_ HWND  hWnd,
    3.   _In_ UINT_PTR uIDEvent
    4. );
     

    Вложения:

    • 1.png
      1.png
      Размер файла:
      146,4 КБ
      Просмотров:
      2.908
    • 7.png
      7.png
      Размер файла:
      188 КБ
      Просмотров:
      1.983
    Последнее редактирование: 13 янв 2021
  20. Mikl___

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

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

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

    makedll64.bat

    Код (Text):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=tut_26a
    4. if exist %filename%.dll del %filename%.dll
    5. %masm64_path%bin\RC /r  %filename%.rc || exit
    6. %masm64_path%bin\ml64.exe /c /Cp /I %masm64_path%include %filename%.asm || exit
    7. %masm64_path%bin\Link.exe %filename%.obj %filename%.res /LIBPATH:%masm64_path%lib ^
    8. /SUBSYSTEM:WINDOWS /ENTRY:DllEntry /DLL /ALIGN:16 /section:.bss,S ^
    9. /stub:%masm64_path%bin\stubby.exe /DEF:%filename%.def || exit
    10. del %filename%.obj
    11. del %filename%.exp
    12. del %filename%.res

    tut_26a.asm

    Код (ASM):
    1. OPTION DOTNAME
    2. option casemap:none
    3. include temphls.inc
    4. include win64.inc
    5. include kernel32.inc
    6. includelib kernel32.lib
    7. include user32.inc
    8. includelib user32.lib
    9. include gdi32.inc
    10. includelib gdi32.lib
    11. OPTION PROLOGUE:none
    12. OPTION EPILOGUE:none
    13. IMAGE_BASE      equ 400000h
    14. .data
    15. BitmapName  db "MySplashBMP",0
    16. ClassName   db "SplashWndClass",0
    17. hBitMap     dq ?
    18. TimerID     dq ?
    19. hInstance   dq ?
    20.  
    21. .code
    22.  
    23. DllEntry proc hInst:QWORD, reason:QWORD, reserved1:QWORD
    24.     sub esp,28h
    25.     cmp edx,DLL_PROCESS_ATTACH  ; When the dll is loaded
    26.     jnz @f
    27.         mov hInstance,rcx
    28.         call ShowBitMap      
    29. @@:     add esp,28h
    30.     mov eax,TRUE
    31.     retn
    32. DllEntry Endp
    33. ShowBitMap proc
    34.  
    35. LOCAL msg:MSG
    36.  
    37.     push rbp
    38.     mov ebp,esp
    39.     sub esp,sizeof MSG
    40.  
    41.     xor ebx,ebx
    42.     push 10029h ;hIconSm
    43.     lea rdi,ClassName
    44.         push rdi    ;lpszClassName
    45.     push rbx    ;lpszMenuName
    46.         push COLOR_WINDOW+1;hbrBackground
    47.     push 10005h ;hCursor
    48.     push 10029h        ;hIcon
    49.     push hInstance
    50.         push rbx        ;cbClsExtra & cbWndExtra
    51.         lea rax,WndProc
    52.     push rax    ;lpfnWndProc
    53.         push sizeof WNDCLASSEX;cbSize & style
    54.     mov rcx,rsp ;addr WNDCLASSEX
    55.     sub esp,20h
    56.         call RegisterClassEx
    57.     push rbx
    58.     push hInstance
    59.     push rbx
    60.     push rbx
    61.     push 250
    62.     push 250
    63.     push CW_USEDEFAULT
    64.     push CW_USEDEFAULT
    65.     mov r9d,WS_POPUP or WS_VISIBLE
    66.     mov r8,rbx
    67.     mov edx,edi
    68.     xor ecx,ecx
    69.     sub esp,20h
    70.         call CreateWindowEx
    71.         lea edi,msg
    72. @@:     mov ecx,edi
    73.     xor edx,edx
    74.     mov r8,rbx
    75.     mov r9,rbx
    76.         call GetMessage
    77.     or eax,eax
    78.     jz @f
    79.     mov ecx,edi
    80.         call DispatchMessage
    81.         jmp @b
    82. @@: leave
    83.     retn
    84. ShowBitMap endp
    85.  
    86. WndProc proc hWnd:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD
    87.  
    88. LOCAL ps:PAINTSTRUCT
    89. LOCAL hMemoryDC:HDC
    90. LOCAL hOldBmp:QWORD
    91. LOCAL bitmap:BITMAP
    92. LOCAL DlgHeight:QWORD
    93. LOCAL DlgWidth:QWORD
    94. LOCAL DlgRect:RECT
    95. LOCAL DesktopRect:RECT
    96.  
    97.     push rbp
    98.     mov ebp,esp
    99.     sub esp,(20h+sizeof PAINTSTRUCT+sizeof BITMAP+2*(sizeof RECT)+4*32+15)and(-16)        
    100.  
    101.     mov hWnd,rcx
    102.     mov wParam,r8
    103.     mov lParam,r9
    104.  
    105.     cmp edx,WM_DESTROY
    106.     je wmDESTROY
    107.     cmp edx,WM_CREATE
    108.     je wmCREATE
    109.     cmp edx,WM_TIMER
    110.     je wmTIMER
    111.     cmp edx,WM_PAINT
    112.     je wmPAINT
    113.     cmp edx,WM_LBUTTONDOWN
    114.     je wmLBUTTONDOWN
    115.     leave
    116.     jmp DefWindowProc
    117.  
    118. wmDESTROY:mov rcx,hBitMap
    119.     jrcxz @f;.if hBitMap!=0
    120.         call DeleteObject
    121. @@:     xor ecx,ecx
    122.         call PostQuitMessage
    123.         jmp wmBYE
    124. wmCREATE:lea rdx,DlgRect
    125.         call GetWindowRect
    126.         call GetDesktopWindow
    127.         mov ecx,eax
    128.     lea rdx,DesktopRect
    129.         call GetWindowRect
    130.         push rbx                   ;Part of the later call to MoveWindow (no repaint)
    131.         mov eax,DlgRect.bottom              ;Get the bottom of our dialogs window
    132.         sub eax,DlgRect.top                 ;subtract the y value at the top of our window
    133.         mov DlgHeight,rax                   ;And store it as the dialog's height
    134.         push rax                   ;Push it for the call to MoveWindow
    135.         mov r9d,DlgRect.right               ;The X coordinate of the right side of our dialog
    136.         sub r9d,DlgRect.left                ;minus that of the left side
    137.         mov DlgWidth,r9                     ;gives us the width
    138.         mov r8d,DesktopRect.bottom          ;Get the bottom of the desktop window
    139.         sub r8,DlgHeight                   ;Subtract the height of our dialog
    140.         shr r8,1                            ;and divide by 2...this gives the middle of the screen
    141.         mov edx,DesktopRect.right           ;Get the right side of the desktop
    142.         sub rdx,DlgWidth                    ;Minus the width of our dialog
    143.         shr edx,1                           ;Divide by 2
    144.         mov rcx,hWnd                        ;Push the window handle
    145.     sub esp,20h
    146.         call MoveWindow                     ;Move the window                
    147.     lea edx,BitmapName
    148.     mov rcx,hInstance
    149.         call LoadBitmap
    150.         mov hBitMap,rax
    151.     mov r9,rbx
    152.     mov r8d,2000
    153.     mov edx,1
    154.     mov rcx,hWnd
    155.         call SetTimer
    156.         mov TimerID,rax
    157.     jmp wmBYE
    158. wmTIMER:mov r9,rbx
    159.         mov r8,rbx
    160.     mov edx,WM_LBUTTONDOWN
    161.     mov rcx,hWnd
    162.         call SendMessage
    163.     mov rdx,TimerID
    164.     mov rcx,hWnd
    165.         call KillTimer
    166.     jmp wmBYE
    167. wmPAINT:lea edx,ps
    168.         call BeginPaint
    169.         mov ecx,eax
    170.         call CreateCompatibleDC
    171.         mov hMemoryDC,rax
    172.     mov rdx,hBitMap
    173.     mov ecx,eax
    174.         call SelectObject
    175.         mov hOldBmp,rax
    176.     lea r8,bitmap
    177.     mov edx,sizeof BITMAP
    178.     mov rcx,hBitMap
    179.         call GetObject
    180.         push SRCCOPY
    181.     mov eax,bitmap.bmHeight
    182.         push rax
    183.     mov eax,bitmap.bmWidth
    184.         push rax
    185.         push rbx
    186.         push rbx
    187.     push hMemoryDC
    188.     push 250
    189.     mov r9d,250
    190.     mov r8,rbx
    191.     xor edx,edx
    192.     mov rcx,ps.hdc
    193.     sub esp,20h
    194.         call StretchBlt
    195.         mov rdx,hOldBmp
    196.     mov rcx,hMemoryDC
    197.         call SelectObject
    198.     mov rcx,hMemoryDC
    199.         call DeleteDC
    200.     lea edx,ps
    201.     mov rcx,hWnd
    202.         call EndPaint
    203.     jmp wmBYE
    204. wmLBUTTONDOWN:call DestroyWindow
    205. wmBYE:  leave
    206.         retn
    207. WndProc endp
    208. end

    tut_26a.def

    Код (Text):
    1. LIBRARY tut_26a.dll

    tut_26a.rc

    Код (C):
    1. MySplashBMP  BITMAP "Images\\JourneyStart.bmp"

    tut_26b.asm

    Код (ASM):
    1. OPTION DOTNAME
    2. include temphls.inc
    3. include win64.inc
    4. include kernel32.inc
    5. includelib kernel32.lib
    6. include user32.inc
    7. includelib user32.lib
    8. OPTION PROLOGUE:none
    9. OPTION EPILOGUE:none
    10. IMAGE_BASE      equ 400000h
    11. .code
    12. WinMain proc
    13. local msg:MSG
    14.  
    15.         push rbp   
    16.  
    17.     mov ecx,offset libName
    18.     call LoadLibrary
    19.     or eax,eax
    20.     je @f
    21.     mov ecx,eax
    22.     call FreeLibrary
    23.  
    24. @@:     mov ebp,esp
    25.     sub esp,sizeof MSG
    26.     xor ebx,ebx
    27.     mov eax,10029h
    28.     mov edi,offset ClassName
    29.         mov esi,IMAGE_BASE
    30.  
    31.     push rax    ;hIconSm
    32.     push rdi    ;lpszClassName
    33.     push rbx    ;lpszMenuName
    34.     push COLOR_WINDOW;hbrBackground
    35.     push 10005h ;hCursor
    36.     push rax        ;hIcon
    37.    
    38.     push rsi    ;hInstance
    39.     push rbx        ;cbClsExtra & cbWndExtra
    40.     db 68h
    41.     dd WndProc      ;lpfnWndProc
    42.     push sizeof WNDCLASSEX;cbSize & style
    43.     mov rcx,rsp ;addr WNDCLASSEX
    44.     sub esp,20h
    45.         call RegisterClassEx   
    46.     push rbx
    47.     push rsi    ;rsi=400000h
    48.     shl esi,9   ;rsi=CW_USEDEFAULT
    49.     push rbx
    50.     push rbx
    51.     push 240
    52.     push 400
    53.     push rsi
    54.     push rsi
    55.     mov r9d,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    56.     mov r8,rdi  ;offset ClassName
    57.     mov edx,edi ;offset ClassName
    58.     xor ecx,ecx
    59.     sub esp,20h
    60.         call CreateWindowEx
    61.  
    62.         lea edi,msg
    63. @@:     mov ecx,edi
    64.     xor edx,edx
    65.     mov r8,rbx
    66.     mov r9,rbx
    67.         call GetMessage
    68.     mov ecx,edi
    69.         call DispatchMessage
    70.         jmp @b
    71. WinMain endp
    72. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    73.  
    74.         cmp  edx,WM_DESTROY
    75.         je   wmDESTROY        
    76.         jmp DefWindowProc
    77. wmDESTROY: xor ecx,ecx
    78.         call ExitProcess
    79. WndProc endp
    80. ;---------------------------------------
    81. ClassName   db 'Win64 Iczelion''s lesson #26: Splash Screen',0
    82. libName     db 'tut_26A.dll',0
    83. end