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

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

Метки:
  1. Mikl___

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

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

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


    [​IMG]
    Что бы не было вопросов "для чего всё это?", "При чём здесь братец Кролик и ассемблер?" Считайте этот цикл пародией, которую, как бы, написал Джоэль Чандлер Харрис на "Уроки Iczelion'а", переписанные под Win x64.
    Рисунки из фильма "Песня Юга" (англ. "Song of the South" 1946) любезно предоставлены [​IMG]
    1. Глава первая. Как Братец Кролик достал пакет MASM64
    2. Глава вторая. Прототип нашей IDE
    3. Глава третья. Как Братец Кролик уменьшал размер программы
    4. Глава четвертая. Как Братец Кролик создал универсальный bat-файл
    5. Глава пятая. Братец Кролик разбирается с потрохами MessageBox
    6. Глава шестая. Братец Кролик выводит MessageBox всеми возможными способами
    7. Глава седьмая. Как Братец Кролик создал простейшее окно
    8. Глава восьмая. Как Братец Кролик выводил текст на экран при помощи функции DrawText
    9. Глава девятая. Как Братец Кролик создал «генератор окошек»
    10. Глава десятая. Вращающийся текст
    11. Глава одиннадцатая. Как Братец Кролик выводил текст на экран всеми возможными способами
    12. Глава двенадцатая. Как Братец Кролик осваивал мышь
    13. Глава тринадцатая. Братец Кролик продолжает осваивать мышь
    14. Глава четырнадцатая. «Липкая» надпись
    15. Глава пятнадцатая. Братец Кролик изучает иконки и курсоры
    16. Глава шестнадцатая. Братец Кролик изучает меню
    17. Глава семнадцатая. Братец Кролик создает меню через функцию LoadMenu
    18. Глава восемнадцатая. Программное создание меню
    19. Глава девятнадцатая. Создание динамического меню
    20. Глава двадцатая. «Плавающее» меню
    21. Глава двадцать первая. Создание меню через Template-структуру
    22. Глава двадцать вторая. Работа с таблицей акселераторов
    23. Глава двадцать третья. Работа с инструментальной панелью (toolbar)
    24. Глава двадцать четвертая. Всплывающие подсказки
    25. Глава двадцать пятая. Инструментальная панель
    26. Глава двадцать шестая. Братец Кролик подключает стандартные диалоги к инструментальной панели
    27. Глава двадцать седьмая. Пример использования диалога «Открыть файл»
    28. Глава двадцать восьмая
    29. Глава двадцать девятая. Братец Кролик создает дочерние окна
    30. Глава тридцатая. Братец Кролик и диалоги
    31. Глава тридцать первая. Братец Кролик разбирается с памятью и файлами
    32. Глава тридцать вторая. Братец Кролик и файлы проецируемые в память
    33. Глава тридцать третья. Братец Кролик изучает процессы
    34. Глава тридцать четвертая. Братец Кролик и треды (ветви)
    35. Глава тридцать пятая. Братец Кролик и объект события
    36. Глава тридцать шестая. Братец Кролик и динамические библиотеки
    37. Глава тридцать седьмая. Братец Кролик и элементы управления
    38. Глава тридцать восьмая. Братец Кролик и окно просмотра деревьев
    39. Глава тридцать девятая. Братец Кролик изучает сабклассинг окна
    40. Глава сороковая. Братец Кролик и пайп
    41. Глава сорок первая. Братец Кролик и суперклассинг
    42. Глава сорок вторая. Братец Кролик и иконки в system tray
    43. Глава сорок третья. Братец Кролик и Windows-хуки
    44. Глава сорок четвертая. Братец Кролик и сплэш-экран
    45. Глава сорок пятая. Братец Кролик и подсказки (toolip control)
    46. Глава сорок шестая. Братец Кролик и Контрол Listview
    47. Глава сорок седьмая. Братец Кролик создает MDI-приложение
    48. Глава сорок восьмая. Братец Кролик и немного БСДМ MSDN'а
    49. Глава сорок девятая. Память Братца Кролика
    50. Глава пятидесятая. Братец Кролик подключает к MessageBox'у кнопку «Help»
    51. Глава пятьдесят первая. Братец Кролик узнает о тексте еще больше
    52. Глава пятьдесят вторая. Братец Кролик и просмотр видео-файлов
     

    Вложения:

    Последнее редактирование: 29 янв 2024
    Vagners, mantissa, MaKaKa и 6 другим нравится это.
  2. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Глава первая. Как Братец Кролик достал пакет MASM64
    (или, как создать IDE из говна и палок)
    tumblr_ncoya2EgGG1s2wio8o1_500.gif[​IMG][​IMG]
    Братец Кролик​
    Братец Си​
    Братец Дельфи​
    В те далекие времена в южных штатах 64-разрядный ассемблерный компилятор и линкер не распространялись отдельно, но ml64.exe и link.exe Братец Кролик получил бесплатно, потому как, прочитал и дал клятву соблюдать "Лицензионное соглашение" (;)), после чего (в образовательных целях) скачал комплект C++ компиляторов от от Матушки Виндоуз. Братцу Кролику потребовалось сходить по одной из двух ссылок:
    После установки WDK Братец Кролик нашел ml64.exe и link.exe в папке C:\WinDDK\7600.16385.1\bin\x86\amd\ ― Что-то слишком просто у меня получилось ― сказал Братец Кролик и установил на всякий случай еще и SDK. В SDK ml64.exe и link.exe содержались в папках C:\Program Files\Microsoft Visual Studio\2022\Preview\VC \Tools\MSVC\14.30.30401\bin\Hostx64\x64\
    Тут Братец Кролик вспомнил, ЧТО ему говорил Братец Опоссум ― Обычно используют 64-битный link из папки amd64, а ml64 уже 32-битный из папки x86_amd64. В папке amd64 есть 64-битная версия ml64, но при работе с большим количеством макросов он у Братца Опоссума почему-то зависал или просто медленно работал. В конце концов выбор остается за тобой, Братец Кролик...
    1. Братец Кролик установил Microsoft Visual Studio 22.0
    2. Затем Братец Кролик создал каталог, который назвал masm64, а в этом каталоге создал подкаталоги bin, include, lib, samples
    3. Из интернета Братец Кролик скачал файл hiew32.exe и разместил его в masm64\bin
    4. В Program Files\Microsoft Visual Studio\2022\ Preview\VC\ Tools\ MSVC\ 14.30.30401\bin\HostX64\x64 Братец Кролик нашел файлы
      • ml64.exe
      • link.exe
      • link.exe.config
      • cvtres.exe
      • dumpbin.exe
      • lib.exe
      и скопировал их в папку masm64\bin
    5. А в папке c:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64 Братец Кролик нашел файлы rc.exe и rcdll.dll которые также поместил в masm64\bin
    6. Чтобы ml64 и link заработали на другом компьютере, где Microsoft Visual Studio 22.0 еще не установлена, понадобится целая куча вспомогательных dll'ок которые находятся в папке Windows\System32
    7. Братец Кролик набрал в командной строке
      d:\masm64\bin\hiew32 ml64.exe

      72.png
      Нажал на клавишу F4 (mode) Decode,
      73.png
      затем на клавишу F8 (header),
      74.png
      затем на F7 (import), чтобы посмотреть какие функции импортируются из других файлов
      75.png
      и наконец на F7 (DLLs), чтобы получить список dll, используемых в файле
      76.png
    8. Сперва Братцу Кролику пришлось заполнить вот такую таблицу
      вспомогательная dllисполь-
      зуется
      находится в каталоге
      api-ms-win-crt-conio-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-convert-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-environment-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-filesystem-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-heap-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-locale-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-math-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-process-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-runtime-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-stdio-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-string-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-time-l1-1-0.dllml64.exe
      link.exe
      Program Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      api-ms-win-crt-utility-l1-1-0.dlllink.exeProgram Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      msvcp140.dlllink.exeProgram Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      vcruntime140.dllml64.exeProgram Files\Microsoft Visual Studio\2022\ Preview\Common7\IDE\Remote Debugger\x64
      mscoree.dlllink.exeWindows\system32
      advapi32.dllml64.exe
      link.exe
      Windows\system32
      kernel32.dllml64.exe
      link.exe
      Windows\system32
      imagehlp.dlllink.exeWindows\system32
      mspdbcore.dlllink.exeProgram Files\Microsoft Visual Studio\2022\ Preview\VC\bin\amd64
      msvcdis140.dlllink.exeProgram Files\Microsoft Visual Studio\2022\ Preview\VC\bin\amd64
      pgodb140.dlllink.exeProgram Files\Microsoft Visual Studio\2022\ Preview\VC\bin\amd64
    9. Затем Братец Кролик скопировал файлы api*.dll, а также msvcp140.dll и vcruntime140.dll из папки Program Files\Microsoft Visual Studio\2022\ Preview\BuildTools\Common7\IDE\Remote Debugger\x64 а также файлы mspdbcore.dll, msvcdis140.dll, pgodb140.dll из папки Program Files\Microsoft Visual Studio\2022\ Preview\BuildTools\VC\bin\amd64 в папку masm64\bin
    10. После Братец Кролик отправился на сайт dsmhelp.narod.ru где, как он слышал, можно найти набор lib- и inc-файлов для создания 64-разрядных приложений на ассемблере, а также кучу полезных макросов

    Как Братец Кролик создал первую программу

    В папке bin Братец Кролик при помощи программы Notepad.exe ( [​IMG]) создал файл asm.bat
    Код (DOS):
    1. :: очистка экрана
    2. cls
    3. :: имя asm-файла без расширения
    4. set filename=%~n1
    5. :: здесь путь к папке masm64, у Вас он может быть другим
    6. set %masm64_path%=\masm64\
    7. :: если существует exe-файл с таким же именем ― удаляем его
    8. if exist %filename%.exe del %filename%.exe
    9. :: компиляция
    10. :: если во время компиляции будут ошибки, тогда они будут перечислены в файле errors.txt
    11. %masm64_path%bin\ml64 /c %filename%.asm >> errors.txt
    12. :: обнаружены ошибки компиляции ― выходим
    13. if errorlevel 1 exit
    14. :: линковка
    15. :: если во время линковки будут ошибки ― тогда они будут перечислены в файле errors.txt
    16. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /entry:WinMain %filename%.obj >> errors.txt
    17. :: обнаружены ошибки линковки ― выходим
    18. if errorlevel 1 exit
    19. :: раз мы здесь ― значит ошибок нет ― удаляем "программный мусор"
    20. del %filename%.obj
    21. del errors.txt

    Братец Кролик пишет программу на ассемблере

    «скелет» ассемблерного файла для x64

    Код (ASM):
    1. ; служебная информация для компиляции и линковки
    2. .data
    3. ; здесь определены данные для программы
    4. .code
    5. WinMain proc
    6. ; здесь определен текст программы
    7. WinMain endp
    8. end
    Братец Кролик заметил, что в отличии от ассемблерного файла для 16-/32-разрядных программ в тексте
    1. отсутствует упоминание модели и типа CPU
    2. "вход" в программу определяется не по последней строчке в asm-файле "end <метка>", а по опции в bat-файле "/entry:<метка>"

    Текст первой программы Братца Кролика

    Братец Кролик набрал в блокноте ([​IMG]) текст программы и сохранил ее с именем tut_02.asm в каталоге samples
    Код (ASM):
    1. OPTION DOTNAME ; разрешить использовать точку в имени переменной
    2. option casemap:none ; различать в именах Строчные и прописные буквы
    3. include \masm64\include\temphls.inc ; в этом файле описание invoke и другие высокоуровневые инструкции
    4. include \masm64\include\win64.inc  ; здесь описаны константы NULL и MB_OK
    5. include \masm64\include\kernel32.inc ; здесь описаны функции из библиотеки kernel32.dll
    6. includelib \masm64\lib\kernel32.lib
    7. include \masm64\include\user32.inc  ; здесь описаны функции из библиотеки userl32.dll
    8. includelib \masm64\lib\user32.lib
    9. OPTION PROLOGUE:none ; пролог функций пишем сами
    10. OPTION EPILOGUE:none ; эпилог функций пишем сами
    11. .data                ; данные
    12. MsgCaption      db "Win64 Iczelion's lesson #2: MessageBox",0
    13. MsgBoxText      db "Win64 Assembly is Great!",0
    14. .code                ; код программы
    15. WinMain proc
    16.     sub rsp,28h
    17.       invoke MessageBox, NULL, &MsgBoxText, &MsgCaption, MB_OK
    18.       invoke ExitProcess,NULL
    19. WinMain endp
    20. end

    Братец Кролик создает ассоциации ассемблерных файлов с файлом asm.bat


    1. Братец Кролик щелкнул по значку файла tut_02.asm
      [​IMG]
    2. выбрал пункт "Открыть с помощью", нашел в папке \masm64\bin файл asm.bat и выбрал его
      [​IMG]
      [​IMG]
    3. Братец Кролик кликнул по файлу tut_02.asm и получил файл tut_02.exe
      [​IMG]
    Дзен и Братец Кролик

    Братец Кролик начал медитировать и на него снизошло просветление ― Братец Кролик понял, что тоже самое можно сделать более дзенно.
    1. Братец Кролик нажал на кнопку "Пуск"→"Выполнить"→и запустил программу regedit
    2. в ветке HKEY_CLASSES_ROOTApplication Братец Кролик создал раздел asm.bat shell open command
      [​IMG]
    3. в ветке HKEY_CURRENT_USER Software Microsoft Windows CurrentVersion Explorer FileExts он создал раздел .asm
    [​IMG]




    © Mikl___ 2021
     

    Вложения:

    • 04.png
      04.png
      Размер файла:
      367,2 КБ
      Просмотров:
      7.518
    • 02.png
      02.png
      Размер файла:
      483,3 КБ
      Просмотров:
      7.695
    • brer-fox-o.gif
      brer-fox-o.gif
      Размер файла:
      117,1 КБ
      Просмотров:
      1.550
    • 00.png
      00.png
      Размер файла:
      74,3 КБ
      Просмотров:
      7.802
    • 01.png
      01.png
      Размер файла:
      80,1 КБ
      Просмотров:
      7.826
    • 02.png
      02.png
      Размер файла:
      74,7 КБ
      Просмотров:
      7.682
    sergeykuzne и DOSAsm386 нравится это.
  3. Mikl___

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

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

    [​IMG]
    Вот пообедали Братец Кролик, и Матушка Крольчиха, и все ребятки, и никто им не мешал. А потом приходит Братец Дельфи и говорит:
    ― Братец Кролик, уж очень большого размера получилась у тебя программа! Ну прям, как у меня...1
    Взял Братец Кролик текст своей первой программы и стал ее разглядывать
    Код (ASM):
    1. OPTION DOTNAME ; разрешить использовать точку в имени переменной
    2. option casemap:none ; различать в именах Строчные и прописные буквы
    3. include \masm64\include\temphls.inc ; в этом файле описание invoke и другие высокоуровневые инструкции
    4. include \masm64\include\win64.inc  ; здесь описаны константы NULL и MB_OK
    5. include \masm64\include\kernel32.inc ; здесь описаны функции из библиотеки kernel32.dll
    6. includelib \masm64\lib\kernel32.lib
    7. include \masm64\include\user32.inc  ; здесь описаны функции из библиотеки userl32.dll
    8. includelib \masm64\lib\user32.lib
    9. OPTION PROLOGUE:none ; пролог функций пишем сами
    10. OPTION EPILOGUE:none ; эпилог функций пишем сами
    11. .data                ; данные
    12. MsgCaption      db "Win64 Iczelion's lesson #2: MessageBox",0
    13. MsgBoxText      db "Win64 Assembly is Great!",0
    14. .code                ; код программы
    15. WinMain proc
    16.     sub rsp,28h
    17.       invoke MessageBox, NULL, &MsgBoxText, &MsgCaption, MB_OK
    18.       invoke ExitProcess,NULL
    19. WinMain endp
    20. end
    Собирал Братец Кролик свою программу при помощи bat-файла со следующим содержанием
    Код (DOS):
    1. :: очистить экран
    2. cls
    3. :: здесь можно указать какой-нибудь другой путь к пакету masm64
    4. set masm64_path=\masm64\
    5. :: здесь будет название asm-файла
    6. set filename=%~n1
    7. %masm64_path%bin\ml64 /c /Cp %filename%.asm
    8. %masm64_path%bin\link /SUBSYSTEM:WINDOWS ^
    9. /entry:WinMain %filename%.obj
    10. ::   удалить программный "мусор"
    11. del %filename%.obj
    Обратите внимание на символ «^». Одним из условий удобочитаемости текста программы является его ширина: все строки должны умещаться на экране ― это примерная ширина текста в 80 символов. А при написании bat-файла основное требование ― запись команды в одну строку. Использование символа «^» позволяет устранить это противоречие. Символ «^» экранирует конец строки ― это позволяет разместить содержимое длинной строки на экране в нескольких строках, а система, как и прежде, будет считать их одной длинной строкой.
    В результате компиляции и линковки получил Братец Кролик программу tut_02.ехе размером в 2560 байт. Стал Братец Кролик рассматривать внутренностей ехе-файла при помощи программы hiew32, которую ему подарил Братец Ёж. И увидел Братец Кролик, что 90% содержимого программы заполнено нулями. Возник в голове Братца Кролика вопрос, а как уменьшить размер программы, но чтобы при этом не терялась ее функциональность?

    Шаг первый ― Братец Кролик уменьшает размер выравнивания сегментов

    У программы link.exe есть ключ /ALIGN:размер Ключ /ALIGN[MENT] указывает компоновщику на необходимость выравнивания сегментов в исполняемом файле на границу, кратную значению размер. Размер ― это число равное двум возведенное в целую степень (20=1, 21=2, 22=4, 23=8, ... до 216=65536 включительно). Если об этом ключе не упоминать, то выравнивание равно 29=512 байт. Посмотреть значение выравнивания можно при помощи редактора бинарных файлов hiew32 в заголовке программы в поле «File alignment» (Открываем наш exe программой hiew32, нажимаем F4 (Mode) и выбираем Hex, нажимаем F8 (Header) в поле «File alignment» видим число 20016 = 51210).
    [​IMG]
    Кроме ключа /ALIGN Братец Кролик добавил в файл asm.bat еще некоторые ключи компиляции и линковки, которые позволили уменьшить количество служебной информации в asm-файлах, но привели к увеличению размера bat-файла (кого-то когда-нибудь волновал размер bat-файла? :))
    • Добавление ключа компиляции /Cp ― «сохранить регистр символов во всех идентификаторах» позволяет не писать каждый раз в тексте ассемблерного файла «option casemap:none».
    • Добавление ключа компиляции /I:путь ― «установить путь для включаемых файлов» позволяет нам не указывать каждый раз полный путь к inc-файлам.
    • Ключ /LIBPATH:путь говорит линкеру, где находятся библиотеки импорта, что позволяет нам не указывать каждый раз полный путь к lib-файлам.
    Таким было содержимое файла asm.bat до изменений:
    Код (DOS):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=%~n1
    4. %masm64_path%bin\ml64 /c /Cp %filename%.asm
    5. %masm64_path%bin\link /SUBSYSTEM:WINDOWS  ^
    6. /entry:WinMain %filename%.obj
    7. del %filename%.obj
    а таким стало после изменений:
    Код (DOS):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=%~n1
    4. %masm64_path%bin\ml64 /Cp /c /I"%masm64_path%Include" %filename%.asm
    5. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:"%masm64_path%Lib" ^
    6. /ALIGN:256 /entry:WinMain %filename%.obj
    7. del %filename%.obj
    А таким стало содержимое файла tut_02.asm после изменений
    Код (ASM):
    1. OPTION DOTNAME
    2. include temphls.inc
    3. include win64.inc
    4. include kernel32.inc
    5. includelib kernel32.lib
    6. include user32.inc
    7. includelib user32.lib
    8. OPTION PROLOGUE:none
    9. OPTION EPILOGUE:none
    10. .data
    11. MsgCaption      db "Win64 Iczelion's lesson #2: MessageBox",0
    12. MsgBoxText      db "Win64 Assembly is Great!",0
    13. .code
    14. WinMain proc
    15.     sub rsp,28h
    16.       invoke MessageBox, NULL, &MsgBoxText, &MsgCaption, MB_OK
    17.       invoke ExitProcess,NULL
    18. WinMain endp
    19. end
    при компиляции Братец Кролик получил сообщение
    LINK : warning LNK4108: /ALIGN specified without /DRIVER; image may not run
    warning означает "предупреждение" поэтому Братец Кролик не испугался, а продолжил эксперимент ― размер файла tut_02.exe изменился с 2560 до 1536 байт и, не смотря, на глупое предупреждение компилятора image may not run файл tut_02.exe переделанный Братцем Кроликом благополучно запустился.
    Братец Кролик педантично изменял значение /ALIGN:размер, наблюдал за уменьшением размера файла tut_02.exe, а результаты записывал в таблицу
    /ALIGN:размерзаголовоккодданныеимпортобщий размер
    tut_02.exe
    при создании файла ключ
    /ALIGN:размер не указан
    10245125125122560 байт
    размер = 2567682562562561536 байт
    размер = 1286401281282561152 байт
    размер = 645766464192896 байт
    размер = 325766464192896 байт
    размер = 165764864192880 байт
    размер = 8ошибка при
    создании файла
    tut_02.obj : fatal error LNK1164: section 0x1 alignment (16) greater then /ALIGN value
    когда выравнивание сегментов стало кратным 16 байтам, Братец Кролик понял, что в этом направлении он достиг предела и от исходной программы в 2560 байт пришел к программе размером в 880 байт. Братец Кролик сказал себе ― «Для начала и это уже не плохо!»


    1 дельфи в самом худшем смысле, когда компилятор туп настолько, что по бреду, который он генерирует, его можно безошибочно узнать в листинге (© f13nd)
     
    Alexey и sergeykuzne нравится это.
  4. Mikl___

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

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

    Шаг второй ― Братец Кролик объединяет сегмент кода и сегмент данных

    Программа Братца Кролика использует два сегмента, сегмент кода и сегмент данных. Посмотрел Братец Кролик внимательно на свою программу через hiew32 ― и обратил внимание на прослойку из нулей между этими сегментами, а цель-то у Братца Кролика была избавится от лишних нулей. Вспомнил Братец Кролик, что во времена дядюшки DOS'а можно было создавать COM-файлы, которые в единственном сегменте содержали и код, и стек, и данные. А нельзя ли, подумал Братец Кролик, и здесь создать, что-то подобное?
    Посидел Братец Кролик в Интернете и выяснил, что оказывается можно ― при помощи ключа /SECTION можно присвоить секции кода с атрибутами «выполнять» и «читать» атрибуты секции данных ― «читать» и «писать».
    Чтобы поменьше писать в asm-файлах служебной информации Братец Кролик в папке include создал файл win64a.inc со следующим содержимым
    Код (ASM):
    1. OPTION DOTNAME
    2. include temphls.inc
    3. include win64.inc
    4. include kernel32.inc
    5. includelib kernel32.lib
    6. include user32.inc
    7. includelib user32.lib
    8. OPTION PROLOGUE:none
    9. OPTION EPILOGUE:none
    Внес Братец Кролик изменения в текст программы
    Код (ASM):
    1. include win64a.inc
    2. .code
    3. MsgCaption      db "Win64 Iczelion's lesson #2: MessageBox",0
    4. MsgBoxText      db "Win64 Assembly is Great!",0
    5. WinMain proc
    6.     sub rsp,28h
    7.     invoke MessageBox, NULL, &MsgBoxText, &MsgCaption, MB_OK
    8.     invoke ExitProcess,NULL
    9. WinMain endp
    10. end
    и изменения в файл asm.bat
    Код (DOS):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=%~n1
    4. %masm64_path%bin\ml64 /Cp /c /I"%masm64_path%Include" %filename%.asm
    5. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:"%masm64_path%Lib" ^
    6. /SECTION:.text,W /ALIGN:16 /entry:WinMain %filename%.obj
    7. del %filename%.obj

    Опция линкера /SECTION

    Опция командной строки компоновщика link.exe /SECTION:name,[[!]{DEKPRSW}][,ALIGN=#] позволяет принудительно назначать атрибуты секциям PE-файла. Для секции можно задать один или несколько атрибутов. Следует задавать все атрибуты, которые должна иметь секция; если какой-либо знак атрибута не указан, то его бит будет отключен. Если не указан атрибут R, W или E, то существующее состояние чтения, записи или исполнения остается неизмененным. Чтобы инвертировать атрибут, перед его символом указывают знак «!». С помощью параметра ALIGN=# можно задать значение выравнивания для конкретной секции. Значения знаков атрибутов приведены в следующей таблице.
    букваатрибутзначениеперевод
    DDiscardableMarks the section as discardableСекция помечается как выгружаемая
    EExecuteThe section is executableСекция является выполняемой
    KCacheableMarks the section as not cacheableСекция помечается как некэшируемая
    PPageableMarks the section as not pageableСекция помечается как секция без страничной организации
    RReadAllows read operations on dataДопускаются операции чтения данных
    SSharedShares the section among all processes that load the imageСекция совместно используется всеми процессами, загружающими образ
    WWriteAllows write operations on dataДопускаются операции записи данных
    В данном случае секции с именем «.text» (содержащей код программы) и уже имеющей атрибуты R (доступна для чтения) и E (исполнимая), устанавливается атрибут W (доступна для записи)
    /SECTION:.text,W
    В результате Братец Кролик получил tut_02.exe в 848 байт
    заголовоккод и данныеимпортобщий размер
    544112192848 байт

    Шаг третий ― Братец Кролик занимается алгоритмической оптимизацией

    Братец Кролик наблюдал за работой своего приложения через программу-дебаггер x64dbg и заметил, что при старте приложения на вершине стека лежит адрес функции kernel32.RtlExitUserThread, а именно ее вызывает функция ExitProcess для завершения приложения. Поэтому у Братца Кролика возник вопрос, а необходим ли в этой программе вызов функции ExitProcess? Братец Кролик заменил вызов функции ExitProcess на команду retn, которая извлекла из стека адрес функции RtlExitUserThread и передала управление программой этой функции.
    Код (ASM):
    1. include win64a.inc
    2. .code
    3. MsgCaption      db "Win64 Iczelion's lesson #2: MessageBox",0
    4. MsgBoxText      db "Win64 Assembly is Great!",0
    5. ;------------------------------------------------
    6. WinMain proc
    7.     sub rsp,28h
    8.           invoke MessageBox, NULL, &MsgBoxText, &MsgCaption, MB_OK
    9.     add rsp,28h
    10.           retn
    11. WinMain endp
    12. end
    заголовоккод и данныеимпортобщий размер
    544112112768 байт

    Шаг четвертый ― «хирургический» ― Братец Кролик ампутирует у файла «хвост»

    Просматривал Братец Кролик свою программу через hiew32, просматривал и заметил, что в «хвосте» файла tut_02.exe, сразу за строкой «user32.dll» целых шестнадцать байт содержат нули.
    [​IMG]
    А не удалить ли эти нули вручную? ― подумал Братец Кролик. Сказано ― сделано! Запустил Братец Кролик файловый менеджер far, нажал на F4 (открыл файл на редактирование), нажал на Ctrl+End и перешел в конец файла tut_02.exe. Потом Братец Кролик нажимал на Backspace, удалял лишние байты, затем нажал на F2 и сохранил изменения в файле, нажал на F10 и вышел из файла ― размер tut_02.exe стал 748 байт. Нажал Братец Кролик на него и запустил tut_02.exe ― запускается нормально! Стоп-стоп! 768-748=20 байт, а должно быть 16! Посмотрел Братец Кролик на свою программу через hiew32 ― и, о, боже!, оказалось, что он отрезал вместе с нулями от «user32.dll» кусок «.dll», но файл-то всё равно работал! Запомнил Братец Кролик на будущее, что от DLL достаточно только названия, а точку и расширение файла система подставит сама.
    заголовоккод и данныеимпортобщий размер
    54411292748 байт

    Шаг пятый ― Братец Кролик снова занялся алгоритмической оптимизацией

    Как устроен вызов функции в Win64? Сначала формируется стековый кадр (stack frame), то есть, область памяти в стеке, через которую передаются аргументы и другие "опции", затем устанавливаются аргументы и вызывается инструкция call.
    Win64 ABI требует, чтобы стековый кадр был выравнен на 16 байт. Это ключевой момент, от которого следует отталкиваться во всех последующих рассуждениях.
    При входе в любую функцию значение регистра RSP не кратно 16, оно смещено на 8 байт адресом возврата, который "положила" в стек инструкция call. Если поставить точку останова на любую 64-битную функцию, то при входе в нее можно увидеть, что значение RSP всегда заканчивается на 8. Вот поэтому пролог функции в Win64 делает что-нибудь в стиле "sub rsp, 28h" ― это и есть выравнивание стека на 16. Именно так, как пишут авторы многочисленных книг, то есть, до вызова других функций и до формирования стековых кадров для них.
    Но это еще не все. Все тот же Win64 ABI требует, чтобы на время вызова в стеке было зарезервировано 32 байта под нужды вызываемой функции. Эта область в разных источниках называется по-разному: "register space", "home area" и так далее. Предполагалось, что в этой памяти вызываемая функция сможет временно хранить значения регистров RCX, RDX, R8 и R9. Но нужен ли "home area" для функции MessageBox?
    Заменил Братец Кролик sub rsp,28h на sub rsp,18h и add rsp,28h на add rsp,18h (Братец Кролик соблюдал условие кратности 16 содержимого RSP), компилировал, линковал, запустил на выполнение. Программа отработала и завершилась нормально.
    Это вдохновило Братца Кролика и он снова заменил sub rsp,18h на sub rsp,8 и add rsp,18h на add rsp,8 Опять Братец Кролик компилирует, линкует, запускает на выполнение. Программа и на этот раз отработала и нормально завершилась.
    Хорошо! ― сказал Братец Кролик, почесал лоб и вспомнил, что эквивалентом sub rsp,8 является команда push reg, какой из регистров при этом будет использовать не важно, главное, что размер команды всего один байт! Эквивалент add rsp,8 ― команда pop reg. Внес Братец Кролик очередные изменения в программу, откомпилировал, слинковал, запустил программу на выполнение. Программа отработала и нормально завершилась.
    заголовоккод и данныеимпортобщий размер
    54496112752 байт
     
    Последнее редактирование: 3 сен 2019
    mantissa и sergeykuzne нравится это.
  5. Mikl___

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

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

    Шаг шестой ― Братец Кролик уменьшает DOS-stub

    Код программы Братца Кролика начинался с 0x220=544-го байта. Всё, что выше ― это заголовок EXE-файла ― вот бы его уменьшить! ― решил Братец Кролик. Заголовок файла состоит из двух частей. От строки «MZ» до строки «PE» DOS-заголовок (размер 0xC8-0=200 байт) и от строки «PE» до 0x220=544-го байта PE-заголовок (размер 0x220-0xC8=344 байт). Адрес строки «PE» содержится в DOS-заголовке в двойном слове по смещению 0x3C.
    Братец Кролик начал с уменьшения DOS-стаба. Взял hiew32.exe и создал с его помощью вот такой файл
    [​IMG]
    Братец Кролик присвоил ему имя stubby.exe, сохранил файл в папке bin и дальше использовал этой файл в качестве stub-программы. В очередной раз Братец Кролик внес изменения в файл asm.bat.
    Код (DOS):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=%~n1
    4. %masm64_path%bin\ml64 /Cp /c /I"%masm64_path%Include" %filename%.asm
    5. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:"%masm64_path%Lib" ^
    6. /STUB:%masm64_path%bin\stubby.exe /SECTION:.text,W /ALIGN:16 ^
    7. /entry:WinMain %filename%.obj
    8. del %filename%.obj
    заголовоккод и данныеимпортобщий размер
    48096112688 байт

    Шаг седьмой ― Братец Кролик использует ключи /LARGEADDRESSAWARE, /BASE:адрес и 32-разрядную адресацию

    Приложение, работающее в Win64 может работать с адресами размер которых свыше 2 гигабайта. В 64-разрядных компиляторах для этого используется параметр /LARGEADDRESSAWARE В 32-разрядных компиляторах включен параметр /LARGEADDRESSAWARE:NO
    Параметр /BASE задает базовый адрес программы, переопределяя заданное по умолчанию расположение EXE-файла (для 32-разрядных Win = 0x400000, для 64-разрядных 0x140000000) или DLL-файла (для 32-разрядных = 0x10000000). Сначала операционная система пытается загрузить программу по указанному или используемому по умолчанию базовому адресу. Если по этому адресу недостаточно места, то система перемещает программу в памяти. Для предотвращения перемещения используется параметр /FIXED.
    Если указанный адрес не кратен 64 Кб, компоновщик выдает ошибку. При необходимости можно также указать размер программы, чтобы компоновщик мог выдать предупреждение в случае, если программа не помещается в заданные таким образом пределы.
    [​IMG]
    Внес Братец Кролик изменения в файл asm.bat
    Код (DOS):
    1. cls
    2. set masm64_path=\masm64\
    3. set filename=%~n1
    4. if exist %filename%.exe del %filename%.exe
    5. if exist %filename%.obj del %filename%.obj
    6. %masm64_path%bin\ml64 /Cp /c /I"%masm64_path%Include" %filename%.asm
    7. %masm64_path%bin\link /SUBSYSTEM:WINDOWS /LIBPATH:"%masm64_path%Lib" ^
    8. /LARGEADDRESSAWARE:NO /BASE:0x400000 /STUB:%masm64_path%bin\stubby.exe ^
    9. /SECTION:.text,W /ALIGN:16 /entry:WinMain %filename%.obj
    Братец Кролик в очередной раз внес изменения в asm-файл
    Код (ASM):
    1. include win64.inc
    2. .code
    3. MsgBoxText      db "Win64 Assembly is Great!",0
    4. MsgCaption      db "Win64 Iczelion's lesson #2: MessageBox",0
    5. WinMain proc
    6.     push rbp
    7.     mov edx,offset MsgBoxText
    8.     mov r8d,offset MsgCaption
    9.     invoke MessageBox,NULL,,,MB_OK
    10.     pop rbp
    11.     retn
    12. WinMain endp
    13. end
    [​IMG]

    заголовоккод и данныеимпортобщий размер
    48096112688 байт
    Внес Братец Кролик изменения в asm-файл
    Код (ASM):
    1. include win64a.inc
    2. .code
    3. MsgBoxText      db "Win64 Assembly is Great!",0
    4. MsgCaption      db "Win64 Iczelion's lesson #2: MessageBox",0
    5. WinMain proc
    6.     push rbp
    7.     mov edx,offset MsgBoxText
    8.     lea r8d,[rdx+sizeof MsgBoxText];r8=offset MsgCaption
    9.     invoke MessageBox,NULL,,,MB_OK
    10.     pop rbp
    11.     retn
    12. WinMain endp
    13. end
    [​IMG]

    заголовоккод и данныеимпортобщий размер
    48096112688 байт

    Шаг восьмой ― Братец Кролик сокращает размеры заголовка и импорта

    Так как код программы и данные нам сократить не удастся ― поэтому сократим размеры заголовка и импорта PE-файла. Нам потребуется создать bin-файл, заголовок которого мы напишем самостоятельно. Для этого требуются ассемблеры, которые могут создавать бинарные файлы (NASM, FASM). За основу бинарного файла возьмем содержимое предыдущего tut_02.exe размером 688 байта
    Код (ASM):
    1. format binary as "exe"
    2. include "win64a.inc"
    3. struc dbs [data]
    4. {
    5.   common
    6.   . db data
    7.   .size = $ - .
    8. }
    9.  
    10. IMAGE_DOS_SIGNATURE        equ 5A4Dh
    11. IMAGE_NT_SIGNATURE        equ 00004550h
    12. PROCESSOR_AMD_X8664        equ 8664h
    13. IMAGE_SCN_CNT_CODE        equ 00000020h
    14. IMAGE_SCN_MEM_WRITE        equ 80000000h
    15. IMAGE_SCN_MEM_READ        equ 40000000h
    16. IMAGE_SCN_CNT_INITIALIZED_DATA    equ 00000040h
    17. IMAGE_SUBSYSTEM_WINDOWS_GUI    equ 2
    18. IMAGE_NT_OPTIONAL_HDR64_MAGIC    equ 20Bh
    19. IMAGE_FILE_RELOCS_STRIPPED    equ 1
    20. IMAGE_FILE_EXECUTABLE_IMAGE    equ 2
    21. IMAGE_BASE            equ 0x400000
    22. align1                equ 0x10
    23. use64
    24. org 0
    25. ;--------DOS-stub-------------------------------
    26. Signature        dw IMAGE_DOS_SIGNATURE
    27. times 0x3A db 0
    28. NewExe_offset        dd ntHeader
    29. times 0x48 db 0
    30. ;-------PE-заголовок--------------------------------------------------
    31. ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    32. ;image_header----Файловый заголовок
    33. Machine         dw PROCESSOR_AMD_X8664;Тип центрального процессора
    34. Count_of_section    dw 2;Количество секций
    35. TimeStump        dd 0;Информация о времени, когда был собран данный PE-файл
    36. Symbol_table_offset    dd 0;Указатель на размер отладочной информации
    37. Symbol_table_count    dd 0;Указатель на COFF-таблицу символов PE-формата
    38. Size_of_optional_header dw section_table-optional_header;Размер опционального заголовка
    39. Characteristics     dw IMAGE_FILE_RELOCS_STRIPPED or \
    40. IMAGE_FILE_EXECUTABLE_IMAGE;Атрибуты файла
    41. ;-------Стандартные поля NT
    42. optional_header:
    43. Magic_optional_header    dw IMAGE_NT_OPTIONAL_HDR64_MAGIC;Состояние отображаемого файла
    44. Linker_version_major_and_minor dw 9;Содержат версию линковщика, создавшего данный файл
    45. Size_of_code        dd Import_Table-begin;Суммарный размер секций кода
    46. Size_of_init_data    dd 0x70;Суммарный размер инициализированных данных
    47. Size_of_uninit_data    dd 0;Суммарный размер неинициализированных данных
    48. entry_point        dd start
    49. base_of_code        dd begin
    50. ;------Дополнительные поля NT-----------------------------------------------
    51. image_base        dq IMAGE_BASE
    52. section_alignment    dd align1
    53. file_alignment        dd align1
    54. OS_version_major_minor    dw 5,2
    55. image_version_major_minor dd 0
    56. subsystem_version_major_minor dw 5,2
    57. Win32_version        dd 0
    58. size_of_image        dd end_import
    59. size_of_header        dd begin
    60. checksum        dd 0
    61. subsystem        dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    62. DLL_flag        dw 8000h;IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    63. Stack_allocation    dq 0x100000
    64. Stack_commit        dq 0x1000
    65. Heap_allocation     dq 0x100000
    66. Heap_commit        dq 0x1000
    67. loader_flag        dd 0
    68. number_of_dirs        dd 16
    69. export_RVA_size     dq 0
    70. import_RVA_size     dd _import,user32_table2-_import;_import,0x3C;end_import-import
    71. resurce         dd 0,0
    72. exception        dd 0,0
    73. security        dd 0,0
    74. fixups_         dd 0,0
    75. debug            dd 0,0
    76. description        dd 0,0
    77. MIPS_GP         dd 0,0
    78. TLS            dd 0,0
    79. Load_config        dd 0,0
    80. Bound_import        dd 0,0
    81. import_table1        dd Import_Table,_import-Import_Table;0x10
    82. delay_import        dd 0,0
    83. com_runtime        dd 0,0
    84. reserved        dd 0,0
    85. ;------------------------------------------------
    86. section_table        dq ".text"
    87. .virtual_size        dd a0-begin;0x57
    88. .virtual_address    dd begin
    89. .Physical_size        dd Import_Table-begin
    90. .Physical_offset    dd begin
    91. .Relocations_and_Linenumbers dq 0
    92. .Relocations_and_Linenumbers_count dd 0
    93. .Attributes        dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
    94. ;------------------------------------------------
    95. section_table1        dq ".rdata"
    96. .virtual_size        dd a1-Import_Table;0x62
    97. .virtual_address    dd Import_Table
    98. .Physical_size        dd end_import-Import_Table
    99. .Physical_offset    dd Import_Table
    100. .Relocations_and_Linenumbers dq 0
    101. .Relocations_and_Linenumbers_count dd 0
    102. .Attributes         dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
    103. ;--------данные и код-----------------------------------------
    104. begin:
    105. MsgBoxText    dbs "Win64 Assembly is Great!",0
    106. MsgCaption    db "Win64 Iczelion's lesson #2: MessageBox",0
    107. start:
    108.     push rbp
    109.     xor ecx,ecx
    110.     mov edx,MsgBoxText+IMAGE_BASE
    111.     lea r8d,[rdx+MsgBoxText.size]
    112.     xor r9d,r9d
    113.     call [MessageBox]
    114.     pop rbp
    115.     retn
    116. a0:
    117.   times (($ - begin) mod align1) db 0
    118. ;выравнивание сегмента кода и данных до значения равного align1
    119. ;---------секция импорта---------------------------------------
    120. Import_Table:
    121. user32_table:
    122. MessageBox  dq _MessageBox,0
    123. _import:
    124. dd user32_table2,0,0,user32_dll,user32_table
    125. dd 0,0,0,0,0
    126. user32_table2:
    127. dq _MessageBox,0
    128. _MessageBox        db 0xE2,1,"MessageBoxA",0
    129. user32_dll db "user32.dll",0
    130. a1:
    131. times (($ - Import_Table) mod align1) db 0
    132. end_import:
    заголовоккод и данныеимпортобщий размер
    48096112688 байт
     
  6. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    сократим DOS-stub до 64 байт, уменьшаем таблицу разделов, по умолчанию количество разделов должно быть равным 16, нам же требуется только секция импорта. В таблице разделов секция импорта идет сразу после секции экспорта.
    Код (ASM):
    1. format binary as "exe"
    2. include "win64a.inc"
    3.  
    4. struc dbs [data]
    5. {
    6.   common
    7.   . db data
    8.   .size = $ - .
    9. }
    10.  
    11. IMAGE_DOS_SIGNATURE        equ 5A4Dh
    12. IMAGE_NT_SIGNATURE        equ 00004550h
    13. PROCESSOR_AMD_X8664        equ 8664h
    14. IMAGE_SCN_CNT_CODE        equ 00000020h
    15. IMAGE_SCN_MEM_WRITE        equ 80000000h
    16. IMAGE_SCN_MEM_READ        equ 40000000h
    17. IMAGE_SCN_CNT_INITIALIZED_DATA    equ 00000040h
    18. IMAGE_SUBSYSTEM_WINDOWS_GUI    equ 2
    19. IMAGE_NT_OPTIONAL_HDR64_MAGIC    equ 20Bh
    20. IMAGE_FILE_RELOCS_STRIPPED    equ 1
    21. IMAGE_FILE_EXECUTABLE_IMAGE    equ 2
    22. IMAGE_BASE            equ 0x400000
    23. align1                equ 0x10
    24. IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
    25. use64
    26. org 0
    27. ;--------DOS-stub-------------------------------
    28. Signature        dw IMAGE_DOS_SIGNATURE
    29. times 0x3A db 0
    30. NewExe_offset        dd ntHeader
    31. ;times 0x48 db 0
    32. ;-------PE-заголовок--------------------------------------------------
    33. ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    34. ;image_header----Файловый заголовок
    35. Machine         dw PROCESSOR_AMD_X8664;Тип центрального процессора
    36. Count_of_section    dw 2;Количество секций
    37. TimeStump        dd 0;Информация о времени, когда был собран данный PE-файл
    38. Symbol_table_offset    dd 0;Указатель на размер отладочной информации
    39. Symbol_table_count    dd 0;Указатель на COFF-таблицу символов PE-формата
    40. Size_of_optional_header dw section_table-optional_header;Размер опционального заголовка
    41. Characteristics     dw IMAGE_FILE_RELOCS_STRIPPED or \
    42. IMAGE_FILE_EXECUTABLE_IMAGE;Атрибуты файла
    43. ;-------Стандартные поля NT
    44. optional_header:
    45. Magic_optional_header    dw IMAGE_NT_OPTIONAL_HDR64_MAGIC;Состояние отображаемого файла
    46. Linker_version_major_and_minor dw 9;Содержат версию линковщика, создавшего данный файл
    47. Size_of_code        dd Import_Table-begin;Суммарный размер секций кода
    48. Size_of_init_data    dd 0x70;Суммарный размер инициализированных данных
    49. Size_of_uninit_data    dd 0;Суммарный размер неинициализированных данных
    50. entry_point        dd start
    51. base_of_code        dd begin
    52. ;------Дополнительные поля NT-----------------------------------------------
    53. image_base        dq IMAGE_BASE
    54. section_alignment    dd align1
    55. file_alignment        dd align1
    56. OS_version_major_minor    dw 5,2
    57. image_version_major_minor dd 0
    58. subsystem_version_major_minor dw 5,2
    59. Win32_version        dd 0
    60. size_of_image        dd end_import
    61. size_of_header        dd begin
    62. checksum        dd 0
    63. subsystem        dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    64. DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    65. Stack_allocation    dq 0x100000
    66. Stack_commit        dq 0x1000
    67. Heap_allocation     dq 0x100000
    68. Heap_commit        dq 0x1000
    69. loader_flag        dd 0
    70. number_of_dirs        dd (section_table-export_RVA_size)/8
    71. export_RVA_size     dq 0
    72. import_RVA_size     dd _import,user32_table2-_import;_import,0x3C;end_import-import
    73. ;resurce         dd 0,0
    74. ;exception         dd 0,0
    75. ;security         dd 0,0
    76. ;fixups_         dd 0,0
    77. ;debug             dd 0,0
    78. ;description         dd 0,0
    79. ;MIPS_GP         dd 0,0
    80. ;TLS             dd 0,0
    81. ;Load_config         dd 0,0
    82. ;Bound_import         dd 0,0
    83. ;import_table1         dd Import_Table,_import-Import_Table
    84. ;delay_import         dd 0,0
    85. ;com_runtime         dd 0,0
    86. ;reserved         dd 0,0
    87. ;------------------------------------------------
    88. section_table        dq ".text"
    89. .virtual_size        dd a0-begin;0x57
    90. .virtual_address    dd begin
    91. .Physical_size        dd Import_Table-begin
    92. .Physical_offset    dd begin
    93. .Relocations_and_Linenumbers dq 0
    94. .Relocations_and_Linenumbers_count dd 0
    95. .Attributes        dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
    96. ;------------------------------------------------
    97. section_table1        dq ".rdata"
    98. .virtual_size        dd a1-Import_Table;0x62
    99. .virtual_address    dd Import_Table
    100. .Physical_size        dd end_import-Import_Table
    101. .Physical_offset    dd Import_Table
    102. .Relocations_and_Linenumbers dq 0
    103. .Relocations_and_Linenumbers_count dd 0
    104. .Attributes         dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
    105. ;--------данные и код-----------------------------------------
    106. begin:
    107. MsgBoxText    dbs "Win64 Assembly is Great!",0
    108. MsgCaption    db "Win64 Iczelion's lesson #2: MessageBox",0
    109. start:
    110.     push rbp
    111.     xor ecx,ecx
    112.     mov edx,MsgBoxText+IMAGE_BASE
    113.     lea r8d,[rdx+MsgBoxText.size]
    114.     xor r9d,r9d
    115.     call [MessageBox]
    116.     pop rbp
    117.     retn
    118. a0:
    119.   times (($ - begin) mod align1) db 0
    120. ;выравнивание сегмента кода и данных до значения равного align1
    121. ;---------секция импорта---------------------------------------
    122. Import_Table:
    123. user32_table:
    124. MessageBox  dq _MessageBox,0
    125. _import:
    126. dd user32_table2,0,0,user32_dll,user32_table
    127. dd 0,0,0,0,0
    128. user32_table2:
    129. dq _MessageBox,0
    130. _MessageBox        db 0xE2,1,"MessageBoxA",0
    131. user32_dll db "user32.dll",0
    132. a1:
    133. times (($ - Import_Table) mod align1) db 0
    134. end_import:
    заголовоккод и данныеимпортобщий размер
    29696112504 байт
    Что можно сократить в секции импорта? Секция импорта содержит две одинаковые таблицы (Import LookUp Table и Import Address Table), содержащие ссылки на названия импортируемых функций. Можно ли обойтись только таблицей Import Address Table? Удаляем Import LookUp Table и обнуляем ссылки на user32_table2
    Код (ASM):
    1. format binary as "exe"
    2. include "win64a.inc"
    3.  
    4. struc dbs [data]
    5. {
    6.   common
    7.   . db data
    8.   .size = $ - .
    9. }
    10.  
    11. IMAGE_DOS_SIGNATURE        equ 5A4Dh
    12. IMAGE_NT_SIGNATURE        equ 00004550h
    13. PROCESSOR_AMD_X8664        equ 8664h
    14. IMAGE_SCN_CNT_CODE        equ 00000020h
    15. IMAGE_SCN_MEM_WRITE        equ 80000000h
    16. IMAGE_SCN_MEM_READ        equ 40000000h
    17. IMAGE_SCN_CNT_INITIALIZED_DATA    equ 00000040h
    18. IMAGE_SUBSYSTEM_WINDOWS_GUI    equ 2
    19. IMAGE_NT_OPTIONAL_HDR64_MAGIC    equ 20Bh
    20. IMAGE_FILE_RELOCS_STRIPPED    equ 1
    21. IMAGE_FILE_EXECUTABLE_IMAGE    equ 2
    22. IMAGE_BASE            equ 400000h
    23. align1                equ 10h
    24. IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
    25. use64
    26. org 0
    27. ;--------DOS-stub-------------------------------
    28. Signature        dw IMAGE_DOS_SIGNATURE
    29. times 0x3A db 0
    30. NewExe_offset        dd ntHeader
    31. ;-------PE-заголовок--------------------------------------------------
    32. ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    33. ;image_header----Файловый заголовок
    34. Machine         dw PROCESSOR_AMD_X8664;Тип центрального процессора
    35. Count_of_section    dw 2;Количество секций
    36. TimeStump        dd 0;Информация о времени, когда был собран данный PE-файл
    37. Symbol_table_offset    dd 0;Указатель на размер отладочной информации
    38. Symbol_table_count    dd 0;Указатель на COFF-таблицу символов PE-формата
    39. Size_of_optional_header dw section_table-optional_header;Размер опционального заголовка
    40. Characteristics     dw IMAGE_FILE_RELOCS_STRIPPED or \
    41. IMAGE_FILE_EXECUTABLE_IMAGE;Атрибуты файла
    42. ;-------Стандартные поля NT
    43. optional_header:
    44. Magic_optional_header    dw IMAGE_NT_OPTIONAL_HDR64_MAGIC;Состояние отображаемого файла
    45. Linker_version_major_and_minor dw 9;Содержат версию линковщика, создавшего данный файл.
    46. Size_of_code        dd Import_Table-begin;Суммарный размер секций кода
    47. Size_of_init_data    dd 0x70;Суммарный размер инициализированных данных
    48. Size_of_uninit_data    dd 0;Суммарный размер неинициализированных данных
    49. entry_point        dd start
    50. base_of_code        dd begin
    51. ;------Дополнительные поля NT-----------------------------------------------
    52. image_base        dq IMAGE_BASE
    53. section_alignment    dd align1
    54. file_alignment        dd align1
    55. OS_version_major_minor    dw 5,2
    56. image_version_major_minor dd 0
    57. subsystem_version_major_minor dw 5,2
    58. Win32_version        dd 0
    59. size_of_image        dd end_import
    60. size_of_header        dd begin
    61. checksum        dd 0
    62. subsystem        dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    63. DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    64. Stack_allocation    dq 0x100000
    65. Stack_commit        dq 0x1000
    66. Heap_allocation     dq 0x100000
    67. Heap_commit        dq 0x1000
    68. loader_flag        dd 0
    69. number_of_dirs        dd (section_table-export_RVA_size)/8
    70. export_RVA_size     dq 0
    71. import_RVA_size     dd _import,0x3C;_import,0x3C;end_import-import
    72. ;------------------------------------------------
    73. section_table        dq ".text"
    74. .virtual_size        dd a0-begin;0x57
    75. .virtual_address    dd begin
    76. .Physical_size        dd Import_Table-begin
    77. .Physical_offset    dd begin
    78. .Relocations_and_Linenumbers dq 0
    79. .Relocations_and_Linenumbers_count dd 0
    80. .Attributes        dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE;0x80000020
    81. ;------------------------------------------------
    82. section_table1        dq ".rdata"
    83. .virtual_size        dd end_import-Import_Table;0x62
    84. .virtual_address    dd Import_Table
    85. .Physical_size        dd end_import-Import_Table
    86. .Physical_offset    dd Import_Table
    87. .Relocations_and_Linenumbers dq 0
    88. .Relocations_and_Linenumbers_count dd 0
    89. .Attributes         dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA;0x40000040
    90. ;--------данные и код-----------------------------------------
    91. begin:
    92. MsgBoxText    dbs "Win64 Assembly is Great!",0
    93. MsgCaption    db "Win64 Iczelion's lesson #2: MessageBox",0
    94. start:
    95.     push rbp
    96.     xor ecx,ecx
    97.     mov edx,MsgBoxText+IMAGE_BASE
    98.     lea r8d,[rdx+MsgBoxText.size]
    99.     xor r9d,r9d
    100.     call [MessageBox]
    101.     pop rbp
    102.     retn
    103. a0:
    104.   times (($ - begin)mod align1) db 0
    105. ;выравнивание сегмента кода и данных до значения равного align1
    106. ;секция импорта
    107. Import_Table:
    108. user32_table:
    109. MessageBox  dq _MessageBox,0
    110. _import:
    111. dd 0,0,0,user32_dll,user32_table
    112. dq 0,0,0
    113. _MessageBox        db 0xE2,1,"MessageBoxA",0
    114. user32_dll db "user32"
    115. end_import:
    и как в четвертом шаге удаляем 16 нулевых байтов в конце файла и окончание ".dll"
    заголовоккод и данныеимпортобщий размер
    2969680472 байт
     
  7. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    сокращаем DOS-stub на 16 байтов (3Ah-2Eh-4=10h), смещение e_lfanew оказывается внутри PE-заголовка. Так как у программы со стабом короче 64 байт смещение от начала файла 3Ch (поле e_lfanew) попадает уже внутрь PE-заголовка, то нужно, чтобы он не попал на поле PE-заголовка имеющее критическое значение при загрузке файла. Помещаем указатель на ntHeader в поле Symbol_table_offset. При размещении в нем числа 30h получаем работоспособное приложение.
    Код (ASM):
    1. format binary as "exe"
    2. include "win64a.inc"
    3.  
    4. struc dbs [data]
    5. {
    6.   common
    7.   . db data
    8.   .size = $ - .
    9. }
    10.  
    11. IMAGE_DOS_SIGNATURE        equ 5A4Dh
    12. IMAGE_NT_SIGNATURE        equ 00004550h
    13. PROCESSOR_AMD_X8664        equ 8664h
    14. IMAGE_SCN_CNT_CODE        equ 00000020h
    15. IMAGE_SCN_MEM_WRITE        equ 80000000h
    16. IMAGE_SCN_MEM_READ        equ 40000000h
    17. IMAGE_SCN_CNT_INITIALIZED_DATA    equ 00000040h
    18. IMAGE_SUBSYSTEM_WINDOWS_GUI    equ 2
    19. IMAGE_NT_OPTIONAL_HDR64_MAGIC    equ 20Bh
    20. IMAGE_FILE_RELOCS_STRIPPED    equ 1
    21. IMAGE_FILE_EXECUTABLE_IMAGE    equ 2
    22. IMAGE_BASE            equ 400000h
    23. align1                equ 10h
    24. IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
    25. use64
    26. org 0
    27. ;--------DOS-stub-------------------------------
    28. Signature        dw IMAGE_DOS_SIGNATURE
    29. times 0x2E db 0
    30. ;NewExe_offset         dd ntHeader
    31. ;-------PE-заголовок--------------------------------------------------
    32. ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    33. ;image_header----Файловый заголовок
    34. Machine         dw PROCESSOR_AMD_X8664;Тип центрального процессора
    35. Count_of_section    dw 2;Количество секций
    36. TimeStump        dd 0;Информация о времени, когда был собран данный PE-файл
    37. Symbol_table_offset    dd ntHeader;Указатель на размер отладочной информации
    38. Symbol_table_count    dd 0;Указатель на COFF-таблицу символов PE-формата
    39. Size_of_optional_header dw section_table-optional_header;Размер опционального заголовка
    40. Characteristics     dw IMAGE_FILE_RELOCS_STRIPPED or \
    41. IMAGE_FILE_EXECUTABLE_IMAGE;Атрибуты файла
    42. ;-------Стандартные поля NT
    43. optional_header:
    44. Magic_optional_header    dw IMAGE_NT_OPTIONAL_HDR64_MAGIC;Состояние отображаемого файла
    45. Linker_version_major_and_minor dw 9;Содержат версию линковщика, создавшего данный файл
    46. Size_of_code        dd Import_Table-begin;Суммарный размер секций кода
    47. Size_of_init_data    dd 0x70;Суммарный размер инициализированных данных
    48. Size_of_uninit_data    dd 0;Суммарный размер неинициализированных данных
    49. entry_point        dd start
    50. base_of_code        dd begin
    51. ;------Дополнительные поля NT-----------------------------------------------
    52. image_base        dq IMAGE_BASE
    53. section_alignment    dd align1
    54. file_alignment        dd align1
    55. OS_version_major_minor    dw 5,2
    56. image_version_major_minor dd 0
    57. subsystem_version_major_minor dw 5,2
    58. Win32_version        dd 0
    59. size_of_image        dd end_import
    60. size_of_header        dd begin
    61. checksum        dd 0
    62. subsystem        dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    63. DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    64. Stack_allocation    dq 0x100000
    65. Stack_commit        dq 0x1000
    66. Heap_allocation     dq 0x100000
    67. Heap_commit        dq 0x1000
    68. loader_flag        dd 0
    69. number_of_dirs        dd (section_table-export_RVA_size)/8
    70. export_RVA_size     dq 0
    71. import_RVA_size     dd _import,0x3C
    72. ;------------------------------------------------
    73. section_table        dq ".text"
    74. .virtual_size        dd a0-begin
    75. .virtual_address    dd begin
    76. .Physical_size        dd Import_Table-begin
    77. .Physical_offset    dd begin
    78. .Relocations_and_Linenumbers dq 0
    79. .Relocations_and_Linenumbers_count dd 0
    80. .Attributes        dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE
    81. ;------------------------------------------------
    82. section_table1        dq ".rdata"
    83. .virtual_size        dd end_import-Import_Table
    84. .virtual_address    dd Import_Table
    85. .Physical_size        dd end_import-Import_Table
    86. .Physical_offset    dd Import_Table
    87. .Relocations_and_Linenumbers dq 0
    88. .Relocations_and_Linenumbers_count dd 0
    89. .Attributes         dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA
    90. ;--------данные и код-----------------------------------------
    91. begin:
    92. MsgBoxText    dbs "Win64 Assembly is Great!",0
    93. MsgCaption    db "Win64 Iczelion's lesson #2: MessageBox",0
    94. start:
    95.     push rbp
    96.     xor ecx,ecx
    97.     mov edx,MsgBoxText+IMAGE_BASE
    98.     lea r8d,[rdx+MsgBoxText.size]
    99.     xor r9d,r9d
    100.     call [MessageBox]
    101.     pop rbp
    102.     retn
    103. a0:
    104.   times (($ - begin) mod align1) db 0
    105. ;выравнивание сегмента кода и данных до значения равного align1
    106. ;---------секция импорта---------------------------------------
    107. Import_Table:
    108. user32_table:
    109. MessageBox  dq _MessageBox,0
    110. _import:
    111. dd 0,0,0,user32_dll,user32_table
    112. dq 0,0,0
    113. _MessageBox        db 0xE2,1,"MessageBoxA",0
    114. user32_dll db "user32"
    115. end_import:
    заголовоккод и данныеимпортобщий размер
    2809680456 байт
    сокращаем DOS-stub еще на 16 байтов (2Eh-1Eh=10h), смещение e_lfanew оказывается в поле Size_of_code. По одним источникам ― это поле используется для первичного отведения памяти под приложение. По другим ― не используются вообще. Если поместить туда число 20h, то практическая проверка показывает, что приложение с таким stub'ом работает нормально.
    Код (ASM):
    1. format binary as "exe"
    2. include "win64a.inc"
    3.  
    4. struc dbs [data]
    5. {
    6.   common
    7.   . db data
    8.   .size = $ - .
    9. }
    10.  
    11. IMAGE_DOS_SIGNATURE        equ 5A4Dh
    12. IMAGE_NT_SIGNATURE        equ 00004550h
    13. PROCESSOR_AMD_X8664        equ 8664h
    14. IMAGE_SCN_CNT_CODE        equ 00000020h
    15. IMAGE_SCN_MEM_WRITE        equ 80000000h
    16. IMAGE_SCN_MEM_READ        equ 40000000h
    17. IMAGE_SCN_CNT_INITIALIZED_DATA    equ 00000040h
    18. IMAGE_SUBSYSTEM_WINDOWS_GUI    equ 2
    19. IMAGE_NT_OPTIONAL_HDR64_MAGIC    equ 20Bh
    20. IMAGE_FILE_RELOCS_STRIPPED    equ 1
    21. IMAGE_FILE_EXECUTABLE_IMAGE    equ 2
    22. IMAGE_BASE            equ 0x400000
    23. align1                equ 0x10
    24. IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
    25. use64
    26. org 0
    27. ;--------DOS-stub-------------------------------
    28. Signature        dw IMAGE_DOS_SIGNATURE
    29. times 0x1E db 0
    30. ;-------PE-заголовок--------------------------------------------------
    31. ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    32. ;image_header----Файловый заголовок
    33. Machine         dw PROCESSOR_AMD_X8664;Тип центрального процессора
    34. Count_of_section    dw 2;Количество секций
    35. TimeStump        dd 0;Информация о времени, когда был собран данный PE-файл
    36. Symbol_table_offset    dd 0;Указатель на размер отладочной информации
    37. Symbol_table_count    dd 0;Указатель на COFF-таблицу символов PE-формата
    38. Size_of_optional_header dw section_table-optional_header;Размер опционального заголовка
    39. Characteristics     dw IMAGE_FILE_RELOCS_STRIPPED or \
    40. IMAGE_FILE_EXECUTABLE_IMAGE;Атрибуты файла
    41. ;-------Стандартные поля NT
    42. optional_header:
    43. Magic_optional_header    dw IMAGE_NT_OPTIONAL_HDR64_MAGIC;Состояние отображаемого файла
    44. Linker_version_major_and_minor dw 9;Содержат версию линковщика, создавшего данный файл
    45. Size_of_code        dd ntHeader;Суммарный размер секций кода
    46. Size_of_init_data    dd 0x70;Суммарный размер инициализированных данных
    47. Size_of_uninit_data    dd 0;Суммарный размер неинициализированных данных
    48. entry_point        dd start
    49. base_of_code        dd begin
    50. ;------Дополнительные поля NT-----------------------------------------------
    51. image_base        dq IMAGE_BASE
    52. section_alignment    dd align1
    53. file_alignment        dd align1
    54. OS_version_major_minor    dw 5,2
    55. image_version_major_minor dd 0
    56. subsystem_version_major_minor dw 5,2
    57. Win32_version        dd 0
    58. size_of_image        dd end_import
    59. size_of_header        dd begin
    60. checksum        dd 0
    61. subsystem        dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    62. DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    63. Stack_allocation    dq 0x100000
    64. Stack_commit        dq 0x1000
    65. Heap_allocation     dq 0x100000
    66. Heap_commit        dq 0x1000
    67. loader_flag        dd 0
    68. number_of_dirs        dd (section_table-export_RVA_size)/8
    69. export_RVA_size     dq 0
    70. import_RVA_size     dd _import,0x3C
    71. ;------------------------------------------------
    72. section_table        dq ".text"
    73. .virtual_size        dd a0-begin
    74. .virtual_address    dd begin
    75. .Physical_size        dd Import_Table-begin
    76. .Physical_offset    dd begin
    77. .Relocations_and_Linenumbers dq 0
    78. .Relocations_and_Linenumbers_count dd 0
    79. .Attributes        dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE
    80. ;------------------------------------------------
    81. section_table1        dq ".rdata"
    82. .virtual_size        dd end_import-Import_Table
    83. .virtual_address    dd Import_Table
    84. .Physical_size        dd end_import-Import_Table
    85. .Physical_offset    dd Import_Table
    86. .Relocations_and_Linenumbers dq 0
    87. .Relocations_and_Linenumbers_count dd 0
    88. .Attributes         dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA
    89. ;--------данные и код-----------------------------------------
    90. begin:
    91. MsgBoxText    dbs "Win64 Assembly is Great!",0
    92. MsgCaption    db "Win64 Iczelion's lesson #2: MessageBox",0
    93. start:
    94.     push rbp
    95.     xor ecx,ecx
    96.     mov edx,MsgBoxText+IMAGE_BASE
    97.     lea r8d,[rdx+MsgBoxText.size]
    98.     xor r9d,r9d
    99.     call [MessageBox]
    100.     pop rbp
    101.     retn
    102. a0:
    103.   times (($ - begin) mod align1) db 0
    104. ;выравнивание сегмента кода и данных до значения равного align1
    105. ;---------секция импорта---------------------------------------
    106. Import_Table:
    107. user32_table:
    108. MessageBox  dq _MessageBox,0
    109. _import:
    110. dd 0,0,0,user32_dll,user32_table
    111. dq 0,0,0
    112. _MessageBox        db 0xE2,1,"MessageBoxA",0
    113. user32_dll db "user32"
    114. end_import:
    заголовоккод и данныеимпортобщий размер
    2649680440 байт
     
    Последнее редактирование: 27 дек 2016
  8. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    сокращаем DOS-stub до 16 байт, смещение e_lfanew оказывается в поле base_of_code. При размещении в нем числа 10h получаем работоспособное приложение.
    В начале секции импорта убрали байты для выравнивания.
    Код (ASM):
    1. format binary as "exe"
    2. include "win64a.inc"
    3.  
    4. struc dbs [data]
    5. {
    6.   common
    7.   . db data
    8.   .size = $ - .
    9. }
    10.  
    11. IMAGE_DOS_SIGNATURE        equ 5A4Dh
    12. IMAGE_NT_SIGNATURE        equ 00004550h
    13. PROCESSOR_AMD_X8664        equ 8664h
    14. IMAGE_SCN_CNT_CODE        equ 00000020h
    15. IMAGE_SCN_MEM_WRITE        equ 80000000h
    16. IMAGE_SCN_MEM_READ        equ 40000000h
    17. IMAGE_SCN_CNT_INITIALIZED_DATA    equ 00000040h
    18. IMAGE_SUBSYSTEM_WINDOWS_GUI    equ 2
    19. IMAGE_NT_OPTIONAL_HDR64_MAGIC    equ 20Bh
    20. IMAGE_FILE_RELOCS_STRIPPED    equ 1
    21. IMAGE_FILE_EXECUTABLE_IMAGE    equ 2
    22. IMAGE_BASE            equ 0x400000
    23. align1                equ 0x10
    24. IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
    25. use64
    26. org 0
    27. ;--------DOS-stub-------------------------------
    28. Signature        dw IMAGE_DOS_SIGNATURE
    29. times 0xE db 0
    30. ;-------PE-заголовок--------------------------------------------------
    31. ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    32. ;image_header----Файловый заголовок
    33. Machine         dw PROCESSOR_AMD_X8664;Тип центрального процессора
    34. Count_of_section    dw 2;Количество секций
    35. TimeStump        dd 0;Информация о времени, когда был собран данный PE-файл
    36. Symbol_table_offset    dd 0;Указатель на размер отладочной информации
    37. Symbol_table_count    dd 0;Указатель на COFF-таблицу символов PE-формата
    38. Size_of_optional_header dw section_table-optional_header;Размер опционального заголовка
    39. Characteristics     dw IMAGE_FILE_RELOCS_STRIPPED or \
    40. IMAGE_FILE_EXECUTABLE_IMAGE;Атрибуты файла
    41. ;-------Стандартные поля NT
    42. optional_header:
    43. Magic_optional_header    dw IMAGE_NT_OPTIONAL_HDR64_MAGIC;Состояние отображаемого файла
    44. Linker_version_major_and_minor dw 9;Содержат версию линковщика, создавшего данный файл
    45. Size_of_code        dd Import_Table-begin;Суммарный размер секций кода
    46. Size_of_init_data    dd 0x70;Суммарный размер инициализированных данных
    47. Size_of_uninit_data    dd 0;Суммарный размер неинициализированных данных
    48. entry_point        dd start
    49. base_of_code        dd ntHeader
    50. ;------Дополнительные поля NT-----------------------------------------------
    51. image_base        dq IMAGE_BASE
    52. section_alignment    dd align1
    53. file_alignment        dd align1
    54. OS_version_major_minor    dw 5,2
    55. image_version_major_minor dd 0
    56. subsystem_version_major_minor dw 5,2
    57. Win32_version        dd 0
    58. size_of_image        dd end_import
    59. size_of_header        dd begin
    60. checksum        dd 0
    61. subsystem        dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    62. DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    63. Stack_allocation    dq 0x100000
    64. Stack_commit        dq 0x1000
    65. Heap_allocation     dq 0x100000
    66. Heap_commit        dq 0x1000
    67. loader_flag        dd 0
    68. number_of_dirs        dd (section_table-export_RVA_size)/8
    69. export_RVA_size     dq 0
    70. import_RVA_size     dd _import,0x3C
    71. ;------------------------------------------------
    72. section_table        dq ".text"
    73. .virtual_size        dd a0-begin
    74. .virtual_address    dd begin
    75. .Physical_size        dd Import_Table-begin
    76. .Physical_offset    dd begin
    77. .Relocations_and_Linenumbers dq 0
    78. .Relocations_and_Linenumbers_count dd 0
    79. .Attributes        dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE
    80. ;------------------------------------------------
    81. section_table1        dq ".rdata"
    82. .virtual_size        dd end_import-Import_Table
    83. .virtual_address    dd Import_Table
    84. .Physical_size        dd end_import-Import_Table
    85. .Physical_offset    dd Import_Table
    86. .Relocations_and_Linenumbers dq 0
    87. .Relocations_and_Linenumbers_count dd 0
    88. .Attributes         dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA
    89. ;--------данные и код-----------------------------------------
    90. begin:
    91. MsgBoxText    dbs "Win64 Assembly is Great!",0
    92. MsgCaption    db "Win64 Iczelion's lesson #2: MessageBox",0
    93. start:
    94.     push rbp
    95.     xor ecx,ecx
    96.     mov edx,MsgBoxText+IMAGE_BASE
    97.     lea r8d,[rdx+MsgBoxText.size]
    98.     xor r9d,r9d
    99.     call [MessageBox]
    100.     pop rbp
    101.     retn
    102. ;---------секция импорта---------------------------------------
    103. Import_Table:
    104. user32_table:
    105. MessageBox  dq _MessageBox,0
    106. _import:
    107. dd 0,0,0,user32_dll,user32_table
    108. dq 0,0,0
    109. _MessageBox        db 0xE2,1,"MessageBoxA",0
    110. user32_dll db "user32"
    111. end_import:
    Мы удалили таблицу, которую используют для импорта по ординалам. Внимание, вопрос ― А зачем нам в таком случае ординалы перед названием функций? Обнуляем ординалы и задаем себе следующий вопрос ― Если ординал нулевой ― нужен ли нулевой байт на конце строки? Удаляем нуль-терминаторы, а заодно и нулевые байты, которые делали адреса названий функции кратными двум, так как импортируемые функции теперь у нас заканчиваются двумя нулевыми байтами (а это расточительно!) ― помещаем в поле ординала последний символ названия функции или названия dll и нуль-терминатор
    количество секций делаем равным 0, удаляем секцию '.rdata' и '.text', размещаем импорт в секции кода.
    уменьшаем DOS-stub до 4 байт. смещение e_lfanew оказывается в поле file aligment. section aligment=file aligment=4 уменьшение импорта
    Код (ASM):
    1. format binary as "exe"
    2. include "win64a.inc"
    3.  
    4. struc dbs [data]
    5. {
    6.   common
    7.   . db data
    8.   .size = $ - .
    9. }
    10.  
    11. IMAGE_DOS_SIGNATURE        equ 5A4Dh
    12. IMAGE_NT_SIGNATURE        equ 00004550h
    13. PROCESSOR_AMD_X8664        equ 8664h
    14. IMAGE_SCN_CNT_CODE        equ 00000020h
    15. IMAGE_SCN_MEM_WRITE        equ 80000000h
    16. IMAGE_SCN_MEM_READ        equ 40000000h
    17. IMAGE_SCN_CNT_INITIALIZED_DATA    equ 00000040h
    18. IMAGE_SUBSYSTEM_WINDOWS_GUI    equ 2
    19. IMAGE_NT_OPTIONAL_HDR64_MAGIC    equ 20Bh
    20. IMAGE_FILE_RELOCS_STRIPPED    equ 1
    21. IMAGE_FILE_EXECUTABLE_IMAGE    equ 2
    22. IMAGE_BASE            equ 0x400000
    23. IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE equ 8000h
    24. use64
    25. org 0
    26. ;--------DOS-stub-------------------------------
    27. Signature        dw IMAGE_DOS_SIGNATURE,0
    28. ;-------PE-заголовок--------------------------------------------------
    29. ntHeader        dd IMAGE_NT_SIGNATURE;'PE'
    30. ;image_header----Файловый заголовок
    31. Machine         dw PROCESSOR_AMD_X8664;Тип центрального процессора
    32. Count_of_section    dw 0;Количество секций
    33. TimeStump        dd 0;Информация о времени, когда был собран данный PE-файл
    34. Symbol_table_offset    dd 0;Указатель на размер отладочной информации
    35. Symbol_table_count    dd 0;Указатель на COFF-таблицу символов PE-формата
    36. Size_of_optional_header dw begin-optional_header;Размер опционального заголовка
    37. Characteristics     dw IMAGE_FILE_RELOCS_STRIPPED or \
    38. IMAGE_FILE_EXECUTABLE_IMAGE;Атрибуты файла
    39. ;-------Стандартные поля NT
    40. optional_header:
    41. Magic_optional_header    dw IMAGE_NT_OPTIONAL_HDR64_MAGIC;Состояние отображаемого файла
    42. Linker_version_major_and_minor dw 9;Содержат версию линковщика, создавшего данный файл
    43. Size_of_code        dd Import_Table-begin;Суммарный размер секций кода
    44. Size_of_init_data    dd 0x70;Суммарный размер инициализированных данных
    45. Size_of_uninit_data    dd 0;Суммарный размер неинициализированных данных
    46. entry_point        dd start
    47. base_of_code        dd begin
    48. ;------Дополнительные поля NT-----------------------------------------------
    49. image_base        dq IMAGE_BASE
    50. section_alignment    dd 4
    51. file_alignment        dd ntHeader
    52. OS_version_major_minor    dw 5,2
    53. image_version_major_minor dd 0
    54. subsystem_version_major_minor dw 5,2
    55. Win32_version        dd 0
    56. size_of_image        dd end_import
    57. size_of_header        dd begin
    58. checksum        dd 0
    59. subsystem        dw IMAGE_SUBSYSTEM_WINDOWS_GUI
    60. DLL_flag        dw IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
    61. Stack_allocation    dq 0x100000
    62. Stack_commit        dq 0x1000
    63. Heap_allocation     dq 0x100000
    64. Heap_commit        dq 0x1000
    65. loader_flag        dd 0
    66. number_of_dirs        dd (begin-export_RVA_size)/8
    67. export_RVA_size     dq 0
    68. import_RVA_size     dd _import,end_import-import
    69. ;------------------------------------------------
    70. ;section_table        dq '.text'
    71. ;.virtual_size        dd a0-begin
    72. ;.virtual_address    dd begin
    73. ;.Physical_size        dd Import_Table-begin
    74. ;.Physical_offset    dd begin
    75. ;.Relocations_and_Linenumbers dq 0
    76. ;.Relocations_and_Linenumbers_count dd 0
    77. ;.Attributes        dd IMAGE_SCN_MEM_WRITE or IMAGE_SCN_CNT_CODE
    78. ;------------------------------------------------
    79. ;section_table1        dq '.rdata'
    80. ;.virtual_size        dd end_import-Import_Table
    81. ;.virtual_address    dd Import_Table
    82. ;.Physical_size        dd end_import-Import_Table
    83. ;.Physical_offset    dd Import_Table
    84. ;.Relocations_and_Linenumbers dq 0
    85. ;.Relocations_and_Linenumbers_count dd 0
    86. ;.Attributes         dd IMAGE_SCN_MEM_READ or IMAGE_SCN_CNT_INITIALIZED_DATA
    87. ;--------данные, код и импорт-----------------------------------------
    88. begin:
    89. MsgBoxText    dbs "Win64 Assembly is Great!",0
    90. MsgCaption    db "Win64 Iczelion's lesson #2: MessageBox",0
    91. start:
    92.     push rbp
    93.     xor ecx,ecx
    94.     mov edx,MsgBoxText+IMAGE_BASE
    95.     lea r8d,[rdx+MsgBoxText.size]
    96.     xor r9d,r9d
    97.     call [MessageBox]
    98.     pop rbp
    99.     retn
    100. ;---------секция импорта---------------------------------------
    101. Import_Table:
    102. user32_table:
    103. MessageBox  dq _MessageBox
    104. _import:
    105. dd 0,0,0,user32_dll,user32_table,0
    106. user32_dll db "user32"
    107. dd 0
    108. _MessageBox        db 0,0,"MessageBoxA"
    109. end_import:
    заголовоккод и данныеимпортобщий размер
    1568755298 байт

    Мораль

    Так путем нехитрых преобразований Братец Кролик на порядок уменьшил размер ехе-файла с 2560 байт до 298 байт :)

    © Mikl___ 2021
     
  9. Mikl___

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

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

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

    [​IMG]
    — Наверное, у мамы твоей гости, — сказал дядюшка Римус, когда Джоэль вбежал к нему с большущим куском слоёного пирога. — А если не гости, так, уж верно, она потеряла ключ от буфета, а ты нашёл его.
    — Просто, дядюшка Римус, мне мама дала пирога, а я подумал — притащу его тебе.
    Старик улыбнулся:
    — Спасибо, спасибо, сынок. Этот пирог как раз мне поможет собраться с силами, чтоб рассказать дальше про Братца Кролика и про его друзей.
    Тут старик замолчал и принялся за пирог. Он справился с ним очень быстро. Потом стряхнул крошки с бороды и начал:

    Окно ― это прямоугольная область экрана, в которой приложение отображает выходные данные и в которую позволяет пользователю вводить свои данные.
    Окна могут образовывать иерархию родительских и дочерних окон. Закрытие родительского окна всегда приводит к закрытию вместе с ним всех его дочерних окон. А вот закрытие дочернего окна никак не влияет на родительское.
    Сообщение ― это уведомление приложения о наступлении того или иного события (нажатие на кнопку, сворачивание, разворачивание, перемещение окна, ввода в поле Edit и так далее) и определенный код, переданный приложению с дополнительными параметрами, которые зависят от кода сообщения, но в большинстве сообщений они отсутствуют. Некоторые сообщения.
    СообщениеОписание
    WM_CLOSEЗакрытие окна
    WM_COMMANDУведомление от дочернего элемента управления
    WM_CREATEСоздание окна
    WM_DESTROYУничтожение окна
    WM_INITDIALOGОтправляется перед отображением диалогового окна
    WM_KILLFOCUSПотерян фокус ввода
    WM_MOVEОтправляется окну после того как оно было перемещено
    WM_PAINTПерерисовка окна
    WM_QUITЗавершение прикладной программы
    WM_SETFOCUSПолучен фокус ввода
    WM_SHOWWINDOWПосылается перед показом или сокрытием
    WM_SIZEПосылается после изменения размеров окна
    WM_TIMERСообщение от таймера
    Все сообщения, за исключением сообщений WM_PAINT, WM_TIMER и WM_QUIT помещаются системой в конец очереди сообщений, которая работает по принципу FIFO (First In, First Out ― первым пришел ― первым ушел).
    Работа любой windows-программы с графическим интерфейсом основана на обработке сообщений. В Windows у каждого процесса есть своя очередь сообщений, в нее попадают все сообщения, адресованные всем окнам данного процесса. Процесс (приложение) извлекает из этой очереди сообщение, обрабатывает его, потом берет следующее и так до бесконечности пока не будет получено сообщение WM_QUIT, требующее завершить данное приложение.
    В основе каждого окна лежит так называемый класс окна, который определяет все основные свойства данного окна.

    Регистрация класса окна

    Класс окна описывается структурой WNDCLASSEX
    Код (ASM):
    1. WNDCLASSEX STRUCT
    2.   cbSize            DWORD ? ;Размер структуры
    3.   style             DWORD ? ;стиль класса
    4.   lpfnWndProc       QWORD ? ;адрес оконной процедуры
    5.   cbClsExtra        DWORD ? ;объем дополнительной памяти резервируемой за структурой (обычно 0)
    6.   cbWndExtra        DWORD ? ;объем дополнительной памяти за экземпляром окна оконной процедуры (обычно 0)
    7.   hInstance         QWORD ? ;дескриптор модуля, в котором описана оконная процедура
    8.   hIcon             QWORD ? ;дескриптор иконки (если не используется иконка по умолчанию)
    9.   hCursor           QWORD ? ;дескриптор курсора (если не используется курсор по умолчанию)
    10.   hbrBackground     QWORD ? ;дескриптор кисти или системный цвет фона
    11.   lpszMenuName      QWORD ? ;адрес строкового имени ресурса меню (NULL если меню нет) или его идентификатор
    12.   lpszClassName     QWORD ? ;адрес строкового имени класса
    13.   hIconSm           QWORD ? ;дескриптор малой иконки, ассоциированной с классом
    14. WNDCLASSEX ENDS
    Стиль класса (CLASS STYLE ― CS_) ― это один из предопределенных стилей классов окна или их сочетание. Существуют следующие стили классов:
    Стиль классаЗначениеОписание
    binHex
    CS_VREDRAW0000000000000001
    1​
    Перерисовывать окно при изменении вертикальных размеров
    CS_HREDRAW0000000000000010
    2​
    Перерисовывать все окно при изменении ширины
    CS_KEYCVTWINDOW0000000000000100
    4​
    В окне выполняется преобразование виртуальных клавиш
    CS_DBLCLKS0000000000001000
    8​
    Посылать сообщение от мыши при двойном щелчке в пределах окна
    CS_0000000000010000
    10​
    ?​
    CS_OWNDC0000000000100000
    20​
    У каждого окна уникальный контекст устройства
    CS_CLASSDC0000000001000000
    40​
    Контекст устройства, который будет разделяться всеми окнами класса. При нескольких потоках операционная система разрешит доступ только одному потоку
    CS_PARENTDC0000000010000000
    80​
    У дочернего окна будет область отсечки от родительского. Повышает производительность
    CS_NOKEYCVT0000000100000000
    100​
    Отключено преобразование виртуальных клавиш
    CS_NOCLOSE0000001000000000
    200​
    Отключить команду закрыть
    CS_0000010000000000
    400​
    ?​
    CS_SAVEBITS0000100000000000
    800​
    Позволяет сохранять область экрана в виде битовой матрицы закрытую в данный момент другим окном, используется для восстановления экрана
    CS_BYTEALIGNCLIENT00010000000000001000(по горизонтали) выравнивание рабочей области окна по границе байта. Влияет на ширину окна и его горизонтальное положение на экране
    CS_BYTEALIGNWINDOW00100000000000002000(по вертикали) выравнивает окна по границе байта
    CS_GLOBALCLASS,
    CS_PUBLICCLASS
    01000000000000004000Позволяет приложению создавать окно класса независимо от значения параметра hInstance, передаваемого в функцию CreateWindow или CreateWindowEx. Если вы не укажете этот стиль, параметр hInstance, передаваемый в функцию CreateWindow (или CreateWindowEx), должен совпадать с параметром hInstance, передаваемым в функцию RegisterClass. Вы можете создать глобальный класс, создав класс окна в динамически подключаемой библиотеке (DLL) и указав имя библиотеки DLL в реестре под следующими ключами:
    Создавать глобальный класс, который можно поместить в динамическую библиотеку dll.
    Существуют следующие системные цвета фона:
    Символьное обозначениеЦвет "по-умолчанию"значение
    в win.h
    COLOR_ACTIVEBORDERБелый10
    COLOR_ACTIVECAPTIONЧерный_____2
    COLOR_APPWORKSPACEТемно-серый12
    COLOR_BACKGROUNDТемно-серый1
    COLOR_BTNFACEБелый15
    COLOR_BTNSHADOWСветло-серый16
    COLOR_BTNTEXTТемно-серый18
    COLOR_CAPTIONTEXTЧерный9
    COLOR_GRAYTEXTТемно-серый17
    COLOR_HIGHLIGHTТемно-серый13
    COLOR_HIGHLIGHTTEXTСиний14
    COLOR_INACTIVEBORDERСветло-серый11
    COLOR_INACTIVECAPTIONСиний3
    COLOR_MENUБледно-голубой4
    COLOR_MENUTEXTЧерный7
    COLOR_SCROLLBARНа фон попадает все то, что собой закрыло окно0
    COLOR_WINDOWБелый5
    COLOR_WINDOWFRAMEБелый6
    COLOR_WINDOWTEXTЧерный8
    [​IMG]
    Но пользователь в любой момент может изменить эти цвета
    [​IMG]
    Для того, чтобы операционная система узнала о новом класса, его нужно зарегистрировать. Делается это с помощью функции RegisterClassEx(A/W). Прототип функции:
    Код (C):
    1. ATOM WINAPI RegisterClassEx(
    2. __in const WNDCLASSEX *lpwcx // указатель на структуру WNDCLASSEX
    3. );
    В случае успеха данная функции возвращает уникальный идентификатор зарегистрированного класса. Если же функция по какой-либо причине не может выполнить свою задачу, тогда возвращаемое ею значение равно нулю.
    Уничтожить или «снять с регистрации» класс можно функцией UnRegisterClass(A/W) (это делается только после того, как уничтожены все окна построенные на основе этого класса), вот ее описание:
    Код (C):
    1. BOOL WINAPI UnregisterClass(
    2. __in LPCTSTR lpClassName, // адрес строки с именем удаляемого класса
    3. __in_opt HINSTANCE hInstance /* дескриптор модуля, в котором был создан
    4. класс */
    5. );
    Если данная функция успешно отрабатывает, то возвращается не нулевое значение. Если же функция не может выполнить возложенную на нее задачу (не найден такой класс, существуют окна, основанные на этом классе и другие) тогда возвращается ноль.
    Если у приложения всего одно окно, которое уничтожается по завершении работы приложения ― тогда вызывать эту функцию необязательно. Но если у приложения много окон, созданных на основе разных классов, тогда вызывать функцию UnregisterClass(A/W) необходимо ― иначе появится «утечка памяти».
     
  10. Mikl___

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

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

    Создание окна. Стили окна

    После того как класс окна зарегистрирован, можно создать окно. Окна создаются с помощью функции CreateWindowEx(A/W).
    Код (C):
    1. HWND WINAPI CreateWindowEx(
    2. __in DWORD dwExStyle, // Расширенные стили окна
    3. __in_opt LPCTSTR lpClassName, /* Указатель на строку с именем
    4. зарегистрированного класса */
    5. __in_opt LPCTSTR lpWindowName, // Указатель на строку заголовка окна
    6. __in DWORD dwStyle, // Стили окна
    7. __in int x, // Начальное положение окна по горизонтали
    8. __in int y, // Начальное положение окна по вертикали
    9. __in int nWidth, // Ширина окна
    10. __in int nHeight, // Высота окна
    11. __in_opt HWND hWndParent, // Дескриптор родительского окна
    12. __in_opt HMENU hMenu, // Дескриптор меню (NULL если его нет)
    13. __in_opt HINSTANCE hInstance, // Дескриптор модуля, связанного с окном
    14. __in_opt LPVOID lpParam /* Параметр lParam, который будет передан
    15. окну с сообщением WM_CREATE */
    16. );
    Существуют следующие стили окна (WINDOWS STYLE ― WS_), которые сочетаются друг с другом:
    Стиль окнаЗначениеОписание
    BinHex
    WS_OVERLAPPED,
    WS_TILED
    00000000000000000000000000000000
    0​
    Перекрывающееся окно (может располагаться поверх других окон)
    WS_ACTIVECAPTION00000000000000000000000000000001
    1​
    WS_OPENWindow is opened
    WS_ICONIFY00000000000000000000000000000010
    2​
    Window is iconified
    WS_MENU00000000000000000000000000000100
    4​
    Window has menu
    WS_TOOLBAR00000000000000000000000000001000
    8​
    Window has toolbar
    WS_GROW00000000000000000000000000010000
    10​
    Window use graphic effects
    WS_UNTOPPABLE00000000000000000000000000100000
    20​
    Window is untoppable
    WS_FORM00000000000000000000000001000000
    40​
    Window is a formular
    WS_FORMDUP00000000000000000000000010000000
    80​
    Window is a duplicated formular
    WS_MODAL00000000000000000000000100000000
    100​
    Window is modal
    WS_FRAME_ROOT00000000000000000000001000000000
    200​
    Window is a frame root
    WS_FRAME00000000000000000000010000000000
    400​
    Window is a framed window
    WS_CUSTOMWindow is a custom handled window (the handle does not correspond to the AES one)
    WS_ALLICNF00000000000000000000100000000000
    800​
    All windows are iconified
    WS_FULLSIZE00000000000000000001000000000000
    1000​
    Window has fulled size
    WS_PEXEC00000000000000000010000000000000
    2000​
    Window is closed because of Pexec-call (reserved by ShelWrite)
    WS_FOREIGN00000000000000000100000000000000
    4000​
    Window is not created by application but inserted in WinDom list
    WS_WIN_IN_USE00000000000000001000000000000000
    8000​
    Window structure is in use: should not be free-ed (see mt_WindDelete())
    WS_DESTROYED00000000000000010000000000000000
    10000​
    Window structure should be free-ed as soon as possible
    WS_MAXIMIZEBOXДобавляет в заголовок окна кнопку «развернуть»
    WS_TABSTOPВключает обход управляющего элемента по TAB
    WS_MINIMIZEBOX00000000000000100000000000000000
    20000​
    Добавляет в заголовок окна кнопку «cвернуть»
    WS_GROUPПервый элемент управления в группе управляющих элементов. При указании у формы отображает кнопку «свернуть окно»
    WS_SIZEBOX,
    WS_THICKFRAME
    00000000000001000000000000000000
    40000​
    Окно, у которого можно изменять размер
    WS_SYSMENU00000000000010000000000000000000
    80000​
    Окно, имеющее системное меню (кнопки «закрыть», «свернуть», «развернуть»).
    WS_HSCROLL00000000000100000000000000000000
    100000​
    Отображает горизонтальную полосу прокрутки (scroll)
    WS_VSCROLL00000000001000000000000000000000
    200000​
    Вертикальная полоса прокрутки
    WS_DLGFRAME00000000010000000000000000000000
    400000​
    Отображает двойную границу окна, но без заголовка (не работает при WS_CAPTION). Используется в диалогах
    WS_BORDER00000000100000000000000000000000
    800000​
    Отображает тонкую границу окна
    WS_CAPTION00000000110000000000000000000000
    C00000​
    Отображает заголовок окна (автоматически устанавливает флаг WS_BORDER)
    WS_OVERLAPPEDWINDOW,
    WS_TILEDWINDOW
    00000000110011110000000000000000
    CF0000​
    Перекрывающееся окно с включенными флагами WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX,WS_MAXIMIZEBOX
    WS_MAXIMIZE00000001000000000000000000000000
    1000000​
    Создает окно максимальных размеров
    WS_CLIPCHILDREN00000010000000000000000000000000
    2000000​
    Окно включает области занятые дочерними окнами, которые могут быть отрисованы в отсутствие родительского окна
    WS_CLIPSIBLINGS00000100000000000000000000000000
    4000000​
    Отсекает дочерние окна друг от друга, то есть когда определенное дочернее окно получает сообщение WM_PAINT, стиль отсекает все другие дочерние окна, которые перекрывают данное дочернее окно, от той области дочернего окна, которая будет обновлена. Если стиль WS_CLIPSIBLINGS не определен и дочерние окна перекрывают друг друга, то можно при рисовании в клиентской области одного дочернего окна рисовать и в клиентской области соседнего дочернего окна.
    WS_DISABLED00001000000000000000000000000000
    8000000​
    Недоступное окно
    WS_VISIBLE0001000000000000000000000000000010000000Видимое окно
    WS_ICONIC,
    WS_MINIMIZE
    0010000000000000000000000000000020000000Отображает свернутое окно
    WS_CHILD,
    WS_CHILDWINDOW
    0100000000000000000000000000000040000000Создаваемое окно является дочерним. Не используется с WS_POPUP
    WS_POPUP1000000000000000000000000000000080000000Флаг, обратный к WS_OVERLAPPED. Создается окно, не имеющее ничего, кроме поверхности.
    WS_POPUPWINDOW1000000010001000000000000000000080880000Окно с включенными флагами WS_BORDER, WS_POPUP, WS_SYSMENU

    Расширенные стили окна


    Расширенный стиль окнаЗначениеОписание
    BinHex
    WS_EX_LEFT0000000000000000000
    0​
    Окно с левосторонним выравниванием элементов и текста
    WS_EX_LTRREADINGТекст окна отображается слева направо
    WS_EX_RIGHTSCROLLBARВертикальная полоса прокрутки (scrollbar) в правой части окна
    WS_EX_DLGMODALFRAME0000000000000000001
    1​
    окно с двойной рамкой может сочетаться с WS_CAPTION
    WS_EX_0000000000000000010
    2​
    WS_EX_NOPARENTNOTIFY0000000000000000100
    4​
    Дочернее окно, созданное с этим стилем не посылает сообщение WM_PARENTNOTIFY родительскому окну, когда оно создается или разрушается
    WS_EX_TOPMOST0000000000000001000
    8​
    Определяет, что окно, созданное с этим стилем должно быть размещено выше всех, не самых верхних окон и должно стоять выше их, даже тогда, когда окно дезактивировано
    WS_EX_ACCEPTFILES0000000000000010000
    10​
    Определяет окно, способное принимать перетаскиваемые на него файлы из «Проводника Windows»
    WS_EX_TRANSPARENT0000000000000100000
    20​
    прозрачное окно
    WS_EX_MDICHILD0000000000001000000
    40​
    Создает MDI дочернее окно
    WS_EX_TOOLWINDOW0000000000010000000
    80​
    окно с тонким заголовком
    WS_EX_WINDOWEDGE0000000000100000000
    100​
    окно имеет рамку с выпуклым краем
    WS_EX_PALETTEWINDOW0000000000110001000
    188​
    WS_EX_WINDOWEDGE+WS_EX_TOOLWINDOW+WS_EX_TOPMOST
    WS_EX_CLIENTEDGE0000000001000000000
    200​
    окно с «утопленной» клиентской частью
    WS_EX_OVERLAPPEDWINDOW0000000001100000000
    300​
    WS_EX_CLIENTEDGE+WS_EX_WINDOWEDGE
    WS_EX_CONTEXTHELP0000000010000000000
    400​
    включает кнопку помощи (знак вопроса) в заголовок окна
    WS_EX_0000000100000000000
    800​
    WS_EX_RIGHT0000001000000000000
    1000​
    Окно с правосторонним выравниванием элементов и текста
    WS_EX_RTLREADING0000010000000000000
    2000​
    Текст окна отображается справа налево
    WS_EX_LEFTSCROLLBAR0000100000000000000
    4000​
    Размещает вертикальную полосу прокрутки (scrollbar) в левой части окна
    WS_EX_0001000000000000000
    8000​
    WS_EX_CONTROLPARENT001000000000000000010000Включает возможность навигации пользователя по элементам формы с использованием клавиши TAB
    WS_EX_STATICEDGE010000000000000000020000Окно с трехмерным стилем рамки
    WS_EX_APPWINDOW100000000000000000040000на панель задач выводится кнопка окна
    Функция CeateWindowEx(A/W) возвращает дескриптор созданного окна или ноль если не удалось создать окно.
    Уничтожается окно функцией DestroyWindow
    Код (C):
    1. BOOL WINAPI DestroyWindow(
    2. __in HWND hWnd // Дескриптор уничтожаемого окна
    3. );
    Если функции не удается уничтожить окно, тогда возвращается ноль, иначе возвращается ненулевое значение.

    Цикл обработки сообщений

    Цикл выполняет следующие задачи:
    1. извлекает из очереди сообщений очередное сообщение, требующее обработки;
    2. приводит полученное сообщение к аппаратно-независимому виду;
    3. передает его соответствующей оконной процедуре, которая его и обрабатывает.
    Все сообщения, посылаемые приложению, помещаются в очередь сообщений приложения. Из этой очереди нужно извлечь очередное обрабатываемое сообщение. Для этого используется функция GetMessage(A/W)
    Код (C):
    1. BOOL WINAPI GetMessage(
    2. __out LPMSG lpMsg, /* адрес структуры MSG, в которую заносится
    3. информация о сообщении извлеченном из очереди сообщений */
    4. __in_opt HWND hWnd, /* Дескриптор окна-адресата сообщения (если NULL, то извлекаются все сообщения, адресованные всем окнам данного приложения) */
    5. __in UINT wMsgFilterMin,// Минимальный код извлекаемого сообщения
    6. __in UINT wMsgFilterMax // Максимальный код извлекаемого сообщения
    7. );
    Функция GetMessage(A/W) не вернет управление до тех пор, пока не придет какое-либо сообщение. Функция возвращает одно из трех возможных значений:
    • Ненулевое положительное число ― если из очереди извлечено сообщение;
    • 0 ― если получено сообщение WM_QUIT, требующее закрыть приложение;
    • -1 ― если в процессе работы функции произошла какая-то ошибка
    Параметры wMsgFilterMin и wMsgFilterMax служат для отбора строго определенных сообщений, например, сообщений от клавиатуры.
    Структура MSG имеет следующий вид:
    Код (ASM):
    1. MSG STRUCT STRUCT_ALIGN
    2.     hwnd        QWORD ? ;Дескриптор окна, которому адресовано сообщение
    3.     message        DWORD ?,?;Код сообщения
    4.     wParam        QWORD ? ;Параметр сообщения (зависит от кода)
    5.     lParam        QWORD ? ;Параметр сообщения (зависит от кода)
    6.     time        DWORD ? ;Время, когда сообщение было помещено в очередь
    7.     pt        POINT <> ;Позиция курсора мыши в момент, когда сообщение было помещено в очередь
    8.                        DWORD ?
    9. MSG ENDS
    Передача полученного сообщения в оконную процедуру осуществляется функцией DispatchMessage(A/W)
    Код (C):
    1. LRESULT WINAPI DispatchMessage(
    2. __in const MSG *lpmsg // адрес структуры MSG с передаваемым сообщением
    3. );
    Функция DispatchMessage(A/W) возвращает операционной системе значение, возвращенное оконной процедурой при обработке переданного ей сообщения.
     
  11. Mikl___

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

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

    Оконная процедура

    Оконная процедура осуществляет непосредственную обработку полученных сообщений. Когда в цикле обработки сообщений вызывается функция DispatchMessage(A/W), операционная система находит окно, которому адресовано сообщение, находит оконную процедуру этого окна и передает ей управление.
    Код (C):
    1. LRESULT CALLBACK WindowProc(
    2. __in HWND hwnd, // дескриптор окна, которому адресовано сообщение
    3. __in UINT uMsg, // код сообщения
    4. __in WPARAM wParam, // параметр сообщения
    5. __in LPARAM lParam // параметр сообщения
    6. );

    Пример оконного приложения

    Скачайте пример здесь.
    Код (ASM):
    1. ; GUI #
    2. include win64a.inc
    3. .code
    4. WinMain proc
    5. local msg:MSG
    6.     xor ebx,ebx        ;rbx = 0
    7.     mov esi,IMAGE_BASE    ; дескриптор нашей программы
    8.     mov eax,10027h          ; дескриптор иконки
    9.     mov edi,offset ClassName; Имя нашего класса окна
    10. ; заполнение структуры wc и регистрация класса
    11.     push rax    ;hIconSm дескриптор малой иконки
    12.     push rdi    ;lpszClassName Имя класса окна
    13.     push rbx    ;lpszMenuName меню
    14.     push COLOR_WINDOWTEXT;hbrBackground Цвет фона
    15.     push 10003h ;hCursor дескриптор курсора
    16.     push rax    ;hIcon дескриптор иконки
    17.     push rsi    ;hInstance дескриптор модуля
    18.     push rbx    ;cbClsExtra & cbWndExtra
    19.     pushaddr WndProc  ;lpfnWndProc
    20. ;Адрес процедуры окна, ответственной за окна, создаваемых на основе этого класса
    21.     push sizeof WNDCLASSEX;cbSize & style
    22. ;Стиль окон, создаваемых из этого класса. Вы можете
    23. ;комбинировать несколько стилей вместе, используя оператор "or".
    24.     invoke RegisterClassEx,esp    ;rsp = addr WNDCLASSEX
    25. ;регистрация нашего класса окна
    26. ;После регистрации класса окна, мы должны вызвать CreateWindowEx,
    27. ;чтобы создать наше окно, основанное на этом классе
    28. ;--------------------------+
    29. ; creating the main window |
    30. ;--------------------------+
    31.     push rbx        ;lрParam: Опциональный указатель на структуру данных,
    32. ;передаваемых окну. Это используется окнами MDI, чтобы передать структуру
    33. ;CLIENTCREATESTRUCT. Обычно этот параметр установлен в NULL, означая, что
    34. ;никаких данных не передается через CreateWindow(). Окно может получать значение
    35. ;этого параметра через вызов функции GetWindowsLong.
    36.     push rsi    ;rsi=400000h hInstance: дескриптор программного модуля, создающего окно
    37.     shl esi,9    ;rsi=CW_USEDEFAULT
    38.     push rbx        ;hMenu: дескриптор меню окна. NULL - если будет использоваться меню,
    39. ;определенное в классе окна. Взгляните на код, объясненный ранее, член структуры
    40. ;WNDCLASSEX lрszMenuName. Он определяет меню "по умолчанию" для класса окна.
    41. ;Каждое окно, созданное из этого класса будет иметь тоже меню по умолчанию, до
    42. ;тех пор пока вы не определите специально меню для какого-то окна, используя
    43. ;параметр hMenu. Этот параметр - двойного назначения. В случае, если ваше окно
    44. ;основано на предопределенном классе окна, оно не может иметь меню. Тогда hMenu
    45. ;используется как ID этого контрола. Windows может определить действительно ли
    46. ;hMenu - это дескриптор меню или же ID контрола, проверив параметр lрClassName. Если
    47. ;это имя предопределенного класса, hMenu - это идентификатор контрола. Если нет,
    48. ;это дескриптор меню окна
    49.      push rbx        ;hWndParent: дескриптор родительского окна (если существует). Этот
    50. ;параметр говорит Windows является ли это окно дочерним (подчиненным) другого
    51. ;окна, и, если так, кто родитель окна. Заметьте, что это не родительско-дочерние
    52. ;отношения в окна MDI (multiрly document interface). Дочерние окна не ограничены
    53. ;границами клиентской области родительского окна. Эти отношения нужны для
    54. ;внутреннего использования Windows. Если родительское окно уничтожено, все
    55. ;дочерние окна уничтожаются автоматически. Это действительно просто. Так как в
    56. ;нашем примере всего лишь одно окно, мы устанавливаем этот параметр в NULL
    57.     push rsi        ;X-координата верхнего левого угла окна.
    58. ;Обычно эти значения равны CW_USEDEFAULT, что позволяет Windows решить, куда
    59. ;поместить окно. nWidth, nHeight: Ширина и высота окна . Вы можете также использовать
    60. ;CW_USEDEFAULT, чтобы позволить Windows выбрать соответствующую ширину и высоту
    61. ;для вас
    62.     push rsi        ;Y-координата верхнего левого угла окна
    63.     push rsi        ;ширина окна в пикселях
    64.     push rsi        ;высота окна в пикселях
    65.     sub esp,20h
    66.         invoke CreateWindowEx,0,\;dwExStyle: Дополнительные стили окна
    67.     edi,\;lpClassName: Адрес ASCIIZ строки, содержащей имя класса окна
    68.     edi,\;lpWindowName: Адрес ASCIIZ строки, содержащей имя окна
    69.     WS_OVERLAPPEDWINDOW or WS_VISIBLE;dwStyle: Стили окна. Вы можете
    70. ;определить появление окна здесь. Можно передать NULL без проблем, тогда у окна
    71. ;не будет кнопок изменения размеров, закрытия и системного меню. Большого прока
    72. ;от этого окна нет. Самый общий стиль - это WS_OVERLAPPEDWINDOW. Стиль окна всегда
    73. ;лишь битовый флаг, поэтому вы можете комбинировать различные стили окна с помощью
    74. ;оператора "or", чтобы получить желаемый результат. Стиль WS_OVERLAPPEDWINDOW в
    75. ;действительности комбинация большинства общих стилей с помощью этого метода.
    76. ;цикл обработки сообщений
    77.         lea edi,msg
    78. @@:    invoke GetMessage,edi,NULL,0,0
    79.     invoke DispatchMessage,edi
    80.     jmp @b
    81. WinMain endp
    82. ;----------------------+
    83. ; the window procedure |
    84. ;----------------------+
    85. WndProc:cmp edx,WM_DESTROY
    86.     je wmDESTROY
    87.     jmp NtdllDefWindowProc_;все сообщения, не обрабатываемые в функции
    88. ;WndProc, направляются на обработку по умолчанию
    89. wmDESTROY: invoke RtlExitUserProcess,NULL; выходим из программы
    90. ;data-----------------------------
    91. ClassName db "Win64 Iczelion's lesson #3a: Simple windows",0
    92. end
    [​IMG]

    Чем наши исходники будут отличаться от классических


    • при линковке программ секции кода присвоены атрибуты чтение, запись и выполнение ― поэтому и данные и код будут размещены в единственной секции .code
    • любая программа начинается с
      Код (ASM):
      1.     invoke GetModuleHandle, NULL
      2.     mov    hInstance,rax
      но мы при линковке указываем ключ /BASE:400000h поэтому функция GetModuleHandle будет возвращать значение 400000h
    • обратите внимание на этот фрагмент
      Код (ASM):
      1.     xor ebx,ebx        ;rbx = 0
      WinAPI-функции сохраняют неизменными значения в регистрах RBP, RSP, RBX, RDI, RSI, R11-R15 этим мы и воспользуемся ― обнулим значение в регистре RBX и будем применять значения этого регистра везде, где понадобится нулевое значение, в RSI мы сохранили значение дескриптора программы после вызова RegisterClassA, в RDI сохраняется указатель на структуру MSG после вызовов GetMessage и DispatchMessage
    • Переворачиваем структуру WNDCLASSEX "вверх ногами" и помещаем ее в стек. Значения, возвращаемые функциями LoadCursor и LoadIcon, смотрим заранее программой
      Код (ASM):
      1. ; GUI #
      2. include win64a.inc
      3. OPTION PROLOGUE:rbpFramePrologue
      4. .code
      5. WinMain proc
      6. local buffer[96]:byte
      7. local hIcon:qword
      8. local hCursor:qword
      9.    
      10.     invoke LoadIcon,NULL,IDI_APPLICATION
      11.     mov hIcon,rax
      12.         invoke LoadCursor,NULL,IDC_ARROW
      13.     mov hCursor,rax    
      14. invoke GetModuleHandle,NULL
      15.     invoke wsprintf,&buffer,&fmt,hIcon,hCursor,rax
      16.         invoke MessageBox,NULL,&buffer,&MsgCaption,MB_OK
      17.         invoke RtlExitUserProcess,NULL
      18. WinMain endp
      19. ;data-------------------------------------
      20. MsgCaption db "Iczelion's tutorial #1",0
      21. fmt db "hCursor = %08Xh",0Ah,"hIcon = %08Xh",0Ah,"hInstance =%08Xh",0
      22. end
      [​IMG]
      Так как курсор и иконка передаваемые нами соответствуют стандартной стрелочке и стандартной иконке, то их идентификаторы предопределены и не изменяются. Поэтому мы и не используем LoadCursor и LoadIcon. Значение вершины стека передаем как указатель на структуру WNDCLASSEX
    • пятым, шестым, седьмым и восьмым параметром в стек помещается значение CW_USEDEFAULT = 80000000h, то есть в коде нашей программы четыре раза появляется 5-ти байтовый код команды push 80000000h (6800000080h), но ведь в регистре RSI уже есть число 400000h (тем более значение hInst дальше нигде не используется )
      400000h × 200h = 80000000h
      200h = 512 = 29
      400000h << 9 = 80000000h
      сдвигаем hInst влево на 9 разрядов командой shl esi,9 (код 0C1E609h) и четыре раза помещаем в стек содержимое регистра RSI командой push rsi (код 56h)
     

    Вложения:

    • tut_03.zip
      Размер файла:
      4,4 КБ
      Просмотров:
      1.218
    Последнее редактирование: 21 июн 2021
    mantissa нравится это.
  12. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    • После регистрации окна обычно проделывают три операции:
      1. создает окно (функция CreateWindow);
      2. Отображает его на экране (ShowWindow)
      3. Обновить содержимое окна (UpdateWindow)
      Код (ASM):
      1. invoke CreateWindowEx,NULL,\
      2.                 ADDR ClassName,\
      3.                 ADDR AppName,\
      4.                 WS_OVERLAPPEDWINDOW,\
      5.         . . .
      6.     invoke ShowWindow, hwnd,CmdShow ; отобразить наше окно на десктопе
      7.         invoke UpdateWindow, hwnd ; обновить клиентскую область
      но, как показала практика, добавление к стилю окна (обычно WS_OVERLAPPEDWINDOW) во время создания окна стиля WS_VISIBLE делает ненужным вызов функций ShowWindow и UpdateWindow. Еще один фокус с CreateWindow ― правильное указание класса окна необходимо, если мы создаем элемент окна, с заранее определенными свойствами (STATIC, BUTTON, EDIT и тому подобное) правда в этом случае нам не нужен RegisterClass. Когда мы создаем наше главное окно, то его название может быть любым, в том числе и на русском или на китайском в юникоде. Экономим на этом, и название класса окна, и название имени окна, которое будет показано на title bar'е ― будут одним и тем же.
      Код (ASM):
      1.  mov edi,offset wTitle; Имя нашего класса окна и имя окна
      2.     . . .
      3.     invoke CreateWindow,0,edi,edi,\
      4.         WS_OVERLAPPEDWINDOW + WS_VISIBLE,\
      Модуль цикла сообщений:
      Код (ASM):
      1. LOCAL msg:MSG
      2.  . . .
      3. .WHILE TRUE   ; Enter message loop
      4.        invoke GetMessage, ADDR msg,NULL,0,0
      5.     .BREAK .IF (!eax)
      6.        invoke TranslateMessage, ADDR msg
      7.        invoke DispatchMessage, ADDR msg
      8.     .ENDW
      Наш модуль обработки цикла сообщений.
      Код (ASM):
      1.         lea edi,msg
      2. @@:    invoke GetMessage,edi,NULL,0,0
      3.     invoke DispatchMessage,edi
      4.     jmp @b
      Обычно к циклу сообщений добавляют еще и TranslateMessage хотя обработка нажатий на клавиши в программе и не предусматривается
    • Единственное сообщение, которое вы ОБЯЗАНЫ обработать ― это WM_DESTROY. Это сообщение посылается вашему окну, когда оно закрывается (пользователь нажал на крестик в правом верхнем углу ([​IMG]) или комбинацию клавиш Alt+F4 или выбрал пункт «Закрыть» в системном меню). В то время, когда процедура окна его получает, окно уже исчезло с экрана. Это всего лишь напоминание, что ваше окно было уничтожено, поэтому вы должны готовиться к выходу в Windows. Если вы хотите дать шанс пользователю предотвратить закрытие окна, тогда вы должны предусмотреть обработку сообщения WM_CLOSE. После получения сообщения WM_DESTROY программа выполнит RtlExitUserProcess.
      Код (ASM):
      1. WndProc: cmp edx,WM_DESTROY            ; если пользователь закрывает окно
      2.         je wmDESTROY
      3.         jmp NtdllDefWindowProc_; все остальные сообщения
      4. wmDESTROY: invoke RtlExitUserProcess,NULL
    • В программе предусмотрена обработка только одного сообщения, поэтому нам не нужен стандартный пролог.
    • Стандартная обработка остальных сообщений
    Код (ASM):
    1.  invoke DefWindowProc,hWnd,uMsg, wParam,lParam
    2. leave
    3. ret
    заменена на
    Код (ASM):
    1. jmp NtdllDefWindowProc_
    Существует методика оптимизации, связанная с переходами и вызовами, которая называется «сращиванием хвостов». Она подразумевает преобразование вызовов в переходы: вызовы по самой своей природе требуют большего времени, чем переходы, поскольку помещают в стек адрес возврата и в результате требуют больше обращений к памяти. «Сращивание хвостов» ― это просто преобразование команды CALL, непосредственно за которой следует команда RET, в команду JMP. Например, последовательность:
    Код (ASM):
    1. Proc1 proc
    2.      ...
    3.      call Proc2
    4.      ret
    5.  Proc1 endp
    6.  
    7.  Proc2 proc
    8.      ...
    9.      ret
    10.  Proc2 endp
    преобразуется в более быструю:
    Код (ASM):
    1. Proc1 proc
    2.      ...
    3.      jmp Proc2
    4.  Proc1 endp
    5.  
    6.  Proc2 proc
    7.      ...
    8.      ret
    9.  Proc2 endp
    Такая оптимизация приводит к следующему: поскольку адрес команды, вызывающей PROC1, находится в стеке на входе в PROC2, процедура PROC2 возвращается прямо к исходной вызывающей программе, тем самым устраняются лишние команды CALL и RET. Если программа PROC2 физически (в памяти) следует за программой PROC1, то можно обойтись даже без команды JMP PROC2, и за выполнением PROC1 может сразу же следовать PROC2.
    Для функции NtdllDefWindowProc_ передаются те же параметры, что и для функции WndProc, поэтому достаточно будет поставить jmp вместо call и ret. Так как функция WndProc была вызвана без пролога, то мы выходим из нее без эпилога "leave".
    Код (ASM):
    1.     .ENDIF
    2.      xor eax,eax
    3.      ret
    4.  WndProc endp
    5.  end
    сброс регистра RAX в ноль при возврате из функции WndProc (как показатель обработанных сообщений) на самом деле не требуется (это необходимо только для диалогов или если вызывается функция SendMessage).
    © Mikl___ 2021
     
  13. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Глава восьмая. Как Братец Кролик выводил текст на экран при помощи функции DrawText
    Братец Лис кончил зализывать лапу и мрачно посмотрел на Братца Кролика. Братец Кролик ответил ему не менее мрачным взглядом. Он наверняка демонстративно отвернулся бы, если б не переломаная шея.
    ― Это неправильно. ― констатировал Братец Лис. В его голосе звучала неприкрытая неприязнь ко всем представителям кроличьего рода, и к жизни вообще, позволяющей таким вещам происходить. ― Кроликам кусаться не положено. Природой. У тебя зубы для этого не приспособлены. Сам посмотри, тебе это ни хрена не помогло, а мне теперь две недели хромать.
    ― В общем, конечно, ― сказал Братец Кролик, и в затухающем голосе его прозвучала задумчивость. ― Но с другой стороны, попробовать стоило...
    [​IMG]
    перепишем программу, отображающую текстовую строку "Win64 assembly with MASM is great and easy" в центре клиентской области.
    Сообщение WM_PAINT посылается в окно каждый раз, когда требуется изменить содержание рабочей области окна (client area) (рисунок 1). Рабочая область ― это пространство, которое программист заполняет самостоятельно (область внутри рамки и заголовок), в то время как Windows заботится об остальной части приложения.
    [​IMG]
    рисунок 1​
    Если программист не контролирует какую-либо часть рабочей области, Windows через небольшое время закрасит эту область определенным цветом. Если окно, часть окна было перекрыто другим окном, то после того как окно "откроется", оно получит сообщение WM_PAINT, так как необходимо перерисовать область, которая была скрыта другим окном. Если окно было свернуто, то при развороте на весь экран или при восстановлении до нормального размера окно также получит сообщение WM_PAINT.
    Основное назначение функции BeginPaint ― обеспечить доступ к контексту устройства для окна.
    Контекст устройства (DC ― Device Context) представляет собой соединительное звено между программой пользователя и некоторыми внешними устройствами подключенными к компьютеру. Контексты устройств формируют интерфейс между приложением и принтером или видеокартой.
    00.png
    Важность контекстов устройств обусловлена существованием множества различных типов видеокарт и принтеров
    Функция EndPaint завершает рисование на рабочей области окна. Параметрами BeginPaint и EndPaint являются HWND и адрес PAINTSTRUCT. Структура PAINTSTRUCT имеет следующие поля
    Код (ASM):
    1. PAINTSTRUCT STRUCT
    2.     hdc        QWORD ? ;дескриптор DC
    3.     fErase        DWORD ? ;истина, если перерисовывается заполнение окна
    4.     rcPaint        RECT <> ;координаты области перерисовки
    5.     fRestore    BOOL ?  ;зарезервировано
    6.     fIncUpdate    BOOL ?  ;зарезервировано
    7.     rgbReserved    BYTE 36 dup(?);зарезервировано
    8. PAINTSTRUCT ENDS
    Первое поле ― копия контекста для окна. Второе поле указывает должен или нет перерисовываться фон. Третье поле определяет прямоугольник, в котором происходит рисование. Оставшиеся три поля зарезервированы Windows и интереса (пока) не представляют...
    Функция DrawText использует в качестве параметров контекст устройства, строку, длину строки, RECT и некоторые флажки nFormat (таблица 1). Прототип функции DrawText
    Код (C):
    1. virtual int DrawText( LPCTSTR lpszString,\ /* Указатель на строку, которая будет
    2. выведена. Если nCount -1, строка должна быть с нулевым символом в конце */
    3. int nCount,\ /* Определяет количество символов в строке. Если nCount -1,
    4. то lpszString принят, чтобы быть длинным указателем на строку с нулевым
    5. символом в конце, и DrawText вычисляет символьный счет автоматически */
    6. LPRECT lpRect,\ /* Указатель на структуру RECT, который содержит
    7. прямоугольник (в логических координатах) в котором текст должен форматироваться */
    8. UINT nFormat  //Определяет метод форматирования текста
    9. );
    Таблица 1. Флаги форматирования текста
    DT_BOTTOMОпределяет выровненный по нижней части текст. Это значение должно быть объединено с DT_SINGLELINE.
    DT_CALCRECTОпределяет ширину и высоту прямоугольника. Если имеются мнострочные строки текста, DrawText использует ширину прямоугольника, указанного на lpRect и расширит прямоугольник к последней строке текста. Если имеется только одна строка текста, DrawText изменит правую сторону прямоугольника так, чтобы это ограничило последний символ в строке. В любом случае, DrawText возвращает высоту форматируемого текста, но не выводит текст.
    DT_CENTERВыравнивает по центру текст горизонтально.
    DT_END_ELLIPSIS или DT_PATH_ELLIPSISЗаменяет часть данной строки с эллипсами, в случае необходимости, так, чтобы результат находился в определенном прямоугольнике. Данная строка не изменяется, если флажок DT_MODIFYSTRING не определен. Вы можете определять DT_END_ELLIPSIS, чтобы заменить символы в конце строки, или DT_PATH_ELLIPSIS, чтобы заменить символы в середине строки. Если строка содержит наклонную черту влево ("\"), символы, DT_PATH_ELLIPSIS сохраняют в максимально возможной степени текста после последней наклонной черты влево.
    DT_EXPANDTABSРазворачивает символы табуляции. Заданное по умолчанию число символов позиций табуляции ― восемь.
    DT_EXTERNALLEADINGВключает внешнюю подачу шрифта в высоте строки. Обычно, внешняя подача не включена в высоту строки текста.
    DT_LEFTВыравнивает влево текст.
    DT_MODIFYSTRINGИзменяет данную строку, чтобы согласовать к отображаемому тексту. Этот флажок не имеет никакого эффекта, если DT_END_ELLIPSIS или флажок DT_PATH_ELLIPSIS не определен. Обратите внимание, что некоторые комбинации флажков uFormat могут заставлять переданную строку изменяться. Использование DT_MODIFYSTRING или с DT_END_ELLIPSIS или DT_PATH_ELLIPSIS может заставлять строку изменяться, вызывая исключение в отмене CString.
    DT_NOCLIPВыводит без отсечения. Функция DrawText отрабатывает немного быстрее, когда используется флаг DT_NOCLIP.
    DT_NOPREFIXВыключает обработку префиксных символов. Обычно, DrawText интерпретирует символ амперсанда ("&") как директиву, чтобы подчеркнуть символ, который следует после амперсанта (&A→A), и два символа амперсанда ("&&") как директиву, чтобы печатать одиночный символ амперсанда. Если установлен флаг DT_NOPREFIX ― эта обработка амперсандтов отключена.
    DT_RIGHTВыравнивание теста вправо
    DT_SINGLELINEВывод текста как одиночная строка. Символ с кодом 0Dh ("возврат каретки") и символ с кодом 0Ah ("перевод строки") не разрывают строку.
    DT_TABSTOPУстанавливает табуляторы. Старший байт nFormat ― число символов для каждой позиции табуляции. По умолчанию число символов в табуляции равно восьми.
    DT_TOPОпределяет выровненный по верху текст (только в комбинации с флагом DT_SINGLELINE).
    DT_VCENTERОпределяет вертикально центрированный текст (только в комбинации с флагом DT_SINGLELINE).
    DT_WORDBREAKОпределяет символ, который будет использоваться для "разрыва" строки. Строки будут автоматически разбиты между такими символами, если слово простиралось бы дальше края прямоугольника, определенного lpRect. Символы перевода строки и символы возврата каретки также разорвут строку
    Если по какой-то причине Вам лень вычислять длину выводимой строки ― тогда передайте в качестве третьего параметра -1 и тогда функция DrawText сама подберет длину строки. Параметр RECT определяет область внутрь которой будет выведена строка.
    Скачайте пример здесь.
    Код (ASM):
    1.  
    2. include win64a.inc
    3. .code
    4. WinMain proc
    5. local msg:MSG
    6. xor ebx,ebx
    7. mov esi,IMAGE_BASE
    8. mov eax,10027h
    9. mov edi,offset ClassName
    10. push rax   ;hIconSm
    11. push rdi   ;lpszClassName
    12. push rbx   ;lpszMenuName
    13. push COLOR_WINDOW+1;hbrBackground
    14. push 10003h   ;hCursor
    15. push rax       ;hIcon
    16. push rsi   ;hInstance
    17. push rbx       ;cbClsExtra & cbWndExtra
    18. pushaddr WndProc     ;lpfnWndProc
    19.         mov rax,((CS_HREDRAW or CS_VREDRAW)shl 32)+sizeof WNDCLASSEX
    20. push rax       ;cbSize & style
    21. invoke RegisterClassEx,esp ;addr WNDCLASSEX
    22. push rbx
    23. push rsi ;rsi=400000h
    24. shl esi,9 ;rsi=CW_USEDEFAULT
    25. push rbx
    26. push rbx
    27. push rsi
    28. push rsi
    29. push rsi
    30. push rsi
    31. sub esp,20h
    32.     invoke CreateWindowEx,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    33.     lea edi,msg
    34. @@:     invoke GetMessage,edi,NULL,0,0
    35. invoke DispatchMessage,edi
    36.         jmp @b
    37. WinMain endp
    38. WndProc proc hWnd:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD
    39. local ps:PAINTSTRUCT
    40. local expRect:RECT
    41.         mov  hWnd,rcx
    42.         cmp  edx,WM_DESTROY
    43.         je   wmDESTROY
    44.         cmp  edx,WM_PAINT
    45.         je   wmPAINT
    46.         leave
    47.         jmp DefWindowProc
    48. wmDESTROY: invoke ExitProcess,NULL
    49. wmPAINT:invoke BeginPaint,,&ps
    50.         invoke GetClientRect,hWnd,&expRect
    51.         mov edx,offset expTxt
    52.         invoke DrawText,ps.hdc,,-1,&expRect,DT_SINGLELINE or DT_CENTER or DT_VCENTER
    53.         invoke EndPaint,hWnd,&ps
    54. wmBYE:  leave
    55.         retn
    56. WndProc endp
    57. ;---------------------------------------
    58. ClassName db 'Win64 Iczelion''s lesson #4: Painting with Text',0
    59. expTxt    db 'Win64 assembly with MASM is great and easy',0
    60. end
    Iczelion отводит в стеке место под три локальных переменных.
    Код (ASM):
    1. LOCAL hdc:HDC
    2. LOCAL ps:PAINTSTRUCT
    3. LOCAL rect:RECT
    Переменная hdc используемая для сохранения дескриптора контекста устройства, возвращенного функцией BeginPaint используется всего один раз. В варианте Iczelion'а мы вызываем GetClientRect, чтобы получить размеры клиентской области, размеры возвращаются в переменной rect, которую вы передаете функции DrawText как один из параметров. Но клиентскую область также можно обнаружить в структуре PAINTSTRUCT поэтому нам не нужна функция GetClientRect
    © Mikl___ 2016
     

    Вложения:

    • tut_04.zip
      Размер файла:
      3 КБ
      Просмотров:
      1.206
  14. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Глава пятьдесят первая. Братец Кролик узнает о тексте еще больше
    Когда Братец Кролик Джуниор выскочил на дорогу, по которой как раз прогуливался весьма довольный собой и миром Братец Лис, потрясены и испуганы были оба.
    ― О великая рыжая Сила, помилуй мя грешного, свят-свят-свят... ― бормотал в истерике Братец Лис, от испуга побледнев до желтизны. Братец Кролик же застыл на середине дороги, то ли от страха, то ли прикидываясь булыжником.
    ― Кролик Братца Призрака... То есть, призрак Братца Кролика! ― стонал Лис. ― Чего ты хочешь от меня? Я съел тебя по всем правилам...
    До Братца Кролика младшего стало понемногу доходить, почему Братец Лис в таком состоянии. К сожалению, это объясняло и то, куда подевался почтенный папа-Кролик. Он уже намеревался драпануть, пока Братец Лис трясется и несет околесицу, но ему стало жалко старого Братца Лиса. И в самом деле, Братец Лис был так напуган, что инстинктивно ковырял лапами землю, явно пытаясь закопаться.
    ― Почтенный Братец Лис ― сказал Братец Кролик младший, гордясь собой ― Вы перепутали меня с моим отцом. И хотя, как сын, я испытываю негодование, тем не менее я счел своим долгом разве...
    Братец Лис рванул с места, не произнеся и звука, и Братец Кролик младший закачался в его зубах.
    ― Суеверия... предрашшудки... штарею... ― бормотал Братец Лис с набитым ртом. ― Хорошо хоть, быштрота реакции ошталась при мне...
    [​IMG]
    Добавляем в файл win64a.inc следующие строки
    Код (ASM):
    1. include gdi32.inc
    2. includelib gdi32.lib
    3. ;-----------------------
    4. pushadr MACRO x
    5.     db 68h
    6.     dd x
    7. ENDM
    Функция TextOut ― стандартная функция вывода текста в Windows. Выводит текст в заданных координатах, использует выбранный шрифт. Прототип функции:
    Код (C):
    1. BOOL TextOut(
    2.     HDC hdc,
    3.     int x, // логическая x-координата начала текста
    4.     int y, // логическая y-координата начала текста
    5.     LPCTSTR lpszString, // Указатель на символьную строку
    6.     int nCount // число байт в строке
    7. );

    Установка цвета текста и фона

    Если не предпринимать никаких усилий ― тогда функция TextOut выводит текст на экран черным цветом на текущем фоне окна. Но цвет текста и фона можно изменить при помощи функций SetTextColor и SetBkColor.
    Прототип функции SetTextColor:
    Код (C):
    1. COLORREF SetTextColor(HDC hdc, COLORREF crColor);
    Функция устанавливает цвет текста выбранный пользователем. Система использует этот цвет текста при выводе текста на этом контексте устройства и также при преобразовании точечных рисунков между цветными и одноцветными контекстами устройства.
    Прототип функции SetBkColor:
    Код (C):
    1. OLORREF SetBkColor(HDC hdc, COLORREF crColor);
    Функция устанавливает цвет фона выбранный пользователем. Если функцией SetBkMode установлен режим OPAQUE, система использует цвет фона, чтобы заполнить промежутки в стилях линий, промежутки между заштрихованными линиями в кистях, и фоновом режиме в символьных ячейках. Система также использует фоновый цвет при преобразовании точечных рисунков между цветными и одноцветными контекстами устройства.
    Если устройство не может предоставить цвет, выбранный функциями SetTextColor и SetBkColor, тогда система установит цвет текста или цвет фона к ближайшему физическому цвету.
    Функция SetTextColor устанавливает текущий цвет текста для устройства, ассоциированного с контекстом hdc. Цвет задается параметром crColor (при этом устройство может выбирать реальный цвет, ближайший к заданному, который устройство в состоянии отобразить). Функция SetBkColor устанавливает цвет фона (или ближайший к нему возможный для устройства), задаваемый параметром crColor. Обе функции возвращают предыдущий цвет текста и фона, а при возникновении ошибки ― значение CLR_INVALID
    Цвет задается 32-разрядным значением типа COLORREF, которое кодируется следующим образом:
    старший​
    младший​
    Байты
    3​
    2​
    1​
    0​
    Содержимое
    Заполнен
    нулями​
    Интенсивность
    синего​
    Интенсивность
    зеленого​
    Интенсивность
    красного​
    Цвет получают как сумму основных цветовых компонент в разных соотношениях. Интенсивность каждого цветовой компоненты задается в диапазоне от 0 до 0FFh. Видеосистема компьютера (как и цветное телевидение) основывается на трехкомпонентной модели цвета (RGB-модель), которая позволяет получить большинство существующих в природе цветовых тонов сложением чистых красного, синего и зеленого цветовых тонов в разных соотношениях яркости. При одинаковой яркости всех трех компонент получается черно-белая гамма в диапазоне от черного до белого максимальной яркости. Количество градаций яркости компонентов и соответственно количество результирующих тонов зависят от видеорежима, а точнее ― от количества бит, отводимых под хранение информации под цветовую компоненту.
    Для выбора и использования встроенных шрифтов программа должна создать дескриптор шрифта ― переменную типа HFONT. Получить его можно при помощи функции GetCurrentObject с параметрами hdc и OBJ_FONT Затем необходимо загрузить требуемый шрифт при помощи функции SelectObject
    Получение метрик текста

    Большинство шрифтов в Windows являются пропорциональными, поэтому символы одного и того же кегля могут иметь разную ширину. Кроме того, высота символов и размер нижних выносных элементов (буквы р, ц, у, щ, д) могут быть разными у разных шрифтов. Разными могут быть и расстояния между строками текста. Windows требует, чтобы программист самостоятельно управлял практически всем процессом отображения текста.
    Windows обеспечивает только минимальную поддержку отображения текста в рабочей области окна. Основной функцией вывода текста является TextOut. Эта функция выводит строку текста на экран начиная с указанной позиции. TextOut никак не форматирует выводимый текст, не различает символы "возврат каретки" и "переход в начало строки". Управление выводом текста в рабочую область окна полностью возложена на программиста.
    Размеры шрифта могут быть разными, сами шрифты могут меняться в процессе выполнения программы. Для определения высоты шрифта и интервала между строками используют функцию GetTextMetrics. Функция имеет следующий прототип
    Код (C):
    1. BOOL GetMetrics(
    2.     HDC hdc,// дескриптор контекста отображения
    3.     LPTEXTMETRIC lpTAtrib //указатель на структуру типа TEXTMETRIC
    4. );
    при успешном завершении функции структура типа TEXTMETRIC будет содержать все параметры (метрики) выбранного текста. Структура TEXTMETRIC определена следующим образом
    Код (ASM):
    1. TEXTMETRICA STRUCT
    2.   tmHeight              DWORD      ?;полная высота шрифта
    3.   tmAscent              DWORD      ?;высота над основной линией
    4.   tmDescent             DWORD      ?;размер нижнего выступа
    5.   tmInternalLeading     DWORD      ?;размер верхнего выступа
    6.   tmExternalLeading     DWORD      ?;междустрочный интервал
    7.   tmAveCharWidth        DWORD      ?;средняя ширина символа
    8.   tmMaxCharWidth        DWORD      ?;максимальная ширина символа
    9.   tmWeight              DWORD      ?;насыщенность шрифта
    10.   tmOverhang            DWORD      ?;дополнительная ширина символа для специальных шрифтов
    11.   tmDigitizedAspectX    DWORD      ?;горизонтальный аспект?
    12.   tmDigitizedAspectY    DWORD      ?;вертикальный аспект?
    13.   tmFirstChar           BYTE      ?;первый символ
    14.   tmLastChar            BYTE      ?;последний символ
    15.   tmDefaultChar         BYTE      ?;символ по умолчанию
    16.   tmBreakChar           BYTE      ?;символ для обозначения границы слова
    17.   tmItalic              BYTE      ?;не 0 если шрифт курсив (italic)
    18.   tmUnderlined          BYTE      ?;не 0, если подчеркнутый шрифт
    19.   tmStruckOut           BYTE      ?;не 0, если шрифт зачеркнутый
    20.   tmPitchAndFamily      BYTE      ?;семейство и гарнитура
    21.   tmCharSet             BYTE      ?;идентификатор множества символов
    22. TEXTMETRICA ENDS
    Большинство атрибутов получаемых программой не используются. Наиболее важные атрибуты ― для определения расстояния между строками текста. Для определения начала следующей строки вызывают GetTextMetric, получают значение высоты символа tmHeight и межстрочного интервала tmExternalLeading. Их сумма ― расстояние между строками в логических единицах.
    Для вычисления длинны текста в логических единицах используется функция GetTextExtentPoint32. Функция имеет следующий прототип
    Код (C):
    1. BOOL GetTextExtentPoint32(
    2.     HDC hdc,          //контекст устройства вывода
    3.     LPCSTR lpszString,//указатель на строку длинну которой хотят узнать
    4.     int len,          //количество символов в строке
    5.     LPSIZE lpSize     //структура типа POINT куда будут записаны вычисленные ширина и высота
    6. );
    После выхода из функции GetTextExtentPoint32 поле x структуры POINT содержит длину строки, которую можно использовать для определения начала следующей строки, если требуется выводить следующую строку, начиная с того места, где окончилась предыдущая строка.
    Изменение шрифтов

    Для создания собственного шрифта используются функции CreatFont, CreatFontIndirect, CreatFontIndirectEx. Функция CreatFont использует для создания логического шрифта 14 параметров, поэтому не слишком удобна в использовании. Вместо нее лучше использовать функцию CreatFontIndirect Эта функция получает указатель на структуру LOGFONT в которой упакованы эти же 14 параметров. Функция имеет следующий прототип
    Код (C):
    1. HFONT CreateFontIndirect(
    2.     CONST LOGFONT* lplf
    3. );
    Структура LOGFONT определена следующим образом
    Код (ASM):
    1. LOGFONTA STRUCT
    2.   lfHeight          DWORD     ?
    3.   lfWidth           DWORD     ?
    4.   lfEscapement      DWORD     ?
    5.   lfOrientation     DWORD     ?
    6.   lfWeight          DWORD     ?
    7.   lfItalic          BYTE      ?
    8.   lfUnderline       BYTE      ?
    9.   lfStrikeOut       BYTE      ?
    10.   lfCharSet         BYTE      ?
    11.   lfOutPrecision    BYTE      ?
    12.   lfClipPrecision   BYTE      ?
    13.   lfQuality         BYTE      ?
    14.   lfPitchAndFamily  BYTE      ?
    15.   lfFaceName        BYTE LF_FACESIZE dup(?)
    16.               BYTE 4 dup(?)
    17. LOGFONTA ENDS
    18. LOGFONT TYPEDEF LOGFONTA
    19. LPLOGFONT    TYPEDEF PTR LOGFONT
    Члены структуры LOGFONT

    Требуемая высота шрифта задается параметром lfHeight, а ширина параметром lfWidth. Если параметр lfWidth равен нулю, Windows автоматически подставляет значение, определяемое текущими пропорциями шрифта. Значения lfHeight и lfWidth задаются в логических единицах.
    • lfHeight ― желаемая высота символов (включая поле, отведенное для специальных знаков над символами, но не включая поле, установленное для межстрочного интервала). Поскольку размер шрифта в пунктах ― это и есть высота шрифта без величины поля, отведенного для специальных знаков над символами, то здесь вы на самом деле определяете значение межстрочного интервала. Вы можете установить значение lfHeight равным 0 для задания размера по умолчанию. Если вы зададите значение lfHeight отрицательным числом, Windows использует абсолютное значение этого числа в качестве желаемого размера высоты шрифта, а не как межстрочный интервал. Если вы хотите задать конкретное значение размера в пунктах, его надо преобразовать в логические единицы, и поле lfHeight установить в отрицательное значение результата
    • lfWidth ― желаемая ширина символов. В большинстве случаев его устанавливают в 0 и позволяют Windows выбирать шрифт, основываясь исключительно на высоте.
    • Угол наклона текста в окне может быть любым. Угол по отношению к горизонтальной оси задается параметром nEscapement. Для текста, отображаемого горизонтально, этот параметр должен быть равен нулю. Иначе он определяет угол наклона текста в десятых долях градуса. Значение nEscapement = 900 определяет угол наклона текста 90o.
    • Угол наклона для каждого символа указывается при помощи параметра lfOrientation. Этот параметр задает угол наклона каждого символа по отношению к горизонтальной оси в десятых долях градуса.
    • Параметр lfWeight задает насыщенность (жирность) шрифта и может принимать значения от 0 до 900. Для насыщенности шрифта существуют следующие константы:
      FW_DONTCARE
      0​
      насыщенность по умолчанию
      FW_THIN100
      FW_EXTRALIGHT, FW_ULTRALIGHT200
      FW_LIGHT300
      FW_NORMAL, FW_REGULAR400нормальная насыщенность
      FW_MEDIUM500
      FW_SEMIBOLD, FW_DEMIBOLD600
      FW_BOLD700жирный шрифт
      FW_EXTRABOLD, FW_ULTRABOLD800
      FW_HEAVY, FW_BLACK900супержирный шрифт
    • Для создания наклонного шрифта (курсив) задают ненулевое значение параметра lfItalic.
    • Если параметр lfUnderline не равен нулю ― создается подчеркнутый шрифт.
    • Для создания шрифта, все символы которого зачеркнуты горизонтальной линией, необходимо задать ненулевое значение для параметра lfStrikeOut.
    • lfCharSet ― определяет множество символов шрифта
      ANSI_CHARSET0
      DEFAULT_CHARSET1
      SYMBOL_CHARSET2
      MAC_CHARSET77
      SHIFTJIS_CHARSET128японские шрифты
      HANGEUL_CHARSET
      JOHAB_CHARSET
      130
      129
      корейские шрифты
      GB2312_CHARSET
      CHINESEBIG5_CHARSET
      134
      136
      китайские шрифты
      GREEK_CHARSET161греческие шрифты
      TURKISH_CHARSET162турецкие шрифты
      VIETNAMESE_CHARSET163вьетнамские шрифты
      HEBREW_CHARSET177еврейские шрифты
      ARABIC_CHARSET178арабская вязь
      BALTIC_CHARSET
      EASTEUROPE_CHARSET
      186
      238
      восточно-европейские шрифты
      RUSSIAN_CHARSET204кириллические шрифты
      THAI_CHARSET222тайские шрифты
      OEM_CHARSET255
    • lfOutPrecision ― задает точность отображения шрифта, которая определяет, насколько точно созданный шрифт соответствует задаваемым характеристикам
      OUT_DEFAULT_PRECIS0
      OUT_STRING_PRECIS1
      OUT_CHARACTER_PRECIS2
      OUT_STROKE_PRECIS3
      OUT_TT_PRECIS4
      OUT_DEVICE_PRECIS5
      OUT_RASTER_PRECIS6
      OUT_TT_ONLY_PRECIS7
      OUT_OUTLINE_PRECIS8
    • lfClipPrecision ― определяет "точность отсечения" шрифта (как будут "отсекаться" символы шрифта, не попадающие в видимую область вывода)
      hexbin
      CLIP_DEFAULT_PRECIS
      0​
      00000000
      CLIP_CHARACTER_PRECIS
      1​
      00000001
      CLIP_STROKE_PRECIS
      2​
      00000010
      CLIP_
      4​
      00000100
      CLIP_
      8​
      00001000
      CLIP_MASK
      F​
      00001111
      CLIP_LH_ANGLES
      10​
      00010000
      CLIP_TT_ALWAYS
      20​
      00100000
      CLIP_
      40​
      01000000
      CLIP_EMBEDDED
      80​
      10000000
    • lfQuality ― определяет в какой степени создаваемый логический шрифт будет соответствовать физическим шрифтам, доступным для данного устройства вывода
      DEFAULT_QUALITY0
      DRAFT_QUALITY1
      PROOF_QUALITY2
    • lfPitchAndFamily ― задает тип и семейство шрифта.
      binhexописаниешрифт
      семей-
      ство
      тип
      DEFAULT_PITCH
      XXXX​
      0000
      X0​
      шаг по умолчанию, который зависит от реализации
      FIXED_PITCH
      XXXX​
      0001
      X1​
      Моноширинный, или непропорциональный шрифт, все знаки (кегельные площадки знаков) которого имеют одинаковую ширину«Courier», «Courier New», «Lucida Console», «Terminus», «Monaco»
      VARIABLE_PITCH
      XXXX​
      0010
      X2​
      шрифт, знаки (кегельные площадки знаков) которого имеют разную ширину в зависимости от пропорций букв
      X_PITCH
      XXXX​
      0100
      X4​
      ?​
      X_PITCH
      XXXX​
      1000
      X8​
      ?​
      FF_DONTCARE
      0000​
      XXXX
      0X​
      шрифт по умолчанию, который зависит от реализации
      FF_ROMAN
      0001​
      XXXX
      1X​
      Шрифты с изменяемой шириной хода, которые пропорциональны фактической ширине глифов и имеют засечки.«MS Serif»
      FF_SWISS
      0010​
      XXXX
      2X​
      Шрифты с изменяемой шириной хода, которые пропорциональны фактической ширине глифов и не имеют засечек.«MS Sans Serif»
      FF_MODERN
      0011​
      XXXX
      3X​
      Шрифты с постоянной шириной хода, с или без засечек. Шрифты с фиксированной шириной обычно являются шрифтом типа «модерн»«Pica», «Elite», «Courier New»
      FF_SCRIPT
      0100​
      XXXX
      4X​
      Шрифты, предназначенные для того, чтобы выглядеть как рукописные«Script», «Cursive».
      FF_DECORATIVE
      0101​
      XXXX
      5X​
      новые шрифты.«Old English»
      Значения параметра формируются из FF_XX и XX_PITCH при помощи оператора «or»
    • lfFaceName ― указатель на массив типа BYTE содержащий имя шрифта (например, «Courier New», «Arial» или «Times New Roman»). Строка не может быть длинее 32 символов
    При успешном завершении функции CreateFont, CreatFontIndirect, CreatFontIndirectEx возвращают дескриптор созданного логического шрифта. В случае ошибки будет возвращен 0.
    Шрифты, создаваемые при помощи CreateFont, CreateFont, CreatFontIndirect, CreatFontIndirectEx должны быть удалены, прежде чем программа завершит свое выполнение. Для удаления шрифта используется функция DeleteObject
    Для создания собственного шрифта используется функция CreatFont
    Код (C):
    1. HFONT CreateFont(
    2.     int  lfHeight,      /* желаемая высота символов (включая поле, отведенное для специальных знаков над символами, но не включая поле, установленное для межстрочного интервала) в логических
    3. единицах. Поскольку размер шрифта в пунктах — это и есть высота шрифта без величины поля, отведенного
    4. для специальных знаков над символами, то здесь вы на самом деле определяете значение межстрочного
    5. интервала. Вы можете установить значение lfHeight равным 0 для задания размера по умолчанию. Если вы зададите значение lfHeight отрицательным числом, Windows использует абсолютное значение этого числа
    6. в качестве желаемого размера высоты шрифта, а не как межстрочный интервал. Если вы хотите задать
    7. конкретное значение размера в пунктах, его надо преобразовать в логические единицы, и поле lfHeight
    8. установить в отрицательное значение результата */
    9.     int  lfWidth,       /* желаемая ширина символов в логических единицах. В большинстве случаев
    10. его устанавливают в 0 и позволяют Windows выбирать шрифт, основываясь исключительно на высоте */
    11.         int  nEscapement,   /* угол по отношению к горизонтальной оси. Для текста, отображаемого
    12. горизонтально, этот параметр должен быть равен нулю. В противном случае он определяет угол наклона
    13. текста в десятых долях градуса, значение 900 определяет угол наклона, равный 90 градусам  */
    14.     int  lfOrientation, // определяет угол поворота отдельных символов в десятых долях градуса
    15.     int  lfWeight,
    16.     QWORD  lfItalic,    // ненулевое значение поля определяет курсив
    17.     QWORD  lfUnderline, // ненулевое значение поля задает подчеркивание
    18.     QWORD  lfStrikeOut, // ненулевое значение поля определяет шрифт с зачеркиванием символов
    19.     QWORD  lfCharSet,
    20.     QWORD  lfOutPrecision,
    21.     QWORD  lfClipPrecision,
    22.     QWORD  lfQuality,
    23.     QWORD  lfPitchAndFamily,
    24.     LPCSTR  lfFaceName  // указатель на массив типа BYTE содержащий имя шрифта
    25. );
    Параметры функции CreateFont
    • lfWeight ― это поле позволяет задать насыщенность (жирность) шрифта и может принимать значения от 0 до 1000. Для насыщенности шрифта существуют следующие константы:
    • lfCharSet ― определяет множество символов шрифта
    • lfOutPrecision ― задает точность отображения шрифта, которая определяет, насколько точно созданный шрифт соответствует задаваемым характеристикам
    • lfClipPrecision ― определяет "точность отсечения" шрифта (как будут "отсекаться" символы шрифта, не попадающие в видимую область вывода)
    • lfQuality ― определяет в какой степени создаваемый логический шрифт будет соответствовать физическим шрифтам, доступным для данного устройства вывода
    • lfPitchAndFamily ― задает тип и семейство шрифта
    При успешном завершении CreateFont возвращает дескриптор созданного логического шрифта. В случае ошибки будет возвращен 0.
    Шрифты, создаваемые при помощи CreateFont, должны быть удалены, прежде чем программа завершит свое выполнение. Для удаления шрифта используется функция DeleteObject
     
  15. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Скачайте пример здесь.
    Код (ASM):
    1. ; GUI
    2. # include win64a.inc
    3. .code
    4. WinMain proc
    5. local msg:MSG
    6.  
    7.  xor ebx,ebx
    8.  mov ecx,offset FileName
    9.  invoke LoadCursorFromFile
    10.  mov esi,IMAGE_BASE
    11.  mov edi,offset ClassName
    12.  push rax ;hIconSm
    13.  push rdi ;lpszClassName
    14.  push rbx ;lpszMenuName
    15.  push COLOR_WINDOW;hbrBackground
    16.  push 10003h ;hCursor
    17.  push rax ;hIcon
    18.  push rsi ;hInstance
    19.  push rbx ;cbClsExtra & cbWndExtra
    20.  pushaddr WndProc ;lpfnWndProc
    21.  push sizeof WNDCLASSEX;cbSize & style
    22.  invoke RegisterClassEx,esp;addr WNDCLASSEX
    23.  push rbx
    24.  push rsi ;rsi=400000h
    25.  shl esi,9 ;rsi=CW_USEDEFAULT
    26.  push rbx
    27.  push rbx
    28.  push rsi
    29.  push rsi
    30.  push rsi
    31.  push rsi
    32.  sub rsp,20h
    33.  invoke CreateWindowEx,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    34.  lea edi,msg
    35. @@: invoke GetMessage,edi,0,0,0
    36.  invoke DispatchMessage,edi
    37.  jmp @b
    38. WinMain endp
    39. ;----------------------------------------
    40. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    41. local ps:PAINTSTRUCT
    42.  
    43.  mov hWnd,rcx
    44.  cmp edx,WM_DESTROY
    45.  je wmDESTROY
    46.  cmp edx,WM_CREATE
    47.  je wmCREATE
    48.  cmp edx,WM_PAINT
    49.  je wmPAINT
    50.  leave
    51.  jmp NtdllDefWindowProc_
    52. wmDESTROY:invoke SelectObject,hdc,hFont
    53.  invoke DeleteObject,newFont
    54.  invoke RtlExitUserProcess,0
    55. wmCREATE:invoke GetDC
    56.  mov hdc,rax
    57.  invoke GetCurrentObject,eax,OBJ_FONT
    58.  mov hFont,rax ;default font object
    59.  mov ecx,offset lf
    60.  invoke CreateFontIndirect
    61.  mov newFont,rax
    62.  invoke SelectObject,hdc,eax
    63.  invoke SetTextColor,hdc,32C8C8h
    64.  invoke SetBkColor,hdc,0FF0000h
    65.  jmp wmBYE
    66. wmPAINT:lea edx,ps
    67.  invoke BeginPaint
    68.  mov qword ptr [rbp-60h],sizeof expTxt ;help us to count the string length
    69.  mov r9d,offset expTxt
    70.  invoke TextOut,hdc,0,0
    71.  lea edx,ps
    72.  invoke EndPaint,hWnd
    73. wmBYE: leave
    74.  retn
    75. WndProc endp
    76. ;-----------------------------------------
    77. ClassName db "Uncle Remus tales:#5d More about Text",0
    78. expTxt db "Win64 assembly with MASM is great and easy",0
    79. FileName db "..\Images\br_Rabbit3.cur",0
    80. hdc HDC ?
    81. hFont dq ?
    82. newFont dq ?
    83. lf LOGFONT <26,12,0,0,400,0,0,0,OEM_CHARSET,0,0,0,DEFAULT_PITCH or FF_SCRIPT,"script"> end
    tut_04.png
    asm-файл
    программа демонстрирует работу с логическими шрифтами. При выборе команды меню сменить шрифт выбирается и отображается новый шрифт.
    Код (ASM):
    1. ; GUI #
    2. include win64a.inc
    3. ID_RESET    equ 0
    4. ID_FONT        equ 1
    5. ID_HELP        equ 2
    6. ID_EXIT        equ 3
    7. IDM_MENU    equ 37
    8. IDC_ICON1    equ 500
    9. IDR_MAINACCEL    equ 105
    10. .code
    11. WinMain proc
    12. local msg:MSG
    13.  
    14.     xor     ebx,ebx
    15.     mov     ecx,offset FileName
    16.     invoke     LoadCursorFromFile
    17.     mov     esi,IMAGE_BASE
    18.     mov     edi,offset ClassName
    19.     push     rax    ;hIconSm
    20.     push     rdi    ;lpszClassName
    21.     push     IDM_MENU;lpszMenuName
    22.     push     COLOR_WINDOW;hbrBackground
    23.     push     10003h    ;hCursor
    24.     push     rax    ;hIcon
    25.     push     rsi    ;hInstance
    26.     push     rbx    ;cbClsExtra & cbWndExtra
    27.     pushaddr WndProc;lpfnWndProc
    28.     push     sizeof WNDCLASSEX;cbSize & style
    29.     invoke     RegisterClassEx,esp;addr WNDCLASSEX
    30.     push     rbx
    31.     push     rsi    ;rsi=400000h
    32.     shl     esi,9   ;rsi=CW_USEDEFAULT
    33.     push     rbx
    34.     push     rbx
    35.     push     rsi
    36.     push     rsi
    37.     push     rsi
    38.     push     rsi
    39.     sub     esp,20h
    40.         invoke     CreateWindowEx,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    41.     mov    hwnd,rax
    42.     invoke    LoadAccelerators,IMAGE_BASE,IDR_MAINACCEL
    43.     mov    ACC,rax
    44.         lea     edi,msg
    45. @@:     invoke    GetMessage,edi,0,0,0
    46.         invoke    TranslateAccelerator,hwnd,ACC,edi
    47.     or    eax,eax
    48.     jne    @b
    49.     invoke    TranslateMessage,edi;разрешить использование клавиатуры
    50.     invoke    DispatchMessage,edi
    51.     jmp    @b
    52. WinMain endp
    53. ;----------------------------------------
    54. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    55. local ps:PAINTSTRUCT
    56. local hDC:qword
    57. local size0:POINT
    58. local string[255]:byte
    59. local cf:CHOOSEFONT
    60. local lf:LOGFONT
    61.  
    62.         mov    hWnd,rcx
    63.  
    64.         cmp    edx,WM_DESTROY
    65.         je    wmDESTROY
    66.         cmp    edx,WM_CREATE
    67.         je    wmCREATE
    68.         cmp    edx,WM_PAINT
    69.         je    wmPAINT
    70.         cmp    edx,WM_COMMAND
    71.         je    wmCOMMAND
    72.         leave
    73.         jmp    NtdllDefWindowProc_
    74. wmDESTROY:invoke DeleteDC,memDC
    75.     invoke    DeleteObject,hFont
    76.     invoke    RtlExitUserProcess,0
    77. wmCOMMAND:and    r8d,11111y;wParam
    78.     cmp    r8d,ID_EXIT
    79.     ja    wmBYE
    80.     jmp    handler[r8*8]
    81. FONT:   push    rdi
    82.     xor    eax,eax
    83.     mov    ecx,(sizeof CHOOSEFONT)/8
    84.     lea    edi,cf
    85.     rep    stosq
    86.     pop    rdi;    ZeroMemory
    87.     lea    ecx,cf
    88.     mov     [rcx].CHOOSEFONT.lStructSize,sizeof CHOOSEFONT
    89.     mov    rax,hwnd
    90.     mov    [rcx].CHOOSEFONT.hwndOwner,rax
    91.     lea    eax,lf
    92.     mov    [rcx].CHOOSEFONT.lpLogFont,rax
    93.     mov    [rcx].CHOOSEFONT.Flags,CF_SCREENFONTS or CF_EFFECTS;
    94.     invoke    ChooseFont
    95.     or    eax,eax
    96.     jz    wmBYE
    97. ;удаляем если он установлен предыдущий шрифт
    98.     mov    rcx,hFont
    99.     jecxz    @f
    100.         invoke    DeleteObject
    101. @@:    lea    ecx,lf
    102.     invoke    CreateFontIndirect
    103.     mov    hFont,rax
    104.     invoke    SelectObject,memDC,eax
    105. ;устанавливаем заданный цвет текста и прозрачный фон
    106.     invoke    SetTextColor,memDC,cf.rgbColors
    107. ;получить метрики текста
    108.     mov    edx,offset tm
    109.     invoke    GetTextMetrics,memDC
    110.     lea    ecx,string
    111.     mov    edx,offset fmt1
    112.     lea    r8d,lf.lfFaceName
    113.     mov    r9d,tm.tmHeight
    114.     invoke    wsprintf
    115. ;выводим строку
    116.     lea    r9d,string
    117.     invoke    TextOut,memDC,X,Y,,rax
    118. ;рассчитываем положение следующей строки
    119.     mov    eax,tm.tmHeight
    120.     add    eax,tm.tmExternalLeading
    121.     add    Y,rax
    122.     mov    qword ptr [rsp+20h],sizeof string1
    123.     lea    r9d,string1
    124.     invoke    TextOut,memDC,X,Y
    125. ;определить длину строки
    126.     mov    r8d,sizeof string1
    127.     mov    edx,offset string1
    128.     lea    r9d,size0
    129.     invoke    GetTextExtentPoint32,memDC
    130.     mov    eax,size0.x
    131.     mov    X,rax
    132.     lea    ecx,string
    133.     mov    edx,offset fmt2
    134.     mov    r8d,size0.x
    135.     invoke    wsprintf
    136.     lea    r9d,string
    137.     invoke    TextOut,memDC,X,Y,,rax
    138. ;рассчитываем положение следующей строки
    139.     mov    eax,tm.tmHeight
    140.     add    eax,tm.tmExternalLeading
    141.     add    Y,rax
    142.     and    X,0
    143.     lea    ecx,string
    144.     mov    edx,offset fmt3
    145.     invoke    wsprintf,,,maxX,maxY
    146.     lea    r9d,string
    147.     invoke    TextOut,memDC,X,Y,,rax
    148. ;рассчитываем положение следующей строки
    149.     mov    eax,tm.tmHeight
    150.     add    eax,tm.tmExternalLeading
    151.     add    Y,rax
    152.     invoke    InvalidateRect,hWnd,0,1
    153.     jmp    wmBYE
    154. RESET:;стираем с экрана
    155.     and    X,0
    156.     and    Y,0
    157.     invoke    PatBlt,memDC,0,0,maxX,maxY,PATCOPY
    158.            invoke    InvalidateRect,hWnd,0,TRUE
    159.     jmp    wmBYE
    160. HELP:    mov    ecx,offset mb
    161.     invoke    MessageBoxIndirect
    162.     jmp    wmBYE
    163. wmCREATE:invoke    GetSystemMetrics,SM_CXSCREEN
    164.     mov    maxX,rax
    165.         invoke    GetSystemMetrics,SM_CYSCREEN
    166.     mov    maxY,rax
    167.     invoke    GetDC,hWnd
    168.     mov    hDC,rax
    169.     invoke    CreateCompatibleDC,eax
    170.     mov    memDC,rax
    171.     invoke    CreateCompatibleBitmap,hDC,maxX,maxY
    172.         invoke    SelectObject,memDC,eax
    173.     invoke    GetStockObject,WHITE_BRUSH
    174.         invoke    SelectObject,memDC,eax
    175.     invoke    PatBlt,memDC,0,0,maxX,maxY,PATCOPY
    176.     invoke    SetBkMode,memDC,TRANSPARENT
    177.         invoke    ReleaseDC,hWnd,hDC
    178.     jmp    wmBYE
    179. wmPAINT:lea    edx,ps
    180.         invoke    BeginPaint  
    181.         invoke    BitBlt,eax,0,0,maxX,maxY,memDC,0,0,SRCCOPY  
    182.         lea    edx,ps
    183.         invoke    EndPaint,hWnd
    184. wmBYE:  leave
    185.         retn
    186. handler dq RESET,FONT,HELP,wmDESTROY
    187. WndProc endp
    188. ;-----------------------------------------
    189. ClassName    db "Uncle Remus tales:#5e Work with Font",0
    190. expTxt        db "Win64 assembly with MASM is great and easy",0
    191. FileName    db "..\Images\br_Rabbit3.cur",0
    192. hwnd        dq ?
    193. fmt1        db 'Font height "%s" is %d pixels',0;"Высота шрифта %s равна %d пикселов",0
    194. string1     db "This is the next line.",0;"Это следующая строка.",0
    195. fmt2        db "Length of the previous line is %d units",0;"Длина предыдущей строки %d единиц",0
    196. fmt3        db "Screen size is %d by %d",0;"Размер экрана %d на %d",0
    197. ACC        dq ?
    198. tm        TEXTMETRICA <>
    199. X        dq 0
    200. Y        dq 0
    201. maxX        dq ?
    202. maxY        dq ?
    203. memDC        dq ?
    204. hFont         dq 0
    205. MBText         db "F1: Help",10,"F2: Choose font",10,"F3: Clear screen",10,\
    206.         "Ctrl+X: Exit",0
    207. mb        label   MSGBOXPARAMS
    208.           dd sizeof MSGBOXPARAMS,?;cbSize    
    209.           dq 0            ;hwndOwner
    210.           dq IMAGE_BASE      ;hInstance    
    211.           dq MBText          ;lpszText  
    212.           dq ClassName      ;lpszCaption
    213.           dd MB_OK or MB_USERICON or MB_TOPMOST,?;dwStyle
    214.           dq IDC_ICON1      ;lpszIcon
    215.           dd 0,?        ;dwContextHelpId
    216.           dq 0          ;lpfnMsgBoxCallback
    217.           dd 0,?        ;dwLanguageId
    218. end
    rc-файл
    Код (C):
    1. #include "resource.h"
    2. #define ID_RESET    0
    3. #define ID_FONT        1
    4. #define ID_HELP        2
    5. #define ID_EXIT        3
    6. #define IDM_MENU    37
    7. #define IDC_ICON1    500
    8. #define IDR_MAINACCEL    105
    9. IDC_ICON1 ICON DISCARDABLE "..\\Images\\br_Bear4.ico"
    10. IDM_MENU MENU
    11. {
    12.     POPUP "&Font"
    13.     {
    14.         MENUITEM "Choose &font\tF2",ID_FONT
    15.         MENUITEM "&Clear Screeen\tF3",ID_RESET
    16.         MENUITEM "&Help\tF1",ID_HELP
    17.     MENUITEM SEPARATOR
    18.         MENUITEM "E&xit\tCtrl+X",ID_EXIT
    19.     }
    20.     MENUITEM "E&xit",ID_EXIT
    21. }
    22. IDR_MAINACCEL ACCELERATORS DISCARDABLE
    23. {  // определение акселераторов
    24.  VK_F1, ID_HELP,VIRTKEY
    25.  VK_F2, ID_FONT,VIRTKEY
    26.  VK_F3, ID_RESET,VIRTKEY
    27.  "^X",  ID_EXIT
    28. }
    Работа с виртуальным окном

    После создания виртуального окна вывод программы направляется в виртуальное окно.
    Код (ASM):
    1. ;получить метрики текста
    2.     mov    edx,offset tm
    3.     invoke    GetTextMetrics,memDC
    4.     lea    ecx,string
    5.     mov    edx,offset fmt1
    6.     lea    r8d,lf.lfFaceName
    7.     mov    r9d,tm.tmHeight
    8.     invoke    wsprintf
    9. ;выводим строку
    10.     lea    r9d,string
    11.     invoke    TextOut,memDC,X,Y,,rax
    12. ;рассчитываем положение следующей строки
    13.     mov    eax,tm.tmHeight
    14.     add    eax,tm.tmExternalLeading
    15.     add    Y,rax
    16.     mov    qword ptr [rsp+20h],sizeof string1
    17.     lea    r9d,string1
    18.     invoke    TextOut,memDC,X,Y
    19. ;определить длину строки
    20.     mov    r8d,sizeof string1
    21.     mov    edx,offset string1
    22.     lea    r9d,size0
    23.     invoke    GetTextExtentPoint32,memDC
    24.     mov    eax,size0.x
    25.     mov    X,rax
    26.     lea    ecx,string
    27.     mov    edx,offset fmt2
    28.     mov    r8d,size0.x
    29.     invoke    wsprintf
    30.     lea    r9d,string
    31.     invoke    TextOut,memDC,X,Y,,rax
    32. ;рассчитываем положение следующей строки
    33.     mov    eax,tm.tmHeight
    34.     add    eax,tm.tmExternalLeading
    35.     add    Y,rax
    36.     and    X,0
    37.     lea    ecx,string
    38.     mov    edx,offset fmt3
    39.     invoke    wsprintf,,,maxX,maxY
    40.     lea    r9d,string
    41.     invoke    TextOut,memDC,X,Y,,rax
    42. ;рассчитываем положение следующей строки
    43.     mov    eax,tm.tmHeight
    44.     add    eax,tm.tmExternalLeading
    45.     add    Y,rax
    46.     invoke    InvalidateRect,hWnd,0,1
    Весь вывод будет направлен на устройство соответствующее контексту memDC, после чего вызывается функция InvalidateRect для перерисовки реального окна. Режим рисования фона установлен в TRANSPARENT, поэтому текст отображаемый в окне, не изменяет цвет фона. При получении сообщения WM_PAINT содержимое виртуального окна копируется в реальное окно на экране
    Код (ASM):
    1. wmPAINT:;перерисовка окна
    2.     lea    edx,ps
    3.         invoke    BeginPaint
    4. ;копируем растр из памяти на экран
    5.         invoke    BitBlt,eax,0,0,maxX,maxY,memDC,0,0,SRCCOPY    
    6.         lea    edx,ps
    7. ;освобождаем контекст устройства
    8.         invoke    EndPaint,hWnd
    при копировании изображения из memDC в DC используется функция BitBlt. Параметр SRCCOPY определяет процесс копирования "как есть". Весь вывод программы направлялся контексту memDC, после этой операции выводимая информация отображается на экране. Если окно было перекрыто другим окном, а затем восстановлено, будет получено сообщение WM_PAINT и содержимое окна автоматически восстановится.​


    © Mikl___ 2021
     

    Вложения:

    • tut_05.zip
      Размер файла:
      3,3 КБ
      Просмотров:
      1.104
  16. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Глава десятая. Вращающийся текст
    Понимаешь, Братец Кролик, женщины ― это... задумчиво протянул Братец Лис, подергивая левым ухом. Братец Кролик устало вздохнул.
    ― А может, ты просто съешь меня, и все? ― спросил он. ― Прекрати издеваться.
    ― Если б я хотел тебя просто съесть, я бы и съел! ― обозлился Братец Лис. ― А я хочу побеседовать. Ну, а потом уже съесть. Меня Лисица задрала, понимаешь? Эх, да что ты понимаешь... Твоя-то ― Крольчиха, а моя ― Лиса. А иногда кажется, что просто Гиена... Вынь да положь ей кролика. Каждый день ― неси ей кролика. А не принес кролика ― душу вынет...
    ― Да... Братец Кролик из дипломатических соображений постарался попасть в тон Братцу Лису, что было довольно трудно, так как Братец Лис придавил его к земле двумя лапами. ― Моя тоже... требует Кролика. Но... кхм... в немножко другом плане. И знаешь, тоже нелегко. Даже очень...
    Оба вздохнули, думая о своем. В сердце у каждого разгоралась противоестественная зависть. "Лучше бы сожрала!" ― думал Братец Кролик. "Пусть бы затрахала!" ― думал Братец Лис.
    Внезапно раздался грохот и над ухом у Братца Лиса просвистела пуля. Братец Кролик и Братец Лис разлетелись в разные стороны и пустились от охотников наутек. Домой. К женам.
    [​IMG]
    А теперь заставим текст из пятой главы вращаться. Проблема состоит в том, что Windows позволяет вращать текст вокруг левого нижнего угла указанного текста, а хотелось бы, чтобы центр вращения был в центре строки. Для начала вычислим "центр тяжести" нашей строки ― получить вертикальный и горизонтальный размер текста можно:
    1. либо добавив функцию GetTextExtentPoint32 в текст программы из четвертого урока
      Код (ASM):
      1. local local expSize:POINT
      2.     . . .
      3.     mov edx,offset expTxt
      4.     mov r8d,sizeof expTxt
      5.     invoke GetTextExtentPoint32,ps.hdc,,,&expSize
      В полях x и y структуры POINT функция GetTextExtentPoint32 вернет вертикальный и горизонтальный размер текста;
    2. либо скопировать окошко в PaintBrush и тупо подсчитать размер в пикселях по-вертикали и по-горизонтали
    [​IMG]
    Вертикальный размер равен высоте шрифта 26, длина строки ― 453 пиксела, "центр тяжести" лежит на середине гипотенузы
    4532 + 262 = 205209 + 676 = 205885
    [math]\sqrt{205885} = 453,74552339389531436197386053733[/math]
    453,74552339389531436197386053733×0,5 = 226,87276169694765718098693026867 ≈ 227
    При вращении текста нам приходится оперировать с двумя углами: AngleLine ― угол наклона строки и AngleChars ― угол наклона букв в строке. AngleLine опережает AngleChars на 180о. AngleChars должен быть в градусах, AngleLine ― в радианах для вычисления синуса и косинуса. При этом, также придется учесть, что повороте на угол больше 360о величину AngleChars необходимо обнулять, а вот при вычислении синуса и косинуса такую поправку вносить не нужно (команда fsincos эту поправку делает автоматически). При изменении размера экрана равномерность поворота (1,6о в 50 mSec) нарушается ― поэтому приходится синхронизировать AngleLine и AngleChars.
    Код (ASM):
    1.  
    2. ; GUI #
    3. include win64a.inc
    4. .code
    5. WinMain proc
    6. local msg:MSG
    7.  
    8.  xor ebx,ebx
    9.  mov esi,IMAGE_BASE
    10.  mov edi,offset ClassName
    11.  mov ecx,offset FileName
    12.  invoke LoadCursorFromFile
    13.  mov r12,rax
    14.  mov ecx,0FF0000h
    15.  invoke CreateSolidBrush
    16.  push r12 ;hIconSm
    17.  push rdi ;lpszClassName
    18.  push rbx ;lpszMenuName
    19.  push rax ;hbrBackground
    20.  push 10003h ;hCursor
    21.  push r12 ;hIcon
    22.  push rsi ;hInstance
    23.  push rbx ;cbClsExtra & cbWndExtra
    24.  pushaddr WndProc ;lpfnWndProc
    25.  push sizeof WNDCLASSEX;cbSize & style
    26.  invoke RegisterClassEx,esp ;addr WNDCLASSEX
    27.  push rbx
    28.  push rsi ;rsi=400000h
    29.  shr esi,7 ;Special CreateWindow position value CW_USEDEFAULT=8000h
    30.  push rbx
    31.  push rbx
    32.  push rsi
    33.  push rsi
    34.  push rsi
    35.  push rsi
    36.  sub esp,20h
    37.  invoke CreateWindowEx,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
    38. ;создаем таймер #0 на 50mSec
    39.  invoke SetTimer,eax,0,50,0
    40. ; +---------------------------+
    41. ; | entering the message loop |
    42. ; +---------------------------+
    43.  lea edi,msg
    44. @@: invoke GetMessage,edi,0,0,0
    45.  invoke DispatchMessage,edi
    46.  jmp @b
    47. WinMain endp
    48. WndProc proc hwnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    49. local ps:PAINTSTRUCT
    50. local newFont:qword
    51. local oldFont:qword
    52. local x1:dword
    53. local y1:dword
    54. local hDC:qword
    55. local lpPoint:POINT
    56.  
    57.  mov hwnd,rcx
    58.  cmp edx,WM_DESTROY
    59.  je wmDESTROY
    60.  cmp edx,WM_PAINT
    61.  je wmPAINT
    62.  cmp edx,WM_SIZE
    63.  je wmSIZE
    64.  cmp edx,WM_TIMER
    65.  je wmTIMER
    66.  leave
    67.  jmp NtdllDefWindowProc_
    68. wmDESTROY:invoke KillTimer,,0;уничтожаем таймер #0
    69.  invoke RtlExitUserProcess,0
    70. wmSIZE: mov edx,offset expRect
    71.  invoke GetClientRect ;получаем размеры клиентской области.
    72. ;Размеры возвращаются в переменную expRect
    73.  mov eax,expRect.bottom
    74.  shr eax,1
    75.  mov y,eax ;y-координата середины экрана
    76.  mov eax,expRect.right
    77.  shr eax,1
    78.  mov x,eax ;x-координата середины экрана
    79.  jmp wmBYE
    80. wmTIMER:fninit
    81.  fld angle2;угол наклона строки в радианах
    82.  fadd pi_mul_1_6_div_180 ;увеличиваем угол наклона строки на 1,6 градуса
    83.  fst angle2 fmul _1800_div_pi ;переводим радианы в градусы
    84.  fsub _1800 ;угол наклона букв в строке отстает от угла наклона строки на 90 градусов
    85.  fld st(0)
    86.  fmul one_div_3600
    87.  fsubp st(1),st(0)
    88.  fistp angle ;делим угол наклона букв на 360 градусов и запоминаем остаток
    89.  invoke InvalidateRect,,0,TRUE;перерисовываем текст с текущим значением угла
    90.  jmp wmBYE
    91. wmPAINT:lea edx,ps
    92.  invoke BeginPaint
    93.  mov hDC,rax
    94.  invoke SetTextColor,hDC,32C8C8h;RGB=50,200,200 золотистые буквы
    95.  invoke SetBkColor,hDC,0FF0000h;RGB=0,0,255 на синем фоне
    96. ;создаем шрифт с углом вращения, указанным в lfEscapement и lfOrientation
    97.  pushaddr expFont
    98.  push DEFAULT_PITCH or FF_SCRIPT
    99.  push rbx ;DEFAULT_QUALITY=0
    100.  push rbx ;CLIP_DEFAULT_PRECIS=0
    101.  push rbx ;OUT_DEFAULT_PRECIS=0
    102.  push OEM_CHARSET
    103.  push rbx
    104.  push rbx
    105.  push rbx
    106.  push 400
    107.  sub esp,20h
    108.  invoke CreateFont,26,12,angle,r8
    109.  mov newFont,rax
    110.  invoke SelectObject,hDC,eax
    111.  mov oldFont,rax
    112. ;---------вывожу текст
    113.  mov qword ptr [rsp+20h],sizeof expTxt;длина строки
    114.  mov r9d,offset expTxt ;адрес строки
    115. ;---------рассчитываю положение начала текста
    116.  fld angle2;угол наклона строки в радианах
    117.  fsincos ;в st(0) синус угла, в st(1) косинус
    118.  mov y1,227 ;половина гипотенузы
    119.  fimul y1 ;гипотенуза * sin = x
    120.  fistp x1
    121.  mov edx,x
    122.  add edx,x1
    123.  fimul y1 ;гипотенуза * cos = y
    124.  fistp y1 ;-y
    125.  mov r8d,y
    126.  sub r8d,y1
    127.  invoke TextOut,hDC
    128.  invoke DeleteObject,newFont ;delete the new font
    129.  invoke SelectObject,hDC,oldFont;return the old font to the system
    130.  invoke EndPaint,hwnd,&ps
    131. wmBYE:leave
    132.  retn
    133. WndProc endp
    134. ;---------------------------------------
    135. ClassName db "Uncle Remus tales:#5 Painting with Rotation Text",0
    136. expTxt db "Win64 assembly with MASM is great and easy!",0
    137. expFont db "script",0
    138. angle dq 0
    139. angle2 dq 3.14159265358979323846264338328
    140. pi_mul_1_6_div_180 dq 0.02792526803190927323077905229;pi*1,6/180
    141. _1800_div_pi dq 572.957795130823208767981548141;1800/pi
    142. _1800 dq 1800.0
    143. one_div_3600 dq 0.00027777777777777777777777778;1/3600 ;
    144. hIcon dq ?
    145. FileName db "br_Rabbit3.cur",0
    146. expRect RECT <>
    147. x dd ?
    148. y dd ?
    149. end
    image01.png


    © Mikl___ 2021
     

    Вложения:

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

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Глава одиннадцатая. Как Братец Кролик выводил текст на экран всеми возможными способами
    Проблема не в том, что можно 10 способами вывести
    текст на экран. Проблема начинается тогда,
    когда вы пытаетесь вывести на экран
    длинный, многострочный текст...

    (фраза приписываемая Джоэлю Чандлеру Харрису — американскому
    журналисту, писателю и фольклористу, автору сказок о дядюшке Римусе)​
    [​IMG]
    добавляем в win64a.inc строки
    Код (ASM):
    1. include comctl32.inc
    2. includelib comctl32.lib

    Вывод текста в дочернее окно класса "STATIC"

    Создаем дочернее окно класса "STATIC" и выводим текст в поле отвечающее за заголовок
    Код (ASM):
    1. .data
    2. expTxt0 db "Было еще не так поздно, а госпожа Салли, мама семилетнего Джоэля, уже"
    3.     ...
    4. ;создания элемента STATIC
    5.         push rbx
    6. push IMAGE_BASE
    7. push rbx
    8. push hWnd
    9. push BOTTOM+100
    10. push RIGHT
    11. push TOP+25
    12. push LEFT
    13. sub esp,20h
    14. invoke CreateWindowEx,0,&aStatic,&expTxt0,SS_LEFT or WS_CHILD or WS_VISIBLE
    15. mov hStaticText1,rax
    16.  
    "Стереть" такой текст можно сделав окно невидимым
    Код (ASM):
    1. invoke ShowWindow,hStaticText1,SW_HIDE
    Текстовая строка понимает символы перевода и табуляции строки, автоматически переносит слова, не требует обработки сообщения WM_PAINT
    00.png

    Вывод текста при помощи функции SendMessage

    Создаем еще одно дочернее окно класса "STATIC" с пустым заголовком
    Код (ASM):
    1.  
    2. ;создания элемента STATIC для вывода текста функцией SendMessage
    3.       push rbx
    4. push IMAGE_BASE
    5. push rbx
    6. push hWnd
    7. push BOTTOM
    8. push RIGHT;640
    9. push TOP+25
    10. push LEFT
    11. sub esp,20h
    12. invoke CreateWindowEx,0,&aStatic,0,SS_LEFT or WS_CHILD; or WS_VISIBLE
    13. mov hStaticText2,rax
    14.  
    Когда требуется вывести текст ― вызываем функцию SendMessage с параметром WM_SETTEXT
    Код (ASM):
    1.  
    2. SendMessage1::invoke SendMessage,hStaticText2,WM_SETTEXT,0,&expTxt7
    3. invoke ShowWindow,hStaticText2,SW_SHOW
    4.  
    07.png
    Чтобы "стереть" текст выведенный таким образом ― вызываем функцию SendMessage с указателем на "нулевую строку"
    Код (ASM):
    1.        invoke SendMessage,hWnd,WM_SETTEXT,0,&ClassName
    2.         invoke ShowWindow,hStaticText2,SW_HIDE
    Текстовая строка понимает символы перевода и табуляции строки, автоматически переносит слова, не требует обработки сообщения WM_PAINT
    Прототип функции SendMessageA/W:
    Код (C):
    1. LRESULT WINAPI SendMessage(
    2. _In_ HWND hWnd, // дескриптор окна, которому посылается сообщение
    3. _In_ UINT Msg, // код посылаемого сообщения
    4. _In_ WPARAM wParam, // дополнительные параметры сообщения
    5. _In_ LPARAM lParam
    6. );

    Вывод текста при помощи функции DrawTextEx и DrawText

    Функции DrawTextEx и DrawText выводят отформатированный текст в заданном прямоугольнике.
    Форматирование текста происходит согласно заданному методу (дополнительная табуляция, выравнивание символов, переносы строк, и так далее).
    Прототип функции DrawTextEx
    Код (C):
    1. int DrawTextEx(
    2. _In_    HDC hdc, /* дескриптор контекста устройства, в котором происходит
    3. прорисовка */
    4. _Inout_    LPTSTR lpchText,/* указатель на строку, которая содержит текст
    5. для рисования. Строка должна быть с нулевым символом в конце, если параметр
    6. cchText не равен -1 */
    7. _In_    int cchText,/* длина строки, но если cchText = -1 и строка закончена
    8. нулевым символом - длина строки рассчитывается автоматически */
    9. _Inout_    LPRECT lprc,/* указатель на структуру RECT, содержащую прямоугольник
    10. (в логических координатах), в который выводится отформатированный текст */
    11. _In_    UINT dwDTFormat, // Параметры форматирования
    12. _In_    LPDRAWTEXTPARAMS lpDTParams /* указатель на структуру DRAWTEXTPARAMS
    13. которая определяет дополнительные опции форматирования. Может быть нулем */
    14. );

    Прототип функции DrawText

    Код (C):
    1. int DrawText(
    2. _In_    HDC hdc,/* дескриптор контекста устройства, в котором происходит
    3. прорисовка */
    4. _Inout_    LPCTSTR lpszString, /* Указатель на строку,
    5. которая будет выведена. Если nCount = -1, строка должна быть с нулевым символом
    6. в конце */
    7. _In_    int nCount, /* Определяет количество символов в строке. Если nCount -1,
    8. то lpszString принят, чтобы быть длинным указателем на строку с нулевым символом
    9. в конце, и DrawText вычисляет символьный счет автоматически */
    10. _Inout_    LPRECT lpRect, /* указатель на структуру RECT, содержащую прямоугольник
    11. (в логических координатах), в который выводится отформатированный текст */
    12. _In_    UINT nFormat // Параметры форматирования
    13. );
    Позиции табуляции преобразуются в соответствующее количество пробелов, текст выравнивается влево, вправо или по центру данного прямоугольника, текст разрывается в строках, которые приспосабливаются внутри данного прямоугольника.
    Параметр nCount передает количество символов в строке
    • для ANSI-строк ― количество байтов (BYTE) в строке
    • для Unicode-строк ― количество слов (WORD) в строке
    Для вывода текста функции используют выбранный шрифт, цвет текста и фона из контекста устройства.
     

    Вложения:

    • 01.png
      01.png
      Размер файла:
      58,8 КБ
      Просмотров:
      5.818
    • 02.png
      02.png
      Размер файла:
      58,3 КБ
      Просмотров:
      5.833
    • 03.png
      03.png
      Размер файла:
      47,5 КБ
      Просмотров:
      5.809
    • 04.png
      04.png
      Размер файла:
      51,8 КБ
      Просмотров:
      5.782
    • 06.png
      06.png
      Размер файла:
      56,1 КБ
      Просмотров:
      5.624
    • 08.png
      08.png
      Размер файла:
      7,5 КБ
      Просмотров:
      5.549
    • 09.png
      09.png
      Размер файла:
      7,1 КБ
      Просмотров:
      5.452
    • 05.png
      05.png
      Размер файла:
      27,8 КБ
      Просмотров:
      5.747
    Последнее редактирование: 12 янв 2021
  18. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Параметры DT_XX (Draw Text)
    ЗначениеhexПредназначение
    DT_TOP
    0​
    Выравнивает текст по верху прямоугольника
    DT_LEFT
    0​
    Выравнивает текст слева
    DT_CENTER
    1​
    Центрует текст в прямоугольнике по горизонтали
    DT_RIGHT
    2​
    Выравнивает текст справа
    DT_VCENTER
    4​
    Центрирует текст по вертикали. Это значение используется только со значением DT_SINGLELINE
    DT_BOTTOM
    8​
    Выравнивает текст по основанию прямоугольника. Это значение используется только со значением DT_SINGLELINE.
    DT_WORDBREAK
    10​
    Строки автоматически разделяются по словам, если те выходят за края прямоугольника, заданного параметром lprc. Символы "Возврат каретки" и "Перевод строки" также разделяют строку
    DT_SINGLELINE
    20​
    Выводит текст на экране как одну строку. Символы "Возврат каретки" и "Перевод строки" игнорируются
    DT_EXPANDTABS
    40​
    Увеличивает число символов в табуляции. Заданное по умолчанию число символов в табуляции ― восемь
    DT_TABSTOP
    80​
    Устанавливает шаги табуляции. Структура DRAWTEXTPARAMS указывает, что параметр lpDTParams задает число символов средней ширины на шаг табуляции
    DT_NOCLIP
    100​
    Вывод текста без отсечения по границам прямоугольника. Функции DrawTextEx/ DrawText отрабатывают немного быстрее, когда используется DT_NOCLIP
    DT_EXTERNALLEADING
    200​
    Включает межстрочное расстояние шрифта в высоту строки. Обычно, межстрочное расстояние не включается в высоту строки текста.
    DT_CALCRECT
    400​
    Выясняет ширину и высоту прямоугольника. Если имеется несколько строк текста, функции DrawTextEx/DrawText используют ширину прямоугольника, указанную параметром lprc и продлевают основу прямоугольника, чтобы ограничить последнюю строку текста. Если есть только одна строка текста, DrawTextEx/ DrawText изменяют правую сторону прямоугольника так, чтобы она ограничивала последнюю букву в строке. И в том, и в другом случае, DrawTextEx/ DrawText возвращают значение высоты отформатированного текста, но не выводят текст на экран
    DT_NOPREFIX
    800​
    Выключает обработку префиксных символов. Обычно, функция DrawTextEx/ DrawText воспринимает символ амперсандта (&) как директиву, чтобы подчеркнуть букву, которая стоит после амперсандта, а двойной амперсандт (&&) как директиву, чтобы отобразить на экране амперсандт (&). Установка параметра DT_NOPREFIX, эту обработку отключает. Сравнивает с флажком DT_HIDEPREFIX и DT_PREFIXONLY
    DT_INTERNAL
    1000​
    Использует системный шрифт, чтобы рассчитать метрики текста
    DT_EDITCONTROL
    2000​
    Дублирует характеристики отображения текста в многострочном поле редактирования текста. А именно, средняя ширина символа рассчитывается в той же самой манере, как и для поля редактирования текста, и функция не показывает на экране частично видимую последнюю строку
    DT_PATH_ELLIPSIS
    4000​
    Для отображаемого на экране текста, заменяет буквы (символы) в середине строки на тире так, чтобы результат вместился в заданный прямоугольник. Если строка содержит знак наклонной черты влево ("\"), флажок DT_PATH_ELLIPSIS сохраняет в максимально возможной степени текст после последней наклонной черты влево. Строка не модифицируется, если флажок DT_MODIFYSTRING не установлен.
    Сравнивается с флажками DT_END_ELLIPSIS и DT_WORD_ELLIPSIS
    DT_END_ELLIPSIS
    8000​
    В отображаемом на экране тексте конец строки заменился "эллипсисами" так, чтобы результат вместился в заданный прямоугольник. Любое слово (не в конце строки) которое выходит за пределы прямоугольника, обрезается без эллипсисов (тире). Строка не модифицируется, если DT_MODIFYSTRING не установлен. Сравнивается с DT_PATH_ELLIPSIS и DT_WORD_ELLIPSIS
    DT_MODIFYSTRING
    10000​
    Изменяет заданную строку, чтобы согласовать отображаемый на экране текст. Это значение не имеет никакого действия, если не установлен флажок DT_END_ELLIPSIS или DT_PATH_ELLIPSIS
    DT_RTLREADING
    20000​
    Изменяет порядок вывода символов на экран на вывод текста справа налево для двунаправленного текста, если шрифт, выбранный в hdc ― арабский или иврит. Заданный по умолчанию порядок чтения для всех текстов ― слева направо
    DT_WORD_ELLIPSIS
    40000​
    Обрезает любое слово, которое не вмещается в прямоугольник и добавляет эллипсисы. Сравнивается с флажками DT_END_ELLIPSIS и DT_PATH_ELLIPSIS
    DT_NOFULLWIDTHCHARBREAK
    80000​
    Не допускает перенос строки в DBCS (символьная строка двойной ширины), так, чтобы правило переноса строки было эквивалентно SBCS строкам (символьная строка одинарной ширины). Например, это свойство может быть использовано в окнах с корейскими шрифтами, для большей удобочитаемости корейских символов. Это значение не имеет никакого действия, если флажок DT_WORDBREAK не установлен
    DT_HIDEPREFIX
    100000​
    Игнорирует в тексте символ амперсанда (&), но не игнорирует символ двойного амперсанда (&&). Буква, которая следует за амперсандом, не будет подчеркнута, но двойной амперсанд обрабатывается. Например: строка "A&bc&&d" преобразуется функцией DrawText/DrawTextEx в строку "Abc&d", а функция DrawText/DrawTextEx с параметром DT_HIDEPREFIX сформирует строку "Abc&d". Сравнивается с DT_NOPREFIX и DT_PREFIXONLY.
    DT_PREFIXONLY
    200000​
    Рисует только подчеркивание в позиции буквы, следующей за амперсантом (&). Не выводит никакие другие символы в строке. Например, введенная строка: "A&bc&&d" преобразуется функцией DrawText/DrawTextEx в строку "Abc&d", а функция DrawText/DrawTextEx с флажком DT_PREFIXONLY сформирует строку " _ " Сравнивает с флажками DT_HIDEPREFIX и DT_NOPREFIX
    Если не используется формат DT_NOCLIP, DrawTextEx и DrawText отсекают текст так, чтобы текст не появился снаружи данного прямоугольника. Если выбран формат DT_SINGLELINE, тогда символ переноса строк игнорируется.
    Если выбранный шрифт слишком большой для определенного прямоугольника, функции DrawTextEx и DrawText не пытаются заменять его меньшим шрифтом.
    Если определен флажок DT_CALCRECT, прямоугольник, определенный lpRect будет модифицироваться, чтобы отразить ширину и высоту, необходимую, чтобы вывести текст.
    Если установлен флажок выравнивания текста TA_UPDATECP, DrawText отобразит текст, начинающийся в текущей позиции, скорее чем налево от данного прямоугольника. DrawText не будет переносить по словам текст, когда установлен флажок TA_UPDATECP (тогда флажок DT_WORDBREAK не будет иметь никакого эффекта).
    Код (ASM):
    1. DrawTextEx1::;вывод текста при помощи функции DrawTextEx
    2. invoke DrawTextEx,MemDC,&expTxt5,-1,&expRect,DT_LEFT or DT_WORDBREAK or DT_EXPANDTABS or \
    3. DT_END_ELLIPSIS or DT_MODIFYSTRING or DT_WORD_ELLIPSIS,0
    [​IMG]
    Код (ASM):
    1. DrawText1::;вывод текста при помощи функции DrawText
    2. invoke DrawText,MemDC,&expTxt1,-1,&expRect,DT_WORDBREAK
    [​IMG]

    Вывод текста при помощи функции TextOut

    Прототип функции TextOut
    Код (C):
    1. BOOL TextOut(
    2. __in    HDC hdc,/* дескриптор контекста устройства, в котором происходит
    3. прорисовка */
    4. __in    int x, //логическая x-координата начала строки
    5. __in    int y, //логическая y-координата начала строки
    6. __in    LPCTSTR lpszString, //указатель на строку
    7. __in    int nCount //число символов в строке
    8. );
    Записывает строку в определенное место экрана, используя выбранный шрифт.
    Если программа должна модифицировать текущую позицию строки, тогда перед вызовом TextOut, вызывают функцию SetTextAlign с параметром nFlags равным TA_UPDATECP. Когда этот флажок установлен, Windows игнорирует значение параметров x и y при последующих обращениях к TextOut, используя текущую позицию строки.
    Для вывода нескольких строк с использованием функции TextOut, необходимо знать вертикальный размер символов шрифта. Следующие друг за другом строки текста размещаем с учетом высоты символа. Размеры символов можно получить с помощью функции GetTextMetrics, ее возвращаемым значением является информация о шрифте, выбранном в данное время в контексте устройства.
     
    Последнее редактирование: 15 июл 2019
  19. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    Прототип функции GetTextMetrics
    Код (C):
    1. BOOL GetTextMetrics(
    2. __in    HDC hdc, // дескриптор контекста устройства
    3. __in    LPTEXTMETRIC lpTAttrib // указатель на структуру TEXTMETRIC
    4. );
    Так как размеры системного шрифта не меняются в течение одного сеанса ― можно вызвать GetTextMetrics один раз при обработке сообщения WM_CREATE
    Код (ASM):
    1. TEXTMETRICA STRUCT
    2.     tmHeight              DWORD     ?   ;полная высота шрифта
    3.     tmAscent              DWORD     ?   ;высота над основной линией
    4.       tmDescent             DWORD     ?   ;размер нижнего выступа
    5.       tmInternalLeading     DWORD     ?   ;величина пустого пространства,
    6. ;отведенного для указания специальных знаков над символом. Если это значение равно 0,
    7. ;то помеченные прописные буквы делаются немного меньше, чтобы специальный символ
    8. ;поместился внутри верхней части символа
    9.       tmExternalLeading     DWORD     ?   ;величина пустого пространства,
    10. ;которое разработчик шрифта установил для использования между строками символов.
    11.       tmAveCharWidth        DWORD     ?   ;усредненная
    12. ;ширина символов строки
    13.       tmMaxCharWidth        DWORD     ?   ;ширина самого широкого символа шрифта
    14.       tmWeight              DWORD     ?   ;насыщенность шрифта
    15.       tmOverhang            DWORD     ?   ;дополнительная ширина символа - для специальных шрифтов
    16.       tmDigitizedAspectX    DWORD     ?   ;горизонтальный аспект
    17.       tmDigitizedAspectY    DWORD     ?   ;вертикальный аспект
    18.       tmFirstChar           BYTE      ?   ;первый символ
    19.       tmLastChar            BYTE      ?   ;последний символ
    20.       tmDefaultChar         BYTE      ?   ;символ по умолчанию
    21.       tmBreakChar           BYTE      ?   ;символ для обозначения границы слова
    22.       tmItalic              BYTE      ?   ;не ноль если шрифт курсив (italic)
    23.       tmUnderlined          BYTE      ?   ;не ноль если буквы подчеркнуты
    24.       tmStruckOut           BYTE      ?   ;не ноль если буквы зачеркнуты
    25.       tmPitchAndFamily      BYTE      ?   ;семейство и гарнитура
    26.       tmCharSet             BYTE      ?   ;идентификатор множества символов
    27. TEXTMETRICA ENDS
    Код (ASM):
    1. local tm:TEXTMETRIC ;определили структуру которая будет содержать все параметры выбранного шрифта
    2. local y:QWORD
    3.     ...
    4.         mov y,100;y-координата первой строки
    5.         invoke GetTextMetrics,hdc,&tm; получаем информацию о текущем шрифте      
    6.        mov eax,tm.tmHeight
    7.         add eax,tm.tmExternalLeading
    8.         mov font_height,eax
    9.         xor ebx,ebx
    10.         mov edi,font_height
    11.         mov y,ebx
    12.         . . . .          
    13. TextOut1::;вывод текста при помощи функции TextOut
    14. @@: mov r9d,stringtable3[rbx]
    15.           mov eax,stringtable3[rbx+4]
    16.           or eax,eax
    17.            jz a1
    18.           sub eax,r9d
    19.            mov [rsp+20h],rax
    20.            invoke TextOut,MemDC,LEFT,y
    21.            add y,edi
    22.            add ebx,4
    23.            jmp @b
    24. a1:  ....
    [​IMG]

    Вывод текста при помощи функции TabbedTextOut

    Чтобы создать многоколонную таблицу с выровненными столбцами текста используют TabbedTextOut
    Код (C):
    1. LONG TabbedTextOut (
    2. _In_           HDC     hDC,
    3. _In_        int x, //логическая x-координата начала строки
    4. _In_        int y, //логическая y-координата начала строки
    5. _In_        LPCTSTR lpszString, //указатель на строку
    6. _In_        int nCount,//число символов в строке. Если nCount = -1, тогда длина вычисляется
    7. _In_        int nTabPositions, //число значений в массиве позиций позиции табуляции.
    8. _In_ const    LPINT lpnTabStopPositions,/* Указатель на массив, содержащий позиции позиции
    9. табуляции (в логических модулях). Табуляторы должны сортироваться в увеличивающемся
    10. порядке, самое маленькое x-значение должно быть первый элементом массива */
    11. _In_        int nTabOrigin /* x-координата исходной позиции, из которой позиции табуляции
    12. расширены (в логических модулях) */
    13. );
    Вызов этой функции для записи символьной строки в определенном расположении, разворачивая позиции табуляции к значениям, определенным в массиве позиций позиции табуляции. Текст написан в настоящее время выбранном шрифте. Если nTabPositions = 0, и lpnTabStopPositions = NULL, позиции табуляции расширены до восьми раз средней символьной ширины.
    Если nTabPositions = 1, табуляторы отделяются расстоянием, определенным первым значением в lpnTabStopPositions массиве. Если lpnTabStopPositions массив содержит больше чем одно значение, табулятор установлен для каждого значения в массиве, до номера, определенного nTabPositions. nTabOrigin параметр позволяет прикладной программе называть функцию TabbedTextOut несколькими разами для одиночной строки. Если вызовы из прикладной программы больше чем один раз с набором nTabOrigin к тому же самому значению каждый раз, функция разворачивают позиции табуляции относительно позиции, определенной nTabOrigin.
    По умолчанию, текущая позиция не используется или модифицируется функцией. Если программа должна модифицировать текущую позицию, тогда перед вызовом TabbedTextOut вызывают функцию SetTextAlign функцию с параметром nFlags равным TA_UPDATECP. Когда этот флажок установлен, Windows игнорирует значение параметров x и y при последующих обращениях к TabbedTextOut, используя текущую позицию
    Код (ASM):
    1. TabbedTextOut1::;вывод текста при помощи функции TabbedTextOut
    2. @@:     mov r9d,stringtable4[rbx]
    3.         or r9,r9
    4. jz a1
    5. invoke TabbedTextOut,MemDC,LEFT,y,,-1,0,0,0
    6. add y,edi
    7.         add ebx,4
    8.         jmp @b
    9. a1: ...
    [​IMG]

    Вывод текста при помощи функции PolyTextOut

    PolyTextOut рисует несколько строк используя шрифт и текущие цвета текста выбранные в контексте устройства.
    Код (C):
    1. BOOL PolyTextOut(
    2.     HDC hdc,    // дескриптор контекста устройства
    3.     CONST POLYTEXT *pptxt, /* Указатель на массив структур POLYTEXT,
    4. описывающих строки, которые будут выведены. Массив содержит одну
    5. структуру для каждой строки, которая будет выведена */
    6.     int cStrings // число структур POLYTEXT в массиве pptxt
    7.    );
    Код (ASM):
    1. POLYTEXTA STRUCT
    2.   x         DWORD       ?
    3.   y         DWORD       ?
    4.   n         DWORD       ?
    5.   lpStr     DWORD       ?
    6.   uiFlags   DWORD       ?
    7.   icl       RECT        <>
    8.   pdx       DWORD       ?
    9. POLYTEXTA ENDS
    Каждая структура POLYTEXT содержит координаты опорной точки, которую Windows использует для, выравнивания соответствующей строки текста. Приложение может устанавливать опорную точку, используя вызов функции SetTextAlign. Приложение может выяснить текущие настройки выравнивания текста для заданного контекста устройства при помощи функции GetTextAlign.
    Код (ASM):
    1. .data
    2. pptxt POLYTEXT <0,TOP+16*0, expTxt6b-expTxt6a,expTxt6a,ETO_OPAQUE,{0,0,100,200},0>
    3.       POLYTEXT <0,TOP+16*1, expTxt6c-expTxt6b,expTxt6b,ETO_OPAQUE,{0,0,100,200},0>
    4.       POLYTEXT <0,TOP+16*2, expTxt6d-expTxt6c,expTxt6c,ETO_OPAQUE,{0,0,100,200},0>
    5.       POLYTEXT <0,TOP+16*3, expTxt6e-expTxt6d,expTxt6d,ETO_OPAQUE,{0,0,100,200},0>
    6.       POLYTEXT <0,TOP+16*4, expTxt6f-expTxt6e,expTxt6e,ETO_OPAQUE,{0,0,100,200},0>
    7.       POLYTEXT <0,TOP+16*5, expTxt6g-expTxt6f,expTxt6f,ETO_OPAQUE,{0,0,100,200},0>
    8.       POLYTEXT <0,TOP+16*6, expTxt6h-expTxt6g,expTxt6g,ETO_OPAQUE,{0,0,100,200},0>
    9.       POLYTEXT <0,TOP+16*7, expTxt6i-expTxt6h,expTxt6h,ETO_OPAQUE,{0,0,100,200},0>
    10.       POLYTEXT <0,TOP+16*8, expTxt6j-expTxt6i,expTxt6i,ETO_OPAQUE,{0,0,100,200},0>
    11.       POLYTEXT <0,TOP+16*9, expTxt6k-expTxt6j,expTxt6j,ETO_OPAQUE,{0,0,100,200},0>
    12.       POLYTEXT <0,TOP+16*10,expTxt6l-expTxt6k,expTxt6k,ETO_OPAQUE,{0,0,100,200},0>
    13.       POLYTEXT <0,TOP+16*11,expTxt6m-expTxt6l,expTxt6l,ETO_OPAQUE,{0,0,100,200},0>
    14.       POLYTEXT <0,TOP+16*12,expTxt6n-expTxt6m,expTxt6m,ETO_OPAQUE,{0,0,100,200},0>
    15.       POLYTEXT <0,TOP+16*13,expTxt6o-expTxt6n,expTxt6n,ETO_OPAQUE,{0,0,100,200},0>
    16.       POLYTEXT <0,TOP+16*14,expTxt6p-expTxt6o,expTxt6o,ETO_OPAQUE,{0,0,100,200},0>
    17.       POLYTEXT <0,TOP+16*15,expTxt6q-expTxt6p,expTxt6p,ETO_OPAQUE,{0,0,100,200},0>
    18.       POLYTEXT <0,TOP+16*16,expTxt6r-expTxt6q,expTxt6q,ETO_OPAQUE,{0,0,100,200},0>
    19.       POLYTEXT <0,TOP+16*17,expTxt6s-expTxt6r,expTxt6r,ETO_OPAQUE,{0,0,100,200},0>
    20.       POLYTEXT <0,TOP+16*18,aStatic -expTxt6t,expTxt6s,ETO_OPAQUE,{0,0,100,200},0>
    21. expTxt6a db 'Раз после ужина мальчик прибежал к старому негру, чтобы послушать ещё про Братца Кролика и его'
    22. expTxt6b db
    23.     ...
    24. PolyTextOut1::;вывод текста при помощи функции PolyTextOut
    25. invoke PolyTextOut,MemDC,&pptxt,19
    [​IMG]

    Вывод текста при помощи функции ExtTextOut

    Функция ExtTextOut выводит текст используя текущий выбранный шрифт, цвета фона и текста. Вы можете произвольно предоставить размеры, которые используются для отсечения, окраски, или обоих параметров.
    Прототип функции ExtTextOut
    Код (ASM):
    1. BOOL WINAPI ExtTextOut(
    2. _In_    HDC hdc,    // дескриптор DC
    3. _In_    int x,        // логическая x-координата первого символа строки
    4. _In_    int y,        // логическая y-координата верхней части первого символа строки
    5. _In_    UINT nOptions,    /* тип прямоугольника. Может быть ETO_CLIPPED либо ETO_OPAQUE,
    6. либо ETO_CLIPPED+ETO_OPAQUE, либо 0 */
    7. _In_ const RECT *lpRect,/* Указатель на структуру RECT, которая определяет размерности
    8. ;прямоугольника. Параметр может быть равен NULL */
    9. _In_    LPCTSTR    lpszString,// Указатель на символьную строку
    10. _In_    UINT nCount,    // число символов в строке
    11. _In_ const INT lpDxWidths/* Указатель на массив значений, которые указывают расстояние
    12. между происхождением смежных символьных ячеек. Например, lpDxWidths логические
    13. модули отделит происхождение символьной ячейки и символьная ячейка + 1. Если
    14. lpDxWidths = NULL, ExtTextOut использует значение по умолчанию, располагающее
    15. между символами */
    16. );
    Константы ExtTextOut ― ETO_XX
     
    Последнее редактирование: 23 мар 2019
  20. Mikl___

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

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    ЗначениеhexbinПредназначение
    ETO_
    0​
    00000000000000
    ETO_GRAYED
    1​
    00000000000001недокументирован
    ETO_OPAQUE
    2​
    00000000000010Текущий фоновый цвет заполняет прямоугольник. Можно устанавливать и делать запрос текущего фонового цвета функциями SetBkColor и GetBkColor
    ETO_CLIPPED
    4​
    00000000000100текст отсечен к прямоугольнику
    ETO_
    8​
    00000000001000?
    ETO_GLYPH_INDEX
    10​
    00000000010000Массив lpString относится к массиву, возвращаемому функцией GetCharacterPlacement, и должен анализироваться непосредственно графическим интерфейсом устройств (GDI), поскольку никакая более поздняя ориентированная на конкретный язык обработка не требуется. Глиф индексируется только тогда, когда применяются шрифты TrueType, но флажок может использоваться для растровых и векторных шрифтов, чтобы указать, что в более поздней обработке языка нет необходимости, а графический интерфейс устройств должен обработать строку непосредственно. Заметьте, что все индексы глифа ― 16-разрядные значения даже при том, что строка предназначена для растровых шрифтов быть массивом значений из 8 битов.
    Функция ExtTextOutW, индексы глифа сохраняет в метафайле. Однако, чтобы показать на экране правильные символы, метафайл должен быть воспроизведен, используя тот же самый шрифт. Функция ExtTextOutA, индексы глифа не сохраняет
    ETO_
    20​
    00000000100000?
    ETO_
    40​
    00000001000000?
    ETO_RTLREADING
    80​
    00000010000000Если это значение установлено, в контексте устройства выбираются и еврейский, и арабский шрифт, строка выводится, используя порядок зеркального изображения. Если это значение не определено, строка выводится в порядке слева направо. Тот же самый результат может быть достигнут установкой значения TA_RTLREADING в параметре функции SetTextAlign. Это значение сохраняется для совместимости вниз
    ETO_
    100​
    00000100000000
    ETO_
    200​
    00001000000000
    ETO_NUMERICSLOCAL
    400​
    00010000000000показывать числа с использованием цифр, применяемых в данной стране
    ETO_NUMERICSLATIN
    800​
    00100000000000показывать числа с использованием римских цифр
    ETO_IGNORELANGUAGE
    1000​
    01000000000000Зарезервирован для системного использования. Если приложение устанавливает этот флажок, оно теряет поддержку национальных языков, и, в некоторых случаях, оно не может показать на экране никакого текста вообще
    ETO_PDY200010000000000000Когда он устанавливается, массив, на который указывает параметр lpDx содержит пары значений. Первое значение каждой пары, как обычно, расстояние между началами координат смежных символьных ячеек, а второе значение - смещение по вертикальному направлению шрифта
    Значения ETO_GLYPH_INDEX и ETO_RTLREADING не могут использоваться вместе. Поскольку ETO_GLYPH_INDEX подразумевает, что вся обработка языка была закончена, функция игнорирует флажок ETO_RTLREADING если он также установлен.
    Вызов функции, записывает строку внутрь прямоугольной области, использующей в настоящее время выбранный шрифт. Прямоугольная область может быть непрозрачна (заполнена текущим фоновым цветом), и это может быть область отсечения.
    Если nOptions = 0, и lpRect = NULL, текст выводиться в контекст устройства без использования прямоугольной области. По умолчанию, текущая позиция не используется или модифицируется функцией. Если программа должна модифицировать текущую позицию, тогда перед вызовом ExtTextOut вызывают функцию SetTextAlign с параметром nFlags равным TA_UPDATECP. Когда этот флажок установлен, Windows игнорирует значения параметров в x и y при последующих обращениях к ExtTextOut и использует текущую позицию. Когда прикладная программа использует TA_UPDATECP, чтобы модифицировать текущую позицию, ExtTextOut устанавливает текущую позицию или к концу предыдущей строки текста или к позиции, определенной последним элементом массива, указанного в lpDxWidths.
    Код (ASM):
    1. .data
    2. expTxt2A db 'На другой день мальчик пришёл к дядюшке Римусу послушать, чем кончилась история с лошадью Братца'
    3. expTxt2B db 'Кролика. Но дядюшка Римус был не в духе.'
    4. expTxt2C ...
    5.  ....
    6. stringtable2 dd expTxt2A,expTxt2B,expTxt2C,expTxt2D,expTxt2E,expTxt2F, expTxt2G,expTxt2H,expTxt2I,expTxt2J,expTxt2K,expTxt2L
    7. dd expTxt2M,expTxt2N,expTxt2O,expTxt2P,expTxt2Q,expTxt2R,expTxt2S, expTxt2T,expTxt2U,expTxt2V,expTxt2W,expTxt2X,expTxt2Y
    8. dd expTxt2Z,expTxt2a,expTxt2b,expTxt2c,expTxt2d,expTxt2e,expTxt2f,expTxt2g, expTxt2h,expTxt2i,expTxt2j,expTxt2k,expTxt2l
    9. dd expTxt2m,expTxt2n,expTxt2o,expTxt2p,expTxt2q,expTxt2r,expTxt2s,expTxt2t, expTxt2u,expTxt2v,expTxt2w,expTxt2x,expTxt2y
    10. dd expTxt2z,expTxt2@,0
    11. .code
    12. ExtTextOut1::;вывод текста при помощи функции ExtTextOut
    13. @@: mov r10d,stringtable2[rbx]
    14.           mov eax,stringtable2[rbx+4]
    15.           or eax,eax
    16.           jz a1
    17.           sub eax,r10d
    18.           invoke ExtTextOut,MemDC,LEFT,y,0,&expRect,r10,rax,0
    19.           add y,edi
    20.           add ebx,4
    21.           jmp @b
    [​IMG]

    Изменение заголовка окна

    Можно сделать двумя способами:
    1. Для того чтобы изменить заголовок окна используется функция SetWindowTextA/W
      Прототип функции:
      Код (C):
      1. BOOL WINAPI SetWindowText(
      2. _In_ HWND hWnd, //Дескриптор окна
      3. _In_opt LPCTSTR lpString //Адрес строки с новым заголовком
      4. );
      Код (ASM):
      1. Title1::invoke SetWindowText,hWnd,&expTxt8
      [​IMG]
    2. Вывод текста в заголовок окна через SendMessage
    Код (ASM):
    1. Title2::invoke SendMessage,hWnd,WM_SETTEXT,0,&expTxt9
    [​IMG]
     
    Последнее редактирование: 4 сен 2019
    mantissa и rococo795 нравится это.