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

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

Метки:
  1. Mikl___

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

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

    Превращаем SoftModalMessageBox при помощи хуков в окно диалога


    70.png
    Теория ― МАТЬ СКЛЕРОЗА
    Все, что нужно – это установить локальный хук, вызвать SoftModalMessageBox, выполнить в обработчике хука все необходимые действия и снять хук по завершении SoftModalMessageBox
    первое оповещение типа HCBT_CREATEWND, пришедшее в наш обработчик, даст нам HWND окна сообщения, которое мы и будем использовать в дальнейшем
    Функции Windows для работы с хуками
    Приложения Windows используют функции SetWindowsHookEx, UnhookWindowsHookEx, и CallNextHookEx для управления очередью функций-фильтров хука.
    SetWindowsHookEx
    Функция SetWindowsHookEx добавляет функцию-фильтр к хуку. Эта функция принимает четыре аргумента:
    1. Целочисленный код, описывающий хук, к которому будет прикреплена фильтрующая функция. Эти коды определены в WINUSER.H
    2. Адрес функции-фильтра. Эта функция должна быть описана как экспортируемая включением ее в секцию EXPORTS файла определения приложения или библиотеки динамической линковки (DLL), или использованием соответствующих опций компилятора.
    3. Хэндл модуля, содержащего фильтрующую функцию. В Win32 этот параметр должен быть NULL при установке хука на поток, но данное требование не является обязательным. При установке хука для всей системы или для потока в другом процессе, нужно использовать описатель DLL, содержащей функцию-фильтр.
    4. Идентификатор потока, для которого устанавливается хук. Если этот идентификатор ненулевой, установленная фильтрующая функция будет вызываться только в контексте указанного потока. Если идентификатор равен нулю, установленная функция имеет системную область видимости и может быть вызвана в контексте любого потока в системе. Приложение или библиотека могут использовать функцию GetCurrentThreadId для получения идентификатора текущего потока.
    Некоторые хуки могут быть установлены только с системной областью видимости, некоторые можно устанавливать как для всей системы, так и для одного потока, как показано в следующей таблице.
    ХукОбласть видимости
    WH_CALLWNDPROCПоток или вся система
    WH_CBT
    WH_DEBUG
    WH_GETMESSAGE
    WH_FOREGROUNDIDLE
    WH_SHELL
    WH_KEYBOARD
    WH_MOUSE
    WH_MSGFILTER
    WH_SYSMSGFILTERТолько система
    WH_JOURNALRECORD
    WH_JOURNALPLAYBACK
    Для любого данного типа хука, первыми вызываются хуки потоков, и только затем системные хуки.
    Хуки потоков:
    • Не создают лишней работы приложениям, которые не заинтересованы в вызове хука.
    • Не помещают все события, относящиеся к хуку, в очередь (так, чтобы они поступали не одновременно, а одно за другим). Например, если приложение установит клавиатурный хук для всей системы, то все клавиатурные сообщения будут пропущены через фильтрующую функцию этого хука, оставляя неиспользованными системные возможности многопотоковой обработки ввода. Если эта функция прекратит обрабатывать клавиатурные события, система будет выглядеть зависшей, хотя на самом деле и не зависнет. Пользователь всегда сможет использовать комбинацию CTRL+ALT+DEL для того, чтобы выйти из системы (log-out) и решить проблему, но ему это вряд ли понравится. К тому же, пользователь может не знать, что подобную ситуацию можно решить, войдя в систему под другим именем (log-out/log-in).
    • Не требуют нахождения функции-фильтра в отдельной DLL. Все системные хуки и хуки для потоков в другом приложении должны находиться в DLL.
    • Им не нужно разделять данные между DLL, загруженными в разные процессы. Фильтрующие функции с системной областью видимости, которые обязаны находиться в DLL, должны к тому же разделять необходимые данные с другими процессами. Так как такое поведение не является типичным для DLL, вы должны принимать специальные меры предосторожности при реализации системных фильтрующих функций. Если функция-фильтр не умеет разделять данные и неправильно использует данные в другом процессе, этот процесс может рухнуть.
    SetWindowsHookEx возвращает описатель установленного хука. Приложение или библиотека должны использовать этот описатель для вызова функции UnhookWindowsHookEx. SetWindowsHookEx возвращает NULL если она не смогла добавить функцию к хуку. SetWindowsHookEx также устанавливает код последней ошибки в одно из следующих значений для индикации неудачного завершения функции.
    • ERROR_INVALID_HOOK_FILTER: Неверный код хука.
    • ERROR_INVALID_FILTER_PROC: Неверная фильтрующая функция.
    • ERROR_HOOK_NEEDS_HMOD: Глобальный хук устанавливается с параметром hInstance, равным NULL либо локальный хук устанавливается для потока, который не принадлежит данному приложению.
    • ERROR_GLOBAL_ONLY_HOOK: Хук, который может быть только системным, устанавливается как потоковый.
    • ERROR_INVALID_PARAMETER: Неверный идентификатор потока.
    • ERROR_JOURNAL_HOOK_SET: Для регистрационного хука (journal hook) уже установлена фильтрующая функция. В любой момент времени может быть установлен только один записывающий или воспроизводящий хук. Этот код ошибки может также означать, что приложение пытается установить регистрационный хук в то время, как запущен хранитель экрана.
    • ERROR_MOD_NOT_FOUND: Параметр hInstance в случае, когда хук является глобальным, не ссылался на библиотеку. (На самом деле, это значение означает лишь, что модуль User не смог обнаружить данный описатель в списке модулей.)
    • Любое другое значение: Система безопасности не позволяет установить данный хук, либо в системе закончилась память.
    UnhookWindowsHookEx
    Для удаления функции-фильтра из очереди хука вызовите функцию UnhookWindowsHookEx. Эта функция принимает описатель хука, полученный от SetWindowsHookEx и возвращает логическое значение, показывающее успех операции. На данный момент UnhookWindowsHookEx всегда возвращает TRUE.
    WH_CBT
    Для написания подчиненного приложения, разработчик должен координировать его работу с работой главного приложения, для которого оно разрабатывается. Для достижения этой цели Windows предоставляет разработчикам хук WH_CBT. Windows передает фильтрующей функции код хука, показывающий, какое произошло событие, и соответствующие этому событию данные.
    Фильтр для хука WH_CBT должен знать о десяти хуковых кодах:
    • HCBT_ACTIVATE
    • HCBT_CREATEWND
    • HCBT_DESTROYWND
    • HCBT_MINMAX
    • HCBT_MOVESIZE
    • HCBT_SYSCOMMAND
    • HCBT_CLICKSKIPPED
    • HCBT_KEYSKIPPED
    • HCBT_SETFOCUS
    • HCBT_QS
    HCBT_ACTIVATE
    Windows вызывает хук WH_CBT с этим кодом при активации какого-нибудь окна. Когда хук WH_CBT установлен как локальный, это окно должно принадлежать потоку, на который установлен хук. Если фильтр в ответ на это событие вернет TRUE, окно не будет активизировано.
    Параметр wParam содержит описатель активизируемого окна. В lParam содержится указатель на структуру CBTACTIVATESTRUCT, которая описана следующим образом:
    Код (C):
    1. typedef struct tagCBTACTIVATESTRUCT
    2. {
    3.    BOOL    fMouse;      // TRUE, если активация наступила в результате
    4.                         //  мышиного клика; иначе FALSE.
    5.    HWND    hWndActive;  // Содержит описатель окна, активного
    6.                         //  в настоящий момент.
    7. } CBTACTIVATESTRUCT, *LPCBTACTIVATESTRUCT;
    HCBT_CREATEWND
    Windows вызывает хук WH_CBT с этим при создании окна. Когда хук установлен как локальный, это окно должно создаваться потоком, на который установлен хук. Хук WH_CBT вызывается до того, как Windows пошлет новому окну сообщения WM_GETMINMAXINFO, WM_NCCREATE, или WM_CREATE. Таким образом, фильтрующая функция может запретить создание окна, вернув TRUE.
    В параметре wParam содержится описатель создаваемого окна. В lParam ― указатель на следующую структуру.
    Код (C):
    1. // данные для HCBT_CREATEWND, на которые указывает lParam
    2. struct CBT_CREATEWND
    3. {
    4.     struct tagCREATESTRUCT *lpcs; // Данные для создания  нового окна.
    5.     HWND  hwndInsertAfter;        // Описатель окна, после которого будет
    6.                                   //  добавлено это окно (Z-order).
    7. } CBT_CREATEWND, *LPCBT_CREATEWND;
    Функция-фильтр может изменить значение hwndInsertAfter или значения в lpcs.
    HCBT_DESTROYWND
    Windows вызывает хук WH_CBT с этим кодом перед уничтожением какого-либо окна. Если хук является локальным, это окно должно принадлежать потоку, на который установлен хук. Windows вызывает хук WH_CBT до посылки сообщения WM_DESTROY. Если функция-фильтр вернет TRUE, окно не будет уничтожено.
    Параметр wParam содержит описатель уничтожаемого окна. В lParam находится 0.
    HCBT_MINMAX
    Windows вызывает хук WH_CBT с этим кодом перед минимизацией или максимизацией окна. Когда хук установлен как локальный, это окно должно принадлежать потоку, на который установлен хук. Если фильтр вернет TRUE, действие будет отменено.
    В wParam передается описатель окна, которое готовится к максимизации/минимизации. lParam содержит одну из SW_-констант, определенных в WINUSER.H и описывающих операцию над окном.
    HCBT_MOVESIZE
    Windows вызывает хук WH_CBT с этим кодом перед перемещением или изменением размеров окна, сразу после того, как пользователь закончил выбор новой позиции или размеров окна. Если хук установлен как локальный, это окно должно принадлежать потоку, на который установлен хук. Если фильтр вернет TRUE, действие будет отменено.
    В wParam передается описатель перемещаемого/изменяемого окна. lParam содержит LPRECT, который указывает на новые координаты окна.
    HCBT_SYSCOMMAND
    Windows вызывает хук WH_CBT с этим кодом во время обработки системной команды. Если хук установлен как локальный, окно, чье системное меню вызвало данное событие, должно принадлежать потоку, на который установлен хук. Хук WH_CBT вызывается из функции DefWindowsProc. Если приложение не передает сообщение WH_SYSCOMMAND функции DefWindowsProc, это хук не получит управление. Если функция-фильтр вернет TRUE, системная команда не будет выполнена.
    В wParam содержится системная команда (SC_TASKLIST, SC_HOTKEY, и так далее), готовая к выполнению. Если в wParam передается SC_HOTKEY, в младшем слове lParam содержится описатель окна, к которому относится горячая клавиша. Если в wParam передается любое другое значение и если команда системного меню была выбрана мышью, в младшем слове lParam будет находиться горизонтальная позиция, а в старшем слове ― вертикальная позиция указателя мыши.
    Следующие системные команды приводят к срабатыванию этого хука изнутри DefWindowProc:
    SC_CLOSEЗакрыть окно
    SC_HOTKEYАктивировать окно, связанное с определенной горячей клавишей
    SC_HSCROLLГоризонтальная прокрутка
    SC_KEYMENUВыполнить команду меню по комбинации клавиш
    SC_MAXIMIZEРазвернуть окно на весь экран
    SC_MINIMIZEСвернуть окно
    SC_MOUSEMENUВыполнить команду меню по щелчку мыши
    SC_MOVEПереместить окно
    SC_NEXTWINDOWПерейти к следующему окну
    SC_PREVWINDOWПерейти к предыдущему окну
    SC_RESTOREСохранить предыдущие координаты (контрольная точка ― checkpoint)
    SC_SCREENSAVEЗапустить хранитель экрана
    SC_SIZEИзменить размер окна
    HCBT_CLICKSKIPPED
    Windows вызывает хук WH_CBT с этим кодом при удалении события от мыши из входной очереди потока, в случае, если установлен хук мыши. Windows вызовет системный хук, когда из какой-либо входной очереди будет удалено событие от мыши и в системе установлен либо глобальный, либо локальный хук мыши. Данный код передается только в том случае, если к хуку WH_MOUSE прикреплена фильтрующая функция. Несмотря на свое название, HCBT_CLICKSKIPPED генерируется не только для пропущенных событий от мыши, но и в случае, когда событие от мыши удаляется из системной очереди. Его главное назначение ― установить хук WH_JOURNALPLAYBACK в ответ на событие мыши.
    В wParam передается идентификатор сообщения мыши ― например, WM_LBUTTONDOWN или любое из сообщений WM_*BUTTON*. lParam содержит указатель на структуру MOUSEHOOKSTRUCT, которая описана следующим образом:
    Код (C):
    1. typedef struct tagMOUSEHOOKSTRUCT {
    2.     POINT   pt;          // Позиция курсора мыши в координатах экрана
    3.     HWND    hwnd;        // Окно, получающее сообщение
    4.     UINT wHitTestCode;   // Результат проверки координат (hit-testing)
    5.     DWORD   dwExtraInfo; // Доп.информация о сообщении
    6. } MOUSEHOOKSTRUCT, FAR *LPMOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT;
    HCBT_KEYSKIPPED
    Windows вызывает хук WH_CBT с этим кодом при удалении клавиатурного события из системной очереди, в случае, если установлен клавиатурный хук. Windows вызовет системный хук, когда из какой-либо входной очереди будет удалено событие от клавиатуры и в системе установлен либо глобальный, либо локальный клавиатурный хук. Данный код передается только в том случае, если к хуку WH_KEYBOARD прикреплена фильтрующая функция. Несмотря на свое название, HCBT_KEYSKIPPED генерируется не только для пропущенных клавиатурных событий, но и в случае, когда клавиатурное событие удаляется из системной очереди. Его главное назначение ― установить хук WH_JOURNALPLAYBACK в ответ на клавиатурное событие.
    В wParam передается виртуальный код клавиши ― то же самое значение, что и в wParam функций GetMessage или PeekMessage для сообщений WM_KEY*. lParam содержит то же значение, что и lParam функций GetMessage или PeekMessage для сообщений WM_KEY*.
    WM_QUEUESYNC
    Часто приложение должно реагировать на события в процессе, для которого оно разработано. Обычно такими событиями являются события от клавиатуры или мыши. К примеру, пользователь нажимает на кнопку OK в диалоговом окне, после чего приложение желает послать главному приложению серию клавиатурных нажатий. Приложение может использовать хук мыши для определения момента нажатия кнопки OK. После этого, приложение должно выждать некоторое время, пока главное приложение не закончит обработку нажатия кнопки OK.
    Приложение может использовать сообщение WM_QUEUESYNC для определения момента окончания нужного действия. Слежение производится с помощью клавиатурного или мышиного хуков. Наблюдая за главным приложением с помощью хуков, приложение узнает о наступлении необходимого события. После этого приложение должно подождать окончания этого события, прежде чем приступать к выполнению ответных действий.
    Для определения момента окончания обработки события, приложение делает следующее:
    1. Ждет от Windows вызова хука WH_CBT с кодом HCBT_CLICKSKIPPED или HCBT_KEYSKIPPED. Это происходит при удалении из системной очереди события, которое приводит к срабатыванию обработчика в главном приложении.
    2. Устанавливает хук WH_JOURNALPLAYBACK. Приложение не может установить этот хук, пока не получит код HCBT_CLICKSKIPPED или HCBT_KEYSKIPPED. Хук WH_JOURNALPLAYBACK посылает приложению сообщение WM_QUEUESYNC. Когда приложение получает такое сообщение, оно может выполнить необходимые действия, например, послать главному приложению серию клавиатурных нажатий.
    HCBT_SETFOCUS
    Windows вызывает хук WH_CBT с таким кодом, когда Windows собирается передать фокус ввода какому-либо окну. Когда хук установлен как локальный, это окно должно принадлежать потоку, на который установлен хук. Если фильтр вернет TRUE, фокус ввода не изменится.
    В wParam передается описатель окна, получающего фокус ввода. lParam содержит описатель окна, теряющего фокус ввода.
    HCBT_QS
    Windows вызывает хук WH_CBT с этим кодом когда из системной очереди удаляется сообщение WM_QUEUESYNC, в то время как происходит изменение размеров или перемещение окна. Ни в каком другом случае этот хук не вызывается. Если хук установлен как локальный, это окно должно принадлежать потоку, на который установлен хук.
    Оба параметра ― и wParam, и lParam ― содержат ноль.
    ПРАКТИКА ― СЕСТРА ШИЗОФРЕНИИ
    файл tut_10m-0.asm
    Код (ASM):
    1. ; GUI #
    2. IDC_IMG      equ 1001
    3. IDC_ICON1    equ 1000
    4. include win64a.inc
    5. includelib tut_10m-2.lib
    6. UninstallHook proto
    7. InstallHook proto :QWORD
    8. MSGBOXDATA struct
    9.      params              MSGBOXPARAMS <>
    10.      pwndOwner           QWORD ?
    11.      wLanguageId         DWORD ?,?
    12.      pidButton           QWORD ?         ; // Array of button IDs
    13.      ppszButtonText      QWORD ?         ; // Array of button text strings
    14.      cButtons            DWORD ?
    15.      DefButton           DWORD ?
    16.      CancelId            DWORD ?
    17.      Timeout             DWORD ?
    18. MSGBOXDATA ends
    19. .code
    20. MsgBoxText1    dw 0
    21. MsgCaption8: du <Tutorial 10m: SoftModalMessageBox+Hook>
    22. sBTN1: du <Say Hello>
    23. sBTN2: du <Exit>
    24. dwBtnIds  dd 1,2
    25. dwTxtTbl  dq sBTN1,sBTN2
    26. aEdit db "EDIT",0
    27. expTxt   db "Hello!",0
    28. align 16
    29. WinMain proc
    30. local mb:MSGBOXPARAMS
    31. local mbxData:MSGBOXDATA
    32.    mov mbxData.params.cbSize,sizeof MSGBOXPARAMS
    33.    mov mbxData.params.hwndOwner,0
    34.    mov mbxData.params.hInstance,IMAGE_BASE
    35.    movr mbxData.params.lpszText,MsgBoxText1 ;адрес текста в окне сообщений
    36.   movr mbxData.params.lpszCaption,MsgCaption8 ;адрес заголовка в окне сообщений
    37.         mov mbxData.params.dwStyle,MB_OK or MB_USERICON
    38.   mov mbxData.params.lpszIcon,IDC_ICON1
    39.    mov mbxData.params.dwContextHelpId,0
    40.   movr mbxData.params.lpfnMsgBoxCallback,MsgBoxCallback ;адрес процедуры если нажмут кнопку "Say Hello!"
    41.   mov mbxData.params.dwLanguageId,0
    42.         and mbxData.pwndOwner,0
    43.     and mbxData.wLanguageId,0
    44.    movr mbxData.pidButton,dwBtnIds
    45.     movr mbxData.ppszButtonText,dwTxtTbl
    46.     mov mbxData.cButtons,2 ;две кнопки
    47.     mov mbxData.DefButton,0 ;кнопка "по умолчанию" -- "Say Hello!"
    48.     mov mbxData.CancelId,2
    49.     or mbxData.Timeout,-1
    50. ;----------------------------------
    51.         invoke InstallHook
    52. ;-----------------------------------
    53.    lea ecx,mbxData
    54.       invoke SoftModalMessageBox
    55. ;----------------------------------------
    56.         invoke UninstallHook
    57.    invoke RtlExitUserProcess,NULL
    58. WinMain endp
    59. align 16
    60. MsgBoxCallback proc lpHelpInfo:qword;(LPHELPINFO lpHelpInfo)
    61.    invoke GetActiveWindow
    62.    invoke FindWindowEx,eax,0,&aEdit,0
    63.    invoke SendMessage,eax,WM_SETTEXT,0,&expTxt
    64.    leave
    65.    xor eax,eax
    66.    retn
    67. MsgBoxCallback endp
    68. end
    файл tut_10m-0.rc (ресурсы для EXE-файла)
    Код (C):
    1. #define IDC_ICON1    1000
    2. IDC_ICON1 ICON DISCARDABLE "br_Fox1.ico"
    файл tut_10m-2.asm для создания DLL, которая будет использована сперва для установки, а потом для снятия локального хука
    Код (ASM):
    1. ; DLL #
    2. include win64a.inc
    3. IDC_IMG equ 1001
    4. ;OPTION PROLOGUE:none
    5. .data
    6. hInstance dq ?
    7. hHook dq ?
    8. aStatic db "STATIC",0
    9. aButton db "BUTTON",0
    10. aEdit db "EDIT"
    11. aNull db 0
    12. EditBoxString db "Wow! I'm in an edit box now",0
    13. hwndMessageBox dq 0
    14. m_hwndEditBox dq 0
    15. image dd ?
    16. FileBmp db "03.bmp",0
    17. .code
    18. DllMain proc ;hInstDLL:QWORD, reason:QWORD, unused:QWORD
    19. mov hInstance,rcx
    20. mov eax,TRUE
    21.         retn
    22. DllMain Endp
    23. MouseProc proc nCode:QWORD,wParam:QWORD,lParam:QWORD
    24. mov nCode,rcx
    25. mov wParam,rdx
    26. mov lParam,r8
    27.         cmp ecx, HCBT_CREATEWND
    28. jz hcbtCREATEWND
    29.         cmp ecx, HCBT_ACTIVATE
    30. jnz hcbtBYE
    31. hcbtACTIVATE:   mov rax,m_hwndEditBox
    32. or eax,eax
    33. jnz hcbtBYE
    34. mov rax,wParam
    35. cmp hwndMessageBox,rax
    36. jnz hcbtBYE
    37. invoke InsetEditBox
    38. jmp hcbtBYE
    39. hcbtCREATEWND:  mov rax,hwndMessageBox
    40. or eax, eax
    41. jnz hcbtBYE
    42. mov rax,wParam
    43. mov hwndMessageBox,rax ;hwndMessageBox = hwnd
    44. hcbtBYE:invoke CallNextHookEx,hHook,nCode,wParam,lParam
    45. leave
    46. ret
    47. MouseProc endp
    48. InstallHook proc hwnd:QWORD
    49. lea edx,MouseProc
    50. invoke SetWindowsHookEx,WH_CBT,,hInstance,NULL
    51. mov hHook,rax
    52. leave
    53. ret
    54. InstallHook endp
    55. UninstallHook proc
    56. enter 20h,0
    57. invoke UnhookWindowsHookEx,hHook
    58. leave
    59. ret
    60. UninstallHook endp
    61. CalcEditBoxRect proc rectEditBox:qword,nGap:qword
    62. local rectTmp:RECT
    63. local hwndTextOrIcon:qword
    64. local hdcMessageBox:qword
    65. local old_rdi:qword
    66.         mov     nGap,rdx
    67.         mov rectEditBox,rcx
    68. mov old_rdi,rdi
    69. ;Ищем иконку или текст, если иконки нет
    70. invoke FindWindowEx,hwndMessageBox,0,&aStatic,0
    71. mov hwndTextOrIcon,rax
    72. or eax, eax
    73. jz locret
    74. lea edx, rectTmp
    75. invoke GetWindowRect,eax;hwndTextOrIcon
    76. or eax, eax
    77. jz locret
    78. ;Тут мы получили .left, отступ по вертикали, и, возможно, .bottom
    79. mov edx, rectTmp.left
    80. mov rdi, rectEditBox
    81. mov [rdi].RECT.left, edx ;rectEditBox->left = rectTmp.left
    82. invoke MapWindowPoints,0,hwndMessageBox,&rectTmp,1
    83. mov rdx, nGap
    84. mov eax, rectTmp.top
    85. mov [rdx], eax
    86. mov ecx, rectTmp.bottom
    87. mov [rdi].RECT.bottom, ecx
    88. ;Ищем текст (если до этого нашли иконку)
    89. invoke FindWindowEx,hwndMessageBox,hwndTextOrIcon,&aStatic,0
    90. or eax, eax
    91. jz @f
    92. lea edx,rectTmp
    93. invoke GetWindowRect,eax
    94. or eax, eax
    95. jz locret
    96. ;получили .right и .bottom
    97. @@:     mov edx, rectTmp.right
    98. mov [rdi].RECT.right,edx    ;rectEditBox->right = rectTmp.right
    99. mov eax,rectTmp.bottom ;if( rectTmp.bottom > rectEditBox->bottom )
    100. cmp eax,[rdi].RECT.bottom
    101. jbe @f
    102. mov [rdi].RECT.bottom, eax  ;rectEditBox->bottom = rectTmp.bottom
    103. ;Теперь нужно рассчитать размер текста и галочки
    104. @@: invoke GetWindowDC,hwndMessageBox
    105. mov hdcMessageBox,rax
    106. or eax, eax
    107. jz locret
    108. @@: invoke GetSystemMetrics,SM_CXMENUCHECK
    109. mov rectTmp.left,eax
    110. mov ecx,[rdi].RECT.left
    111. sub rectTmp.right,ecx    ;rectTmp.right -= rectEditBox->left
    112. mov rectTmp.top, 0
    113. mov rectTmp.bottom,4000h
    114.         mov qword ptr [rsp+20h],DT_CALCRECT or DT_WORDBREAK or DT_NOPREFIX; format
    115. lea edx,EditBoxString ; m_lpEditBoxString
    116. invoke DrawText,hdcMessageBox,,-1,&rectTmp
    117. invoke ReleaseDC,hwndMessageBox,hdcMessageBox
    118. ;Получаем rectEditBox.top
    119. mov eax,[rdi].RECT.bottom   ;rectEditBox->bottom
    120. sub eax,rectTmp.bottom
    121. mov [rdi].RECT.top,eax ;rectEditBox->top = rectEditBox->bottom - rectTmp.bottom
    122. invoke MapWindowPoints,0,hwndMessageBox,rdi,2
    123. locret: mov rdi,old_rdi
    124. leave
    125. retn
    126. CalcEditBoxRect endp
    127. MoveButtonsDown proc nDistance:qword
    128. local rectButton:RECT
    129. local hwndButton:qword
    130. mov nDistance,rcx ;1A
    131. invoke FindWindowEx,hwndMessageBox,0,&aButton,0
    132. mov hwndButton,rax
    133. @@: lea edx, rectButton
    134. invoke GetWindowRect,hwndButton  ;3EBh  24Dh  443h 267h
    135. invoke MapWindowPoints,0,hwndMessageBox,&rectButton,2
    136. mov qword ptr [rsp+30h],SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOREDRAW
    137. and qword ptr [rsp+28h],0;cy
    138. and qword ptr [rsp+20h],0
    139. mov r9d,20; rectButton.top
    140. mov r8d,325;rectButton.left ; X
    141. invoke SetWindowPos,hwndButton,0
    142. invoke FindWindowEx,hwndMessageBox,hwndButton,&aButton,0
    143. mov hwndButton,rax
    144. @@: lea edx, rectButton
    145. invoke GetWindowRect,hwndButton  ;3EBh  24Dh  443h 267h
    146. invoke MapWindowPoints,0,hwndMessageBox,&rectButton,2
    147. mov qword ptr [rsp+30h],SWP_NOSIZE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOREDRAW
    148. and qword ptr [rsp+28h],0;cy
    149. and qword ptr [rsp+20h],0
    150. mov r9d,52; rectButton.top
    151. mov r8d,325;left ; X
    152. invoke SetWindowPos,hwndButton,0
    153. locret: leave
    154. retn
    155. MoveButtonsDown endp
    156. InsetEditBox proc
    157. local nHeightGrow:qword
    158. local rectEditBox:RECT
    159. local rectWindow:RECT
    160. local hwndEditBox:qword
    161.         sub rsp,100h
    162. lea edx,nHeightGrow
    163. lea ecx,rectEditBox
    164. invoke CalcEditBoxRect
    165. or eax, eax
    166. jz locret
    167. ;Создаем EditBox
    168. ;bmp-файл из ресурсов
    169.         mov edx,IDC_IMG
    170.         invoke LoadImage,hInstance,,IMAGE_BITMAP,0,0,0;LR_LOADFROMFILE
    171.         mov image,eax
    172.         and qword ptr [rsp+58h],0 ; lpParam
    173. mov rax,hInstance
    174. mov [rsp+50h],rax ; hInstance
    175. and qword ptr [rsp+48h],IDC_IMG
    176. mov rax,hwndMessageBox
    177. mov [rsp+40h],rax
    178. mov qword ptr [rsp+38h],101;rax
    179. and qword ptr [rsp+30h],128
    180. and qword ptr [rsp+28h],0
    181. and qword ptr [rsp+20h],0
    182. lea edx,aStatic
    183. lea r8d,aNull
    184. invoke CreateWindowEx,0,,,\
    185. WS_CHILD or WS_VISIBLE or SS_BITMAP; or SS_CENTERIMAGE;BITMAP or SS_CENTERIMAGE;REALSIZEIMAGE
    186. invoke SendMessage,eax,STM_SETIMAGE,IMAGE_BITMAP,image
    187. and qword ptr [rsp+58h],0 ; lpParam
    188. ; and qword ptr [rsp+50h],0 ; hInstance
    189. mov rax,hInstance
    190. mov [rsp+50h],rax ; hInstance
    191. and qword ptr [rsp+48h],0 ; hMenu
    192. mov rax,hwndMessageBox ; hWndParent
    193. mov [rsp+40h],rax
    194. mov qword ptr [rsp+38h],26 ; nHeight = rectEditBox.bottom - rectEditBox.top
    195. mov qword ptr [rsp+30h],190 ; nWidth = rectEditBox.right -  rectEditBox.left
    196. mov qword ptr [rsp+28h],35;Y
    197. mov qword ptr [rsp+20h],130;X
    198. lea edx,aEdit
    199. invoke CreateWindowEx,WS_EX_CLIENTEDGE,,&EditBoxString,\
    200. ES_LEFT+WS_TABSTOP+WS_CHILD+WS_VISIBLE;+BS_AUTOCHECKBOX+BS_MULTILINE
    201. ; mov hwndEditBox,rax
    202. ; invoke SetFocus,eax
    203. ; lea edx,aEdit
    204. ; invoke CreateWindowEx,WS_EX_CLIENTEDGE,,&EditBoxString,\
    205. ; ES_LEFT+WS_TABSTOP+WS_CHILD+WS_VISIBLE;+BS_AUTOCHECKBOX+BS_MULTILINE
    206. ; mov hwndEditBox,rax
    207. ;HWND hStatic1 = CreateWindowEx( NULL, "STATIC", "", WS_CHILD | WS_VISIBLE | SS_ICON | SS_WHITERECT, 250,70, 50, 50, hwnd, NULL, GetModuleHandle(NULL), NULL);
    208. ; invoke GetLastError
    209. ;hwndLabel = CreateWindow(
    210. ;                        TEXT("STATIC"),                   /*The name of the static control's class*/
    211. ;                        TEXT("Label 1"),                  /*Label's Text*/
    212. ;                        WS_CHILD | WS_VISIBLE | SS_LEFT,  /*Styles (continued)*/
    213. ;                        0,                                /*X co-ordinates*/
    214. ;                        0,                                /*Y co-ordinates*/
    215. ;                        50,                               /*Width*/
    216. ;                        25,                               /*Height*/
    217. ;                        hwnd,                             /*Parent HWND*/
    218. ;                        (HMENU) ID_MYSTATIC,              /*The Label's ID*/
    219. ;                        hInstance,                        /*The HINSTANCE of your program*/
    220. ;                        NULL);                            /*Parameters for main window*/
    221. ;Увеличиваем окно и сдвигаем все кнопки вниз
    222. @@: lea edx,rectWindow                 ;2F1 1E6 4C0 287h
    223. invoke GetWindowRect,hwndMessageBox
    224. or eax, eax
    225. jz @f
    226. mov qword ptr [rsp+30h],SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE or SWP_NOREDRAW; uFlags
    227. mov qword ptr [rsp+28h],175 ; cy = rectWindow.bottom - rectWindow.top + nHeightGrow
    228. mov qword ptr [rsp+20h],450 ; cx = rectWindow.right - rectWindow.left
    229. invoke SetWindowPos,hwndMessageBox,0,0,0
    230. invoke MoveButtonsDown,nHeightGrow;=1A
    231. @@: lea eax,rectEditBox
    232. mov m_hwndEditBox,rax
    233. locret: leave
    234. retn
    235. InsetEditBox endp
    236. end
    237.  
    файл tut_10m-2.rc (ресурсы для DLL)
    Код (ASM):
    1. #define IDC_IMG      1001
    2. IDC_IMG BITMAP MOVEABLE PURE LOADONCALL DISCARDABLE "03.bmp"
    файл tut_10m-2.def для создания tut_10m-2.lib с помощью которого будет создан tut_10m-2.dll
    Код (ASM):
    1. LIBRARY tut_10m-2
    2. EXPORTS
    3. MouseProc
    4. InstallHook
    5. UninstallHook
    Скачайте файл примера здесь.

    00.png
    Первоначальный вид MessageBox
    01.png
    MessageBox после установки хука
    12.png
    MessageBox после нажатия на кнопку «Say Hello»​
     

    Вложения:

    • 0.png
      0.png
      Размер файла:
      380,4 КБ
      Просмотров:
      1.933
    • 00.png
      00.png
      Размер файла:
      6,4 КБ
      Просмотров:
      687
    Последнее редактирование: 19 июл 2019
    kero нравится это.
  2. Mikl___

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

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

    Глава тридцать первая. Братец Кролик разбирается с памятью и файлами

    [​IMG]
    В этой главе Братец Кролик изучал основы управления памятью и файловые операции ввода/вывода. Для этого он использовал обычные диалоговые окна как устройства ввода/вывода.
    Скачайте пример здесь.

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

    Управление памятью под Win64 с точки зрения приложения достаточно просто и прямолинейно. Используемая модель памяти называется плоской моделью памяти.
    В этой модели все сегментные регистры (или селекторы) указывают на один и тот же стартовый адрес и используется 32-битное смещение, так что приложение может обратиться к любой точке памяти своего адресного пространства без необходимости изменять значения селекторов. Это очень упрощает управление памятью.
    Под Win16 существует две основные категории функций API памяти: глобальные и локальные. Функции глобального типа взаимодействуют с памятью в других сегментах, поэтому они функции "дальней" памяти. Функции локального типа взаимодействуют с локальной кучей процессов, поэтому они функции "ближней" памяти.
    Под Win64 оба этих типа идентичны. Используете ли вы GlobalAlloc или LocalAlloc, вы получите одинаковый результат.
    1. Выделите блок памяти с помощью вызова GlobalAlloc. Эта функция возвращает дескриптор на запрошенный блок памяти.
    2. "Закройте" блок памяти, вызвав GlobalLock. Эта функция принимает дескриптор на блок памяти и возвращает указатель на блок памяти.
    3. Вы можете использовать указатель, чтобы читать или писать в память.
    4. "Откройте" блок памяти с помощью вызова GlobalUnlock. Эта функция возвращает указатель на блок памяти.
    5. Освободите блок памяти с помощью GlobalFree. Эта функции принимает дескриптор на блок памяти.
    Стандартный менеджер памяти ― это не единственный диспетчер кучи в вашей программе. Вам также доступны:
    • HeapAlloc/HeapFree
    • LocalAlloc/LocalFree
    • GlobalAlloc/GlobalFree
    • IMalloc.Alloc/IMalloc.Free
    • CoTaskMemAlloc/CoTaskMemFree
    • SHAlloc/SHFree
    Чем они отличаются? Это разные реализации одной идеи. В системе есть несколько менеджеров памяти, которые имеют разные цели и используются в разных случаях. Вышеуказанные функции ― это точки доступа к различным менеджерам памяти. Когда их надо использовать? Когда вам нужно «поговорить» с кодом, который понимает только их. К примеру, буфер обмена Windows работает с GlobalAlloc/GlobalFree.
    Самое главное, нужно помнить правило: «кто девушку ужинает ― тот ее и танцует». Иными словами, если память выделили через GetMem ― то освобождать ее должны через FreeMem, выделили через VirtualAlloc ― освобождать нужно через VirtualFree, выделили через LocalAlloc ― тогда LocalFree...
    Вы также можете заменить "Global" на "Local", то есть LocalAlloc, LocalLock и так далее.
    Вышеуказанный метод может быть упрощен использованием флага GMEM_FIXED при вызове GlobalAlloc. Если вы используете этот флаг, возвращаемое значение от Global/LocalAlloc будет указателем на зарезервированный блок памяти, а не дескриптор этого блока. Вам не надо будет вызывать Global/LocalLock вы сможете передать указатель Global/LocalFree без предварительного вызова Global/LocalUnlock. Hо в этой главе я использую "традиционный" подход, так как вы можете столкнуться с ним при изучении исходников других программ.
    Файловый ввод/вывод по Win64 имеет значительное сходство с тем, как это делалось под DOS. Все требуемые шаги точно такие же. Вам только нужно заменить прерывания на вызовы API функций.
    1. Откройте или создайте файл функцией CreateFile. Эта функция очень универсальна: не считая файла, она может открывать LPT/COM-порты, пайпы, дисковые приводы и консоли. В случае успеха она возвращает дескриптор файла или устройства. Затем вы можете использовать этот дескриптор, чтобы выполнить определенные действия над файлом или устройством.
    2. Переместите файловый указатель в желаемое местоположение функцией SetFilePointer.
    3. Проведите операцию чтения или записи с помощью вызова ReadFile или WriteFile. Перед этим вы должны зарезервировать достаточно большой блок памяти для данных.
    4. Закройте файл с помощью CloseHandle. Эта функции принимает дескриптор файла.

    GlobalAlloc

    Распределяет указанное число байтов из кучи.
    Примечание Глобальные функции имеют большую нагрузку и обеспечивают меньше возможностей , чем другие функции управления памятью. Новые приложения должны использовать функции кучи, если документация не утверждает, что глобальная функция должна быть использована.
    Код (C):
    1. HGLOBAL WINAPI GlobalAlloc(
    2.   _In_ UINT   uFlags, /* Выделение памяти атрибутов. Если задано нулевое значение по умолчанию
    3. является GMEM_FIXED. Этот параметр может быть одним или более из следующих значений,
    4. за исключением несовместимых комбинаций */
    5.   _In_ SIZE_T dwBytes /* Количество необходимых байт. Если этот параметр равен нулю и параметр
    6. определен как GMEM_MOVEABLE, функция возвращает дескриптор объекта памяти,
    7. который помечен как перемещаемый */
    8. );
    КонстантаhexЗначение
    GMEM_FIXED
    0​
    Выделяет фиксированную память. Возвращаемое значение является указателем.
    GMEM_MOVEABLE
    2​
    Выделяет перемещаемую память. Блоки памяти не перемещаются в физической памяти, но могут быть перемещаемы в пределах кучи.
    Возвращаемое значение представляет собой дескриптор объекта памяти. Для того, чтобы перевести дескриптор в указатель, используйте функцию GlobalLock.
    Это значение не может быть объединено с GMEM_FIXED
    GMEM_ZEROINIT
    40​
    Заполняет выделенный фрагмент памяти нулями.
    GPTR
    40​
    Сочетание GMEM_FIXED и GMEM_ZEROINIT
    GHND
    42​
    Сочетание GMEM_MOVEABLE и GMEM_ZEROINIT.
    Следующие значения являются устаревшими, но предназначены для совместимости с 16-битной Windows. Они игнорируются.
    • GMEM_DDESHARE
    • GMEM_DISCARDABLE
    • GMEM_LOWER
    • GMEM_NOCOMPACT
    • GMEM_NODISCARD
    • GMEM_NOT_BANKED
    • GMEM_NOTIFY
    • GMEM_SHARE

    Возвращаемое значение

    Если функция завершается успешно, возвращаемое значение является дескриптор вновь выделенного объекта памяти. Если функция завершается ошибкой, возвращаемое значение ― NULL.

    Выделив блок, его можно в любой момент зафиксировать при помощи GlobalLock и после этого работать с ним. Функция GlobalUnlock снимает фиксацию и разрешает системе переупорядочивать блоки. При использовании флага GMEM_MOVEABLE возвращается не адрес, а дескриптор. Аргументом функции GlobalLock является дескриптор. Функция GlobalLock возвращает адрес.
    Если флаг GMEM_DISCARDABLE используется вместе с GMEM_MOVEABLE, тогда блок памяти может быть удален из памяти системой, если он не был предварительно зафиксирован. Если блок был удален системой, тогда функция GlobalLock возвратит NULL и придется снова выделять блок и загружать данные.
    Для удаления блока памяти используют GlobalFree. В случае выделения фиксированного блока аргумент функции адрес блока памяти, в случае перемещаемого блока ― дескриптор. Для освобождения удаляемого блока используют функцию GlobalDiscard.
     

    Вложения:

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

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

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

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

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

    Управление памятью, файловый ввод/вывод, GlobalAlloc/Lock/Unlock и GlobalFree

    rc-файл

    Код (ASM):
    1. #define ZZZ_OPEN 1
    2. #define ZZZ_SAVE 2
    3. #define ZZZ_EXIT 3
    4. #define IDR_MAINMENU 30
    5. IDR_MAINMENU MENU
    6. {
    7.         POPUP "&File"
    8.         {       MENUITEM "&Open",ZZZ_OPEN
    9.                 MENUITEM "&Save As",ZZZ_SAVE
    10.                 MENUITEM SEPARATOR
    11.                 MENUITEM "&Exit",ZZZ_EXIT
    12.          }
    13.          MENUITEM "&Exit",ZZZ_EXIT
    14. }

    asm-файл

    Код (ASM):
    1. ; GUI #
    2. include win64a.inc
    3. ZZZ_OPEN   equ  1
    4. ZZZ_SAVE   equ  2
    5. ZZZ_EXIT   equ  3
    6. MAXSIZE         equ 256
    7. MEM_SIZE equ 65535
    8. EditID equ 1
    9. IDR_MAINMENU equ 30
    10. .code
    11. WinMain proc
    12. local msg:MSG
    13.       xor ebx,ebx
    14.      mov edi,offset ClassName
    15.      mov esi,IMAGE_BASE
    16.      mov eax,10029h
    17.      push rax ;hIconSm
    18.      push rdi ;lpszClassName
    19.      push IDR_MAINMENU;lpszMenuName
    20.      push COLOR_WINDOW;hbrBackground
    21.      push 10005h ;hCursor
    22.      push rax        ;hIcon
    23.      push rsi ;hInstance
    24.      push rbx        ;cbClsExtra & cbWndExtra
    25.      db 68h
    26.       dd WndProc ;lpfnWndProc
    27.      push sizeof WNDCLASSEX;cbSize & style
    28.      invoke RegisterClassEx,esp ;addr WNDCLASSEX
    29.      push rbx
    30.      push rsi ;rsi=400000h
    31.      shl esi,9 ;rsi=CW_USEDEFAULT
    32.      push rbx
    33.      push rbx
    34.      push rsi
    35.      push rsi
    36.      push rsi
    37.      push rsi
    38.      sub esp,20h
    39.     invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    40.      invoke SetFocus,hwndEdit
    41.     lea edi,msg
    42. @@:   invoke GetMessage,edi,0,0,0
    43.      invoke TranslateMessage,edi
    44.      invoke DispatchMessage,edi
    45.       jmp @b
    46. WinMain endp
    47. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    48. local hMem:QWORD ;handle to the allocated memory block
    49. local pMem:QWORD ;pointer to the allocated memory block
    50. local szReadWrite:QWORD ;number of bytes actually read or write
    51.       mov edi,offset dlgOpenOfn
    52.       mov hWnd,rcx
    53.         mov wParam,r8
    54.         mov lParam,r9
    55.         cmp  edx,WM_DESTROY
    56.         je   wmDESTROY
    57.         cmp  edx,WM_COMMAND
    58.         je   wmCOMMAND
    59.         cmp  edx,WM_CREATE
    60.         je   wmCREATE
    61.         cmp  edx,WM_SIZE
    62.      je   wmSIZE
    63.         leave
    64.         jmp DefWindowProc
    65. wmDESTROY:invoke ExitProcess,NULL
    66. wmSIZE: mov qword ptr [rbp-28h],TRUE
    67.       movzx rax,word ptr lParam+2
    68.       mov [rbp-30h],rax
    69.      movzx r9,word ptr lParam
    70.      invoke MoveWindow,hwndEdit,0,0
    71.       jmp wmBYE
    72. OPEN: mov [rdi+OPENFILENAME.Flags],OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or\
    73. OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    74.      invoke GetOpenFileName,edi
    75.      test eax,eax
    76.      je @0
    77.      mov [rbp-20h],rbx
    78.       mov qword ptr [rbp-28h],FILE_ATTRIBUTE_ARCHIVE
    79.      mov qword ptr [rbp-30h],OPEN_EXISTING
    80.      mov ecx,offset dOpenBuffer
    81. invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,0
    82. mov  edi,eax;handle to file
    83. invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEM_SIZE
    84. mov hMem,rax
    85. invoke GlobalLock,eax
    86. mov pMem,rax
    87. mov [rbp-30h],rbx
    88.       lea r9d,szReadWrite
    89.      invoke ReadFile,edi,eax,MEM_SIZE-1
    90.      invoke SendMessage,hwndEdit,WM_SETTEXT,0,pMem
    91.      jmp @1
    92. SAVE: mov [rdi+OPENFILENAME.Flags],OFN_LONGNAMES or OFN_EXPLORER or      OFN_HIDEREADONLY
    93.      invoke GetSaveFileName,edi;&dOpenOfn
    94.      test eax,eax
    95.      je @0
    96.      mov [rbp-20h],rbx
    97.      mov qword ptr [rbp-28h],FILE_ATTRIBUTE_ARCHIVE
    98.      mov qword ptr [rbp-30h],CREATE_NEW
    99.      mov ecx,offset dOpenBuffer
    100.      invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or      FILE_SHARE_WRITE,0
    101.      mov edi,eax;hFile
    102.      invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEM_SIZE
    103.      mov hMem,rax
    104.      invoke GlobalLock,eax
    105.      mov pMem,rax
    106.      invoke SendMessage,hwndEdit,WM_GETTEXT,MEM_SIZE-1,rax ;pMem
    107.      mov [rbp-30h],rbx
    108.      lea r9,szReadWrite
    109.      invoke WriteFile,edi,pMem,rax
    110. @1: invoke CloseHandle,edi;hFile
    111.      invoke GlobalUnlock,pMem
    112.      invoke GlobalFree,hMem
    113. @0: invoke SetFocus,hwndEdit
    114.      jmp wmBYE
    115. wmCREATE:mov [rdi+OPENFILENAME.hwndOwner],rcx
    116.      push rbx
    117.      push IMAGE_BASE
    118.      push EditID
    119.      push rcx;hWnd
    120.      push rbx
    121.      push rbx
    122.      push rbx
    123.      push rbx
    124.      mov edx,offset EditClass
    125.      sub esp,20h
    126.       invoke CreateWindowEx,0,,0,WS_VISIBLE or WS_CHILD or \
    127.      ES_LEFT or WS_VSCROLL or WS_HSCROLL or \
    128.      ES_AUTOHSCROLL or ES_AUTOVSCROLL or ES_MULTILINE
    129.      mov hwndEdit,rax
    130.       jmp wmBYE
    131. wmCOMMAND:movzx rax,word ptr wParam
    132.       or r9d,r9d ;cmp lParam,0
    133.      jnz wmBYE
    134.      cmp rax,ZZZ_EXIT
    135.      ja  wmBYE
    136.      jmp  [menu_handlers+rax*8]
    137. EXIT: invoke DestroyWindow
    138. wmBYE:leave
    139.       retn
    140. menu_handlers dq wmBYE,OPEN,SAVE,EXIT
    141. WndProc endp
    142. ;data
    143. ClassName     db 'Win64 Iczelion''s lesson #12: Memory Management, File I/O, GlobalAlloc/Lock/Unlock and GlobalFree',0
    144. dlgOpenTitle  db 'Open File',0
    145.  
    146. align 8
    147. dlgOpenOfn  label  OPENFILENAME
    148. dd sizeof OPENFILENAME,?
    149. dq ?
    150. dq IMAGE_BASE
    151. dq FilterString
    152. dq ?,?
    153. dq dOpenBuffer
    154. dd MAXSIZE
    155. dd 19 dup(?)
    156.  
    157. FilterString  db 'All Files (*.*)',0,'*.*',0
    158.      db 'Text Files (*.txt)',0,'*.txt',0,0
    159. dOpenBuffer   db MAXSIZE dup(?)
    160. hwndEdit      dq ? ;handle for edit control
    161. EditClass     db "edit",0
    162. end

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

    Код (ASM):
    1.      push rbx
    2.    push IMAGE_BASE
    3.    push EditID
    4.    push rcx;hWnd
    5.    push rbx
    6.    push rbx
    7.    push rbx
    8.    push rbx
    9.    mov edx,offset EditClass
    10.    sub esp,20h
    11.       invoke CreateWindowEx,0,,0,WS_VISIBLE or WS_CHILD or \
    12. ES_LEFT or WS_VSCROLL or WS_HSCROLL or \
    13. ES_AUTOHSCROLL or ES_AUTOVSCROLL or ES_MULTILINE
    14. mov hwndEdit,rax
    В секции WM_CREATE мы создаем окно ввода. Параметры, которые определяют координаты, ширину, высоту элемента управления равны нулю, поскольку мы изменим размер элемента управления позже, чтобы покрыть всю клиентскую область родительского окна. Заметьте, что в этом случае мы не вызываем ShowWindow, чтобы заставить появиться элемент управления на экране, так как указан стиль WS_VISIBLE.
    Код (ASM):
    1.   mov edi,offset dlgOpenOfn
    2.    . . . .
    3.    ;        Инициализируем структуру
    4.           mov [rdi+OPENFILENAME.hwndOwner],rcx; rcx=hWnd
    5.    . . . .
    6. ;data
    7. dlgOpenTitle  db 'Open File',0
    8.  
    9. align 8
    10. dlgOpenOfn  label  OPENFILENAME
    11. dd sizeof OPENFILENAME,?
    12. dq ?
    13. dq IMAGE_BASE
    14. dq FilterString
    15. dq ?,?
    16. dq dOpenBuffer
    17. dd MAXSIZE
    18. dd 19 dup(?)
    После создания окна ввода, мы доинициализируем структуру ofn. Основные поля структуры заполнены в секции данных. Так как мы хотим использовать структуру ofn повторно в диалоговом окне, мы заполняем только общие члены, которые используются и GetOpenFileName и GetSaveFileName. Секция WM_CREATE ― это прекрасное место для одноразовой инициализации.
    Код (ASM):
    1. wmSIZE: mov qword ptr [rbp-28h],TRUE
    2.       movzx rax,word ptr lParam+2
    3.       mov [rbp-30h],rax
    4. movzx r9,word ptr lParam
    5. invoke MoveWindow,hwndEdit,0,0
    Мы получаем сообщения WM_SIZE, когда размер клиентской области нашего основного окна изменяется. Мы также получаем его, когда окно создается. Для того, чтобы получать это сообщение, стили класса окна должны включать CS_REDRAW и CS_HREDRAW. Мы используем эту возможность для того, чтобы сделать размер нашего окна ввода равным клиентской области окна. Для начала мы должны узнать текущую ширину и высоту клиентской области родительского окна. Мы получаем эту информацию из lParam. Старшее слово lParam содержит высоту, а младшее слово ― ширину клиентской области. Затем мы используем эту информацию для того, чтобы изменить размер окна ввода с помощью вызова функции MoveWindow, которая может изменять позицию и размер окна на экране.
    Код (ASM):
    1. OPEN: mov [rdi+OPENFILENAME.Flags],OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or\
    2. OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    3.      invoke GetOpenFileName,edi
    4.      test eax,eax
    5.      je @0
    6.      mov [rbp-20h],rbx
    7.       mov qword ptr [rbp-28h],FILE_ATTRIBUTE_ARCHIVE
    8.      mov qword ptr [rbp-30h],OPEN_EXISTING
    9.      mov ecx,offset dOpenBuffer
    10.      invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,0
    11.      mov  edi,eax;handle to file
    12.      invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEM_SIZE
    13.      mov hMem,rax
    14.      invoke GlobalLock,eax
    15.      mov pMem,rax
    16.      mov [rbp-30h],rbx
    17.       lea r9d,szReadWrite
    18.      invoke ReadFile,edi,eax,MEM_SIZE-1
    19.      invoke SendMessage,hwndEdit,WM_SETTEXT,0,pMem
    20.      jmp @1
    21.      . . . .
    22. @1: invoke CloseHandle,edi;hFile
    23. invoke GlobalUnlock,pMem
    24. invoke GlobalFree,hMem
    25. @0: invoke SetFocus,hwndEdit
    Когда пользователь выбирает пункт меню File/Open, мы заполняем в структуре параметр Flags и вызываем функцию GetOpenFileName, чтобы отобразить окно открытия файла.
     
    Последнее редактирование: 1 янв 2017
  4. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    После того, как пользователь выберет файл для открытия, мы вызываем CreateFile, чтобы открыть файл. Мы указываем, что функция должна попробовать открыть файл для чтения и записи. После того, как файл открыт, функция возвращает дескриптор на открытый файл, который мы сохраняем в глобальной переменной для будущего использования. Эта функция имеет следующий синтаксис:
    Код (C):
    1. HANDLE WINAPI CreateFile(
    2. _In_ LPCTSTR  lpFileName,
    3.  _In_ DWORD  dwDesiredAccess,
    4.  _In_ DWORD  dwShareMode,
    5.  _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    6.  _In_ DWORD  dwCreationDisposition,
    7.  _In_ DWORD  dwFlagsAndAttributes,
    8.  _In_opt_ HANDLE  hTemplateFile
    9. );
    • dwDesireAccess указывает, какую операцию вы хотите выполнить над файлом.
      • Открыть файл для проверки его атрибутов. Вы можете писать и читать из файла.
      • GENERIC_READ Открыть файл для чтения.
      • GENERIC_WRITE Открыть файл для записи.
    • dwShareMode указывает, какие операции вы хотите позволить выполнять вашим процессам над открытыми файлами.
      • 0 ― не разделять файл с другими процессами.
      • FILE_SHARE_READ ― позволяет другим процессам прочитать информацию из файла, который был открыт
      • FILE_SHARE_WRITE ― позволяет другим процессам записывать информацию в открытый файл.
    • lpSecurityAttributes не имеет значения под Windows 95.
    • dwCreationDistribution указывает действие, которое будет выполнено над файлом при его открытии.
      • CREATE_NEW ― создание нового файла, если файла не существует.
      • CREATE_ALWAYS ― создание нового файла. Функция перезаписывает файл, если он существует.
      • OPEN_EXISTING ― открытие существующего файла.
      • OPEN_ALWAYS ― открытие файла, если он существует, в противном случае, функция создает новый файл.
      • TRUNCATE_EXISTING ― открытие файла и обрезание его до нуля байтов. Вызывающий функцию процесс должен открывать файл, по крайней мере, с доступом GENERIC_WRITE. Если файл не существует, функция не срабатывает.
    • dwFlagsAndAttributes указывает атрибуты файла
      • FILE_ATTRIBUTE_ARCHIVE ― файл является архивным. Приложения используют этот атрибут для бэкапа или удаления.
      • FILE_ATTRIBUTE_COMPRESSED ― файл или каталог сжаты. Для файла это означает, что вся информация в файле обработана архиватором. Для директории это означает, что сжатие подразумевается по умолчанию для создаваемых вновь файлов и поддиректорий.
      • FILE_ATTRIBUTE_NORMAL ― у файла нет других атрибутов. Этот атрибут действителен, только если используется один.
      • FILE_ATTRIBUTE_HIDDEN ― файл спрятан. Он не включается в обычные листинги директорий.
      • FILE_ATTRIBUTE_READONLY ― файл только для чтения. Приложения могут читать из файла, но не могут писать в него или удалить его.
      • FILE_ATTRIBUTE_SYSTEM ― файл часть операционной системы или используется только ей.
    Код (ASM):
    1. invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEM_SIZE
    2. mov hMem,rax
    3. invoke GlobalLock,eax
    4. mov pMem,rax
    Когда файл открыт, мы резервируем блок память для использования функциями ReadFile и WriteFile. Мы указываем флаг GMEM_MOVEABLE, чтобы позволить Windows перемещать блок памяти, чтобы уплотнять последнюю.
    Когда GlobalAlloc возвращает положительный результат, rax содержит дескриптор зарезервированного блока памяти. Мы передаем этот дескриптор функции GlobalLock, который возвращает указатель на блок памяти.
    Код (ASM):
    1.      mov [rbp-30h],rbx
    2.       lea r9d,szReadWrite
    3.      invoke ReadFile,edi,eax,MEM_SIZE-1
    4.      invoke SendMessage,hwndEdit,WM_SETTEXT,0,pMem
    Когда блок памяти готов к использованию, мы вызываем функцию ReadFile для чтения данных из файла. Когда файл только что открыт или создан, указатель на смещение равен нулю. В этом случае, мы начинаем чтение с первого байта. Первый параметр ReadFile ― это дескриптор файла, из которого необходимо произвести чтение, второй ― это указатель на блок памяти, затем ― количество байтов, которое нужно считать из файла, четвертый параметр ― это адрес переменной размера QWORD, который будет заполнен количеством байтов, в реальности считанных из файла.
    После заполнения блока памяти данными, мы помещаем данные в окно ввода, посылая сообщение WM_SETTEXT элементу управления, причем lParam содержит указатель на блок памяти. После этого вызова окно ввода отображает данные в его клиентской области.
    Код (ASM):
    1.      invoke CloseHandle,edi;hFile
    2.      invoke GlobalUnlock,pMem
    3.      invoke GlobalFree,hMem
    4. @0: invoke SetFocus,hwndEdit
    В этом месте у нас нет необходимости держать файл открытым, так как нашей целью является запись модифицированных данных из окна ввода в другой файл, а не в оригинальный. Поэтому мы закрываем файл функцией CloseHandle, передав ей в качестве параметра дескриптор файла. Затем мы открываем блок памяти и освобождаем его. В действительности, вам не нужно освобождать ее сейчас, вы можете использовать этот же блок во время операции сохранения. Hо в целях обучения ее освобождают сразу.
    Код (ASM):
    1.      invoke SetFocus, hwndEdit
    Когда на экране отображается окно открытия файла, фокус ввода сдвигается на него. Поэтому, когда это окно закрывается, мы должны передвинуть фокус ввода обратно на окно ввода.
    На этом операцию чтения из файла заканчивается. В этом месте пользователь должен отредактировать содержимое окна ввода. И когда он хочет сохранить данные в другой файла, он должен выбрать File/Save, после чего отобразиться диалоговое окно. Создание окна сохранения файла не слишком отличается от создание окна открытия файла. Фактически, они отличаются только именем функций. Вы можете снова использовать большинство из параметров структуры OPENFILENAME, кроме параметра Flags.
    Код (ASM):
    1. SAVE: mov [rdi+OPENFILENAME.Flags],OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    2.      invoke GetSaveFileName,edi;&dOpenOfn
    3.      test eax,eax
    4.      je @0
    5.      mov [rbp-20h],rbx
    6.      mov qword ptr [rbp-28h],FILE_ATTRIBUTE_ARCHIVE
    7.      mov qword ptr [rbp-30h],CREATE_NEW
    8.      mov ecx,offset dOpenBuffer
    9.      invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or FILE_SHARE_WRITE,0
    10.      mov edi,eax;hFile
    11.      invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEM_SIZE
    12.      mov hMem,rax
    13.      invoke GlobalLock,eax
    14.      mov pMem,rax
    15.      invoke SendMessage,hwndEdit,WM_GETTEXT,MEM_SIZE-1,rax ;pMem
    16.      mov [rbp-30h],rbx
    17.      lea r9,szReadWrite
    18.      invoke WriteFile,edi,pMem,rax
    19. @1: invoke CloseHandle,edi;hFile
    20.      invoke GlobalUnlock,pMem
    21.      invoke GlobalFree,hMem
    22. @0: invoke SetFocus,hwndEdit
    В нашем случае, мы хотим создать новый файл, так чтобы OFN_FILEMUSTEXIST и OFN_PATHMUSTEXIST должны быть убраны, иначе диалоговое окно не позволит нам создать файл, который уже не существует.
    Параметр dwCreationDistribution функции CreateFile должен быть установлен в CREATE_NEW, так как мы хотим создать новый файл. Оставшийся код практически одинаков с тем, что используется при создании окна открытия файла, за исключением следующего фрагмента:
    Код (ASM):
    1.      invoke SendMessage,hwndEdit, WM_GETTEXT, MEM_SIZE-1,rax ;pMem
    2.      mov [rbp-30h],rbx
    3.      lea r9,szReadWrite
    4.      invoke WriteFile,edi,pMem,rax
    Мы посылаем сообщение WM_GETTEXT окну ввода, чтобы скопировать данные из него в блок памяти, возвращаемое значение в rax ― это длина данных внутри буфера. После того, как данные оказываются в блоке памяти, мы записываем их в новый файл.
     
    Последнее редактирование: 2 янв 2017
  5. Mikl___

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

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

    Управление памятью, файловый ввод/вывод, GetProcessHeap, HeapAlloc и HeapFree

    [​IMG]

    HeapAlloc

    Выделяет блок памяти из кучи. Выделенная память неперемещаема.
    Код (C):
    1. LPVOID WINAPI HeapAlloc(
    2.   _In_ HANDLE hHeap, /* Дескриптор кучи, из которой будет выделена память.
    3. Этот дескриптор возвращается функцией HeapCreate или GetProcessHeap function. */
    4.   _In_ DWORD  dwFlags, /* Параметры распределения кучи. Указание любого из этих значений
    5. отменит соответствующее значение , указанное при куче была создана с HeapCreate.
    6. Этот параметр может быть одним или более из следующих значений */
    7.   _In_ SIZE_T dwBytes /* Число байтов , которые будут выделены
    8. Если куча заданное параметром hHeap является "не расширяемым" кучи, dwBytes должен
    9. быть меньше , чем 0x7FFF8. Вы создаете не расширяемым кучу, вызвав HeapCreate
    10. функционировать с ненулевым значением.*/
    11. );

    dwFlags


    ЗначениеНазначение
    hex
    HEAP_NO_SERIALIZE
    1​
    Сериализованный доступа не будет использоваться для этого распределения.
    Для того, чтобы гарантировать , что сериализованный доступ отключен для всех вызовов этой функции, указать HEAP_NO_SERIALIZE в вызове HeapCreate. В этом случае нет необходимости дополнительно указывать HEAP_NO_SERIALIZE в этом вызове функции.
    Это значение не должно быть указано при обращении к кучи процесса по умолчанию.Система может создавать дополнительные потоки внутри процесса приложения, такие как обработчик CTRL+C, что одновременно доступ по умолчанию кучи процесса.
    HEAP_GROWABLE
    2​
    Specifies that the heap is growable. Must be specified if HeapBase is NULL
    HEAP_GENERATE_EXCEPTIONS
    4​
    Система поднимет исключение , чтобы указать сбой функции, такие как состояние вне-памяти, вместо возврата NULL.
    Для того, чтобы убедиться , что исключения генерируются для всех вызовов этой функции, указать HEAP_GENERATE_EXCEPTIONS в вызове HeapCreate. В этом случае нет необходимости дополнительно указать HEAP_GENERATE_EXCEPTIONS в этом вызове функции.
    HEAP_ZERO_MEMORY
    8​
    Выделенная память будет инициализирована нулями.
    HEAP_REALLOC_IN_PLACE_ONLY
    10​
    There can be no movement when reallocating a memory block. If this value is not specified, the function may move the block to a new location. If this value is specified and the block cannot be resized without moving, the function fails, leaving the original memory block unchanged.
    HEAP_TAG_SHIFT
    12​
    HEAP_TAIL_CHECKING_ENABLED
    20​
    HEAP_FREE_CHECKING_ENABLED
    40​
    HEAP_DISABLE_COALESCE_ON_FREE
    80​
    HEAP_MAXIMUM_TAG
    0FFF​
    HEAP_PSEUDO_TAG_FLAG
    8000​
    HEAP_CREATE_ALIGN_16
    10000​
    HEAP_CREATE_ENABLE_TRACING
    20000​
    HEAP_CREATE_ENABLE_EXECUTE
    40000​
    HEAP_SKIP_VALIDATION_CHECKS10000000
    HEAP_VALIDATE_PARAMETERS_ENABLED40000000

    HeapFree

    Освобождает блок памяти , который выделяется из кучи с помощью функции HeapAlloc или HeapReAlloc.
    Код (C):
    1. BOOL WINAPI HeapFree(
    2.   _In_ HANDLE hHeap, /* Дескриптор кучи блока памяти который должен быть освобожден.
    3. Дескриптор возвращается либо функцией HeapCreate, либо функцией GetProcessHeap */
    4.  _In_ DWORD  dwFlags,/* Куча свободных вариантов. Задание следующих значений
    5. отменяет соответствующее значение , указанное в параметре flOptions когда эта
    6. куча была создана с помощью функции HeapCreate */
    7.   _In_ LPVOID lpMem /* Указатель на блок памяти будет освобожден. Этот указатель
    8. возвращается функцией HeapAlloc или функцией HeapReAlloc. Если этот указатель равен NULL, поведение не определено */
    9. );

    asm-файл

    Код (ASM):
    1. ; GUI #
    2. include win64a.inc
    3. ZZZ_OPEN   equ  1
    4. ZZZ_SAVE   equ  2
    5. ZZZ_EXIT   equ  3
    6. MAXSIZE         equ 256
    7. MEM_SIZE equ 65535
    8. EditID equ 1
    9. IDR_MAINMENU equ 30
    10. .code
    11. WinMain proc
    12. local msg:MSG
    13.       xor ebx,ebx
    14. mov edi,offset ClassName
    15. mov esi,IMAGE_BASE
    16. mov eax,10029h
    17. push rax ;hIconSm
    18. push rdi ;lpszClassName
    19. push IDR_MAINMENU;lpszMenuName
    20. push COLOR_WINDOW;hbrBackground
    21. push 10005h ;hCursor
    22. push rax        ;hIcon
    23. push rsi ;hInstance
    24. push rbx        ;cbClsExtra & cbWndExtra
    25. mov eax,offset WndProc
    26. push rax ;lpfnWndProc
    27. push sizeof WNDCLASSEX;cbSize & style
    28. invoke RegisterClassEx,esp ;addr WNDCLASSEX
    29. push rbx
    30. push rsi ;rsi=400000h
    31. shl esi,9 ;rsi=CW_USEDEFAULT
    32. push rbx
    33. push rbx
    34. push rsi
    35. push rsi
    36. push rsi
    37. push rsi
    38. sub esp,20h
    39.     invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    40. invoke GetMenu,eax
    41. mov hMenu,rax
    42.     lea edi,msg
    43. @@:   invoke GetMessage,edi,0,0,0
    44. invoke TranslateMessage,edi
    45. invoke DispatchMessage,edi
    46.       jmp @b
    47. WinMain endp
    48. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    49. local hFile:QWORD ;handle of file
    50. local szReadWrite:QWORD ;number of bytes actually read or write
    51.       mov edi,offset dlgOpenOfn
    52. mov hWnd,rcx
    53.       mov lParam,r9
    54.       mov wParam,r8
    55.       cmp  edx,WM_DESTROY
    56.       je   wmDESTROY
    57.       cmp  edx,WM_COMMAND
    58.       je   wmCOMMAND
    59.       cmp  edx,WM_CREATE
    60.       je   wmCREATE
    61.       cmp  edx,WM_SIZE
    62. je   wmSIZE
    63.       leave
    64.       jmp DefWindowProc
    65. wmDESTROY:invoke ExitProcess,NULL
    66. wmSIZE:movzx rax,word ptr lParam+2
    67.       movzx r9,word ptr lParam
    68. invoke MoveWindow,hwndEdit,0,0,,rax,TRUE
    69.       jmp wmBYE
    70. OPEN: mov  [edi+OPENFILENAME.Flags],OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or\
    71. OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    72. invoke GetOpenFileName,edi
    73. test eax,eax
    74. je wmBYE
    75.       movzx edx,word ptr [edi+OPENFILENAME.nFileOffset]
    76. add edx,offset dOpenBuffer
    77. invoke SetWindowText,hWnd
    78. mov ecx,offset dOpenBuffer
    79. invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or \
    80.       FILE_SHARE_WRITE,0,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,rbx
    81. mov hFile,rax;handle to file
    82.       invoke GetFileSize,eax,0
    83. mov FileSize,rax
    84.       invoke GetProcessHeap
    85. mov hHeap,rax
    86. invoke HeapAlloc,eax,HEAP_ZERO_MEMORY,MEM_SIZE
    87. mov pMem,rax
    88. lea r9d,szReadWrite
    89. invoke ReadFile,hFile,eax,MEM_SIZE-1,,rbx
    90. invoke SendMessage,hwndEdit,WM_SETTEXT,0,pMem
    91. xor r9d,r9d
    92. mov r8d,MF_GRAYED
    93. jmp @f
    94. SAVE: mov [edi+OPENFILENAME.Flags],OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    95. invoke GetSaveFileName,edi;&dOpenOfn
    96. test eax,eax
    97. je wmBYE
    98.       movzx edx,word ptr [edi+OPENFILENAME.nFileOffset]
    99. add edx,offset dOpenBuffer
    100. invoke SetWindowText,hWnd
    101. mov ecx,offset dOpenBuffer
    102. invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or \
    103.       FILE_SHARE_WRITE,0,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,rbx
    104. mov hFile,rax
    105. invoke SendMessage,hwndEdit,WM_GETTEXT,FileSize,pMem
    106. lea r9,szReadWrite
    107. invoke WriteFile,hFile,pMem,rax,,rbx
    108. invoke HeapFree,hHeap,0,pMem
    109.       mov r9d,MF_GRAYED
    110. xor r8d,r8d;MF_ENABLED=0
    111. @@: invoke EnableMenuItem,hMenu,0;IDM_OPEN=0
    112. invoke EnableMenuItem,hMenu,ZZZ_SAVE
    113. invoke CloseHandle,hFile
    114. jmp wmBYE
    115. wmCREATE:mov [dlgOpenOfn.hwndOwner],rcx
    116. push rbx
    117. push IMAGE_BASE
    118. push EditID
    119. push rcx;hWnd
    120. push rbx
    121. push rbx
    122. push rbx
    123. push rbx
    124. mov edx,offset EditClass
    125. sub esp,20h
    126.       invoke CreateWindowEx,0,,0,WS_VISIBLE or WS_CHILD or \
    127. ES_LEFT or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or \
    128. ES_AUTOHSCROLL or ES_AUTOVSCROLL
    129. mov hwndEdit,rax
    130.       jmp wmBYE
    131. wmCOMMAND:movzx rax,word ptr wParam
    132.       or r9,r9 ;cmp lParam,0
    133. jnz wmBYE
    134. cmp rax,ZZZ_EXIT
    135. ja  wmBYE
    136. jmp  [menu_handlers+eax*8]
    137. EXIT: ;mov rcx,hWnd ;ax=MI_EXIT
    138.       invoke DestroyWindow
    139. wmBYE:leave
    140.       retn
    141. menu_handlers dq wmBYE,OPEN,SAVE,EXIT
    142. WndProc endp
    143. ;---------------------------------------
    144. ClassName     db 'Win64 Iczelion''s lesson #12b: Memory Management, File I/O, GetProcessHeap, HeapAlloc and HeapFree',0
    145. dlgOpenTitle  db 'Open File',0
    146. dlgOpenOfn    label OPENFILENAME
    147. dd sizeof OPENFILENAME,?
    148. dq ?
    149. dq IMAGE_BASE
    150. dq FilterString
    151. dq ?,?
    152. dq dOpenBuffer
    153. dd MAXSIZE,?
    154. dq 9 dup(?)
    155. ;data
    156. FilterString  db 'All Files (*.*)',0,'*.*',0
    157.      db 'Text Files (*.txt)',0,'*.txt',0,0
    158. dOpenBuffer   db MAXSIZE dup(?)
    159. hwndEdit      dq ? ;handle for edit control
    160. EditClass     db "edit",0
    161. hHeap dq ?
    162. pMem dq ?
    163. FileSize dq ?
    164. hMenu dq ?
    165. end

    rc-файл

    Код (C):
    1. #define ZZZ_OPEN 1
    2. #define ZZZ_SAVE 2
    3. #define ZZZ_EXIT 3
    4. #define IDR_MAINMENU 30
    5. IDR_MAINMENU MENU
    6. {
    7. POPUP "&File"
    8. {       MENUITEM "&Open",ZZZ_OPEN
    9.                 MENUITEM "&Save As",ZZZ_SAVE
    10. MENUITEM SEPARATOR
    11. MENUITEM "&Exit",ZZZ_EXIT
    12. }
    13. MENUITEM "&Exit",ZZZ_EXIT
    14. }
     

    Вложения:

    • 0.png
      0.png
      Размер файла:
      300,6 КБ
      Просмотров:
      1.943
    • 0.png
      0.png
      Размер файла:
      404,8 КБ
      Просмотров:
      1.929
    Последнее редактирование: 19 июл 2019
  6. Mikl___

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

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

    Управление памятью, файловый ввод/вывод, CoTaskMemAlloc и CoTaskMemFree

    [​IMG]

    rc-файл

    Код (C):
    1. #define ZZZ_OPEN 1
    2. #define ZZZ_SAVE 2
    3. #define ZZZ_EXIT 3
    4.  
    5. #define IDR_MAINMENU 30
    6. IDR_MAINMENU MENU
    7. {
    8.     POPUP "&File"
    9.     {       MENUITEM "&Open",ZZZ_OPEN
    10.                 MENUITEM "&Save As",ZZZ_SAVE
    11.         MENUITEM SEPARATOR
    12.         MENUITEM "&Exit",ZZZ_EXIT
    13.     }
    14.     MENUITEM "&Exit",ZZZ_EXIT
    15. }

    asm-файл

    Код (ASM):
    1. ; GUI #
    2. include win64a.inc
    3. ZZZ_OPEN   equ  1
    4. ZZZ_SAVE   equ  2
    5. ZZZ_EXIT   equ  3
    6. MAXSIZE         equ 256
    7. MEM_SIZE    equ 65535
    8. EditID        equ 1
    9. IDR_MAINMENU    equ 30
    10.  
    11. .code
    12. WinMain proc
    13. local msg:MSG
    14.  
    15.       xor ebx,ebx
    16.     mov edi,offset ClassName
    17.     mov esi,IMAGE_BASE
    18.     mov eax,10029h
    19.     push rax     ;hIconSm
    20.     push rdi    ;lpszClassName
    21.     push IDR_MAINMENU;lpszMenuName
    22.     push COLOR_WINDOW;hbrBackground
    23.     push 10005h    ;hCursor
    24.     push rax        ;hIcon
    25.     push rsi    ;hInstance
    26.     push rbx        ;cbClsExtra & cbWndExtra
    27.     mov eax,offset WndProc
    28.     push rax    ;lpfnWndProc
    29.     push sizeof WNDCLASSEX;cbSize & style
    30.     invoke RegisterClassEx,esp    ;addr WNDCLASSEX
    31.     push rbx
    32.     push rsi    ;rsi=400000h
    33.     shl esi,9    ;rsi=CW_USEDEFAULT
    34.     push rbx
    35.     push rbx
    36.     push rsi
    37.     push rsi
    38.     push rsi
    39.     push rsi
    40.     sub esp,20h
    41.         invoke CreateWindowEx, WS_EX_CLIENTEDGE,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    42.     invoke GetMenu,eax
    43.     mov hMenu,rax
    44.      lea edi,msg
    45. @@:       invoke GetMessage,edi,0,0,0
    46.     invoke TranslateMessage,edi
    47.     invoke DispatchMessage,edi
    48.           jmp @b
    49. WinMain endp
    50.  
    51. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    52.  
    53. local hFile:QWORD    ;handle of file
    54. local szReadWrite:QWORD    ;number of bytes actually read or write
    55.  
    56.           mov edi,offset dlgOpenOfn
    57.            mov hWnd,rcx
    58.        mov lParam,r9
    59.        mov wParam,r8
    60.           cmp  edx,WM_DESTROY
    61.           je   wmDESTROY
    62.           cmp  edx,WM_COMMAND
    63.           je   wmCOMMAND
    64.           cmp  edx,WM_CREATE
    65.           je   wmCREATE
    66.           cmp  edx,WM_SIZE
    67.     je   wmSIZE
    68.           leave
    69.           jmp DefWindowProc
    70.  
    71. wmDESTROY:invoke ExitProcess,NULL
    72. wmSIZE:movzx rax,word ptr lParam+2
    73.     movzx r9,word ptr lParam
    74.     invoke MoveWindow,hwndEdit,0,0,,rax,TRUE
    75.           jmp wmBYE
    76. OPEN:    mov  [edi+OPENFILENAME.Flags],OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or\
    77.     OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    78.     invoke GetOpenFileName,edi
    79.     test eax,eax
    80.     je wmBYE
    81.           movzx edx,word ptr [edi+OPENFILENAME.nFileOffset]
    82.     add edx,offset dOpenBuffer
    83.     invoke SetWindowText,hWnd
    84.     mov ecx,offset dOpenBuffer
    85.     invoke CreateFile,,\
    86.     GENERIC_READ or GENERIC_WRITE,\
    87.     FILE_SHARE_READ or FILE_SHARE_WRITE,\
    88.     0,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,0
    89.     mov hFile,rax;handle to file
    90.     invoke GetFileSize,eax,0
    91.     mov FileSize,rax
    92.     invoke CoTaskMemAlloc,rax
    93.     mov buff,rax
    94.     invoke ReadFile,hFile,eax,MEM_SIZE-1,&szReadWrite,rbx
    95.     invoke SendMessage,hwndEdit,WM_SETTEXT,0,buff
    96.     xor r9d,r9d
    97.     mov r8d,MF_GRAYED
    98.     jmp @f
    99. SAVE:    mov [edi+OPENFILENAME.Flags],OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    100.     invoke GetSaveFileName,edi;&dOpenOfn
    101.     test eax,eax
    102.     je wmBYE
    103.     movzx edx,word ptr [edi+OPENFILENAME.nFileOffset]
    104.     add edx,offset dOpenBuffer
    105.     invoke SetWindowText,hWnd
    106.     mov ecx,offset dOpenBuffer
    107.     invoke CreateFile,,\
    108.     GENERIC_READ or GENERIC_WRITE,\
    109.     FILE_SHARE_READ or FILE_SHARE_WRITE,\
    110.     0,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,0
    111.     mov hFile,rax
    112.     invoke SendMessage,hwndEdit,WM_GETTEXT,FileSize,buff
    113.     invoke WriteFile,hFile,buff,rax,&szReadWrite,rbx
    114.     invoke CoTaskMemFree,buff
    115.     mov r9d,MF_GRAYED
    116.     xor r8d,r8d;MF_ENABLED=0
    117. @@:    invoke EnableMenuItem,hMenu,0;IDM_OPEN=0
    118.     invoke EnableMenuItem,hMenu,ZZZ_SAVE
    119.     invoke CloseHandle,hFile
    120.     jmp wmBYE
    121. wmCREATE:mov [dlgOpenOfn.hwndOwner],rcx
    122.     push rbx
    123.     push IMAGE_BASE
    124.     push EditID
    125.     push rcx;hWnd
    126.     push rbx
    127.     push rbx
    128.     push rbx
    129.     push rbx
    130.     mov edx,offset EditClass
    131.     sub esp,20h
    132.           invoke CreateWindowEx,0,,0,WS_VISIBLE or WS_CHILD or \
    133.     ES_LEFT or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or \
    134.     ES_AUTOHSCROLL or ES_AUTOVSCROLL
    135.     mov hwndEdit,rax
    136.           jmp wmBYE
    137. wmCOMMAND:movzx rax,word ptr wParam
    138.           or r9,r9    ;cmp lParam,0
    139.     jnz wmBYE
    140.     cmp rax,ZZZ_EXIT
    141.     ja  wmBYE
    142.     jmp  [menu_handlers+eax*8]
    143. EXIT:    invoke DestroyWindow
    144. wmBYE:    leave
    145.           retn
    146. menu_handlers    dq wmBYE,OPEN,SAVE,EXIT
    147. WndProc endp
    148. ;data
    149. ClassName     db 'Win64 Iczelion''s lesson #12c: Memory Management, File I/O, CoTaskMemAlloc and CoTaskMemFree',0
    150. dlgOpenTitle  db    'Open File',0
    151. dlgOpenOfn    label OPENFILENAME
    152. dd sizeof OPENFILENAME,?
    153. dq ?
    154. dq IMAGE_BASE
    155. dq FilterString
    156. dq ?,?
    157. dq dOpenBuffer
    158. dd MAXSIZE,?
    159. dq 9 dup(?)
    160. FilterString  db 'All Files (*.*)',0,'*.*',0
    161.           db 'Text Files (*.txt)',0,'*.txt',0,0
    162. dOpenBuffer   db MAXSIZE dup(?)
    163. hwndEdit      dq ? ;handle for edit control
    164. EditClass     db "edit",0
    165. buff        dq ?
    166. FileSize    dq ?
    167. hMenu        dq ?
    168. end
     

    Вложения:

    • 0.png
      0.png
      Размер файла:
      245,9 КБ
      Просмотров:
      1.941
    Последнее редактирование: 31 мар 2019
  7. Mikl___

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

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

    Глава тридцать вторая. Братец Кролик и файлы, проецируемые в память

    [​IMG]
    Что такое файлы, проецируемые в память и как их использовать? Процессор не может работать с содержимым файла непосредственно на диске. Для обращения к какому-либо участку файла этот участок необходимо сначала прочитать в память. Если же требуется не просто обработать данные из файла, а модифицировать его содержимое, то к операции чтения файла добавляется еще и операция его записи. Поэтому процедура работы с дисковым файлом включает 4 этапа:
    • открытие файла на диске;
    • чтение требуемого участка файла в созданную заранее переменную;
    • модификация этой переменной;
    • запись модифицированной переменной на то же место в файле на диске.
    Скачайте пример здесь.

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

    Обычно при работе с файлом требуется обращение не к одному его участку, а к нескольким. Так как программа как правило запрашивает не смежные, а разнесенные по всему файлу участки, поэтому каждое такое обращение к файлу существенно замедляет скорость выполнения программы в целом.
    Для эффективности файл читается целиком в память. Поэтому для работы с файлом требуется только одна операция чтения в начале и одна операция записи в конце. Это возможно только с файлами небольшого размера и сильно снижает надежность системы, так как модифицированные данные записываются на диск только в конце сеанса и любой сбой приведет к потере модифицированных данных.
    Windows использует механизм объединивший работу с файлом и чтение/запись в память. Конкретный файл включается в состав физической памяти, в адресном пространстве приложения резервируется участок достаточного размера и часть физической памяти, включающая в себя файл, отображается на этот участок. Дальнейшие операции с файлом заменяются операциями с его отображением в адресном пространстве приложения.
    Если вы хорошо изучили пример из прошлой главы, вы увидите, что у него есть несколько серьезных недостатков:
    • файл, который вы хотите прочитать окажется больше, чем зарезервированный блок памяти
    • строка, которую вы хотите найти будет обрезана посередине, потому что кончился блок памяти
    Традиционный ответ на первый вопрос ― вам нужно последовательно читать данные из файла, пока он не закончится. Ответом на второй вопрос ― вы должны обрабатывать подобную возможность. Она называется проблемой пограничного значения, представляет собой головную большую для программистов и вызывает ошибки в программе.
    Было бы неплохо, если бы мы могли зарезервировать очень большой блок памяти, достаточный для того, чтобы сохранить весь файл, но наша программа стала бы очень прожорливой в плане ресурсов. Проекция файлов в память ― это спасение. Используя его, вы можете считать весь файл уже загруженным в память и использовать указатель на память, чтобы читать или писать данные в файл. Отпадает необходимость одновременного использования функций памяти и файловых функций. Проекция файлов в память также используется для обмена данными между процессами. При использовании проекции файлов в память таким образом, реально не используется никакой файл. Это больше похоже на блок памяти, который могут видеть все процессы. Hо обмен данными между процессами ― весьма деликатный предмет. Вы должны будете обеспечить синхронизацию между процессами и ветвями, иначе ваше приложение очень скоро повиснет.
    Мы не будем касаться того, как использовать проекцию файлов в память для создания общего региона памяти в этой главе. Мы сконцентрируемся на том, как использовать проекцию файлов в память для "загрузки" файла в память. Фактически, PE-загрузчик использует проекцию файлов в память для загрузки исполняемых файлов в память. Это очень удобно, так как только необходимые порции файла будут считываться с диска. Под Win64 вам следует использовать проекция файлов в память так часто, как это возможно.
    Правда, существует несколько ограничений при использовании проекции файлов в память. Как только вы создали такой файл, его размер не может изменяться до закрытия сессии. Поэтому проекция файлов в память прекрасно подходит для файлов из которых нужно только читать или файловых операций, которые не изменяют размер файла. Это не значит, что вы не можете использовать проекцию файлов в память, если хотите увеличить размер файла. Вы можете установить новый размер и создать файл спроецированный в память нового размера и файл увеличится до этого размер. Это просто неудобно, вот и все.
    Достаточно объяснений. Давайте перейдем к реализации проекции файлов в память. Для того, чтобы использовать этот механизм, должны быть выполнены следующие шаги:
    1. Вызов CreateFile для создания или открытия файла.
    2. Вызов CreateFileMaping, которой передается дескриптор файла, возвращенный CreateFile. Эта функция создает объект "проекция файла" из файла, созданного или открытого функцией CreateFile.
    3. Вызов MapViewOfFile, чтобы загрузить выбранный файловый регион или весь файл в память. Эта функция возвращает указатель на первый байт спроецированного в память файлового региона.
    4. Используется указатель, чтобы писать или читать по адресам памяти, на которую отображена проекция, так же как это бы происходило с файлом расположенным на диске.
    5. Вызывается UnmapViewOfFile, чтобы выгрузить спроецированный в памяти файл в файл на диске.
    6. Вызывается CloseHandle с передачей ему дескриптора спроецированного в память файла в качестве одного из параметра, чтобы закрыть его.
    7. Вызывается CloseHandle с передачей ему дескриптор файла, возвращенного CreateFile, чтобы закрыть файл на диске.

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

    Программа, листинг которой приведен ниже, позволит вам открыть файл с помощью диалога открытия файла. Программа откроет файл, используя проекцию файла в память и, если это удастся, заголовок окна изменится на имя открытого файла. Можно сохранить файл под другим именем, выбрав пункт меню File/Save as. Программа скопирует все содержимое открытого файла в новый файл. При этом вы не вызываете функцию GlobalAlloc для резервирования блока памяти.

    rc-файл

    Код (C):
    1. #define ZZZ_OPEN 1
    2. #define ZZZ_SAVE 2
    3. #define ZZZ_EXIT 3
    4. #define IDR_MAINMENU 30
    5.  
    6. IDR_MAINMENU MENU
    7. {
    8.        POPUP "&File"
    9.        {
    10.               MENUITEM "&Open",ZZZ_OPEN
    11.               MENUITEM "&Save As",ZZZ_SAVE
    12.               MENUITEM SEPARATOR
    13.               MENUITEM "&Exit",ZZZ_EXIT
    14.        }
    15.        MENUITEM "&Exit",ZZZ_EXIT
    16. }
     
    Последнее редактирование: 13 янв 2021
  8. Mikl___

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

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

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE  equ 400000h
    3. ZZZ_OPEN   equ  1
    4. ZZZ_SAVE   equ  2
    5. ZZZ_EXIT    equ  3
    6. MAXSIZE     equ 256
    7. MEM_SIZE equ 65535
    8. EditID equ 1
    9. IDR_MAINMENU equ 30
    10.  
    11. .code
    12. WinMain proc
    13. local msg:MSG
    14.  
    15.         xor ebx,ebx
    16. mov edi,offset ClassName
    17. mov esi,IMAGE_BASE
    18. mov eax,10029h
    19. push rax  ;hIconSm
    20. push rdi ;lpszClassName
    21. push IDR_MAINMENU;lpszMenuName
    22. push COLOR_WINDOW;hbrBackground
    23. push 10005h ;hCursor
    24. push rax        ;hIcon
    25. push rsi ;hInstance
    26. push rbx        ;cbClsExtra & cbWndExtra
    27.    db 68h
    28.    dd WndProc;lpfnWndProc
    29.    push sizeof WNDCLASSEX;cbSize & style
    30.    invoke RegisterClassEx,esp ;addr WNDCLASSEX
    31.    push rbx
    32.    push rsi ;rsi=400000h
    33.    shl esi,9 ;rsi=CW_USEDEFAULT
    34.    push rbx
    35.    push rbx
    36.    push rsi
    37.    push rsi
    38.    push rsi
    39.    push rsi
    40.     sub esp,20h
    41.     invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    42. invoke GetMenu,eax
    43.        mov hMenu,rax
    44.         invoke SetFocus,hwndEdit
    45.     lea edi,msg
    46. @@:     invoke GetMessage,edi,0,0,0
    47.        invoke TranslateMessage,edi
    48.        invoke DispatchMessage,edi
    49.         jmp @b
    50. WinMain endp
    51.  
    52. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    53.  
    54. local hFile:QWORD ;handle of file
    55. local szReadWrite:QWORD ;number of bytes actually read or write
    56.  
    57.         mov edi,offset dlgOpenOfn
    58.         mov hWnd,rcx
    59.         mov wParam,r8
    60.         mov lParam,r9
    61.  
    62.         cmp  edx,WM_DESTROY
    63.         je   wmDESTROY
    64.         cmp  edx,WM_COMMAND
    65.         je   wmCOMMAND
    66.         cmp  edx,WM_CREATE
    67.         je   wmCREATE
    68.         cmp  edx,WM_SIZE
    69.         je   wmSIZE
    70.         leave
    71.         jmp DefWindowProc
    72.  
    73. wmDESTROY:invoke ExitProcess,0
    74. wmSIZE: push TRUE
    75.         movzx rax,word ptr lParam+2
    76.         mov [rsp+20h],rax
    77.         movzx r9,word ptr lParam
    78.         invoke MoveWindow,hwndEdit,0,0
    79.         jmp wmBYE
    80. OPEN: mov  [rdi+OPENFILENAME.Flags],OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or\
    81. OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    82.        invoke GetOpenFileName,edi
    83.        test eax,eax
    84.        je wmBYE
    85.        push rbx ;<- align stack 16h
    86.         push rbx
    87.         push FILE_ATTRIBUTE_ARCHIVE
    88.        push OPEN_EXISTING
    89.        mov ecx,offset dOpenBuffer
    90.        sub esp,20h
    91.        invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or \
    92. FILE_SHARE_WRITE,0
    93.         mov hFile,rax;handle to file
    94.          invoke GetFileSize,eax,0
    95.          mov FileSize,rax
    96.         movzx edx,word ptr [rdi+OPENFILENAME.nFileOffset]
    97.         add edx,offset dOpenBuffer
    98.         invoke SetWindowText,hWnd
    99. ;создание в памяти объекта "проекция файла"
    100.         mov [rsp+28h],rbx
    101.         mov [rsp+20h],rbx
    102.         invoke CreateFileMapping,hFile,0,PAGE_READWRITE,0
    103.         mov hMapping,rax
    104. ;отображение проекции файла на адресное пространство процесса
    105.         mov [rsp+20h],rbx
    106.         invoke MapViewOfFile,eax,FILE_MAP_WRITE,0,0
    107.         mov pMapping,rax
    108.         invoke SendMessage,hwndEdit,WM_SETTEXT,0,rax
    109.         mov r9,rbx
    110.         mov r8d,MF_GRAYED
    111.         jmp @f
    112. SAVE: mov [rdi+OPENFILENAME.Flags],OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY
    113.        invoke GetSaveFileName,edi;&dOpenOfn
    114.        test eax,eax
    115.        je wmBYE
    116.        push rbx ;<- align stack 16h
    117.        push rbx
    118.         push FILE_ATTRIBUTE_ARCHIVE
    119.         push CREATE_NEW
    120.         mov ecx,offset dOpenBuffer
    121.         sub esp,20h
    122.         invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,\
    123. FILE_SHARE_READ or FILE_SHARE_WRITE,0
    124.         mov hFile,rax
    125.          movzx edx,word ptr [rdi+OPENFILENAME.nFileOffset]
    126.          add edx,offset dOpenBuffer
    127.         invoke SetWindowText,hWnd
    128.         push rbx ;<- align stack 16h
    129. push rbx
    130. lea r9,szReadWrite
    131.        sub esp,20h
    132.         invoke WriteFile,hFile,pMapping,FileSize
    133.         invoke UnmapViewOfFile,pMapping
    134.         invoke CloseHandle,pMapping
    135.         invoke CloseHandle,hMapping
    136.         mov r9d,MF_GRAYED
    137.         mov r8,rbx;MF_ENABLED=0
    138. @@: invoke EnableMenuItem,hMenu,0;IDM_OPEN=0
    139.         invoke EnableMenuItem,hMenu,ZZZ_SAVE
    140.         invoke CloseHandle,hFile
    141.         jmp wmBYE
    142. wmCREATE:mov [rdi+OPENFILENAME.hwndOwner],rcx
    143.         push rbx
    144.         push IMAGE_BASE
    145.         push EditID
    146.         push rcx;hWnd
    147.         push rbx
    148.          push rbx
    149.         push rbx
    150.         push rbx
    151.         mov edx,offset EditClass
    152.         sub esp,20h
    153.         invoke CreateWindowEx,0,,0,WS_VISIBLE or \
    154.   WS_CHILD or ES_LEFT or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or\
    155.   ES_AUTOHSCROLL or ES_AUTOVSCROLL
    156.         mov hwndEdit,rax
    157.         jmp wmBYE
    158. wmCOMMAND:movzx rax,word ptr wParam
    159.         or r9,r9 ;cmp lParam,0
    160.         jnz wmBYE
    161.         cmp rax,ZZZ_EXIT
    162.         ja  wmBYE
    163.          jmp  [menu_handlers+rax*8]
    164. EXIT: invoke DestroyWindow
    165. wmBYE:  leave
    166.         retn
    167. menu_handlers dq wmBYE,OPEN,SAVE,EXIT
    168. WndProc endp
    169. ;data
    170. ClassName     db 'Win64 Iczelion''s lesson #13: Memory Mapped File',0
    171. dlgOpenTitle  db 'Open File',0
    172. align 8
    173. dlgOpenOfn    OPENFILENAME <{sizeof OPENFILENAME},,IMAGE_BASE,\
    174. FilterString,,,,dOpenBuffer,{MAXSIZE},,,,,,,,,,,,,,>
    175. FilterString  db 'All Files (*.*)',0,'*.*',0
    176.       db 'Text Files (*.txt)',0,'*.txt',0,0
    177. dOpenBuffer   db MAXSIZE dup(?)
    178. hwndEdit      dq ? ;handle for edit control
    179. EditClass     db "edit",0
    180. hMapping dq ? ;file mapped handle
    181. pMapping dq ?
    182. FileSize dq ?
    183. hMenu dq ?
    184. end

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

    Код (ASM):
    1.   invoke CreateFile,ADDR buffer,GENERIC_READ,0,\
    2.           NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL
    Когда пользователь выбирает файл в окне открытия файла, мы вызываем CreateFile, чтобы открыть его. Заметьте, что мы указываем GENERIC_READ, чтобы открыть этот файл в режиме read-only, потому что мы не хотим, чтобы какие-либо другие процессы изменяли файл во время нашей работы с ним.
    Код (ASM):
    1. invoke CreateFileMapping, hFileRead, NULL, PAGE_READONLY,0,0,NULL
    Затем мы вызываем CreateFileMaping, чтобы создать файл проецируемый в память из открытого файла.
    CreateFileMapping имеет следующий синтаксис:
    Код (C):
    1. HANDLE WINAPI CreateFileMapping(
    2.   _In_ HANDLE  hFile, //дескриптор файла, проецируемого на адресное пространство процесса
    3.   _In_opt_ LPSECURITY_ATTRIBUTES lpAttributes,//указатель на структуру SECURITY_ATTRIBUTES, которая относится к объекту "проекция файла", для установки защиты по умолчанию ему присваивается NULL
    4.   _In_ DWORD  flProtect,//желательные атрибуты защиты
    5.   _In_ DWORD  dwMaximumSizeHigh,
    6.   _In_ DWORD  dwMaximumSizeLow,
    7.   _In_opt_ LPCTSTR  lpName
    8. );
    • Вам следует знать, что CreateFileMaping не обязана проецировать весь файл в память. Вы можете спроецировать только часть файла. Размер проецируемого файла вы задаете параметрами dwMaximumSizeHigh и dwMaximumSizeLow. Если вы зададите размер больше, чем его действительный размер, файл будет увеличен до нового размера. Если вы хотите, чтобы проецируемый файл был такого же размера, как и исходный файл, сделайте оба параметра равными нулю.
    • Вы можете использовать NULL в lpFileMappingAttributes, чтобы Windows создала проецируемый файл со значениями безопасности по умолчанию.
    • flProtect ― определяет желаемую защиту для проецируемого файла. В нашем примере, мы используем PAGE_READONLY, чтобы разрешить только операции чтения над проецируемым файлом. Заметьте, что этот атрибут не должен входить в противоречие с атрибутами, указанными в CreateFile, иначе CreateFileMaping возвратит ошибку.
    • lpName ― указывает на имя проецируемого файла. Если вы хотите разделять этот файл с другими процессами, вы должны присвоить ему имя. Hо в нашем примере другие процессы не будут его использовать, поэтому мы игнорируем этот параметр.
    Код (ASM):
    1.                        mov     eax,OFFSET buffer
    2.                        movzx  edx,ofn.nFileOffset
    3.                        add      eax,edx
    4.                        invoke SetWindowText,hWnd,eax
    Если CreateFileMapping выполнилась успешно, мы изменяем название окна на имя открытого файла. Имя файла с полным путем сохраняется в буфере, мы же хотим отобразить только собственно имя файла, поэтому мы должны добавить значение параметра nFileOffset структуры OPENFILENAME к адресу буфера.
    Код (ASM):
    1.                        invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED
    2.                        invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED
    В качестве меры предосторожности, мы не хотим чтобы пользователь мог открыть несколько файлов за pаз, поэтому делаем пункт меню Open недоступным для выбора и делаем доступным пункт Save. EnableMenuItem используется для изменения атрибутов пункта меню. После этого, мы ждем, пока пользователь выберет File/Save или закроет программу.
    Код (ASM):
    1.      .ELSEIF uMsg==WM_DESTROY
    2.  
    3.            .if hMapFile!=0
    4.                call CloseMapFile
    5.            .endif
    6.            invoke PostQuitMessage,NULL
    В выше приведенном коде, когда процедура окна получает сообщение WM_DESTROY, она сначала проверяет значение hMapFile ― равно ли то нулю или нет. Если оно не равно нулю, она вызывает функцию CloseMapFile, которая содержит следующий код:
    Код (ASM):
    1.  CloseMapFile PROC
    2. invoke CloseHandle,hMapFile
    3. mov hMapFile,0
    4. invoke CloseHandle,hFileRead
    5. ret
    6. CloseMapFile endp
    CloseMapFile закрывает проецируемый файл и сам файл, так что наша программа не оставляет за собой следов при выходе из Windows. Если пользователь выберет сохранение информации в другой файл, программа покажет ему окно сохранения файла. После он сможет напечатать имя нового файла, который и будет создать функцией CreateFile.
    Код (ASM):
    1.       invoke MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0
    2.                        mov pMemory,eax
    Когда объект "проекция файла" создан, нужно, чтобы система, зарезервировав регион адресного пространства под данные файла, передала их как физическую память, отображаемую на регион. Это делает функция MapViewOfFile. Эта функция имеет следующий синтаксис:
     
    Последнее редактирование: 2 янв 2017
  9. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Код (C):
    1. LPVOID WINAPI MapViewOfFile(
    2. _In_ HANDLE hFileMappingObject,
    3.  _In_ DWORD  dwDesiredAccess,
    4.  _In_ DWORD  dwFileOffsetHigh,
    5.  _In_ DWORD  dwFileOffsetLow,
    6.  _In_ SIZE_T dwNumberOfBytesToMap
    7. );
    • dwDesiredAccess определяет, какую операцию мы хотим совершить над файлом. В нашем примере мы хотим только прочитать данные, поэтому мы используем FILE_MAP_READ.
    • dwFileOffsetHigh и dwFileOffsetLow задают стартовый файловое смещение файловой порции, которую вы хотите загрузить в память. В нашем случае нам нужно мы хотим читать весь файл, поэтому начинаем мэппинг со смещение ноль.
    • dwNumberOfBytesToMap задает количество байтов, которое нужно промэппировать в память. Чтобы сделать это со всем файлом, передайте ноль MapViewOfFile.
    После вызова MapViewOfFile, желаемое количество загружается в память. Вы получите указатель на блок памяти, который содержит данные из файла.
    Код (ASM):
    1.    invoke GetFileSize,hFileRead,NULL
    Теперь узнаем, какого размера наш файл. Размер файла возвращается в rax. Если файл больше, чем 4 GB, то верхнее двойное слово размера файла сохраняется в FileSizeHighWord. Так как мы не ожидаем встретить таких больших файлов, мы можем проигнорировать это.
    Код (ASM):
    1.    invoke WriteFile,hFileWrite,pMemory,eax,ADDR SizeWritten,NULL
    Запишем данные в выходной файл.
    Код (ASM):
    1.   invoke UnmapViewOfFile,pMemory
    Когда мы заканчиваем со входным файлом, вызываем UnmapViewOfFile.
    Код (ASM):
    1.     call   CloseMapFile
    2.                        invoke CloseHandle,hFileWrite
    И закрываем все файлы.
    Код (ASM):
    1.  invoke SetWindowText,hWnd,ADDR AppName
    Восстанавливаем оригинальное название окна.
    Код (ASM):
    1.  invoke EnableMenuItem,hMenu,IDM_OPEN,MF_ENABLED
    2.                        invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED
    разрешаем доступ к пункту меню Open и запрещаем к Save As.
     
  10. Mikl___

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

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

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

    [​IMG]
    Здесь мы изучим, что такое процесс, как его создать и как прервать.
    Скачайте пример здесь.

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

    Что такое процесс? "Процесс ― это выполняющееся приложение, которое состоит из личного виртуального адресного пространства, кода, данных и других ресурсов операционной системы, таких как файлы, пайпы и синхронизационные объекты, видимые для процесса."
    Как вы можете видеть из вышеприведенного определения, у процесса есть несколько объектов: адресное пространство, выполняемый модуль (модули) и все, что эти модули создают или открывают. Как минимум, процесс должен состоять из выполняющегося модуля, личного адресного пространства и ветви. У каждого процесса по крайней мере одна ветвь. Что такое ветвь?
    Фактически, ветвь ― это выполняющаяся очередь. Когда Windows впервые создает процесс, она делает только одну ветвь на процесс. Эта ветвь обычно начинает выполнение с первой инструкции в модуле. Если в дальнейшем понадобится больше ветвей, он может сам создать их.
    Когда Windows получает команду для создания процесса, она создает личное адресное пространство для процесса, а затем она загружает исполняемый файл в пространство. После этого она создает основную ветвь для процесса.
    Под Win64 вы также можете создать процессы из своих программ с помощью функции CreateProcess. Она имеет следующих синтаксис:
    Код (C):
    1. BOOL WINAPI CreateProcess(
    2.   _In_opt_ LPCTSTR  lpApplicationName,
    3.   _Inout_opt_ LPTSTR  lpCommandLine,
    4.   _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
    5.   _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    6.   _In_ BOOL  bInheritHandles,
    7.   _In_ DWORD  dwCreationFlags,
    8.   _In_opt_ LPVOID  lpEnvironment,
    9.   _In_opt_ LPCTSTR  lpCurrentDirectory,
    10.   _In_ LPSTARTUPINFO  lpStartupInfo,
    11.   _Out_ LPPROCESS_INFORMATION lpProcessInformation
    12. );
    Не пугайтесь количества параметров. Большую их часть мы можем игнорировать.
    • lpAplicationName ― имя исполняемого файла с или без пути, который вы хотите запустить. Если параметр равен нулю, вы должны предоставить имя исполняемого файла в параметре lpCommandLine.
    • lpCommandLine ― Аргументы командной строки к программе, которую вам требуется запустить. Заметьте, что если lpAplicationName равен нулю, этот параметр должен содержать также имя исполняемого файла. Например так: "notepad.exe readme.txt".
    • lpProcessAttributes и lpThreadAttributes ― укажите атрибуты безопасности для процесса и основной ветви. Если они равны NULL, то используются атрибуты безопасности по умолчанию.
    • bInheritHandles ― флаг, который указывает, хотите ли вы, чтобы новый процесс наследовал все открытые дескрипторы из вашего процесса.
    • dwCreationFlags ― несколько флагов, которые определяют поведение процесса, который вы хотите создать, например, хотите ли вы, чтобы процесс был создан, но тут же приостановлен, чтобы вы могли проверить его или изменить, прежде, чем он запустится. Вы также можете указать класс приоритета ветви(ей) в новом процессе. Этот класс приоритета используется, чтобы определить планируемый приоритет ветвей внутри процесса. Обычно мы используем флаг NORMAL_PRIORITY_CLASS.
    • lpEnviroment ― указатель на блок памяти, который содержит несколько переменных окружения для нового процесса. Если этот параметр равен NULL, новый процесс наследует их от родительского процесса.
    • lpCurrentDirectory ― указатель на строку, которая указывает текущий диск и директорию для дочернего процесса. NULL ― если вы хотите, чтобы дочерний процесс унаследовал их от родительского процесса.
    • lpStartupInfo ― указывает на структуру STARTUPINFO, которая определяет, как должно появиться основное окно нового процесса. Эта структура содержит много членов, которые определяют появление главного окна дочернего процесса. Если вы не хотите ничего особенного, вы можете заполнить данную структуру значениями родительского процесса, вызвав функцию GetStartupInfo.
    • lpProcessInformation ― указывает на структуру PROCESS_INFORMATION, которая получает идентификационную информацию о новом процессе. Структура PROCESS_INFORMATION имеет следующие параметры:
    Код (ASM):
    1. PROCESS_INFORMATION STRUCT
    2.            hProcess          HANDLE ? ;дескриптор дочернего процесса
    3.            hThread            HANDLE ?;дескриптор основной ветви дочернего процесса
    4.            dwProcessId     DWORD ? ;ID дочернего процесса
    5.            dwThreadId      DWORD ?  ;ID основной ветви
    6.  PROCESS_INFORMATION ENDS
    Дескриптор процесса и ID процесса ― это две разные вещи. ID процесса ― это уникальный идентификатор процесса в системе. Дескриптор процесса ― это значение, возвращаемое Windows для использования другими API-функциями, связанными с процессами. Дескриптор процесса не может использоваться для идентификации процесса, так как он не уникален.
    После вызова функции CreateProcess, создается новый процесс и функция сразу же возвращается. Вы можете проверить, является ли еще процесс активным, вызвав функцию GetExitCodeProcess, которая имеет следующий синтаксис:
    Код (C):
    1. BOOL WINAPI GetExitCodeProcess(
    2.   _In_ HANDLE  hProcess,
    3.   _Out_ LPDWORD lpExitCode
    4. );
    Если вызов этой функции успешен, lpExitcode будет содержать код выхода запрашиваемого процесса. Если значение в lpExitCode равно STILL_ACTIVE, тогда это означает, что процесс по-прежнему запущен.
    Вы можете принудительно прервать процесс, вызвав функцию TerminateProcess.
    У нее следующий синтаксис:
    Код (C):
    1. BOOL WINAPI TerminateProcess(
    2.   _In_ HANDLE hProcess,
    3.   _In_ UINT  uExitCode
    4. );
    Вы можете указать желаемый код выхода для процесса, любое значение, какое захотите. TerminateProcess ― не лучший путь прервать процесс, так как любые используемые им dll не будут уведомлены о том, что процесс был прерван.

    Практика ― сестра шизофрении

    Следующий пример создаст новый процесс, когда пользователь выберет пункт меню "create process". Он попытается запустить "msgbox.exe". Если пользователь захочет прервать новый процесс, он может выбрать пункт меню "terminate process". Программа будет сначала проверять, уничтожен ли уже новый процесс, если нет, программ вызовет TerminateProcess для этого.

    rc-файл

    Код (C):
    1. #define MI_PROCESS_CREATE 1
    2. #define MI_PROCESS_TERMINATE 2
    3. #define MI_EXIT 3
    4. #define IDR_MAINMENU 30
    5.  
    6. IDR_MAINMENU MENU
    7. {
    8.          POPUP "&Process"
    9.         {
    10.                   MENUITEM "&Create Process",MI_PROCESS_CREATE
    11.                   MENUITEM "&Terminate Process",MI_PROCESS_TERMINATE,GRAYED
    12.                   MENUITEM SEPARATOR
    13.                   MENUITEM "E&xit",MI_EXIT
    14.         }
    15. }
     

    Вложения:

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

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

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

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE  equ 400000h
    3. MI_PROCESS_CREATE equ 1
    4. MI_PROCESS_TERMINATE equ 2
    5. MI_EXIT  equ 3
    6. IDR_MAINMENU equ 30
    7.  
    8. .code
    9. WinMain proc
    10. local msg:MSG
    11.  
    12.         xor ebx,ebx
    13. mov edi,offset ClassName
    14. mov esi,IMAGE_BASE
    15. mov eax,10029h
    16. push rax  ;hIconSm
    17. push rdi ;lpszClassName
    18. push IDR_MAINMENU;lpszMenuName
    19. push COLOR_WINDOW;hbrBackground
    20. push 10005h ;hCursor
    21. push rax        ;hIcon
    22. push rsi ;hInstance
    23. push rbx        ;cbClsExtra & cbWndExtra
    24. db 68h
    25. dd WndProc;lpfnWndProc
    26. push sizeof WNDCLASSEX;cbSize & style
    27. invoke RegisterClassEx,esp ;addr WNDCLASSEX
    28. push rbx
    29. push rsi ;rsi=400000h
    30. shl esi,9 ;rsi=CW_USEDEFAULT
    31. push rbx
    32. push rbx
    33. push 200
    34. push 400
    35. push rsi
    36. push rsi
    37. sub esp,20h
    38.     invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    39. WS_OVERLAPPEDWINDOW or WS_VISIBLE
    40. invoke GetMenu,eax
    41. mov hMenu,rax
    42.     lea edi,msg
    43. @@:     invoke GetMessage,edi,0,0,0
    44. invoke DispatchMessage,edi
    45.         jmp @b
    46. WinMain endp
    47.  
    48. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    49. local progStartInfo:STARTUPINFO
    50.  
    51.        mov edi,offset processInfo
    52.         mov esi,offset proExitCode
    53.         mov hWnd,rcx
    54.         mov wParam,r8
    55.         mov lParam,r9
    56.  
    57.         cmp  edx,WM_DESTROY
    58.         je   wmDESTROY
    59.         cmp  edx,WM_COMMAND
    60.         je   wmCOMMAND
    61.         cmp  edx,WM_INITMENUPOPUP
    62.         je   wmINITMENUPOPUP
    63.         leave
    64.         jmp DefWindowProc
    65.  
    66. wmDESTROY:invoke ExitProcess,0
    67. wmINITMENUPOPUP:invoke GetExitCodeProcess,[rdi+PROCESS_INFORMATION.hProcess],esi
    68. or eax,eax
    69. jz @f;GetExitCodeProcess_TRUE
    70. cmp dword ptr [rsi],STILL_ACTIVE;cmp     [proExitCode],STILL_ACTIVE
    71. jne @f;     GetExitCodeProcess_STILL_ACTIVE
    72. xor r9,r9;MF_ENABLED
    73. mov r8d,MF_GRAYED
    74. jmp @0
    75. @@: mov r9d,MF_GRAYED
    76. xor r8,r8;MF_ENABLED
    77. @0: invoke EnableMenuItem,hMenu,0;MI_PROCESS_CREATE
    78.         invoke EnableMenuItem,hMenu, MI_PROCESS_TERMINATE, MF_ENABLED,MF_GRAYED
    79.         jmp wmBYE
    80. wmCOMMAND:movzx rax,word ptr wParam
    81.         or r9,r9 ;cmp lParam,0
    82. jnz wmBYE
    83. cmp rax,MI_EXIT
    84. ja  wmBYE
    85. jmp  [menu_handlers+eax*8]
    86.  
    87. PROCESS_CREATE:cmp [rdi+PROCESS_INFORMATION.hProcess],rbx
    88. je pi_hProcess_IS_0
    89. invoke CloseHandle,[edi+PROCESS_INFORMATION.hProcess]
    90. mov [rdi+PROCESS_INFORMATION.hProcess],rbx
    91. pi_hProcess_IS_0:
    92. lea esi,progStartInfo
    93. invoke GetStartupInfo,esi
    94. push rdi
    95. push rsi
    96. push rbx
    97. push rbx
    98. push NORMAL_PRIORITY_CLASS
    99. push rbx
    100. mov ecx,offset progName
    101. sub esp,20h
    102. invoke CreateProcess,,0,0,0
    103. mov rcx,[rdi+PROCESS_INFORMATION.hThread]
    104. call CloseHandle
    105. jmp wmBYE
    106. TERMINATE:invoke GetExitCodeProcess,[rdi+PROCESS_INFORMATION.hProcess], esi;proExitCode
    107. cmp dword ptr [rsi],STILL_ACTIVE
    108. jne proExitCode_NOT_STILL_ACTIVE;a4;
    109. invoke TerminateProcess,[rdi+PROCESS_INFORMATION.hProcess],0
    110. proExitCode_NOT_STILL_ACTIVE:
    111. invoke CloseHandle,[rdi+PROCESS_INFORMATION.hProcess]
    112. mov [rdi+PROCESS_INFORMATION.hProcess],rbx;0
    113.         jmp wmBYE
    114.  
    115. EXIT: ;mov rcx,hWnd ;ax=MI_EXIT
    116.         invoke DestroyWindow
    117. wmBYE:  leave
    118.         retn
    119. menu_handlers dq wmBYE,PROCESS_CREATE, TERMINATE, EXIT
    120. WndProc endp
    121. ;data
    122. ClassName       db 'Win64 Iczelion''s lesson #14: Process',0
    123. hMenu dq ?
    124. proExitCode dq ?;process exit code
    125. progName db 'msgbox.exe',0
    126. processInfo PROCESS_INFORMATION <>
    127. end

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

    Программа создает основное окно и получает дескриптор меню для последующего использования. Затем она ждет, пока пользователь выберет команду в меню.
    Когда пользователь выберет "process", мы обрабатываем сообщение WM_INITMENUPOPUP, чтобы изменить пункты меню.
    Код (ASM):
    1. .ELSEIF uMsg==WM_INITMENUPOPUP
    2.            invoke GetExitCodeprocess,processInfo.hProcess,ADDR ExitCode
    3.            .if eax==TRUE
    4.                .if ExitCode==STILL_ACTIVE
    5.                    invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_GRAYED
    6.                    invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_ENABLED
    7.                .else
    8.                    invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
    9.                    invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
    10.                .endif
    11.            .else
    12.                invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
    13.                invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
    14.            .endif
    Почему мы хотим обработать это сообщение? Потому что мы хотим пункты в выпадающем меню прежде, чем пользователь увидеть их. В нашем примере, если новый процесс еще не стартовал, мы хотим разрешить "start process" и запретить доступ к пункту "terminate process". Мы делаем обратное, если программа уже запущена.
    Вначале мы проверяем, активен ли еще новый процесс, вызывая функцию GetExitCodeProcess и передавая ей дескриптор процесса, полученный при вызове CreateProcess. Если GetExitCodeprocess возвращает FALSE, это значит, что процесс еще не был запущен, поэтому запрещаем пункт "terminate process".
    Если GetExitCodeProcess возвращает TRUE, мы знаем, что новый процесс уже стартовал, мы должны проверить, выполняется ли он еще. Поэтому мы сравниваем значение в ExitCode со значением STILL_ACTIVE, если они равны, процесс еще выполняется: мы должны запретить пункт меню "start process", так как мы не хотим, чтобы запустилось несколько совпадающих процессов.
    Код (ASM):
    1. .if ax==IDM_CREATE_PROCESS
    2.                    .if processInfo.hProcess != 0
    3.                         invoke CloseHandle,processInfo.hProcess
    4.                         mov processInfo.hProcess,0
    5.                     .endif
    6.                     invoke GetStartupInfo,ADDR startInfo
    7.                     invoke Createprocess,ADDR programname,NULL,NULL,NULL,FALSE,\
    8.                                                                          NORMAL_PRIORITY_CLASS,\
    9.                                             NULL,NULL,ADDR startInfo,ADDR processInfo
    10.                     invoke CloseHandle,processInfo.hThread
    Когда пользователь выбирает пункт "start process", мы вначале проверяем, закрыт ли уже параметр hProcess структуры PROCESS_INFORMATION. Если это в первый раз, значение hProcess будет всегда равно нулю, так как мы определяем структуру PROCESS_INFORMATION в секции .data. Если значение параметра hProcess не равно нулю, это означает, что дочерний процесс вышел, но мы не закрыли его дескриптор. Поэтому пришло время сделать это.

    Мы вызываем функцию GetSturtupInfo, чтобы заполнить структуру sturtupinfo, которую передаем функцию CreateProcess. После этого мы вызываем функцию CreateProcess. Заметьте, что я не проверил возвращаемое ей значение, потому что это усложнило бы пример. Вам следует проверять это значение. Сразу же после CreateProcess, мы закрываем дескриптор основной ветви, возвращаемой в структуре processInfo. Закрытие дескриптора не означает, что мы прерываем ветвь, только то, что мы не хотим использовать дескриптор для обращения к ветви из нашей программы. Если мы не закроем его, это вызовет потерю ресурсов.
    Код (ASM):
    1.  .elseif ax == IDM_TERMINATE
    2.                    invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
    3.                     .if ExitCode==STILL_ACTIVE
    4.                          invoke TerminateProcess,processInfo.hProcess,0
    5.                     .endif
    6.                     invoke CloseHandle,processInfo.hProcess
    7.                     mov processInfo.hProcess,0
    Когда пользователь выберет пункт меню "terminate process", мы проверяем, активен ли еще новый процесс, вызвав функцию GetExitCodeProcess. Если он еще активен, мы вызываем функцию TerminateProcess, чтобы убить его. Также мы закрываем дескриптор дочернего процесса, так как он больше нам не нужен.
     
    Последнее редактирование: 7 май 2019
  12. Mikl___

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

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

    Глава тридцать четвертая. Братец Кролик и треды (ветви)

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

    Теория ― мать склероза

    В предыдущей главе, вы изучили процесс, состоящий по крайней мере из одного треда: основного. Тред ― это цепь инструкций. Вы также можете создавать дополнительные треды в вашей программе. Вы можете считать мультитрединг как многозадачность внутри одной программы. Если говорить в терминах непосредственной реализации, тред ― это функция, которая выполняется параллельно с основной программой. Вы можете запустить несколько экземпляров одной и той же функции или вы можете запустить несколько функций одновременно, в зависимости от ваших требований. Мультитрединг свойственен Win32 и Win64, под Win16 аналогов не существует.
    Треды выполняются в том же процесс, поэтому они имеют доступ ко всем ресурсам процесса: глобальным переменным, дескрипторам и так далее. Тем не менее, каждый тред имеет свой собственный стек, так что локальные переменные в каждом треде приватны. Каждый тред также имеет свой собственный набор регистров, поэтому когда Windows переключается на другой тред, предыдущий "запоминает" свое состояние и может "восстановить" его, когда он снова получает контроль. Это обеспечивается внутренними средствами Windows. Мы можем поделить треды на две категории:
    1. Тред интерфейса пользователя: тред такого типа создает свое собственное окно, поэтому он получает оконные сообщения. Он может отвечать пользователю с помощью своего окна. Этот тип тредов действуют согласно Win16 Mutex правилу, которое позволяет только один тред пользовательского интерфейсав 16-битном пользовательском и gdi-ядре. Пока один подобный тред выполняет код 16-битного пользовательского и gdi-ядра, другие UI треды не могут использовать сервисы этого ядра. Заметьте, что этот Win16 Mutex свойственен Windows 9x, так как его функции обращаются к 16-битному коду. В Windows NT нет Win16 Mutex'а, поэтому треды пользовательского интерфейса под NT работают более плавно, чем под Windows 95.
    2. рабочий тред: Этот тип тредов не создает окно, поэтому он не может принимать какие-либо windows-сообщения. Он существует только для того, чтобы делать предназначенную ему работу на заднем фоне (согласно своему названию).
    Я советую следующую стратегию при использовании мультитредовых способностей Win32: позвольте основному треду делать все, что связанно с пользовательским интерфейсом, а остальным делать тяжелую работу в фоновом режиме. В этому случае, основной тред ― Правитель, другие треды ― его помощники. Правитель поручает им определенные задания, в то время как сам общается с публикой. Его помощники послушно выполняют работу и докладывают об этом Правителю. Если бы Правитель делал всю работу сам, он бы не смог уделять достаточно внимания народу или прессе. Это похоже на окно, которое занято продолжительной работой в основном треде: оно не отвечает пользователю, пока работа не будет выполнена. Такая программа может быть улучшена созданием дополнительного треда, который возьмет часть работы на себя и позволит основной ветви отвечать на команды пользователя.
    Мы можем создать тред с помощью вызова функции CreateThread, которая имеет следующий синтаксис:
    Код (C):
    1. HANDLE WINAPI CreateThread(
    2.   _In_opt_ LPSECURITY_ATTRIBUTES  lpThreadAttributes,
    3.   _In_ SIZE_T  dwStackSize,
    4.   _In_ LPTHREAD_START_ROUTINE lpStartAddress,
    5.   _In_opt_ LPVOID  lpParameter,
    6.   _In_ DWORD  dwCreationFlags,
    7.   _Out_opt_ LPDWORD  lpThreadId
    8. );
    Функция CreateThread похожа на CreateProcess.
    • lpThreadAttributes ― Вы можете использовать NULL, если хотите, чтобы у треда были установки безопасности по умолчанию.
    • dwStackSize ― укажите размер стека треда. Если вы хотите, чтобы тред имел такой же размер стека, как и у основного, используйте NULL в качестве параметра.
    • lpStartAddress ― адрес функции треда. Эта функция будет выполнять предназначенную для треда работу. Эта функция должна получать один и только один 32-битный параметр и возвращать 32-битное значение.
    • lpParametr ― Параметр, который вы хотите передать функции треда.
    • dwCreationFlags ― 0 означает, что тред начинает выполняться сразу же после его создания. Для обратного можно использовать флаг CREATE_SUSPEND.
    • lpThreadId ― CreateThread поместит сюда ID созданного треда.
    Если вызов CreateThread прошел успешно, она возвращает дескриптор созданного треда, в противном случае она возвращает NULL.
     

    Вложения:

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

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Функция треда запускается так скоро, как только заканчивается вызов CreateThread, если только вы не указали флаг CREATE_SUSPENDED. В этом случае тред будет заморожен до вызова функции ResumThread.
    Когда функция треда возвращается (с помощью инструкции ret) Windows косвенно вызывает ExitThread для функции треда. Вы можете сами вызвать ExitThread, но в этом немного смысла.
    Вы можете получить код выхода треда с помощью функции GetExitCodeThread.
    Если вы хотите прервать тред из другого треда, вы можете вызвать функцию TerminateThread. Hо вы должны использовать эту функцию только в экстремальных условиях, так как эта функция немедленно прерывать тред, не давая ему шанса произвести необходимую чистку за собой.
    Теперь давайте рассмотрим методы коммуникации между тредами. Вот три из них:
    • Использование глобальных переменных
    • Windows-сообщения
    • События
    Треды разделяют ресурсы процесса, включая глобальные переменные, поэтому треды могут использовать их для того, чтобы взаимодействовать друг с другом. Тем не менее, этот метод должен использоваться осторожно.
    Синхронизацию нужно внимательно спланировать. Например, если два треда используют одну и ту же структуру из 10 членов, что произойдет, если Windows вдруг передаст управление от одного треда другому, когда структура обновлена еще только наполовину. Другой тред получит неправильную информацию!
    Не сделайте никакой ошибки, мультитредовые программы тяжелее отлаживать и поддерживать. Этот тип ошибок появляется непредсказуемо и их очень трудно отловить.
    Вы также можете использовать windows-сообщения, чтобы осуществлять взаимодействие между тредами. Если все треды имеют пользовательский интерфейс, то нет проблем: этот метод может использоваться для двухсторонней коммуникации. Все, что вам нужно сделать ― это определить один или более дополнительных windows-сообщений, которые будут использоваться тредами.
    Вы определяете сообщение, используя значение WM_USER как базовое, например так:
    Код (ASM):
    1.  WM_MYCUSTOMMSG equ WM_USER+100h
    Windows не использует сообщения с номером выше WM_USER, поэтому мы можем использовать значение WM_USER и выше для наших собственных сообщений.
    Если один из тредов имеет пользовательский интерфейс, а другой является рабочим, вы не можете использовать данный метод для двухстороннего общения, так как у рабочего треда нет своего окна, а следовательно и очереди сообщений. Вы можете использовать следующие схемы:
    • Тред с пользовательским интерфейсом [math]\to[/math] глобальная переменная [math]\to[/math] рабочий тред
    • рабочий тред [math]\to[/math] windows-сообщение [math]\to[/math] Тред с пользовательским интерфейсом
    Фактически, мы будем использовать этот метод в нашем примере.
    Последний метод, используемый для коммуникации ― это объект события. Вы можете рассматривать его как своего рода флаг. Если объект события "не установлен", значит тред спит. Когда объект события "установлен", Windows "пробуждает" тред и он начинает выполнять свою работу.

    Практика ― сестра шизофрении

    Вам следует скачать zip-файл с примером запустить thread1.exe. Нажмите на пункт меню "Savage Calculation". Это даст команду программе выполнить "add eax,eax" 600.000.000 раз. Заметьте, что во время этого времени вы не сможете ничего сделать с главным окном: вы не сможете его двигать, активировать меню и так далее. Когда вычисление закончится, появится окно с сообщением. После этого окно будет нормально реагировать на ваши команды.
    Чтобы избежать подобного неудобства для пользователя, мы должны поместить процедуру вычисления в отдельный рабочий тред и позволить основному треду продолжать взаимодействие с пользователем. Вы можете видеть, что хотя основное окно отвечает медленнее, чем обычно, оно все же делает это.

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE  equ 400000h
    3. MI_THREAD_CREATE equ 0
    4. MI_EXIT  equ 1
    5. WMU_THREAD_FINISH    equ WM_USER + 100h
    6. IDR_MAINMENU equ 30
    7.  
    8. .code
    9. WinMain proc
    10. local msg:MSG
    11.  
    12.         xor ebx,ebx
    13. mov edi,offset ClassName
    14. mov esi,IMAGE_BASE
    15. mov eax,10029h
    16. push rax  ;hIconSm
    17. push rdi ;lpszClassName
    18. push IDR_MAINMENU;lpszMenuName
    19. push COLOR_WINDOW;hbrBackground
    20. push 10005h ;hCursor
    21. push rax        ;hIcon
    22. push rsi ;hInstance
    23. push rbx        ;cbClsExtra & cbWndExtra
    24. mov eax,offset WndProc
    25. push rax ;lpfnWndProc
    26. push sizeof WNDCLASSEX;cbSize & style
    27. invoke RegisterClassEx,esp ;addr WNDCLASSEX
    28. push rbx
    29. push rsi ;rsi=400000h
    30. shl esi,9 ;rsi=CW_USEDEFAULT
    31. push rbx
    32. push rbx
    33. push 200
    34. push 500
    35. push rsi
    36. push rsi
    37. invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    38. WS_OVERLAPPEDWINDOW or WS_VISIBLE
    39.         mov hWindow,rax
    40. invoke GetMenu,eax
    41. mov hMenu,rax
    42.     lea edi,msg
    43. @@:     invoke GetMessage,edi,0,0,0
    44. invoke DispatchMessage,edi
    45.         jmp @b
    46. WinMain endp
    47.  
    48. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    49. local progStartInfo:STARTUPINFO
    50.  
    51.         mov hWnd,rcx
    52.         cmp  edx,WM_DESTROY
    53.         je   wmDESTROY
    54.         cmp  edx,WM_COMMAND
    55.         je   wmCOMMAND
    56.         cmp  edx,WMU_THREAD_FINISH
    57. je   wmu_THREAD_FINISH
    58.         leave
    59.         jmp DefWindowProc
    60.  
    61. wmDESTROY:invoke ExitProcess,0
    62. wmu_THREAD_FINISH:mov edx,offset ClassName
    63. invoke MessageBox,0,,edx,MB_OK
    64.         jmp wmBYE
    65. wmCOMMAND:cmp    r8w,MI_THREAD_CREATE
    66. je wmCOMMAND_MI_THREAD_CREATE
    67. cmp r8w,MI_EXIT
    68. jne wmBYE
    69. wmCOMMAND_MI_EXIT:call DestroyWindow;,[hWnd]
    70. jmp wmBYE
    71. wmCOMMAND_MI_THREAD_CREATE:
    72. mov eax,offset tId
    73. push rax
    74. push NORMAL_PRIORITY_CLASS
    75. mov r8d,offset thread_procedure
    76. sub esp,20h
    77. invoke CreateThread,NULL,0,,NULL
    78. invoke CloseHandle,eax
    79. wmBYE:  leave
    80.         retn
    81. WndProc endp
    82.  
    83. thread_procedure proc
    84.                    sub rsp,5*8
    85.                    mov rcx,3FFFFFFFh
    86. @@:           add eax,eax
    87.                     loop @b
    88. loopEND:invoke SendMessage,hWindow,WMU_THREAD_FINISH,NULL,NULL
    89.                     add rsp,5*8
    90.                     retn
    91. thread_procedure endp
    92. ;data
    93. ClassName db 'Win64 Iczelion''s lesson #15: Multithreading Programming',0
    94. hMenu   dq ?
    95. hWindow   dq ?
    96. tId         dd ?
    97. end

    rc-файл

    Код (C):
    1. #define MI_THREAD_CREATE 0
    2. #define MI_EXIT 1
    3. #define IDR_MAINMENU 30
    4.  
    5. IDR_MAINMENU MENU
    6. {
    7. POPUP "&Thread"
    8.         {
    9.          MENUITEM "&Create Thread",MI_THREAD_CREATE
    10.          MENUITEM SEPARATOR
    11.          MENUITEM "E&xit",MI_EXIT
    12.         }
    13. }

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

    Основную программу пользователь воспринимает как обычное окно с меню. Если пользователь выбирает в последнем пункт "Создать тред", программа создает тред:
    Код (ASM):
    1. .if ax==IDM_CREATE_THREAD
    2.                    mov  eax,OFFSET Threadproc
    3.                    invoke CreateThread,NULL,NULL,eax,\
    4.                                            NULL,0,ADDR ThreadID
    5.                    invoke CloseHandle,eax
    Вышеприведенная функция создает тред, который запустит процедуру под названием ThreadProc параллельно с основным тредом. Если вызов функции прошел успешно, CreateThread немедленно возвращается и ThreadProc начинает выполняться. Так как мы не используем дескриптор треда, нам следует закрыть его, чтобы не допустить бессмысленное расходование памяти.
    Закрытие дескриптора не прерывает сам тред. Единственным эффектом будет то, что мы не сможем больше использовать его дескриптор.
    Код (ASM):
    1.  ThreadProc proc uses ecx param:DWORD
    2.            mov  ecx,600000000
    3. @@:   add  eax,eax
    4.             loop  @b
    5. Get_out: invoke postMessage,hwnd,WM_FINISH,NULL,NULL
    6.             ret
    7. ThreadProc endp
    Как вы можете видеть ThreadProc выполняет подсчет, требующий некоторого времени, и когда она заканчивает его, она отправляет сообщение WM_FINISH основному окну. WM_FINISH ― это наше собственное сообщение, определенное следующим образом:
    Код (ASM):
    1.      WM_FINISH equ WM_USER+100h
    Вам не обязательно добавлять к WM_USER 100h, но будет лучше сделать это. Сообщение WM_FINISH имеет значение только в пределах нашей программы. Когда основное окно получает WM_FINISH, она реагирует на это показом окна с сообщением о том, что подсчет закончен.
    Вы можете создать несколько тредов, выбрав "Create Thread" несколько раз. В этом примере применяется односторонняя коммуникация, то есть только тред может уведомлять основное окно о чем-либо. Если вы хотите, что основной тред слал команды рабочему, вы должны сделать следующее:
    • добавить пункт меню "Kill Thread".
    • добавить глобальную переменную, используемую в качестве флага. TRUE=остановить тред, FALSE=продолжить тред.
    • Изменить ThreadProc так, чтобы та проверяла в цикле значение флага.
    Когда пользователь выберет "Kill Thread", основная программа установит флаг в TRUE. Когда ThreadProc видит, что значение флага равно TRUE, она выходит из цикла и возвращается, что заканчивает действие треда.
     
    Последнее редактирование: 2 янв 2017
  14. Mikl___

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

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

    Глава тридцать пятая. Братец Кролик и объект события

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

    Теория ― мать склероза

    В предыдущей главе демонстрировалось, как треды взаимодействуют друг с другом через собственные windows-сообщения. Пропущено два других метода:
    • глобальная переменная
    • объект события
    В этой главе будут использованы оба метода.
    Объект события ― это что-то вроде переключателя: у него есть только два состояния: вкл и выкл. Вы создаете объект события и помещаете его в коде соответствующего треда, где наблюдаете за состояние объекта. Если объект события выключен, ждущие его треды будут "спать". В подобном состоянии треды мало загружают CPU.
    Вы можете создать объект события, вызвав функцию CreateEvent, которая имеет следующий синтаксис:
    Код (C):
    1. HANDLE WINAPI CreateEvent(
    2.   _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
    3.   _In_ BOOL  bManualReset,
    4.   _In_ BOOL  bInitialState,
    5.   _In_opt_ LPCTSTR  lpName
    6. );
    • lpEventAttribute ― Если вы укажете значение NULL, у создаваемого объекта будут установки безопасности по умолчанию.
    • bManualReset ― Если вы хотите, чтобы Windows автоматически переключал объект события в "выключено", вы должны присвоить этому параметру значение FALSE. Иначе вам надо будет выключить объект вручную с помощью вызова ResetEvent.
    • bInitialStae ― Если вы хотите, чтобы объект события при создании был установлен в положение "включено", укажите TRUE в качестве данного параметра, в противном случае объект события будет установлен в положение "выключен".
    • lpName ― Указатель на ASCIIZ-строку, которая будет именем объекта события. Это имя будет использоваться, когда вы захотите вызвать OpenEvent.
    Если вызов прошел успешно, CreateEvent возвратит дескриптор на созданный объект события. В противном случае она возвратит NULL.
    Вы можете изменять состояние объекта события с помощью двух API-функций:
    • SetEvent
    • ResetEvent
    Функция SetEvent устанавливает объект события в положение "включено". ResetEvent делает обратное.
    Когда объект события создан, вы должны поместить вызов функции WaitForSingleObject в тред, который должен следить за состоянием объекта события. Эта функция имеет следующий синтаксис:
    Код (C):
    1. DWORD WINAPI WaitForSingleObject(
    2.   _In_ HANDLE hHandle,
    3.   _In_ DWORD  dwMilliseconds
    4. );
    • hObject ― дескриптор одного из синхронизационных объектов. Объект события ― это вид синхронизационного события.
    • dwTimeout ― Указывает в миллисекундах время, которое эта функция будет ждать, пока объект события не перейдет во включенное состояние. Если указанное время пройдет, а объект события все еще выключен, WaitForSingleObject вернет управление. Если вы хотите, чтобы функция наблюдала за объектом бесконечно, вы должны указать значение INFINITE в качестве этого параметра.

    Практика ― сестра шизофрении

    Нижеприведенный пример отображает окно, ожидающее пока пользователь не выберет какую-либо команду из меню. Если пользователь нажмет на "run thread", тред начнет подсчет. Когда счет закончится, появится сообщение, информирующее пользователя о том, что работа выполнена. Во время того, как проводится подсчет, пользователь может выбрать команду "stop thread", чтобы остановить тред.

    asm-файл

    Код (ASM):
    1. include win64a.inc
    2. IMAGE_BASE  equ 400000h
    3. IDM_START_THREAD equ 1
    4. IDM_STOP_THREAD equ 2
    5. IDM_EXIT equ 3
    6. WM_FINISH equ WM_USER+100h
    7. IDR_MAINMENU equ 30
    8.  
    9. .code
    10. WinMain proc
    11. local msg:MSG
    12. xor ebx,ebx
    13.  
    14. mov edi,offset ClassName
    15. mov esi,IMAGE_BASE
    16. mov eax,10029h
    17. push rax  ;hIconSm
    18. push rdi ;lpszClassName
    19. push IDR_MAINMENU;lpszMenuName
    20. push COLOR_WINDOW;hbrBackground
    21. push 10005h ;hCursor
    22. push rax        ;hIcon
    23. push rsi ;hInstance
    24. push rbx        ;cbClsExtra & cbWndExtra
    25. mov eax,offset WndProc
    26. push rax ;lpfnWndProc
    27. push sizeof WNDCLASSEX;cbSize & style
    28. invoke RegisterClassEx,esp ;addr WNDCLASSEX
    29. push rbx
    30. push rsi ;rsi=400000h
    31. shl esi,9 ;rsi=CW_USEDEFAULT
    32. push rbx
    33. push rbx
    34. push 200
    35. push 400
    36. push rsi
    37. push rsi
    38. sub esp,20h
    39.    invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    40. WS_OVERLAPPEDWINDOW or WS_VISIBLE
    41. mov hwnd,rax
    42. invoke GetMenu,eax
    43. mov hMenu,rax
    44.     lea edi,msg
    45. @@:     invoke GetMessage,edi,0,0,0
    46. invoke DispatchMessage,edi
    47.         jmp @b
    48. WinMain endp
    49.  
    50. WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    51.  
    52.         push rbp
    53. mov ebp,esp
    54. sub esp,20h
    55.  
    56. mov hWnd,rcx
    57.         mov wParam,r8
    58.         mov lParam,r9
    59.  
    60.         cmp  edx,WM_DESTROY
    61.         je   wmDESTROY
    62.         cmp  edx,WM_COMMAND
    63.         je   wmCOMMAND
    64.         cmp  edx,WM_CREATE
    65. je   wmCREATE
    66.         cmp  edx,WM_FINISH
    67. je   wmFINISH
    68.         leave
    69.         jmp DefWindowProc
    70.  
    71. wmDESTROY:invoke ExitProcess,0
    72. wmCREATE:invoke CreateEvent,NULL,FALSE,FALSE,NULL
    73. mov hEventStart,rax
    74. db 68h
    75. dd ThreadID
    76. push NORMAL_PRIORITY_CLASS
    77. mov r8d,offset ThrdProc
    78. sub esp,20h
    79. invoke CreateThread, NULL, NULL,,NULL                      
    80. mov hThread,rax
    81. invoke CloseHandle,eax
    82.         jmp wmBYE
    83. wmCOMMAND:movzx rax,word ptr wParam
    84.         cmp r9,rbx ;cmp lParam,0
    85. jnz wmBYE
    86. cmp rax,IDM_EXIT
    87. ja  wmBYE
    88. jmp  [menu_handlers+eax*8]
    89. START_THREAD:invoke SetEvent,hEventStart
    90. invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_GRAYED
    91. invoke EnableMenuItem,hMenu,IDM_STOP_THREAD,MF_ENABLED
    92.         jmp wmBYE
    93. STOP_THREAD:mov EventStop,TRUE
    94. invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_ENABLED
    95. invoke EnableMenuItem,hMenu,IDM_STOP_THREAD,MF_GRAYED
    96.         jmp wmBYE
    97. EXIT: ;mov rcx,hWnd ;ax=MI_EXIT
    98.         invoke DestroyWindow
    99.         jmp wmBYE
    100. wmFINISH:mov r8d,offset ClassName
    101. mov edx,offset SuccessString
    102. invoke MessageBox,NULL,,,MB_OK
    103. wmBYE:  leave
    104.         retn
    105. menu_handlers dq wmBYE, START_THREAD, STOP_THREAD, EXIT
    106. WndProc endp
    107. ;---------------------------------------
    108. ThrdProc:
    109. sub rsp,5*8
    110. invoke WaitForSingleObject,hEventStart,INFINITE
    111.         or  ecx,-1
    112. @0:     cmp EventStop,FALSE
    113. jz @f
    114. mov r8d,offset SuccessString
    115. mov edx,offset StopString
    116. invoke MessageBox,hwnd,,,MB_OK
    117.         mov EventStop,FALSE
    118. add rsp,5*8
    119.         jmp ThrdProc
    120. @@:     add eax,eax
    121.         dec rcx
    122.         jnz @0
    123. invoke PostMessage,hwnd,WM_FINISH,NULL,NULL
    124. invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_ENABLED
    125. invoke EnableMenuItem,hMenu,IDM_STOP_THREAD,MF_GRAYED
    126. add rsp,5*8
    127.         jmp ThrdProc
    128. ;        ret
    129. ;---------------------------------------
    130. ClassName       db 'Win64 Iczelion''s lesson #16: Event Example',0
    131. SuccessString  db "The calculation is completed!",0
    132. StopString  db "The thread is stopped",0
    133. EventStop  BOOL FALSE
    134. hwnd  HANDLE ?
    135. hMenu  HANDLE ?
    136. ThreadID dq ?
    137. ExitCode dq ?
    138. hThread dq ?
    139. hEventStart HANDLE ?
    140. end

    rc-файл

    Код (C):
    1. #define IDM_START_THREAD 1
    2. #define IDM_STOP_THREAD 2
    3. #define IDM_EXIT 3
    4. #define IDR_MAINMENU 30
    5.  
    6. IDR_MAINMENU MENU
    7. {
    8. POPUP "&Thread"
    9.         {
    10.          MENUITEM "&Run Thread",IDM_START_THREAD
    11.          MENUITEM "&Stop Thread",IDM_STOP_THREAD,GRAYED
    12.          MENUITEM SEPARATOR
    13.          MENUITEM "E&xit",IDM_EXIT
    14.         }
    15. }
     

    Вложения:

    • 0.png
      0.png
      Размер файла:
      292,4 КБ
      Просмотров:
      1.856
    Последнее редактирование: 13 янв 2021
  15. Mikl___

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

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

    Анализ:

    В этом примере будет демонстрироваться другая техника работы с тредами.
    Код (ASM):
    1. .IF uMsg==WM_CREATE
    2.            invoke CreateEvent,NULL,FALSE,FALSE,NULL
    3.            mov  hEventStart,eax
    4.            mov  eax,OFFSET Threadproc
    5.            invoke CreateThread,NULL,NULL,eax,\
    6.                                 NULL,0,ADDR ThreadID
    7.            invoke CloseHandle,eax
    Вы можете видеть, что создан объект события и тред во время обработки сообщения WM_CREATE. Я создаю объект события, установленного в состояние "выключено" и обладающего свойством автоматического выключения. После того, как объект события создан, я создаю тред. Тем не менее, тред не начинает выполняться немедленно, так как он ждет, пока не включится объект события:
    Код (ASM):
    1. ThreadProc proc uses ecx param:DWORD
    2.            invoke WaitForSingleObject,hEventStart,INFINITE
    3.            mov  ecx,600000000
    Первая строка процедуры треда ― это вызов WainForSingleObject. Она ждет, пока не включится объект события, а затем возвращается. Это означает, что даже если тред создан, мы помещаем его в спящее состояние.
    Когда пользователь выбирает в меню команду "run thread", мы включаем объект события:
    Код (ASM):
    1. .if ax==IDM_START_THREAD
    2.                    invoke SetEvent,hEventStart
    Вызов SetEvent включает объект события, после чего WainForSingleObject возвращается и тред начинает выполняться. Когда пользователь выбирает команду "stoр thread", мы устанавливаем значение глобальной переменной в TRUE.
    Код (ASM):
    1. .if EventStop==FALSE
    2.                            add  eax,eax
    3.                             dec  ecx
    4.                     .else
    5.                             invoke MessageBox,hwnd,ADDR StopString,ADDR AppName,MB_OK
    6.                             mov  EventStop,FALSE
    7.                             jmp Threadproc
    8.                     .endif
    Это останавливает тред и снова передает управление функции WaitForSingleObject. Заметьте, что мы не должны вручную выключать объект, так как мы указали при вызове функции CreateEvent, что значение bManualReset равно FALSE.
     
  16. Mikl___

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

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

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

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

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

    Если вы программируете достаточно долго, вы заметите, что программы, которые вы пишете, зачастую используют один и те же общие процедуры. Из-за того, что вам приходиться переписывать их снова и снова, вы теряете время. Во времена DOS'а программисты сохраняли эти общие процедуры в одной или более библиотеках. Когда они хотели использовать эти функции, они всего лишь прилинковывали библиотеку к объектному файлу и линкер извлекал функции прямо из библиотек и вставлял их в финальный файл. Этот процесс называется статической линковкой. Хорошим примером являются стандартные библиотеки в C. У этого метода есть изъян ― то, что в каждой программе у вас находятся абсолютно одинаковые копии функций. Впрочем, для DOS-овских программ это не очень большой недостаток, так как только одна программа могла быть активной в памяти, поэтому не происходила трата драгоценной памяти.
    Под Windows ситуация стала более критичной, так как у вас может быть несколько программ, выполняющихся одновременно. Память будет быстро пожираться, если ваша программа достаточно велика. У Windows есть решение этой проблемы: динамические библиотеки (dynamic link librariesdll).
    Динамическая библиотека ― это что-то вроде сборника общих функций. Windows не будет загружать несколько копий DLL в память; даже если одновременно выполняются несколько экземпляров вашей программы, будет только одна копия DLL в памяти. Здесь я должен остановиться и разъяснить чуть поподробнее. В реальности, у всех процессов, использующих одну и ту же dll есть своя копия этой библиотеки, однако Windows делает так, чтобы все процессы разделяли один и тот же код этой dll. Впрочем, секция данных копируется для каждого процесса.
    Программа линкуется к DLL во время выполнения в отличии от того, как это осуществлялось в старых статических библиотеках. Вы также можете выгрузить DLL во время выполнения, если она вам больше не нужна. Если программа одна использует эту DLL, тогда та будет выгружена немедленно. Hо если ее еще используют какие-то другие программы, DLL останется в памяти, пока ее не выгрузит последняя из использующих ее программ.
    Как бы то ни было, перед линкером стоит сложная задача, когда он проводит фиксирование адресов в конечном исполняемом файле. Так как он не может "извлечь" функции и вставить их в финальный исполняемый файл, он должен каким-то образом сохранить достаточно информации о DLL и используемых функциях в выходном файле, чтобы тот смог найти и загрузить верную DLL во время выполнения.
    И тут в дело вступают библиотеки импорта. Библиотека импорта содержит информацию о DLL, которую она представляет. Линкер может получить из нее необходимую информацию и вставить ее в исполняемый файл.
    Когда Windows загружает программу в память, она видит, что программа требует DLL, поэтому ищет библиотеку и проецирует ее в адресное пространство процесса и выполняет фиксацию адресов для вызовов функций в DLL.
    Вы можете загрузить DLL самостоятельно, не полагаясь на Windows-загрузчик.
    • В этом случае вам не потребуется библиотека импорта, поэтому вы сможете загружать и использовать любую DLL, даже если к ней не прилагается библиотеки импорта. Тем не менее, вам все равно нужно знать какие функции находятся внутри нее, сколько параметров они принимают и тому подобную информацию.
    • Когда вы поручаете Windows загружать DLL, если та отсутствует, Windows выдаст сообщение "Требуемый .DLL-файл, xxxxx.dll отсутствует" и все! Ваша программ не может сделать ничего, что изменить это, даже если ваша dll не является необходимой. Если же вы будете загружать DLL самостоятельно и библиотека не будет найдена, ваша программа может выдать пользователю сообщение, уведомляющее об этом, и продолжить работу.
    • Вы можете вызывать недокументированные функции, которые не включены в библиотеки импорта, главное, чтобы у вас было достаточно информации об этих функциях.
    • Если вы используете LoadLibrary, вам придется вызывать GetProcAddress для каждой функции, которую вы заходите вызвать. GetProcAddress получает адрес входной точки функции в определенной DLL. Поэтому ваш код будет чуть-чуть больше и медленнее, но не намного.
    Теперь, рассмотрев преимущества и недостатки использования LoadLibrary, мы подробно рассмотрим как создать DLL.
    Следующий код является каркасом DLL.

    tut_17a.asm

    Код (ASM):
    1. include win64a.inc
    2. .code
    3. DllMain proc hInstDLL:QWORD, reason:QWORD, unused:QWORD
    4. sub esp,28h
    5. .if edx==DLL_PROCESS_ATTACH
    6. lea rdx,LoadMsg
    7. .elseif edx==DLL_PROCESS_DETACH
    8. lea rdx,UnloadMsg
    9. .endif
    10. mov r9d,MB_OK
    11. jmp exit
    12. DllMain Endp
    13. TestHello proc
    14. sub esp,28h
    15. lea rdx,HelloMsg
    16. mov r9d,MB_OK + MB_ICONERROR
    17. exit:: lea r8,AppName
    18. invoke MessageBox,0
    19. add esp,28h
    20. mov eax,TRUE
    21. ret
    22. TestHello endp
    23. AppName db "DLL Skeleton",0
    24. HelloMsg db "Hello, you're calling a function in this DLL",0
    25. LoadMsg db "The DLL is loaded",0
    26. UnloadMsg db "The DLL is unloaded",0
    27. end
    Вышеприведенная программа ― это каркас DLL. Каждая DLL должна иметь стартовую функцию. Windows вызывает эту функцию каждый pаз, когда:
    • DLL загружена в первый раз
    • DLL выгружена
    • Создается тред в том же процессе
    • Тред разрушен в том же процессе
    Код (ASM):
    1. DllMain proc hInstDLL:QWORD, reason:QWORD, unused:QWORD
    2.            . . . .
    3.            mov  eax,TRUE
    4.            ret
    5.    DllMain Endp
    Вы можете назвать стартовую функцию как пожелаете, главное чтобы она упоминалась в bat-файле ("/ENTRY: DllMain"). Эта функция получает три параметра, только первые два из них важны.
    • hInstDLL ― это дескриптор модуля DLL. Это не тоже самое, что дескриптор процесса. Вам следует сохранить это значение, так как оно понадобится вам позже. Вы не сможете ее получить в дальнейшем легко.
    • reason ― может иметь одно из следующих четырех значений:
      • DLL_PROCESS_ATTACH ― DLL получает это значение, когда впервые загружается в адресное пространство процесса. Вы можете использовать эту возможность для того, чтобы осуществить инициализацию.
      • DLL_PROCESS_DETACK ― DLL получает это значение, когда выгружается из адресного пространства процесса. Вы можете использовать эту возможность для того, чтобы "почистить" за собой: освободить память и так далее.
      • DLL_THREAD_ATTACK ― DLL получает это значение, когда процесс создает новую ветвь.
      • DLL_THREAD_DETACK ― DLL получает это значение, когда ветвь в процессе уничтожена.
    Вы возвращаете TRUE в eax, если вы хотите, чтобы DLL продолжала выполняться. Если вы возвратите FALSE, DLL не будет загружена. Например, если ваш инициализационный код должен зарезервировать память и он не может это сделать, стартовой функции следует возвратить FALSE, чтобы показать, что DLL не может запуститься.
    Вы можете поместить ваши функции в DLL следом за стартовой функцией или до нее. Hо если вы хотите, чтобы их можно было вызвать из других программ, вы должны поместить их имена в списке экспортируемых функций в файле установок модуля.
    DLL требуется DEF-файл на стадии разработки.

    файла tut_17a.def

    Код (Text):
    1. LIBRARY tut_17a
    2. EXPORTS DllMain
    3. EXPORTS TestHello
    Обычно у вас должна быть первая строка. Ключевое слово LIBRARY определяет внутреннее имя модуля DLL. Желательно, чтобы оно совпадало с именем файла.
    EXPORTS говорит линкеру, какие функции в DLL экспортируются, то есть, могут вызываться из других программ. В прилагающемся примере нам нужно, чтобы другие модули могли вызывать TestFunction, поэтому мы указываем здесь ее имя.
    Другое отличие заключается в параметрах, передаваемых линкеру. Вы должны указать ключи /DLL и /DEF:
    Код (Text):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=tut_17a
    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:DllMain /DLL /ALIGN:16 ^
    8. /stub:%masm64_path%bin\stubby.exe /SECTION:.text,W /DEF:%filename%.def || exit
    9. del %filename%.obj
    10. del %filename%.exp
    Параметры компилятора те же самые, обычно /c /Cp. После компиляции вы получите dll-файл и lib-файл. Последний файл ― это библиотека импорта, которую вы можете использовать, чтобы прилинковать к другим программам функции из соответствующего dll-файла.
     

    Вложения:

    • 0.png
      0.png
      Размер файла:
      319,9 КБ
      Просмотров:
      1.963
  17. Mikl___

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

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

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


    Динамическую библиотеку можно вызывать двумя способами ― явным и неявным связыванием. Неявное связывание ― наиболее широко используемый способ. Здесь с самого начала компилятору строго говорится, что вот так-то и так-то, вот это вот считать dll и поступать с ней вот так, а не иначе. Явное связывание ― это вызов подпрограммы LoadLibrary для загрузки конкретной библиотеки (например, kernel32.dll), а потом подпрограмма GetProcAddress возвращает адрес конкретной функции (например, IsDebuggerPresent), а далее что-то вроде call [адрес, возвращенный GetProcAddress]. Неявное связывание оставляет следы в таблице импорта, которые легко обнаружить. Явное связывание в таблице импорта не отображается.

    Два варианта EXE-файлов, которые будут работать с tut17a.dll


    1. Вариант первый ― загрузка адреса функции TestHello из tut17a.dll операционной системой в секцию импорта.
      Код (ASM):
      1. include win64a.inc
      2. includelib tut_17a.lib
      3.  
      4. TestHello PROTO
      5.  
      6. .code
      7. WinMain proc
      8. sub esp,28h
      9.         call [TestHello]
      10.        invoke ExitProcess,0
      11. WinMain endp
      12. end
    2. Вариант второй ― «динамическая загрузка» tut17a.dll при помощи функции LoadLibrary. Далее определяется адрес процедуры TestHello с помощью GetProcessAddress, после чего осуществляется вызов TestHello. После того как tut17a.dll стала ненужной ― ее выгружают из памяти функцией FreeLibrary (если вы этого не сделаете ― система выгрузит dll автоматически после закрытия приложения ― использование FreeLibrary это программисткая традиция с целью освободить оперативную память от «лишних» приложений :))
    Код (ASM):
    1. include win64a.inc
    2. .code
    3. WinMain proc
    4. sub esp,28h
    5. mov ecx,offset LibName
    6. invoke LoadLibrary
    7. ; Вызываем LoadLibrary и передаем имя желаемой DLL. Если вызов проходит успешно,
    8. ; будет возвращен дескриптор библиотеки (DLL). Если нет, то будет возвращен NULL.
    9. ; Вы можете передать дескриптор библиотеки функции GetProcAddress или любой другой
    10. ; функции, которая требует его в качестве одного из параметров.
    11.         or eax,eax;.if eax==NULL  ;the dll not found
    12.         jnz @f
    13. mov r8d,offset AppName
    14. mov edx,offset DllNotFound
    15.         invoke MessageBox,0,,,MB_OK
    16.         jmp exit;.else
    17. @@:     mov hLib,rax
    18. mov edx,offset FunctionName
    19. invoke GetProcAddress,eax
    20. ; Когда вы получаете дескриптор библиотеки, вы передаете его GetрrocAddress вместе
    21. ; с именем функции в этой dll, которую вы хотите вызвать. Она возвратит адрес
    22. ; функции, если вызов пройдет успешно. В противном случае, она возвратит NULL.
    23. ; Адреса функций не изменятся, пока вы не перезагрузите библиотеку. Поэтому
    24. ; их можно поместить в глобальные переменные для будущего использования
    25.         or eax,eax;.if eax==NULL ;requested function not found
    26. jnz @f
    27. mov r8d,offset AppName
    28. mov edx,offset FunctionNotFound
    29.         invoke MessageBox,0,,,MB_OK
    30.         jmp @0
    31. @@:     call rax
    32. ; вызываем функцию с помощью call
    33. @0: invoke FreeLibrary,hLib
    34. ; вам больше не требуется библиотека, выгружаете ее с помощью FreeLibrary.
    35. exit:   invoke ExitProcess,0
    36. WinMain endp
    37. LibName db "tut_17a.dll",0
    38. FunctionName db "TestHello",0
    39. DllNotFound db "Cannot load library",0
    40. AppName db "Load Library",0
    41. FunctionNotFound db "TestHello function not found",0
    42. hLib dq ? ; дескриптор библиотеки (DLL)
    43. end
    Как вы можете видеть, использование LoadLibrary чуть сложнее, но гораздо гибче.
     
    Последнее редактирование: 2 янв 2017
  18. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Давайте посмотрим, как создать progress bar и status bar.
     

    Вложения:

    • 1.png
      1.png
      Размер файла:
      194,1 КБ
      Просмотров:
      2.749
    Последнее редактирование: 7 янв 2017
  19. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    В данном пример, мы обновляем progress bar, а затем проверяем, было ли достигнуто максимальное значение. Если это так, мы убиваем таймеp, после чего устанавливаем текст статус-окна с помощью сообщения SB_SETTEXT. Вызывается MessageBox, и когда пользователь нажмет на OK, мы очищаем текст в status bar'е и progress bar'е.
     
    Последнее редактирование: 7 янв 2017
  20. Mikl___

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

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

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

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

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

    Окно просмотра деревьев ― это особый вид окна, который представляет объекты в иерархическом порядке. В качестве примера может случить левая панель Windows Explorer'а. Вы можете использовать этот элемент управления, чтобы показать отношения между объектами.

    Создание окон просмотра деревьев

    Вы можете создать окно просмотра деревьев, вызвав CreateWindowEx и передав ей в качестве имени класса "SysTreeView32" или "WC_TREEVIEW". Желательно чтобы родительское окно включало в свой стиль значения WS_VISIBLE и WS_TABSTOP. Не забудьте поместить вызов InitCommonControls в ваш код.
    Есть несколько стилей присущих только окну просмотра деревьев. Вот наиболее часто используемые:
    • TVS_HASBUTTONS ― отображает слева от изображения кнопки разворачивания (+) и сворачивания (-). Пользователь щелкает мышкой по кнопкам, чтобы открыть или закрыть список дочерних элементов. Чтобы вставить кнопки с пунктами в корень окна просмотра деревьев, также должен быть указан TVS_LINESATROOT.
    • TVS_HASLINES ― ветви древовидных структур соединяются линиями.
    • TVS_LINESATROOT ― корневые узлы соединяются с ветвями дерева линиями. Этот стиль игнорируется, если не указан TVS_HASLINES.
    При задании стилей TVS_HASLINES и TVS_LINESATROOT узлы дерева в окне соединяются линиями. Это придает содержимому окна просмотра деревьев вид древовидной структуры. Применение стиля TVS_HASBUTTONS влечет за собой добавление стандартной кнопки раскрытия/сворачивания слева от каждого элемента дерева, для которого допустимы эти операции. Кнопки содержат знак "+", если элемент дерева можно раскрыть по крайней мере на один уровень, и знак "-", если ветвь дерева полностью развернута. Для разворачивания или сворачивания ветви дерева достаточно щелкнуть мышью на этой кнопке. Обычно при создании окна используют все три стиля.
    Сразу после создания в окне просмотра деревьев не отражается никаких элементов. Окно заполняется путем последовательного добавления отдельных элементов.

    Посылка управляющих сообщений окну просмотра деревьев

    Окно просмотра деревьев, как и любой другой элемент управления, взаимодействует с родительским окном с помощью сообщений. Родительское окно может посылать различные сообщения окну просмотра деревьев, а тот может посылать "уведомительные" сообщения своему родительскому окну. В этом отношении окно просмотра деревьев ничем не отличается от других окон.
    Когда с окном просмотра деревьев происходит что-нибудь, оно посылает сообщение WM_NOTIFY родительскому окну вместе с дополнительной информацией.
    • wParam ― ID элемент управления, но то, что оно будет уникальным не гарантируется, поэтому не используйте его. Вместо этого мы будет использовать hwndFrom или IDFrom из структуры NMHDR, на которую указывает lParam.
    • lParam ― указатель на структуру NMHDR. Некоторые элементы управления могут передавать указатель на большую структуру, но они должны иметь в качестве первого поля структуру NMHDR. Поэтому вы можете быть уверены, что lParam по крайней мере указывает на NMHDR.
    Затем мы проанализируем структуру NMHDR.
    Код (ASM):
    1. NMHDR STRUCT
    2.   hwndFrom QWORD ? ;дескриптор окна элемента управления,
    3. ;который послал это сообщение.
    4.   idFrom   QWORD ? ;ID элемента управления
    5.   _code   DWORD ? ;настоящее сообщение, которое элемент
    6. ;управления хотел послать родительскому окну
    7. NMHDR ENDS
    Уведомления от окна просмотра деревьев начинаются с префикса TVN_ (Tree View Notify).
    Сообщения для окно просмотра деревьев начинаются с TVM_ (Tree View Message), например TVM_CREATEDRAGIMAGE. Окно просмотра деревьев посылает TVN_xxxx в поле code структуры NMHDR. родительское окно может посылать TVM_xxxx элементу управления.

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

    После того, как вы создадите окно просмотра деревьев, вы можете добавить в него пункты. Вы можете сделать это, послав элементу управления TVM_INSERTITEM.
    • wParam=0;
    • lParam=указатель на структуру типа TV_INSERTSTRUCT;
    СообщениеДействие
    TVM_DELETEITEMУдаление элемента из окна просмотра деревьев. При успешном удалении возвращается ненулевое значение. wParam должен быть равен 0. lParam содержит дескриптор удаляемого элемента
    TVM_EXPANDРазворачивание или сворачивание ветви дерева на один уровень. При успешном завершении операции возвращается ненулевое значение. wParam определяет тип операции. Может быть равным TVE_COLAPSE (сворачивание ветви дерева), TVE_COLAPSERESET (сворачивание ветви и удаление порожденных элементов), TVE_EXPAND (раскрытие ветви) TVE_TOGGLE (переключение состояния узла). lParam содержит дескриптор родительского узла ветви
    TVM_GETITEMПолучение атрибутов отдельного элемента дерева. При успешном завершении возвращается ненулевое значение. wParam должен быть равен 0. lParam содержит указатель на структуру типа TV_ITEM, в которую записывается информация об элементе
    TVM_INSERTITEMВставка в дерево нового элемента. Возвращается дескриптор нового элемента либо 0 при ошибке. wParam должен быть равен 0. lParam содержит указатель на структуру типа TV_INSERTSTRUCT описывающую новый элемент
    TVM_SELECTITEMВыборка элемента окна просмотра деревьев. При успешном завершении возвращается ненулевое значение. wParam задает тип операции. TVGN_CARET (элемент выбирается), TVGN_DROPHILITE (элемент подсвечивается для операции перетаскивания (drag-and-drop)), TVGN_FIRSTVISIBLE (содержимое окна просмотра деревьев прокручивается (сдвигается) так, чтобы заданный элемент стал первым видимым элементом). lParam содержит дескриптор элемента дерева
    Вам следует знать кое-какую терминологию, касающуюся взаимоотношений между элементами в окне просмотра деревьев.
    Элемент может быть родительским, дочерним или тем и другим одновременно. Родительский элемент ― это такой элемент, с которым ассоциированы подэлементы. В то же время, родительский элемент может быть дочерним по отношению к какому-то другому. Элемент, у которого нет родителя, называется корнем (root).
    В окне просмотра деревьев может быть много корневых элементов. Теперь мы проанализируем структуру TV_INSERTSTRUCT.
    Код (ASM):
    1. TV_INSERTSTRUCT STRUCT
    2.   hParent      QWORD ? ;дескриптор родительского элемента. Если этот параметр
    3. ;равен TVI_ROOT или NULL, тогда элемент вставляется в корень окна просмотра деревьев
    4.   hInsertAfter QWORD ?;значение определяющее каким образом новый элемент должен быть
    5. ;добавлен в дерево. Если это поле содержит дескриптор элемента, новый элемент будет вставлен
    6. ;после этого элемента
    7.   item       TV_ITEM <>;описывает сам элемент
    8. TV_INSERTSTRUCT ENDS
    • hInsertAfter дескриптор элемента, после которого будет вставляться новый элемент, или одно из следующих значений:
    TVI_FIRSTвставка элемента в начало списка
    TVI_LASTвставка элемента в конец списка
    TVI_SORTвставка элемента в список согласно алфавитному порядку
    Код (ASM):
    1. TV_ITEM STRUCT
    2.   imask DWORD ?,?
    3.   hItem QWORD ?
    4.   state DWORD ?
    5.   stateMask DWORD ?
    6.   pszText QWORD ?
    7.   cchTextMax DWORD ?
    8.   iImage DWORD ?
    9.   iSelectedImage DWORD ?
    10.   cChildren DWORD ?
    11.   lParam DWORD ?
    12. TV_ITEM ENDS
    Эта структура используется для отсылки и получения информации об элементе окно просмотра деревьев (в зависимости от сообщений). Например, с помощью TVM_INSERTITEM, она используется для указания атрибутов элемента, который должен быть вставлен в окно просмотра деревьев. С помощью TVM_GETITEM, она будет заполнена информацией о выбранном элементе окна просмотра деревьев.
    • imask ― используется для указания, какой член структуры TV_ITEM верен. Например, если значение в imask равно TVIF_TEXT, оно означает, что только pszText верно. Вы можете комбинировать несколько флагов вместе.
      содержимое поляназначение
      TVIF_HANDLEИнформация содержится в поле hItem
      TVIF_STATEИнформация содержится в полях state и stateMask
      TVIF_TEXTИнформация содержится в полях pszText и cchTextMax
      TVIF_IMAGEИнформация содержится в поле iImage
      TVIF_SELECTEDIMAGEИнформация содержится в поле iSelectedImage
      TVIF_CHILDRENИнформация содержится в поле cChildren
      TVIF_LPARAMИнформация содержится в поле lParam
     

    Вложения:

    • 0.png
      0.png
      Размер файла:
      205,9 КБ
      Просмотров:
      1.955
    Последнее редактирование: 13 янв 2021