Фокус ввода По умолчанию, после инициализации диалоговой панели фокус ввода имеет первая кнопка. Вы можете определить в качестве кнопки, используемой "по умолчанию" любую из четырех кнопок с помощью следующих констант: hexbinMB_DEFBUTTON10XX000000000000000000000000 MB_DEFBUTTON21XX000000000000000100000000 MB_DEFBUTTON32XX000000000000001000000000 MB_DEFBUTTON43XX000000000000001100000000 MB_DEFMASKFXX000000000000111100000000для программного определения кнопки обладающей фокусомМодальность окна сообщения Немодальный диалог (MessageBox со стилем MB_APPLMODAL) пользователь должен ответить MessageBox'у, прежде чем продолжать работу в текущем окне. Пользователь может перейти на окна других приложений и работать в этих окнах. Немодальный диалог не задерживает выполнение программы, для продолжения работы программы не требуется завершение диалога MessageBox. Разрешается переключаться между диалогом MessageBox и другими приложениями. Модальный диалог. (MessageBox со стилем MB_SYSTEMMODAL) все приложения приостанавливаются, пока пользователь не ответит в окне MessageBox. Система модальных диалогов используется для уведомления пользователя о серьезных или потенциально опасных ошибках, которые требуют немедленного внимания и следует использовать этот стиль с осторожностью. Модальный диалог не позволит переключить ввод на другие окна, порожденные приложением. MessageBox со стилем MB_TASKMODAL аналогичен MessageBox со стилем MB_APPLMODAL. Эта константа зарезервирован для вызывающего приложения или библиотеки, которые не имеет доступа к дескриптору окна MessageBox. Чтобы понять что такое модальный или немодальный диалог, просто запустите какую-нибудь программу а затем выведите на экран MessageBox со стилем MB_SYSTEMMODAL, а теперь попробуйте переключиться на основное меню другой программы. Вы обнаружите, что не можете это сделать, потому что MessageBox блокирует фокус окна. MessageBox как бы говорит пользователю: "Сперва разберись со мной, а потом уже и все остальные программы" вы будете должны закрыть MessageBox, прежде чем сможете продолжить работу с остальными программами. Противоположностью ему является MessageBox со стилем MB_APPLMODAL, когда он появится на экране, вы имеете доступ и к ней, и к главному окну другой программы. Диалоги бывают двух видов: модальные немодальные Модальный диалог характерен тем, что после открытия MessageBox MB_SYSTEMMODAL пользователь может работать только с его элементами управления, а все остальные окна приложения блокируются до тех пор, пока модальный диалог не будет закрыт. Модальные диалоги рекомендуется использовать в тех случаях, когда без информации, вводимой через окно диалога, программа не может больше работать. Если, например, программа на некотором этапе должна ввести данные из того или иного файла, то выбор конкретного файла естественно выполнить с помощью модального диалогового окна. Нормальное состояние модального диалога ― закрытое. Немодальный диалог не препятствует пользователю работать с любыми элементами главного окна приложеия или его внутренних окон. С помощью немодальных диалогов задают масштаб, цвет и другие свойства приложения. Нормальное состояние немодального диалога ― открытое. Другой вид диалоговых панелей ― это немодальные диалоговые панели. В отличие от модальных диалоговых панелей блокирующих при своем появлении родительское окно и все дочерние окна родительского окна, немодальные диалоговые панели работают параллельно с другими окнами приложения без взаимных блокировок. Вы можете работать как с главным окном приложения, так и окном немодальной диалоговой панели. Немодальные диалоговые панели очень удобны для объединения различных инструментальных средств, предназначенных для работы с объектом, расположенным в главном окне или в дочернем окне, созданным главным окном приложения.(Фроловы) С помощью бит 12 и 13 вы можете влиять на модальность MessageBox Прочее Кроме перечисленных выше флагов, объединенных в группы, пользователь может использовать следующие флаги hexbinОписаниеMB_HELP4000h000000000100000000000000MB_NOFOCUS8000h000000001000000000000000 MB_MISCMASK0C000h000000001100000000000000используется для программного определения типа MessageBoxZ-последовательность Z-последовательность (Z order) окна обозначает позицию окна в стеке перекрывающихся окон. Этот оконный стек ориентируется по воображаемой оси, z-оси (оси аппликат), уходящей за пределы экрана. Окно наверху Z-последовательности перекрывает все другие окна. Окно внизу Z-последовательности перекрыто всеми другими окнами. Windows Z-последовательность сохраняет в обычном списке. Добавляя окна в Z-последовательность, она основывается на том, что в любом случае они самые верхние окна, окна верхнего уровня или дочерние окна. Самое верхнее окно (topmost window) накладывается на все другие не самые верхние окна, независимо от того, является ли они активными или приоритетными окнами. Самое верхнее окно имеет стиль WS_EX_TOPMOST. Все самые верхние окна появляются в Z-последовательности перед любыми не самыми верхними окнами. Дочернее окно сгруппировано со своим родителем в Z-последовательности. Когда прикладная программа создает окно, Windows помещает его наверху Z-последовательности для окон того же самого типа. Вы можете использовать функцию BringWindowToTop, чтобы перенести окно в верхнюю часть Z-последовательности для окон того же самого типа. Вы можете перестраивать Z-последовательность, используя функции SetWindowPos и DeferWindowPos. Пользователь изменяет порядок Z-последовательности, активизируя различные окна. Windows устанавливает активное окно наверху Z-последовательности для окон того же самого типа. Когда окно переходит в верхнюю часть Z-последовательности, его дочерние окна делают это также. Вы можете использовать функцию GetTopWindow, чтобы отыскать все дочерние окна родительского окна и возвратить дескриптор дочернего окна, которое является самым высоким в Z-последовательности. Функция GetNextWindow извлекает данные о дескрипторе следующего или предыдущего окна в Z-последовательности. В Windows используется Z-порядок окон. Окна располагаются одно над другим по вертикали. Окно на верху лежит поверх всех. Нижнее окно в самом низу, остальные в промежутке. Для этого в Windows существует специальный список, в котором эти окна расположены. Важность окна зависит от стиля окна. Самую высокую важность имеет стиль WS_EX_TOPMOST. Когда система создает окно она помещает это окно на самом верху относительно окон того же самого типа. Вы можете использовать функцию BringWindowToTop, чтобы вывести окно наверх относительно окон того же самого типа. Вы можете реорганизовать порядок, используя функции DeferWindowPos и SetWindowPos. Функция GetNextWindow возвращает указатель к следующему или предыдущему окну в Z-порядке. Для поиска подчиненных окон используется функция GetTopWindow. Окно поверх всех остальных Вы наверно сталкивались с программами, которые всегда поверх остальных окон. Если Вы запустите диалоговое приложение и нажмете на его кнопки, ваше диалоговое окно будет поверх всех остальных. Определите один из следующих флажков, чтобы указать модальность диалогового окна: Замечания Приоритетное окно ― окно наверху Z-последовательности. Это ― окно, с которым пользователь работает. В среде приоритетной многозадачности, Вы должны вообще дать возможность управления пользователю, окно которого является приоритетным окном. Однако прикладная программа может вызывать функцию SetForegroundWindow, если требуется перевести себя в активный режим, чтобы отобразить критическую ошибку или информацию, которая требует непосредственного внимания пользователя. Хороший пример ― это отладчик, когда он обнаруживает контрольную точку останова программы. Система назначает немного более высокий приоритет потоку, который создал приоритетное окно, чем она делает это по отношению к другим потокам. Если закрыть окно-хозяин (Parent window), то все MessageBox исчезают. После закрытия окна MessageBox со стилем MB_SERVICE_NOTIFICATION и MessageBox со стилем MB_DEFAULT_DESKTOP_ONLY остаются на экране Отобразить сообщение MessageBox может любая служба, для этого нужно в функцию MessageBox помимо прочих флагов передать MB_SERVICE_NOTIFICATION или MB_DEFAULT_DESKTOP_ONLY. Флаг MB_SERVICE_NOTIFICATION заставит функцию вывести сообщение на экран, даже если пользователь ещё не вошёл в систему. Если установлен флаг MB_DEFAULT_DESKTOP_ONLY, сообщение появится только на "нормальном" рабочем столе. MB_SERVICE_NOTIFICATION заставляет сообщение появиться на текущем активном desktop-е, а MB_DEFAULT_DESKTOP_ONLY только на "нормальном". ПриоритетСтильbin1MB_SERVICE_NOTIFICATION0010000000000000000000001MB_DEFAULT_DESKTOP_ONLY0000001000000000000000002MB_TOPMOST0000010000000000000000002MB_SYSTEMMODAL0000000000010000000000003MB_SETFOREGROUND0000000100000000000000003MB_TASKMODAL0000000000100000000000003MB_OK/MB_APPLMODAL000000000000000000000000 если первым запущен MessageBox со стилем MB_SERVICE_NOTIFICATION, то запущенный после него MessageBox со стилем MB_DEFAULT_DESKTOP_ONLY окажется под MessageBox со стилем MB_SERVICE_NOTIFICATION если первым запущен MessageBox со стилем MB_DEFAULT_DESKTOP_ONLY, то запущенный после него MessageBox со стилем MB_SERVICE_NOTIFICATION окажется под MessageBox со стилем MB_DEFAULT_DESKTOP_ONLY несколько запущенных MessageBox со стилем MB_DEFAULT_DESKTOP_ONLY окажутся друг под другом несколько запущенных MessageBox со стилем MB_SERVICE_NOTIFICATION окажутся друг под другом hexbinОписаниеMB_SETFOREGROUND10000h000000010000000000000000Панель MessageBox становится окном переднего плана. При появлении окна со стилем MB_SETFOREGROUND операционная система вызывает функцию SetForegroundWindow для MessageBox'а. Информацию о мерах безопасности при использовании этого флага смотрите в разделе "Интерактивные службы". Этот флаг может создавать интерактивный контент на заблокированном рабочем столе и должен использоваться только для очень ограниченного набора сценариев, таких как исчерпание ресурсов. Функция SetForegroundWindow переводит поток, который создало определяемое окно в приоритетный режим и активизирует окно. Ввод с клавиатуры направлен в окно, а различные визуальные ориентиры изменяются для пользователя. Код (C): BOOL SetForegroundWindow ( HWND hWnd /* дескриптор окна, которое переводится в приоритетный режим */); hWnd идентифицирует окно, которое должно быть активизировано и переведено в приоритетный режим. Если функция завершилась успешно, возвращается значение отличное от нуляMB_DEFAULT_DESKTOP_ONLY20000h000000100000000000000000Если текущий рабочий стол ввода не является рабочим столом по умолчанию, MessageBox не возвращается, пока пользователь не переключится на рабочий стол по умолчанию. "Заданный по умолчанию" рабочий стол ― первая запущенная прикладная программа, после того, как пользователь вошел в систему.MB_TOPMOST40000h000001000000000000000000MessageBox создается со стилем окна WS_EX_TOPMOST Рабочий стол, получивший фокус ввода должен быть рабочим столом, выбираемым по умолчанию. В противном случае функция завершается с ошибкой. Под рабочим столом, выбираемым по умолчанию, понимается рабочий стол, появляющийся после загрузки системы;MB_SERVICE_NOTIFICATION_NT3X40000h000001000000000000000000Вызывающая программа является обслуживающей по уведомлению пользователя о событии. Функция отображает MessageBox на текущем активном рабочем столе, даже если никто из пользователей не вошел в систему компьютера. Если этот флажок установлен, параметр hwndParent должен быть равным NULL. MessageBox с флагом MB_SERVICE_NOTIFICATION_NT3X может появляться на другом рабочем столе, а не только на том, которое соответствует hwndParent.MB_RIGHT80000h000010000000000000000000Обычный текст MessageBox с выравниванием текста в правую сторону MB_RTLREADING100000h000100000000000000000000Предназначен для работы с текстом на иврите или арабском. "Зеркалит" текст сообщения и расположение кнопок и иконки. Довольно интересный эффект ― обратное расположение слов в тексте сообщения MB_SERVICE_NOTIFICATION200000h001000000000000000000000Для Windows NT версии 4.0, значение MB_SERVICE_NOTIFICATION изменилось. Смотрите winuser.h для старых и новых значений. Windows NT 4.0 обеспечивает совместимость вниз для существующих ранее услуг, при помощи преобразования данных старых значений в новых значениях при реализации MessageBox и MessageBoxEx. Это преобразование данных делается только для исполнимых программ (.exe), которые имеют номер версии, как установлено компоновщиком, меньше чем 4.0. Чтобы сформировать обслуживание, которое использует MB_SERVICE_NOTIFICATION и возможность запускать, и Windows NT 3.x и Windows NT 4.0, у вас два варианта: Во время компоновки, определите номер версии меньше чем 4.0; или Во время компоновки, определите версию 4.0. Во время запуска, используйте функцию GetVersionEx, чтобы проверить системную версию. Тогда при продолжении запуска Windows NT 3.x, используйте MB_SERVICE_NOTIFICATION_NT3X; а для Windows NT 4.0, используйте MB_SERVICE_NOTIFICATION.Возвращаемые значения Функция MessageBox возвращает нулевое значение при ошибке или одну из следующих констант, в зависимости от того, какую кнопку нажал пользователь. КонстантаЗначениеНазвание кнопкиПримечаниеIDOK1IDCANCEL2IDABORT3IDRETRY4IDIGNORE5IDYES6IDNO7IDCANCEL2Если у MessageBox стиль MB_OK, тогда будет возвращено IDOK, хотя есть стиль IDCLOSE=8IDHELP9Если у MessageBox MB_HELPIDTRYAGAIN10IDCONTINUE11 © Mikl___ 2019
Глава шестая. Братец Кролик выводит MessageBox всеми возможными способами «. . в желудке слона бегемот, в бегемоте ― лев, внутри льва ― гиена, в гиене ― кролик, в кролике ― утка, в утке ― яйцо, в яйце ― игла, а на конце иглы ― смерть злого колдуна Чомбе. . .» Одна из сказок дядюшки Римуса Казалось бы, что может быть проще функции MessageBox? Но Братец Кролику, в погоне за минимальным размером, захотелось узнать, а что находится внутри MessageBox? Может быть это поможет ему сделать программы еще меньше? Братец Кролик запустил программу, выводящую MessageBox на экран, в дебаггере x64dbg и в тот момент, когда курсор дошел до строки call MessageBoxA нажал не на F8 (Step over), а на клавишу F7 (Step into) и оказался внутри функции MessageBoxA ― оказывается здесь происходит перекодировка ASCII-строк заголовка и текста сообщений в UNICODE и вызов функции MessageBoxW. Братец Кролик снова нажал на F7 ― оказывается внутри MessageBoxW функция MessageBoxExA которая имеет на один параметр больше, чем MessageBoxA и MessageBoxW и этот параметр (dwLanguageId) равен 0 (LANG_NEUTRAL), остальные же параметры совпадают полностью. Внутри MessageBoxExA находится функция MessageBoxExW. Внутри MessageBoxExW ― функция MessageBoxTimeoutA у которой на один параметр больше, чем у MessageBoxExW и этот параметр (Timeout) равен -1 (INFINITE). Внутри MessageBoxTimeoutA, (как можно было догадаться ) MessageBoxTimeoutW. Внутри MessageBoxTimeoutW ― MessageBoxIndirectA эта функция позволяет поменять иконку у MessageBox и может вызвать Справку, но мы оставляем эти параметры с нулевым значением. Внутри MessageBoxIndirectA находится точно такая же, но с UNICODE-строками функция MessageBoxIndirectW внутри которой SoftModalMessageBox внутри которой NtRaiseHardError ― вызов MessageBox на уровне ядра Традиционный способ определения Unicode-строки: Код (ASM): usz dw 'U', 'n', 'i', 'c', 'o', 'd', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g', 0 Братца Кролика этот способ не устраивал, поэтому, он написал для этой цели макрос du (define unicode string), чтобы без проблем переводить ASCII-строки в UNICODE-строки Код (ASM): du macro string local bslash bslash = 0 irpc c,<string> if bslash eq 0 if '&c' eq '\' ;управляющая последовательность символов bslash = 1 elseif '&c' eq "ё" db 51h,4 elseif '&c' eq "Ё" db 1,4 elseif '&c' gt 127 db ('&c'- 0B0h),4;;кириллица else dw '&c' ;;латиница endif else bslash = 0 if '&c' eq "n" ;; \n = новая строка DW 0Dh,0Ah elseif '&c' eq "\";; \\ = обратная косая черта (\) dw '\' elseif '&c' eq "r";; \r = возврат каретки dw 0Dh elseif '&c' eq "l";; \l = LF dw 0Ah elseif '&c' eq "s" dw 20h elseif '&c' eq "c" dw 3Bh elseif '&c' eq "t";; \t = табуляция dw 9 endif endif endm dw 0 endm этот макрос превращает ASCII-символы латиницы в UNICODE-символы добавлением после кода символа нуля, а для кириллицы заменяет 0 на 4 и сдвигает код символа на 176, в конце UNICODE-строки ставится ноль, терминирующий строку. Всё, что требуется от пользователя ― после du написать строку в угловых скобках. То есть представить Unicode-строку так: Код (ASM): usz: du <Unicode string> обратите внимание, что после названия строки стоит двоеточие. MessageBoxEx Функция MessageBoxEx создает, отображает на экране и оперирует окном сообщений. Окно сообщений содержит определяемое программой сообщение и заголовок, плюс любую комбинацию предопределенных пиктограмм и командных кнопок. Параметр wLanguageId определяет, какой устанавливается ресурс языка, который используется для предопределенных командных кнопок. Синтаксис Код (C): int MessageBoxEx ( HWND hWnd, // дескриптор окна владельца LPCTSTR lpText, // адрес текста в окне сообщений LPCTSTR lpCaption, // адрес заголовка в окне сообщений UINT uType, // стиль окна сообщений DWORD dwLanguageId // идентификатор языка ); Параметры hWnd Идентифицирует окно владельца блока сообщений, которым оно было создано. Если этот параметр имеет значение NULL, у блока сообщения нет окна владельца.lpText Указывает на строку с символом нуля в конце, содержащую сообщение, которое должно быть отражено на экране.lpCaption Указывает на строку с символом нуля в конце, используемую для заголовка диалогового окна. Если этот параметр значение NULL, то по умолчанию используется заголовок "Ошибка" ("Error").uType Определяет установку битов флажков, которые обуславливают содержание и поведение диалогового окна. Этот параметр может быть комбинацией флажков.dwLanguageId Определяет язык, на котором будет отображаться текст, содержащийся в предопределенных командных кнопках.Каждый выпуск Windows обычно содержит ресурсы только для ограниченного набора языков. Английская версия предлагает LANG_ENGLISH, французская ― LANG_FRENCH, немецкая ― LANG_GERMAN и японская ― LANG_JAPANESE. Каждая версия предлагает LANG_NEUTRAL. Это ограничивает установку значений, которые могут использоваться с параметром dwLanguageId. Замечания Когда Вы используете системно-модальное окно сообщений, чтобы указать, что система имеет мало памяти, строки, переданные как параметры lpText и lpCaption не должны быть приняты из файла ресурса, так как попытка загрузить ресурс может потерпеть неудачу. Когда прикладная программа вызывает MessageBoxEx и устанавливает флажки MB_ICONHAND и MB_SYSTEMMODAL в параметре uType, Win32 API отображает на экране законченное окно сообщений независимо от доступной памяти. Когда эти флажки установлены, Windows ограничивает длину текста окна сообщений до одной строки. Если Вы создаете окно сообщений, в то время когда присутствует диалоговое окно, дескриптор блока диалога используется как параметр hWnd. Параметр hWnd не должен идентифицировать дочернее окно, типа диалогового окна. MessageBoxTimeout Синтаксис Код (C): int MessageBoxTimeout ( HWND hWnd, // дескриптор окна владельца LPCTSTR lpText, // адрес текста в окне сообщений LPCTSTR lpCaption, // адрес заголовка в окне сообщений UINT uType, // стиль окна сообщений DWORD dwLanguageId, // идентификатор языка DWORD Timeout // время ); Вызов аналогичен функции MessageBox, только добавляются два дополнительных параметра ― идентификатор языка и таймаут в миллисекундах. Код (ASM): ; MessageBox закроется автоматически через 5 секунд invoke MessageBoxTimeout,HWND_DESKTOP,&szMess,&szTitle,MB_OK,0,5000 По истечении отведенного времени окно сообщения закрывается с кодом возврата MB_TIMEDOUT. MessageBoxIndirect Функция MessageBoxIndirect создает, отображает и оперирует окном сообщений. Окно сообщений содержит определяемый программой текст сообщения и заголовок, любую пиктограмму, и любую комбинацию предопределенных командных кнопок. Синтаксис Код (C): int MessageBoxIndirect ( LPMSGBOXPARAMS lpMsgBoxParams // адрес структуры для параметров // окна сообщений ); Параметры lpMsgBoxParams Указатель на структуру MSGBOXPARAMS, которая содержит информацию, используемую для показа на экране окна сообщений.Замечания Когда Вы используете системно-модальное окно сообщений, чтобы указать, что у системы мало памяти, строки, указанные элементами lpszText и lpszCaption структуры MSGBOXPARAMS не должны быть приняты из файла ресурса, потому что попытка загрузить ресурс может потерпеть неудачу. Когда прикладная программа вызывает MessageBoxIndirect и устанавливает флажки MB_ICONHAND и MB_SYSTEMMODAL в элементе dwStyle структуры MSGBOXPARAMS, Windows показывает на экране законченное окно сообщений независимо от доступной памяти. Когда эти флажки установлены, Windows ограничивает длину текста в окне сообщений до трех строк. Windows автоматически не разрывает строки, чтобы подстроить их под окно сообщений, такая строка сообщения должна содержать символы перевода каретки, чтобы разрывать строки в соответствующих местах. Если Вы создаете окно сообщений, в то время, когда присутствует диалоговое окно, используйте дескриптор блока диалога как параметр hWnd. Параметр hWnd не должен идентифицировать дочернее окно, типа органа управления в диалоговом окне. SoftModalMessageBox Функция SoftModalMessageBox создает, отображает и оперирует окном сообщений. Окно сообщений содержит определяемый программой текст сообщения и заголовок, любую пиктограмму, и любую комбинацию предопределенных командных кнопок, любое количество кнопок и любые надписи на кнопках. Синтаксис Код (C): int SoftModalMessageBox ( LPMSGBOXDATA lpMsgBoxData // адрес структуры для параметров // окна сообщений ); Параметры lpMsgBoxData Указатель на структуру MSGBOXDATA, которая содержит информацию, используемую для показа на экране окна сообщений. Код (ASM): MSGBOXPARAMS STRUCT cbSize DWORD ?,?;размер структуры hwndOwner QWORD ?; hInstance QWORD ?; lpszText QWORD ?;адрес текста в окне сообщений lpszCaption QWORD ?;адрес заголовка в окне сообщений dwStyle DWORD ?,?; lpszIcon QWORD ?; dwContextHelpId DWORD ?,?; lpfnMsgBoxCallback QWORD ?; dwLanguageId DWORD ?,?;идентификатор языка MSGBOXPARAMS ENDS MSGBOXDATA struct params MSGBOXPARAMSA <> pwndOwner QWORD ? wLanguageId DWORD ?,? pidButton QWORD ? ; // Array of button IDs ppszButtonText QWORD ? ; // Array of button text strings cButtons DWORD ? DefButton DWORD ? CancelId DWORD ? Timeout DWORD ? MSGBOXDATA ends NtRaiseHardError статус ошибки маска, единичные биты которой соответствуют тем параметрам, которые имеют тип PUNICODE_STRING, а нули ― остальным параметрам общее число параметров указатель на массив параметров варианты ответов двойное слово, куда функция запишет выбранный пользователем ответ (если тип, переданный в следующем параметре, предполагает выбор ответа пользователем) Код (ASM): ; GUI # include win64a.inc MSGBOXDATA struct params MSGBOXPARAMS <> pwndOwner QWORD ? wLanguageId DWORD ?,? pidButton QWORD ? ; // Array of button IDs ppszButtonText QWORD ? ; // Array of button text strings cButtons DWORD ? DefButton DWORD ? CancelId DWORD ? Timeout DWORD ? MSGBOXDATA ends .code MsgBoxText db "Win64 Assembly is Great!",0 MsgBoxText1 dw 'W','i','n','6','4',' ','A','s','s','e','m','b','l','y',' ','i','s',' ','G','r','e','a','t','!',10 dw 'W','i','n','6','4',27719,32534,31243,24207,24456,26834,'!',0;du <Win64 Assembly is Great> MsgCaption0 db "MessageBoxA",0 MsgCaption1: du <MessageBoxW> MsgCaption2 db "MessageBoxExA",0 MsgCaption3: du <MessageBoxExW> MsgCaption4 db "MessageBoxTimeoutA",0 MsgCaption5: du <MessageBoxTimeoutW> MsgCaption6 db "MessageBoxIndirectA",0 MsgCaption7: du <MessageBoxIndirectW> MsgCaption8: du <SoftModalMessageBox> sBTN1: du <OK> dwBtnIds dd 1 dwTxtTbl dq sBTN1 x dq ? align 16 WinMain proc local mbxData:MSGBOXDATA Local MessageBuffer[50h]:WCHAR;в Num+Num1 в вордах Local Message[2]:UNICODE_STRING Local HardErrorPointer[3]:PVOID ;MessageBoxA---------------------------------------- mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption0-MsgBoxText] invoke MessageBox,0,,,MB_OK+10h mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption0-MsgBoxText] invoke MessageBox,0,,,MB_OK+20h mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption0-MsgBoxText] invoke MessageBox,0,,,MB_OK+30h mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption0-MsgBoxText] invoke MessageBox,0,,,MB_OK+40h mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption0-MsgBoxText] invoke MessageBox,0,,,MB_OK+90h mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption0-MsgBoxText] invoke MessageBox,0,,,MB_OK+0A0h mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption0-MsgBoxText] invoke MessageBox,0,,,MB_OK+0B0h ;MessageBoxW---------------------------------------- mov edx,offset MsgBoxText1 lea r8,[rdx+MsgCaption1-MsgBoxText1] invoke MessageBoxW,0,,,MB_OK; or MB_ICONASTERISK ;MessageBoxExA-------------------------------------- xor ecx,ecx mov [rsp+20h],rcx ;wLanguageId mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption2-MsgBoxText] invoke MessageBoxEx,,,,MB_OK; or MB_ICONASTERISK ;MessageBoxExW-------------------------------------- xor ecx,ecx mov [rsp+20h],rcx ;wLanguageId=LANG_NEUTRAL=0 mov edx,offset MsgBoxText1 lea r8,[rdx+MsgCaption3-MsgBoxText1] invoke MessageBoxExW,,,,MB_OK; or MB_ICONASTERISK ;MessageBoxTimeoutA--------------------------------- xor ecx,ecx mov [rsp+20h],rcx ;wLanguageId=LANG_NEUTRAL or dword ptr [rsp+28h],INFINITE;-1 или время в миллисекундах mov edx,offset MsgBoxText lea r8,[rdx+MsgCaption4-MsgBoxText] invoke MessageBoxTimeout,,,,MB_OK; or MB_ICONASTERISK ;MessageBoxTimeoutW--------------------------------- xor ecx,ecx mov [rsp+20h],rcx ;wLanguageId=LANG_NEUTRAL or dword ptr [rsp+28h],INFINITE;-1 mov edx,offset MsgBoxText1 lea r8,[rdx+MsgCaption5-MsgBoxText1] invoke MessageBoxTimeoutW,,,,MB_OK; or MB_ICONASTERISK ;MessageBoxIndirectA------------------------------- xor eax,eax mov mbxData.params.cbSize,sizeof MSGBOXPARAMS mov mbxData.params.hwndOwner,rax;0 mov mbxData.params.hInstance,400000h mov mbxData.params.dwStyle,MB_OK or MB_USERICON mov mbxData.params.lpszIcon,1000 mov mbxData.params.dwContextHelpId,eax;0 mov mbxData.params.lpfnMsgBoxCallback,rax;0 mov mbxData.params.dwLanguageId,eax;LANG_NEUTRAL;0 lea ecx,mbxData.params movr mbxData.params.lpszText,MsgBoxText movr mbxData.params.lpszCaption,MsgCaption6 invoke MessageBoxIndirect ;MessageBoxIndirectW-------------------------------- movr mbxData.params.lpszText,MsgBoxText1 movr mbxData.params.lpszCaption,MsgCaption7 lea ecx,mbxData.params invoke MessageBoxIndirectW ;SoftModalMessageBox-------------------------------- movr mbxData.params.lpszCaption,MsgCaption8;адрес заголовка в окне сообщений 8 movr mbxData.pidButton,dwBtnIds movr mbxData.ppszButtonText,dwTxtTbl mov mbxData.cButtons,1;4 and qword ptr mbxData.DefButton,0; and mbxData.DefButton,0 ; and mbxData.CancelId,0;2 or mbxData.Timeout,-1 lea ecx,mbxData invoke SoftModalMessageBox ;NtRaiseHardError----------------------------------- ;первый параметр lea edi,MessageBuffer mov Message.Buffer+(sizeof UNICODE_STRING)*0,rdi mov dword ptr Message.woLength+(sizeof UNICODE_STRING)*0,30002Eh lea edx,Message+(sizeof UNICODE_STRING)*0 mov HardErrorPointer+(sizeof PVOID)*0,rdx;pwText mov esi,offset MessageText mov ecx,(Num+Num1+2)/4 rep movsd ;второй параметр lea edi,MessageBuffer+Num mov Message.Buffer+(sizeof UNICODE_STRING)*1,rdi mov dword ptr Message.woLength+(sizeof UNICODE_STRING)*1,220020h lea edx,Message+(sizeof UNICODE_STRING)*1 mov HardErrorPointer+(sizeof PVOID)*1,rdx;pwCaption ;третий параметр mov HardErrorPointer+(sizeof PVOID)*2,MB_OK or MB_ICONASTERISK;uType lea r9d,HardErrorPointer;указатель на массив параметров movr qword ptr [rsp+28h],x;куда функция запишет выбранный юзером ответ (если тип, ;переданный в следующем параметре, предполагает выбор ответа пользователем) mov qword ptr [rsp+20h],1 mov edx,4 mov r8d,3 mov ecx,50000018h;STATUS_SERVICE_NOTIFICATION;статус ошибки call NtRaiseHardError ;syscall------------------------------------------- ;первый параметр lea edi,MessageBuffer mov Message.Buffer+(sizeof UNICODE_STRING)*0,rdi mov dword ptr Message.woLength+(sizeof UNICODE_STRING)*0,30002Eh lea edx,Message+(sizeof UNICODE_STRING)*0 mov HardErrorPointer+(sizeof PVOID)*0,rdx;pwText mov esi,offset MessageText mov ecx,(Num1+Num3)/4 rep movsd ;второй параметр lea edi,MessageBuffer+Num mov Message.Buffer+(sizeof UNICODE_STRING)*1,rdi mov dword ptr Message.woLength+(sizeof UNICODE_STRING)*1,10000Eh lea edx,Message+(sizeof UNICODE_STRING)*1 mov HardErrorPointer+(sizeof PVOID)*1,rdx;pwCaption ;третий параметр mov HardErrorPointer+(sizeof PVOID)*2,MB_ICONASTERISK+MB_OK;uType lea r9d,HardErrorPointer;указатель на массив параметров ;переданный в следующем параметре, предполагает выбор ответа пользователем); movr qword ptr [rsp+28h],x;куда функция запишет выбранный юзером ответ (если тип, mov qword ptr [rsp+20h],1 mov edx,4 mov r8d,3 ;параметрам, которые имеют тип PUNICODE_STRING, а нули -- остальным параметрам mov ecx,50000018h;STATUS_SERVICE_NOTIFICATION;статус ошибки mov r10,rcx mov eax,130h push rbx syscall pop rbx invoke RtlExitUserProcess,NULL WinMain endp MessageText dw 'W','i','n','6','4',27719,32534,31243,24207,24456,26834,'!',0 Num = $-MessageText TitleText: du <NtRaiseHardError> Num1 = $-TitleText dw 0 TitleText1: du <syscall> Num3 = $-TitleText1 end Здесь rc-/asm-/exe-файлы и иконка © Mikl___ 2021
Глава девятая. Как Братец Кролик создал «генератор окошек» Когда вы начнете осваивать хитрости программирования под Windows, то вам поможет учебная программа для экспериментов с многочисленными параметрами используемых функций ― чтобы не надо было каждый раз редактировать и компилировать заново исходный код проекта, а можно было бы выбрать необходимые опции прямо в диалоговом окне и тут же увидеть результат При запуске программы появится новое окно с текстом минимальной программы под х64 в рабочей области главного окна приложения. После компиляции будет получено полноценное окно с заголовком, имеющее в левом верхнем углу свой значок, при щелчке по которому появляется системное меню; в правом верхнем углу кнопки «свертнуть», «восстановить» и «развернуть»; размеры самого окна можно изменять, перетаскивая его рамку. Параметры окна заданы программой «Simple Windows Creator» по умолчанию. Для компиляции нужно выбрать «Файл» →«Создать окно» приложение появится в той же папке, в которой находится программа «Simple Windows Creator». Вам остается только запустить программу, код которой вы получили в рабочей области главного окна приложения. С текстом программы можно работать используя пункты «Копировать», «Вставить», «Удалить», «Сохранить», «Выделить все» Основной пункт меню для наших экспериментов ― «Параметры». Содержит следующие пункты: «Windows Class Styles...» «Иконки...» «Курсоры...» «Стили Окна...» «Расширенные Стили Окна...» «Фон Окна...» «Дополнительно...» Каждый из них вызывает диалоговое окно, позволяющее настроить значения полей структуры WNDCLASSEX, параметров функций CreateWindowEx. Далее приводятся описания используемых структур, функций и возможностей по настройке. В winuser.h описаны пятнадцать стилей окна. Для стиля окна из 32 битов отведено 18 и только один из этих битов установлен в единицу Стиль классаЗначениеОписаниеbinhex1CS_VREDRAW000000000000000000000000000000011Перерисовывать окно при изменении вертикальных размеров2CS_HREDRAW000000000000000000000000000000102Перерисовывать все окно при изменении ширины3CS_KEYCVTWINDOW000000000000000000000000000001004Будет выполняться преобразование виртуальных клавиш4CS_DBLCLKS000000000000000000000000000010008Посылать сообщение от мыши при двойном щелчке в пределах окна5―0000000000000000000000000001000010―6CS_OWNDC0000000000000000000000000010000020У каждого окна уникальный контекст устройства7CS_CLASSDC0000000000000000000000000100000040Один и тот же контекст устройства разделяться всеми окнами этого класса. При нескольких потоках операционная система разрешит доступ только одному потоку8CS_PARENTDC0000000000000000000000001000000080Дочерние окна наследуют контекст родительского окна. У дочернего окна будет область отсечки от родительского. Повышает производительность9CS_NOKEYCVT00000000000000000000000100000000100Отключается преобразование виртуальных клавиш10CS_NOCLOSE00000000000000000000001000000000200Убрать команду «Close» из системного меню. Отключить команду закрыть11―00000000000000000000010000000000400―12CS_SAVEBITS00000000000000000000100000000000800Позволяет сохранять область экрана в виде битовой матрицы закрытую в данный момент другим окном, используется для восстановления экрана13CS_BYTEALIGNCLIENT000000000000000000010000000000001000(по горизонтали) выравнивание рабочей области окна по границе байта. Влияет на ширину окна и его горизонтальное положение на экране14CS_BYTEALIGNWINDOW000000000000000000100000000000002000(по вертикали) выравнивает окна по границе байта15CS_GLOBALCLASS, CS_PUBLICCLASS000000000000000001000000000000004000Разрешается создавать глобальный класс, не зависящего от текущего hInstance, который можно поместить в динамическую библиотеку dll.16―000000000000000010000000000000008000―17CS_IME0000000000000001000000000000000010000Редактор метода ввода (IME ― Input Method Editor) ― программа или компонент операционной системы, позволяющий пользователям вводить символы, которых нет на клавиатуре. Используется для ввода текста на восточно-азиатских языках (китайский, японский или корейский) требующих поддержки многобайтовых кодировок и специальных методов ввода. Окно, созданное на основе этого класса, всегда можно найти с именем класса «IME», независимо от фактического имени класса18CS_DROPSHADOW0000000000000010000000000000000020000Включает эффект тени в окне. Эффект включается и выключается через SPI_SETDROPSHADOW. Как правило, это разрешено для небольших короткоживущих окон, таких как меню, чтобы подчеркнуть их отношение Z-порядка к другим окнам. Windows, созданная из класса с таким стилем, должна быть окон верхнего уровня; Они не могут быть дочерними окнами. можно выбрать стандартные иконки. В качестве первого параметра функции LoadIcon можно передать NULL; в этом случае система использует один из стандартных встроенных значков. Описание соответствующего значка отображается в диалоговом окне в виде всплывающей подсказки. можно выбрать иконку из файла ― в этом случае нужно выбрать имя файла с иконкой из каталога и указать «Иконка из файла», если в файле несколько иконок, то придется указать еще и индекс иконки. Если параметр hInstance функции LoadCursor равен NULL, в зависимости от значения второго параметра загружается один из системных курсоров. Описание соответствующего курсора отображается во всплывающей подсказке диалогового окна. можно выбрать курсор из файла ― в этом случае нужно выбрать имя файла с курсором из каталога и указать «Курсор из файла» Основной стиль окна задается в диалоге «Стили окна» Этот параметр может быть комбинацией стилей, приведенных в диалоге «Стили окна», и дополнительных стилей элементов управления, которые здесь не рассматриваются. СтильЗначениеОписаниеbinhexWS_OVERLAPPED, WS_TILED0000.0000.0000.0000.0000.0000.0000.00000Перекрывающееся окно, имеющее заголовок и обрамлениеWS_ACTIVECAPTION0000.0000.0000.0000.0000.0000.0000.00011The window is activeWS_TABSTOP0000.0000.0000.0001.0000.0000.0000.000010000Определяет элемент управления, на который можно переместиться нажатием клавиши . Нажатие данной клавиши перемещает фокус ввода на следующий элемент управления, имеющий стиль WS_TABSTOPWS_MAXIMIZEBOX0000.0000.0000.0001.0000.0000.0000.000010000Создает окно с кнопкой максимизации. Должен быть указан также стиль WS_SYSMENU. Не может быть использован со стилем WS_EX_CONTEXTHELPWS_MINIMIZEBOX0000.0000.0000.0010.0000.0000.0000.000020000Создает окно с кнопкой минимизации. Должен быть указан также стиль WS_SYSMENU. Не может быть использован со стилем WS_EX_CONTEXTHELPWS_GROUP0000.0000.0000.0010.0000.0000.0000.000020000Определяет первый управляющий элемент в группе, причем в группу входит данный элемент и все последующие до следующего управляющего элемента со стилем WS_GROUP. Обычно первый элемент в группе имеет также стиль WS_TABSTOP, чтобы пользователь мог перемещаться от одной группы к другой. Внутри группы пользователь может перемещаться от одного элемента управления к другому с использованием клавиш перемещения курсораWS_THICKFRAME, WS_SIZEBOX0000.0000.0000.0100.0000.0000.0000.000040000Создает окно, имеющее a sizing borderWS_SYSMENU0000000000001000000000000000000080000Создает окно, имеющее системное меню в заголовке. Должен быть также указан стиль WS_CAPTIONWS_HSCROLL00000000000100000000000000000000100000Создает окно, имеющее a horizontal scroll barWS_VSCROLL00000000001000000000000000000000200000Создает окно, имеющее a vertical scroll barWS_DLGFRAME00000000010000000000000000000000400000Создает окно, имеющее a border of a style typically used with dialog boxes. A window with this style cannot have a title barWS_BORDER00000000100000000000000000000000800000Создает окно, имеющее a thin-line borderWS_CAPTION000000001100000000000000000000000C00000Окно с заголовком (включает в себя также стиль WS_BORDER)WS_OVERLAPPEDWINDOW, WS_TILEDWINDOW000000001100111100000000000000000CF0000Создает перекрывающееся окно с заголовком, системным меню, перемещаемым обрамлением и кнопками минимизации и максимизации (комбинация стилей WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX)WS_MAXIMIZE000000010000000000000000000000001000000Creates a window that is initially maximizedWS_CLIPCHILDREN000000100000000000000000000000002000000Исключает область дочернего окна при рисовании в родительском окне. Этот стиль используется при создании родительского окна.WS_CLIPSIBLINGS000001000000000000000000000000004000000Исключение областей дочерних окон друг относительно друга, т.е. при получении дочерним окном сообщения WM_PAINT стиль WS_CLIPSIBLINGS исключает области всех других дочерних окон из зоны рисования. Если стиль WS_CLIPSIBLINGS не указан, а дочерние окна перекрываются, при рисовании в области пользователя дочернего окна возможен заход в область пользователя соседнего дочернего окна.WS_DISABLED000010000000000000000000000000008000000Создает окно, доступ к которому запрещен (окно, которое не может получать ввод от пользователя).WS_VISIBLE0001.0000.0000.0000.0000.0000.0000.000010000000Creates a window that is initially visibleWS_MINIMIZE, WS_ICONIC0010.0000.0000.0000.0000.0000.0000.000020000000Creates a window that is initially minimizedWS_CHILD, WS_CHILDWINDOW0100.0000.0000.0000.0000.0000.0000.000040000000Creates a child window. This style cannot be used with the WS_POPUP style.Excludes the area occupied by child windows when drawing occurs within the parent window. This style is used when creating the parent window.WS_POPUP1000.0000.0000.0000.0000.0000.0000.000080000000Creates a pop-up window. This style cannot be used with the WS_CHILD styleWS_POPUPWINDOW1000000010001000000000000000000080880000Создает всплывающее окно с обрамлением и системным меню (комбинация стилей WS_BORDER, WS_POPUP и WS_SYSMENU). Чтобы меню окна было видимым, необходимо комбинировать данный стиль со стилем WS_CAPTION Расширенные стили окна можно задать на одноименной вкладке диалогового окна «Параметры функции CreateWindowEx» Этот параметр может принимать значения, указанные на вкладке «Расширенные стили». Дополнительные сведения для некоторых из них приведены в таблице 3. СтильЗначениеОписаниеbinhexWS_EX_LEFT00000000000000000000000000000Окно имеет общий стиль «левостороннего» (по умолчанию) выравниванияWS_EX_LTRREADING00000000000000000000000000000Текст окна отображается слева направо (по умолчанию)WS_EX_RIGHTSCROLLBAR00000000000000000000000000000Вертикальная полоса прокрутки (при ее наличии) размещается в правой (по умолчанию) части рабочей области окна соответственноWS_EX_DLGMODALFRAME00000000000000000000000000011Создает окно, которое имеет двойную рамку; окно может быть создано (необязательно) со строкой заголовка, которую определяет стиль WS_CAPTION в параметре dwStyle―00000000000000000000000000102―WS_EX_NOPARENTNOTIFY00000000000000000000000001004Определяет, что дочернее окно, созданное с этим стилем не посылает сообщение WM_PARENTNOTIFY родительскому окну, когда оно создается или разрушаетсяWS_EX_TOPMOST00000000000000000000000010008Окно, созданное с этим стилем, остается поверх всех других окон без данного стиля, даже если фокус ввода принадлежит другому окну Определяет, что окно, созданное с этим стилем должно быть размещено выше всех, не самых верхних окон и должно стоять выше их, даже тогда, когда окно дезактивировано. Чтобы добавить или удалить этот стиль, используйте функцию SetWindowPos The window should be placed above all non-topmost windows and should stay above them, even when the window is deactivated. To add or remove this style, use the SetWindowPos functionWS_EX_ACCEPTFILES000000000000000000000001000010Определяет, что окно, созданное с этим стилем принимает файлы при помощи информационной технологии «перетащи и вставь»WS_EX_TRANSPARENT000000000000000000000010000020Specifies that a window created with this style is to be transparent. That is, any windows that are beneath the window are not obscured by the window. A window created with this style receives WM_PAINT messages only after all sibling windows beneath it have been update Определяет, что окно, созданное с этим стилем должно быть прозрачным. То есть любые окна, которые появляются из-под окна, не затеняются им. Окно, созданное с этим стилем принимает WM_PAINT сообщения только после того, как все сестринские окна под ним модифицировались The window should not be painted until siblings beneath the window (that were created by the same thread) have been painted. The window appears transparent because the bits of underlying sibling windows have already been painted. To achieve transparency without these restrictions, use the SetWindowRgn functionWS_EX_MDICHILD000000000000000000000100000040Создает дочернее MDI-окноWS_EX_TOOLWINDOW000000000000000000001000000080Creates a tool window; that is, a window intended to be used as a floating toolbar. A tool window has a title bar that is shorter than a normal title bar, and the window title is drawn using a smaller font. A tool window does not appear in the task bar or in the window that appears when the user presses ALT+TAB Создает окно инструментальных средств; то есть окно предполагается использовать как плавающую инструментальную панель. Окно инструментальных средств имеет строку заголовка, которая является короче, чем нормальная строка заголовка, а заголовок окна выводится, с использованием меньшего шрифта. Окно инструментальных средств не появляется в панели задач или в диалоговом окне, которое появляется, когда пользователь нажимает ALT+TAB The window is intended to be used as a floating toolbar. A tool window has a title bar that is shorter than a normal title bar, and the window title is drawn using a smaller font. A tool window does not appear in the taskbar or in the dialog that appears when the user presses ALT+TAB. If a tool window has a system menu, its icon is not displayed on the title bar. However, you can display the system menu by right-clicking or by typing ALT+SPACEWS_EX_WINDOWEDGE0000000000000000000100000000100Определяет, что окно имеет рамку с выпуклым краемWS_EX_PALETTEWINDOW0000000000000000000110001000188Объединяет стили WS_EX_WINDOWEDGE, WS_EX_TOOLWINDOW и WS_EX_TOPMOST The window is palette window, which is a modeless dialog box that presents an array of commandsWS_EX_CLIENTEDGE0000000000000000001000000000200Определяет, что окно имеет рамку с углубленным краемWS_EX_OVERLAPPEDWINDOW0000000000000000001100000000300Объединяет стили WS_EX_WINDOWEDGE и WS_EX_CLIENTEDGE The window is an overlapped windowWS_EX_CONTEXTHELP0000000000000000010000000000400Включает вопросительный знак в строку заголовка окна. Когда пользователь щелкает мышью по вопросительному знаку, курсор меняется на вопросительный знак с указателем. Если пользователь затем щелкает мышью по дочернему окну, потомок принимает сообщение WM_HELP. Дочернее окно должно передать сообщение родительской оконной процедуре, которая должна вызваться функцией WinHelp, использующей команду HELP_WM_HELP. Прикладная программа Справки показывает выскакивающее окно, которое обычно содержит справку для дочернего окна. WS_EX_CONTEXTHELP не может использоваться со стилями WS_MAXIMIZEBOX или WS_MINIMIZEBOX―0000000000000000100000000000800―WS_EX_RIGHT00000000000000010000000000001000Окно имеет общий стиль «правостороннего» выравнивания. «Правосторонний» стиль действует лишь, когда установлен язык с обратным порядком чтения (справа налево) Using the WS_EX_RIGHT style for static or edit controls has the same effect as using the SS_RIGHT or ES_RIGHT style, respectively. Using this style with button controls has the same effect as using BS_RIGHT and BS_RIGHTBUTTON stylesWS_EX_RTLREADING00000000000000100000000000002000Текст окна отображается справа налево. Стиль действует лишь в случае, если установлен язык с порядком чтения справа налево.WS_EX_LEFTSCROLLBAR00000000000001000000000000004000Вертикальная полоса прокрутки (при ее наличии) размещается в левой части рабочей области окна. Слева полоса прокрутки может размещаться, если установлен язык с порядком чтения справа налево―00000000000010000000000000008000―WS_EX_CONTROLPARENT000000000001000000000000000010000Позволяет пользователю передвигаться среди дочерних окон основного окна, используя клавишу табуляции (TAB) The window itself contains child windows that should take part in dialog box navigation. If this style is specified, the dialog manager recurses into children of this window when performing navigation operations such as handling the TAB key, an arrow key, or a keyboard mnemonicWS_EX_STATICEDGE000000000010000000000000000020000Создает окно с трехмерным стилем рамки, предполагается использовать для элементов, которые не принимают вводимую информацию от пользователяWS_EX_APPWINDOW000000000100000000000000000040000Активизирует окно верхнего уровня на панель задач, когда окно свернутоWS_EX_LAYERED000000001000000000000000000080000The window is a layered window. This style cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC. Windows 8: The WS_EX_LAYERED style is supported for top-level windows and child windows. Previous Windows versions support WS_EX_LAYERED only for top-level windowsWS_EX_NOINHERITLAYOUT0000000100000000000000000000100000The window does not pass its window layout to its child windowsWS_EX_NOREDIRECTIONBITMAP0000001000000000000000000000200000The window does not render to a redirection surface. This is for windows that do not have visible content or that use mechanisms other than surfaces to provide their visualWS_EX_LAYOUTRTL0000010000000000000000000000400000If the shell language is Hebrew, Arabic, or another language that supports reading order alignment, the horizontal origin of the window is on the right edge. Increasing horizontal values advance to the left―0000100000000000000000000000800000――00010000000000000000000000001000000―WS_EX_COMPOSITED00100000000000000000000000002000000Paints all descendants of a window in bottom-to-top painting order using double-buffering. For more information, see Remarks. This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC. Windows 2000: This style is not supported―01000000000000000000000000004000000―WS_EX_NOACTIVATE10000000000000000000000000008000000A top-level window created with this style does not become the foreground window when the user clicks it. The system does not bring this window to the foreground when the user minimizes or closes the foreground window. The window should not be activated through programmatic access or via keyboard navigation by accessible technology, such as Narrator. To activate the window, use the SetActiveWindow or SetForegroundWindow function. The window does not appear on the taskbar by default. To force the window to appear on the taskbar, use the WS_EX_APPWINDOW styleФон окна можно выбрать на одноименном диалоге через Системные цвета GetStockObject CreateSolidBrush Здесь asm- и rc-файлы, иконки и ехе-файл © Mikl___ 2019
Глава сорок восьмая. Братец Кролик и немного БСДМ MSDN'a Типы окон Overlapped окна Overlapped окно ("перекрывающее окно") - это окно верхнего уровня (top-level window), у которого есть заголовок (title bar), рамка (border) и клиентская область (client area); предполагается, что окно этого типа будет использоваться в качестве главного окна приложения. У него также могут быть системное меню, кнопки максимизации и минимизации и полосы прокрутки. Overlapped окно обычно включает в себя все эти компоненты, когда используется в качестве главного окна приложения. Приложение может создать overlapped окно, указывая стиль WS_OVERLAPPED или WS_OVERLAPPEDWINDOW в функции CreateWindowEx. Если вы использовали стиль WS_OVERLAPPED, то окно будет также иметь заголовок и рамку. Если вы использовали стиль WS_OVERLAPPEDWINDOW, то окно будет иметь заголовок, изменяющую размеры рамку, системное меню и кнопки минимизации и максимизации. флаг WS_OVERLAPPED равен нулю, поэтому если вы не укажете иной стиль, то окно по умолчанию будет иметь стиль WS_OVERLAPPED. Стиль WS_OVERLAPPEDWINDOW - это не самостоятельный стиль, он является простой комбинацией стилей WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX и WS_MAXIMIZEBOX. Pop-up окна Pop-up окно ("всплывающее окно") - это разновидность overlapped окна, используемая для диалоговых окон, окон-сообщений и других аналогичных временных окон, которые появляются вне главного окна приложения. Для них заголовок не обязателен; в противном случае pop-up окна не отличаются от overlapped окон со стилем WS_OVERLAPPED. Вы можете создать pop-up окно, указывая стиль WS_POPUP в функции CreateWindowEx. Чтобы окно имело заголовок, вам нужно отдельно указать стиль WS_CAPTION. Для создания pop-up окна с рамкой и системным меню используйте стиль WS_POPUPWINDOW. Чтобы системное меню можно было увидеть, вам также нужно включить стиль WS_CAPTION вместе со стилем WS_POPUPWINDOW. стиль WS_POPUPWINDOW - это не самостоятельный стиль, он является простой комбинацией стилей WS_POPUP, WS_BORDER и WS_SYSMENU. Child окна Child окно ("дочернее окно") - имеет установленный стиль WS_CHILD и ограничивается клиентской областью его родительского окна (parent window). Как правило, child окна используются приложением, чтобы разделить клиентскую область окна по функциональным зонам. Вы можете создать child окно, указывая стиль WS_CHILD в функции CreateWindowEx. У child окна обязательно должно быть родительское окно. Родительское окно может быть overlapped окном, pop-up окном или даже другим child окном. Вы указываете его во время вызова функции CreateWindowEx в параметре hWndParent. Если же вы укажете стиль WS_CHILD в CreateWindowEx, но не укажете родительское окно, то система откажется создавать окно. Child окно имеет клиентскую область, но больше никаких других дополнительных возможностей, если только вы их явно не запросите. Вы можете добавить заголовок, системное меню, кнопки минимизации и максимизации, рамку и полосы прокрутки, но child окно не может иметь меню. Если вы укажете меню при создании child окна - оно будет проигнорировано. Если вы не укажете тип рамки, то система создаст окно вообще без рамки (borderless window). Приложение может использовать такие окна для разделения клиентской области родительского окна на части без видимого указания разделения пользователю. Примером child окон могут служить фреймы (TFrame), панели (TPanel) и вообще практически любые другие оконные компоненты - например, кнопка (TButton). Тут нужно помнить, что "окно" в смысле пользователя - это "форма" в Delphi. Но термин "окно" в смысле системы - это вовсе не обязательно "окно" в смысле пользователя, это вообще некоторый элемент управления. Все элементы управления в Windows являются окнами в смысле системы. Кнопки, списки, строки ввода, полосы прокрутки, панели и тулбары - всё это "окна". Позиционирование Система всегда размещает child окно, используя относительные координаты - относительно левого-верхнего угла клиентской области родительского окна. Child окно не может выступать за границы своего родительского окна. Если приложение создаёт child окно больше размеров своего родительского окна, либо же размещает его так, что какая-то его часть выходит за границы родителя, то система обрезает child окно так, что часть child окна вне его родительского окна будет просто невидима. Кроме того, действия с родительским окном также будут иметь эффект на все его child окна, как указано в следующей таблице: Parent окноChild окноУничтожаетсяУничтожается непосредственно перед удалением своего родительского окна.СкрываетсяСкрывается непосредственно перед скрытием своего родительского окна. Всякое child окно видимо только если его родительское окно тоже видимо.ПеремещаетсяПеремещается вместе с клиентской областью своего родителя так, что его относительное положение сохраняется. Child окно ответственно за перерисовку своей клиентской области после перемещения.ПоказываетсяПоказывается сразу после показа своего родителяОбрезание По умолчанию система не обрезает child окно, если оно размещено вне клиентской области своего родителя (если клиентская область родителя меньше размеров самого родительского окна, поскольку система всегда обрезает child окно, вылезающее за размеры своего родителя, как указано выше). Это означает, что родительское окно будет рисовать поверх child окна, если оно производит любую операцию рисования в той же области, что и child окно. Однако систему можно попросить обрезать child окно по клиентской области своего родителя, указав стиль WS_CLIPCHILDREN родительскому окну (не дочернему). Если child окно обрезается, то родительское окно не будет рисовать поверх его. Child окно может пересекать другие child окна в той же клиентской области. Child окна, которые разделяют одного родителя, называются sibling окнами ("братские окна"). Пересекающиеся sibling окна могут рисовать в клиентской области друг друга, если только одно из child окон не имеет стиль WS_CLIPSIBLINGS. Если child окно указывает этот стиль, то любая часть любого другого sibling окна, лежащая в рамках этого child окна, будет обрезаться. Установка стилей WS_CLIPCHILDREN или WS_CLIPSIBLINGS приводит к небольшому падению производительности. Каждое окно занимает системные ресурсы, так что приложение не должно использовать дочерние окна без разбора. Для достижения оптимальной производительности приложения, которые должны логически разделять своё главное окно, должны делать это в оконной процедуре главного окна, а не с помощью дочерних окон. Отношение с родительским окном Приложение может изменить родительское окно уже созданного child окна вызовом функции SetParent. В этом случае система удаляет child окно из клиентской области старого родительского окна и размещает его в клиентской области нового родительского окна. Если в SetParent указать 0 в качестве родителя, то новым родительским окном станет рабочий стол (desktop window). В этом случае child окно рисуется на рабочем столе, вне границ любого другого окна. Функция GetParent возвращает описатель родительского окна child окна. Родительское окно уступает часть своей клиентской области child окну, и child окно получает весь ввод (input) с этой области. Родительское и child окно не обязаны иметь один и тот же оконный класс (window class). Это означает, что приложение может заполнить родительское окна child окнами, которые выглядят по-разному и выполняют разные задачи. Например, диалоговое окно может содержать несколько типов элементов управления, каждый из которых является child окном, которое принимает различные типы данных от пользователя. Child окно имеет одно и только одно родительское окно, но родительское окно может иметь сколько угодно child окон. Каждое child окно, в свою очередь, тоже может иметь сколько угодно child окон. В такой цепочке окон каждое child окно называется descendant окном (окном-потомком) исходного родительского окна. Приложение может использовать функцию IsChild, чтобы определить, является ли заданное окно child окном или descendant окном другого окна. Функция EnumChildWindows перечисляет все дочерние окна родительского окна. Функция EnumChildWindows передаёт описатель каждого найденного child окна в функцию обратного вызова приложения. Функция работает рекурсивно, поэтому она также перечисляет все descendant окна заданного родительского окна. Сообщения Система передаёт все сообщения ввода для child окна самому child окну напрямую, минуя окно родителя. Исключением для этого правила является случай, если дочернее окно было отключено вызовом функции EnableWindow. В этом случае система передаёт сообщения, предназначенные child окну, его родителю. Это позволяет родительскому окну проверить сообщения ввода и при необходимости включить child окно. Child окно также может иметь уникальный числовой идентификатор. Идентификаторы child окон важны, если вы работаете с сообщениями. Приложение управляет элементами управления, отправляя им сообщения. Приложение может использовать идентификатор child окна для отправки в него сообщений. Кроме того, если элемент управления шлёт сообщения-уведомления своему родителю, то эти сообщения будут включать в себя идентификатор child-окна, что позволит родителю идентифицировать отправителя сообщения. Приложение указывает идентификатор child окна установкой параметра hMenu функции CreateWindowEx в число - значение идентификатора (а не описатель меню). Layered окна Использование layered окна ("окна со слоями") может здорово улучшить производительность и визуальные эффекты для окон, которые имеют сложную форму, анимацию или применяют альфа-каналы. Система автоматически производит композицию и перерисовку layered окон и окон под ним. В результате layered окна рисуются гладко, без эффектов мерцания на сложных регионах. Кроме того, layered окна могут быть сделаны частично прозрачными. Чтобы создать layered окно, вам нужно указать флаг WS_EX_LAYERED расширенного стиля окна при вызове функции CreateWindowEx или вызвать функцию SetWindowLong уже после создания окна. После вызова CreateWindowEx layered окно не будет видимо до тех пор, пока вы не вызовите для него функцию SetLayeredWindowAttributes или функцию UpdateLayeredWindow. Начиная с Windows 8 WS_EX_LAYERED может использоваться как для окон верхнего уровня, так и для дочерних окон (child окон). Предыдущие версии Windows поддерживают стиль WS_EX_LAYERED только для окон верхнего уровня. Чтобы установить степень непрозрачности (opacity level) или цвет-маску (transparency color key) для заданного layered окна - вызовите функцию SetLayeredWindowAttributes. После этого вызова система всё ещё может запросить окно нарисовать себя при показе или перемещении. Однако, поскольку система запоминает растровое содержимое layered окна, то она не будет просить окно перерисовать себя при частичном перекрытии окна другими окнами или при его перемещении по рабочему столу. При этом устаревшим приложениям не нужно переделывать их код рисования, если они хотят добавить прозрачность или эффекты - потому что система перенаправляет рисование на окнах, которые вызвали SetLayeredWindowAttributes, на внеэкранный растр. Чтобы реализовать более эффективную анимацию или если вам нужно попиксельное альфа-смешение цветов - вы можете вызвать UpdateLayeredWindow. UpdateLayeredWindow следует использовать, когда приложение хочет работать напрямую с формой и содержимым layered окна, минуя внеэкранный буфер, который система предоставляет через вызов SetLayeredWindowAttributes. Кроме того, использование UpdateLayeredWindow более эффективно, потому что системе не нужно выделять дополнительную память для хранения изображения перенаправленного окна. Обратите внимание, что если вы вызвали SetLayeredWindowAttributes, то все последующие вызовы UpdateLayeredWindow завершатся с ошибкой - до тех пор, пока вы не переустановите (сбросите и заново установите) флаг WS_EX_LAYERED. Тестирование на попадание мышью (hit testing) для layered окна основывается на форме и прозрачности окна. Это означает, что зоны окна, которые раскрашены цветовым ключом или же их альфа-канал равен нулю, пропустят щелчки мыши мимо себя. Однако если в окне дополнительно установлен флаг WS_EX_TRANSPARENT, то всё окно целиком будет пропускать щелчки мыши сквозь себя. Message-Only окна Message-only окно ("окно для сообщений") позволяет вам принимать и отправлять оконные сообщения. Оно не видимо, не имеет Z-порядка, не появляется в перечислениях (enum) и не принимает широковещательные сообщения. Это окно просто диспетчеризирует сообщения. Чтобы создать message-only окно, укажите константу HWND_MESSAGE или описатель на другое message-only окно в параметре hWndParent функции CreateWindowEx. Вы также можете сконвертировать любое существующее окно в message-only окно, указав HWND_MESSAGE в качестве параметра hWndNewParent функции SetParent. Чтобы найти message-only окна - укажите HWND_MESSAGE в параметре hwndParent функции FindWindowEx. Кроме того, функция FindWindowEx будет искать message-only окна наравне с окнами верхнего уровня, если оба параметра hwndParent и hwndChildAfter будут равны нулю. Отношения между окнами Существует много способов, которыми одно окно может относится к пользователю или другому окну. Окно может быть владеемым, окном переднего или заднего плана, а также у него может быть задан Z-порядок по отношению к другим окнам: Окна первого и заднего плана Каждый процесс может иметь несколько потоков для выполнения, и каждый поток может создавать окна. Поток, который создал окно, с которым сейчас работает пользователь, называется потоком первого плана (foreground thread), а окно называется foreground окном (окном первого плана). Все прочие потоки называются потоками заднего плана (background thread), а все прочие окна - окнами заднего плана (background окнами). Каждый поток имеет уровень приоритета, который определяет количество времени процессора, получаемое этим потоком. Хотя приложение может менять уровень приоритета своих потоков, обычно поток первого плана получает небольшую прибавку приоритета по отношению к потокам заднего плана. Поэтому поток первого плана будет получать больше процессорного времени, чем потоки заднего плана. Обычно поток первого плана имеет приоритет 9, в то время как обычное значение приоритета потока заднего плана - 7. Пользователь задаёт окно переднего плана, щёлкая по окну, либо используя комбинации клавиш ALT+TAB или ALT+ESC. Чтобы получить описатель окна переднего плана - используйте функцию GetForegroundWindow. Приложение может установить окно переднего плана, используя функцию SetForegroundWindow. Система ограничивает процессы, которым разрешено менять окно переднего плана. Процесс сможет сделать это только если верны условия ниже: процесс является процессом первого плана (т.е. окно первого плана принадлежит ему). процесс был запущен процессом переднего плана. процесс получил последнее сообщение ввода. в системе нет процесса переднего плана. процесс первого плана сейчас отлаживается. смена окна первого плана не заблокирована (функцией LockSetForegroundWindow). истёк таймаут блокировки окна (параметр SPI_GETFOREGROUNDLOCKTIMEOUT в SystemParametersInfo). нет активных меню. Процесс, которому разрешено менять окно первого плана, может передать это право другому процессу вызовом функции AllowSetForegroundWindow или функции BroadcastSystemMessage с флагом BSF_ALLOWSFW. Процесс первого плана также может отключить вызовы SetForegroundWindow, вызвав функцию LockSetForegroundWindow . Owned окна Overlapped или pop-up окно может владеть другим overlapped или pop-up окном. Отношение "владелец - owned окно" накладывают ограничения на последнее: Owned окно всегда находится поверх своего владельца в Z-порядке. Система автоматически уничтожает owned окна при уничтожении их владельца. Owned окно скрывается при минимизации своего владельца. Владельцем может быть только overlapped или pop-up окно; child окна владельцами быть не могут. Приложение может создать owned окно указанием описателя окна-владельца в параметре hwndParent функции CreateWindowEx, когда она создаёт окно со стилями WS_OVERLAPPED или WS_POPUP. Параметр hwndParent должен идентифицировать overlapped или pop-up окно. После создания owned окна приложение не может передать отношение владения другому окну. Диалоговые окна и окна-сообщения (message box) являются owned окнами. Приложение указывает окно-владельца при вызове функции, создающей диалог или окно-сообщение. Приложение может использовать функцию GetWindow с флагом GW_OWNER, чтобы получить описатель окна-владельца. Z-порядок Z-order (Z-порядок) окна указывает положение окна в стопке overlapped окон. Эта стопка окон ориентируется вдоль воображаемой оси (оси Z), идущей от экрана. Окно на вершине Z оси (Z-порядка) перекрывает все прочие окна. Окно на дне Z-порядка может быть перекрыто любым другим окном. Система хранит Z-порядок в едином списке. Она добавляет окна в список в зависимости от их типа: topmost окна, top-level окна и child окна. Окно "поверх всех" (topmost окно) перекрывает все прочие окна (не topmost окна), вне зависимости от того, является ли topmost окно активным. У topmost окон устанавливается стиль WS_EX_TOPMOST. Все topmost окна всегда располагаются в списке Z-порядка перед любыми другими не topmost окнами. Child окна группируются вместе с их родителями. Когда приложение создаёт окно, система помещает окно в список Z-order - на вершину цепочки окон того же типа. Вы можете использовать функцию BringWindowToTop, чтобы переместить окно на вершину списка Z-order (но только в рамках окон того же типа). Вы можете переупорядочивать окна, используя функции SetWindowPos или DeferWindowPos. Пользователь может изменить Z-порядок активируя различные окна. Система перемещает активное окно на вершину Z-порядка для окон того же типа. Child окна перемещаются на вершину вместе со своим родителем. Вы можете использовать функцию GetTopWindow для поиска всех child окон родительского окна и получения описателя child окна на вершине Z-порядка. Функция GetNextWindow возвращает описатель следующего или предыдущего окна в Z-порядке. Состояния отображения окон В любой момент времени произвольное окно может быть активным или не активным; скрытым или видимым; и либо минимизированным, либо максимизированным, либо развёрнутым. Все эти характеристики называются window show state (состояния отображения окон). Они обсуждаются ниже: Активное окно Отключенные окна Видимость окон Минимизированные, максимизированные и развёрнутые окна Активное окно Активное окно (active window) - это окно верхнего уровня (top-level) приложения, с которым сейчас работает пользователь. Чтобы пользователь мог опознать активное окно, система размещает активное окно на вершине Z-порядка, а также изменяет вид заголовка (выделяя его) и рамки окна. Активным окном может быть только окно верхнего уровня. Если пользователь работает с child окном, то система активирует окно верхнего уровня, ассоциированное с текущим child окном. В любой момент времени в системе может быть только одно активное окно. Пользователь может активировать окно (верхнего уровня) просто щёлкая по нему (или по одному из его child окон), либо используя комбинации клавиш для переключения окон (например, ALT+ESC или ALT+TAB). Приложение может сделать окно (верхнего уровня) активным, вызывая функцию SetActiveWindow. Активация окна также может происходить при использовании других функций, включая SetWindowPos, DeferWindowPos, SetWindowPlacement и DestroyWindow. Хотя приложение может менять активное окно в любое время, но чтобы не смущать пользователя, лучше всего это делать в ответ на действия пользователя. Приложение может узнать текущее активное окно вызовом функции GetActiveWindow. Когда активное окно изменяется с окна одного приложения на окно другого приложения, система посылает сообщение WM_ACTIVATEAPP обоим приложениям, уведомляя их об изменении активного окна. Если же активное окно изменяется с одного окна приложения на другое окно этого же приложения, то система посылает сообщение WM_ACTIVATE обоим окнам этого приложения. Отключенные окна Окно может быть отключено. Отключенное окно (disabled window) не может получать ввод пользователя (сообщения клавиатуры и мыши), но может получать сообщения от других окон, приложений и системы. Обычно приложение отключает окно для предотвращения взаимодействия с ним пользователя. К примеру, приложение может отключить кнопку в диалоге, чтобы не дать пользователю на неё нажать. Приложение может включить (enable) отключенное окно в любое время; включение окна восстановит возможность ввода. По умолчанию окно создаётся в доступном состоянии. Но приложение может указать стиль WS_DISABLED для создания окна в отключенном состоянии. Приложение может включить или отключить окно вызовом функции EnableWindow. Непосредственно перед изменением состояния окна система отправляет ему сообщение WM_ENABLE. Приложение может узнать статус окна вызовом функции IsWindowEnabled. Когда child окно отключается, система передаёт сообщения мыши от child окна его родителю. Родительское окно может использовать эти сообщения для определения того, не нужно ли включить child окно. В каждый момент времени только одно окно в системе может принимать клавиатурный ввод; говорят, что такое окно имеет фокус (keyboard focus). Если приложение использует функцию EnableWindow для отключения окна, которое имеет фокус ввода, то, кроме собственно отключения, окно также потеряет фокус ввода. Функция EnableWindow установит клавиатурный фокус в 0, что означает, что в системе нет окна с фокусом ввода. Аналогично, если фокус имеет child окно или descendant окно, а родитель отключается, то descendant окно теряет фокус ввода. Видимость окон Окно может быть либо видимым, либо невидимым (скрытым). Система показывает на экране только видимые окна (visible window). Все скрытые окна (hidden window) не отображаются (не рисуются). Если окно видимо, то пользователь может взаимодействовать с окном (ввод и чтение содержимого). Если окно скрыто, то оно отключается. Скрытое окно может получать сообщения от других окон, приложений или системы, но оно не может получать ввод от пользователя или отображать себя на экране (рисовать). Приложение указывает видимость окон на экране при их создании. Приложение также может менять видимость окон после создания. Окно видимо, если у него установлен стиль WS_VISIBLE. По умолчанию функция CreateWindowEx создаёт скрытое окно, если только приложение явно не укажет стиль WS_VISIBLE. Обычно приложение создаёт окна скрытыми, а затем показывает их - после настройки окна, чтобы пользователь не видел процесс создания окна. Если при вызове функции CreateWindowEx указан стиль WS_VISIBLE, то система отправляет окну сообщение WM_SHOWWINDOW после его создания, но до отображения на экране. Приложение может определить, видимо ли окно, используя функцию IsWindowVisible. Приложение может изменить видимость окон вызовом функций ShowWindow, SetWindowPos, DeferWindowPos, SetWindowPlacement или SetWindowLong. Любая из этих функций может скрыть или показать окно - удалением или добавлением стиля WS_VISIBLE. Они также отправят окну сообщение WM_SHOWWINDOW непосредственно перед его показом или скрытием. Когда окно-владелец (owner) минимизируется, система автоматически скрывает ассоциированные с ним owned окна. Аналогично, когда owner окно восстанавливается, система автоматически показывает ассоциированные owned окна. В обоих случаях система посылает owned окнам сообщения WM_SHOWWINDOW. Иногда приложению может понадобится скрыть owned окна не минимизируя владельца. В этом случае приложение может использовать функцию ShowOwnedPopups. Эта функция устанавливает или удаляет стиль WS_VISIBLE для всех owned окон, а также рассылает им сообщения WM_SHOWWINDOW. Учтите, что скрытие окна-владельца не влияет на видимость ассоциированных с ним owned окон. Когда родительское окно видимо, то его child окна также видимы. Если родительское окно скрывается, то дочерние окна также скрываются. Минимизация родительского окна не влияет на видимость дочерних окон; то есть child окна исчезают с экрана вместе с родителем, но стиль WS_VISIBLE для них не удаляется. Даже если у окна есть стиль WS_VISIBLE, пользователь может не видеть окна на экране; окно может быть перекрыто другими окнами или быть расположено за границами экрана. Кроме того, видимое child окно может обрезаться, как того диктуют отношения с родительским окном. И если родительское окно не видимо, то child окно также не будет видимо. Если родительское окно будет расположено за границами экрана, то его child окна также будут расположены за границами экрана, потому что child окно перемещается вместе с родительским окном, ограничен им и располагается в относительных координатах. Минимизированные, максимизированные и развёрнутые окна Максимизированное окно (maximized window) - это окно, у которого установлен стиль WS_MAXIMIZE. По умолчанию система увеличивает максимизированное окно так, чтобы оно заполняло экран или, в случае child окна, клиентскую область родительского окна. Хотя вы также можете установить размеры и положение окна так, чтобы они совпали с положением максимизированного окна, эти состояния отличаются. Система отключает возможность перемещения окна и изменения его размера для максимизированных окон. Кроме того, кнопка максимизации изменяется на кнопку восстановления размера. Минимизированное окно (minimized window) - это окно, у которого установлен стиль WS_MINIMIZE. По умолчанию система уменьшает окно до размеров его кнопки на панели задач и перемещает окно на панель задач. Развёрнутое окно (restored window) - это окно, которое было восстановлено в исходные размер и положение после минимизации или максимизации. Если приложение укажет стили WS_MAXIMIZE или WS_MINIMIZE в функции CreateWindowEx, окно создастся изначально максимизированным или минимизированным. После создания окна приложение может использовать функцию CloseWindow для минимизации окна. Функция ArrangeIconicWindows упорядочивает свёрнутые окна на рабочем столе или в родительском окне. Функция OpenIcon восстанавливает минимизированное окно. Функция ShowWindow может минимизировать, максимизировать и восстанавливать окна. Она также может менять видимость окон и задавать активное окно. Функция SetWindowPlacement содержит ту же функциональность, что и ShowWindow, но она также может менять размещение окна по умолчанию в положениях минимизации, максимизации и восстановления.
Функции IsZoomed и IsIconic определяют, является ли заданное окно максимизированным или минимизированным соответственно. Функция GetWindowPlacement может получить положения минимизации, максимизации и восстановления для окна, а также определить состояние отображения окна. Когда система получает команду на максимизацию или восстановление минимизированного окна, она отправляет окну сообщение WM_QUERYOPEN. Если оконная процедура вернёт 0, то система проигнориует команду максимизации или восстановления. Система автоматически устанавливает размеры и положение максимизированного окна. Чтобы изменить системные умолчания, приложение может либо использовать функцию SetWindowPlacement, либо обрабатывать сообщение WM_GETMINMAXINFO, которое отправляется окну непосредственно перед максимизацией. Сообщение WM_GETMINMAXINFO содержит указатель на запись MINMAXINFO, содержащую значения размера и положения окна, присвоенные системой. Приложение может изменить эти значения. Размер и положение окна Размер и положение окна выражаются как ограничивающий прямоугольник (bounding rectangle), задавая его координаты относительно экрана (для окон верхнего уровня) или родительского окна (для child окон). Приложение указывает размер и положение окна при его создании, но может изменить их в любое время. В этой секции также рассматриваются: Размеры и положение по умолчанию Отслеживание размера Системные команды Функции для размера и положения Сообщения для размера и положения Размеры и положение по умолчанию Приложение может разрешить системе задать начальные положение и размер окна верхнего уровня, указав CW_USEDEFAULT в функции CreateWindowEx. Если приложение установит координаты окна в значение CW_USEDEFAULT, то если приложение не создало других окон верхнего уровня, то система будет размещать окно относительно экрана; в противном случае система будет размещать окно относительно последнего созданного окна верхнего уровня. Если приложение также укажет CW_USEDEFAULT для ширины и высоты окна, то размеры окна будут определяться системой. Если приложение до этого создавало окна верхнего уровня, то система будет определять размер окна по последнему созданному окну верхнего уровня. Указание CW_USEDEFAULT для child или pop-up окна создаст окно с минимальным размером. Отслеживание размера Система хранит максимальный и минимальный размеры окон со стилем WS_THICKFRAME; окно с этим стилем имеет рамку с возможностью изменения размера. Минимальный размер (minimum tracking size) - это наименьший размер окна, который пользователь сможет задать, перетаскивая рамку окна. Аналогично максимальный размер (maximum tracking size) - это наибольший размер окна, который сможет получить пользователь при изменении размеров окна. Минимальные и максимальные размеры окна устанавливаются в системные значения по умолчанию при создании окна. Приложение может узнать эти значения и заместить их на свои при обработке сообщения WM_GETMINMAXINFO. Системные команды Приложение, которое имеет оконное меню ("системное меню"), может изменить размеры окна отправкой системный команд. Системные команды отправляются, когда пользователь выбирает команды из оконного меню. Приложение может эмулировать действия пользователя отправкой сообщения WM_SYSCOMMAND окну. Следующие системные команды влияют на размер и положение окна: КомандаОписаниеSC_CLOSEЗакрывает окно. Эта команда отправляет сообщение WM_CLOSE окну. Дальнейшие действия зависят от окна (обработчика сообщения WM_CLOSE).SC_MAXIMIZEМаксимизирует окно.SC_MINIMIZEМинимизирует окно.SC_MOVEПеремещает окно.SC_RESTOREВосстанавливает минимизированное или максимизированное окно на его исходные положение и размер.SC_SIZEНачинает процедуру изменения размера. Для изменения размера окна можно использовать мышь или стрелки на клавиатуре.Функции для размера и положения После создания окна приложение может установить размер или положение окна вызовом одной из следующих функций: SetWindowPlacement, MoveWindow, SetWindowPos и DeferWindowPos. SetWindowPlacement может установить минимизированный размер, максимизированный размер, обычный размер и положение окна, а также состояние видимости. Функции MoveWindow и SetWindowPos похожи; они обе устанавливают размер или положение одного окна в приложении. Но функция SetWindowPos допускает изменение видимости окна; а MoveWindow - нет. Используя функции BeginDeferWindowPos, DeferWindowPos и EndDeferWindowPos, приложение может одновременно изменить атрибуты нескольких окон сразу - включая положение, размеры, Z-порядок и видимость. Приложение может получить ограничивающий прямоугольник вызовом функции GetWindowRect. GetWindowRect заполняет запись TRect координатами левого-верхнего и правого-нижнего углов окна. Координаты вычисляются относительно левого-верхнего угла экрана - даже для child окон (позиционирование которых осуществляется относительно родительского окна). Функции ScreenToClient или MapWindowPoints могут быть использованы для перевода экранных координат в относительные координаты для child окон. Функция GetClientRect возвращает координаты клиентской области окна. GetClientRect заполняет TRect координатами левого-верхнего и правого-нижнего углов клиентской области окна, но координаты считаются относительно самой клиентской области окна. То есть левый-верхний угол всегда имеет координаты (0, 0), а координаты правого-нижнего угла - это ширина и высота клиентской области окна. Функция CascadeWindows раскладывает каскадом (cascade) окна на рабочем столе или child окна в родительском окне. Функция TileWindows раскладывает окна черепицей (tiles). Сообщения для размера и положения Окну, чьё положение должно измениться, система отправляет сообщение WM_GETMINMAXINFO. К примеру, это сообщение будет отправлено, если пользователь щёлкнул по командам Переместить или Изменить размер из оконного меню или щёлкает по рамке окна или заголовку; также это сообщение будет отправлено, если приложение вызовет функцию SetWindowPos для перемещения или изменения размера окна. Сообщение WM_GETMINMAXINFO передаёт указатель на запись TMinMaxInfo, которая содержит размер и положение максимизированного окна по умолчанию, равно как и минимальные и максимальные размеры окна. Приложение может изменить эти значения, обрабатывая сообщение WM_GETMINMAXINFO. Чтобы получать сообщения WM_GETMINMAXINFO, окно должно иметь стиль WS_THICKFRAME или WS_CAPTION. Окно со стилем WS_THICKFRAME получит это сообщение во время создания окна, а также при изменениях размера и положения. Окну, чьи размеры, положение, Z-порядок или видимость должны измениться, система отправит сообщение WM_WINDOWPOSCHANGING. Это сообщение содержит указатель на запись TWindowsPos, которая указывает новые положения, размер, Z-порядок и видимость окна. Изменяя поля записи TWindowsPos, приложение может изменить способ отображения окна. После того, как изменились размер, положение, Z-порядок или видимость окна, система отправляет окну сообщение WM_WINDOWPOSCHANGED. Это сообщение содержит указатель на запись TWindowPos, которая информирует окно о его новых атрибутах. Изменение этой записи не приведёт к изменению атрибутов окна. Окно, которому нужно обрабатывать сообщения WM_SIZE и WM_MOVE, должно передавать сообщение WM_WINDOWPOSCHANGED в функцию DefWindowProc; иначе система не будет рассылать сообщения WM_SIZE и WM_MOVE. Когда окно создаётся или изменяет размер, система также отправляет окну сообщение WM_NCCALCSIZE. Это сообщение используется системой для вычисления размера и относительного положения клиентской области окна (относительно левого-верхнего угла самого окна). Как правило, окно просто передаёт это сообщение обработчику по умолчанию; однако это сообщение может быть использовано приложениями, которые хотят изменить размеры клиентской области окна. Анимация окон Вы можете создавать специальные эффекты при показе или скрытии окон, используя функцию AnimateWindow. При использовании этой функции вы можете указать эффекты roll, slide, collapse, expand или fade. По умолчанию система использует анимацию roll. При этой анимации окно "раскатывается" от одного края до противоположного. Вы можете указать в флагах направление анимации: горизонтально, вертикально или по диагонали. С флагом AW_SLIDE система использует анимацию slide. При этом окно "выезжает". Анимация похожа на roll анимацию. roll анимация использует сглаженный край окна, имитируя рулон, а slide использует чёткие края, имитируя плоское окно. Аналогично, вы можете указать в флагах направление анимации: горизонтально, вертикально или по диагонали. Флаг AW_BLEND задаёт анимацию alpha-blended fade. При этом окно постепенно "проявляется" или исчезает. У этой анимации нет направления. Вы также можете использовать флаг AW_CENTER для анимации разворота окна из центра окна. Анимация похожа на roll анимацию, но roll анимация показывает окно от границы к границе окна, а center анимация - от центра окна до его границ. У этой анимации нет направления. Компоновка и отражение Компоновка окна (window layout) определяет способ размещения в окне или контексте устройства (DC - Device Context) текста и объектов GDI. Некоторые языки (вроде английского, французского, русского и немецкого) требуют компоновку left-to-right (LTR) - "слева направо". Другие языки (арабский, иврит) требуют компоновки right-to-left (RTL) - "справа налево". Компоновка окна применяется к тексту, но она также влияет и на другие элементы окна, включая растры, значки, расположение кнопок, многоуровневых деревьев, то, будет ли увеличиваться или уменьшаться горизонтальная координата при сдвиге вправо и т.п. К примеру, после того, как приложение установит компоновку RTL, начало координатной сетки в окне будет расположено в правом-верхнем углу окна (или контекста устройства) - в то время как обычно точка отсчёта располагается в левом-верхнем угле; а горизонтальная координата будет увеличиваться при движении влево, а не вправо, как это было с LTR. Однако не все объекты подчиняются компоновке окна. К примеру, компоновки диалоговых окон, окон-сообщений (message box) и контекстов устройств, не ассоциированных с окном (метафайлы, принтеры и другие) - обрабатываются отдельно. Некоторые такие особенности также упомянуты ниже. Оконные функции позволяют вам указать или изменить компоновку окна. Заметьте, что изменение компоновки на RTL (этот процесс также называется зеркалированием или отражением окна - mirroring) не поддерживается окнами, которые установили стиль CS_OWNDC, а также контекстами устройств в режиме GM_ADVANCED. По умолчанию окно и контексты устройств имеют компоновку left-to-right (LTR). Чтобы изменить компоновку окна на RTL - вызовите функцию CreateWindowEx с флагом WS_EX_LAYOUTRTL. Также, по умолчанию, child окна (т.е. окна, создаваемые со стилем WS_CHILD) будут иметь ту же компоновку, что и их родитель. Чтобы отключить наследование зеркалирования для child окон и задавать компоновку явно - добавьте стиль WS_EX_NOINHERITLAYOUT в вызове CreateWindowEx. Заметьте, что компоновка не наследуется owned окнами (создаваемыми без стиля WS_CHILD) или окнами, чей параметр hWnd в функции CreateWindowEx был установлен в 0. Чтобы отключить наследование зеркалирования в отдельном окне, обработайте сообщение WM_NCCREATE, где используйте функции GetWindowLong и SetWindowLong для замены флага WS_EX_LAYOUTRTL. Вы также можете изменить умолчание для всего процесса вызовом функции SetProcessDefaultLayout. Все окна, создаваемые после вызова SetProcessDefaultLayout (LAYOUT_RTL), будут создаваться зеркалированными (без указания флага), но уже существующие окна не изменятся. Чтобы отключить зеркалирование по умолчанию - используйте SetProcessDefaultLayout(0). Заметьте, что функция SetProcessDefaultLayout отразит контексты устройств только в отзеркалированных окнах. Чтобы отразить произвольный DC - вызовите функцию SetLayout. Растры (bitmap) и значки (icon) в отражённом окне также по умолчанию зеркалируются. Однако это не всегда бывает необходимо. Чтобы отключить зеркалирование растра, вызовите функцию SetLayout с установленным флагом LAYOUT_BITMAPORIENTATIONPRESERVED в dwLayout. Чтобы отключить зеркалирование в DC, вызовите SetLayout(DC, 0). Чтобы узнать текущую компоновку по умолчанию, вызовите функцию GetProcessDefaultLayout. При успешном вызове pdwDefaultLayout будет содержать LAYOUT_RTL или 0. Чтобы узнать компоновку контекста устройства, вызовите функцию GetLayout. При успешном вызове она вернёт набор флагов, управляющих компоновкой, которые вы можете проверить на LAYOUT_RTL и LAYOUT_BITMAPORIENTATIONPRESERVED. Изменить компоновку после создания окна можно функцией SetWindowLong. К примеру, это может понадобится при смене языка UI в run-time. Однако при этом вы также должны обновить (перерисовать) содержимое окна. При отражении окна вы должны думать в терминах "ближе" и "дальше", а не "левее" и "правее". Иначе у вас могут быть проблемы. Частой ошибкой является сценарий конвертирования координат между экранными и клиентскими. К примеру, приложение может использовать такой код для расположения элемента управления в окне: Код (Pascal): // КОД НИЖЕ - ОШИБОЧЕН // Получаем координаты окна в экранных координатах GetWindowRect(hControl, rControlRect); // Проецируем координаты экрана в клиентские координаты диалога ScreenToClient(hDialog, rControlRect.left); ScreenToClient(hDialog, rControlRect.right); Этот код вызовет проблемы в отзеркалированных окнах, потому что в них "лево" становится "право" и наоборот. Чтобы избежать этой проблемы, замените вызовы функции ScreenToClient на вызовы MapWindowPoints: Код (Pascal): // Корректный код GetWindowRect(hControl, ControlRect); MapWindowPoints(0, hDialog, rControlRect, 2); Этот код будет работать корректно, потому что функция MapWindowPoints умеет работать с прямоугольниками (ScreenToClient и ClientToScreen работают только с точками), поэтому она может учесть зеркалированность окон и поменять местами левые и правые грани прямоугольника при необходимости. Ещё одна практика, которая может вызвать проблемы в отзеркалированных окнах - позиционирование элементов управления, используя экранные координаты вместо клиентских. К примеру, код ниже использует разницу в экранных коодинатах для размещения элемента управления в диалоговом окне: Код (Pascal): // КОД НИЖЕ - ОШИБОЧЕН var rdDialog: TRect; rcControl: TRect; hControl: HWND; begin GetWindowRect(hDlg, rcDialog); // Получение прямоугольника в экранных координатах GetWindowRect(hControl, rcControl); MoveWindow(hControl, rcControl.left - rcDialog.left, // Получение позиции x в клиентских координатах rcControl.top - rcDialog.top, nWidth, nHeight, False); Этот код будет работать для диалогового окна с компоновкой left-to-right (LTR) и режимом проецирования (mapping mode) элемента управления MM_TEXT, потому что новая позиция по X в клиентских координатах соответствует разнице в левых краях элемента управления и диалога в экранных координатах. Но для отзеркалированного диалога вам нужно использовать MapWindowPoints, как показано ниже: Код (Pascal): // Корректный код var rcDialog: TRect; rcControl: TRect; hControl: HWND; begin .. GetWindowRect(hControl, rcControl); // MapWindowPoints работает корректно в обоих режимах (прямом и зеркальном) MapWindowPoints(0, hDlg, rcControl, 2); // Теперь rcControl - в клиентских координатах MoveWindow(hControl, rcControl.left, rcControl.top, nWidth, nHeight, False); Отражение диалоговых окон и окон-сообщений Диалоговые окна и окна-сообщения (message box) не наследуют компоновку окна, поэтому вам нужно устанавливать компоновку явно. Чтобы отразить окно-сообщени, используйте флаг MB_RTLREADING при вызове функций MessageBox или MessageBoxEx. Чтобы изменить компоновку диалога - укажите стиль WS_EX_LAYOUTRTL в шаблоне диалога. Вкладки свойств (property sheets) являются частным случаем диалогов. Каждая вкладка трактуется как отдельный диалог, поэтому вам нужно включать стиль WS_EX_LAYOUTRTL для каждой вкладки отдельно. Отражение контекстов устройств, не ассоциированных с окном Контексты устройств (DC), которые не ассоциированы с окном, не наследуют компоновку, поэтому вам нужно указывать её явно. Для изменения компоновки контекста устройства используйте функцию SetLayout. Функция SetLayout редко используется с окнами. Как правило, окна получают ассоциированный DC только при обработке сообщения WM_PAINT. Иногда приложение может создавать DC для окна вызовом GetDC. В любом случае, компоновка этих DC устанавливается в соответствии с компоновкой целевого окна. Вызов функции SetLayout не влияет на значения, возвращаемые функциями GetWindowOrgEx, GetWindowExtEx, GetViewportOrgEx и GetViewportExtEx. При RTL компоновке функция GetMapMode вернёт MM_ANISOTROPIC вместо MM_TEXT. Вызов SetMapMode с MM_TEXT будет работать корректно; будет изменено только возвращаемое значение GetMapMode. Аналогично, вызов SetLayout(hdc, LAYOUT_RTL) при режиме MM_TEXT приведёт к смене режима на MM_ANISOTROPIC. Уничтожение окна В целом, приложение должно уничтожать все окна, которые оно создало. Оно может сделать это вызывая функцию DestroyWindow. Когда окно уничтожается, система скрывает окно (если оно было видимо), а затем удаляет все внутренние данные, ассоциированные с окном. Описатель окна (handle) становится недопустимым и не должен более использоваться приложением. Обычно приложения удаляет окна вскоре после их создания. К примеру, приложение удаляет диалоговое окно со всеми дочерними окнами как только оно получит достаточно информации от пользователя. В конечном итоге приложение уничтожит и главное окно (непосредственно перед выходом). Перед уничтожением окна приложение должно удалить любые данные, ассоциированные с окном (если они есть), а также освободить системные ресурсы, выделенные для окна. Если приложение не освободит ресурсы, то они будут освобождены системой при завершении приложения. Уничтожение окна не влияет на оконный класс этого окна. Приложение может создавать окна данного оконного класса и после удаления окна этого класса, равно как и продолжить использование других окон этого класса. Уничтожение окна также уничтожит все его descendant окна (т.е. все child окна, child окна child окон и так далее). Функция DestroyWindow отправляет сообщение WM_DESTROY сначала самому окну, затем всем его child окнам, а затем, аналогично, и descendant окнам. Окно с оконным меню также получит сообщение WM_CLOSE, когда пользователь щёлкнет по Закрыть. Приложение может запросить у пользователя подтверждение закрытия окна в обработчике этого сообщения. Если пользователь согласен с закрытием окна, то приложение может вызвать функцию DestroyWindow для уничтожения окна. Если уничтожаемое окно является активным, то активным окном становится другое окно. Оно же получает фокус ввода. Окно, которое станет активным, определяется по порядку окон в списке ALT+ESC.
Глава сорок девятая. Память Братца Кролика . . . Так-то Братец Кролик был не злой, но память у него была хорошая. И уж если Братца Кролика кто-то обидит ― жди беды . . . (Джоэль Чандлер Харрис "Неопубликованные сказки дядюшки Римуса") Сегментированная память Windows 1.0 была разработана для 16-разрядных микропроцессоров, способных адресовать 1024 Кб = 1 Мб памяти. Верхние 384 Кб памяти резервировались для видеоадаптера и системного BIOS. Для программ и данных оставалось 1024Кб - 384Кб = 640Кб. Чтобы адресовать 1 Мб требуется 20-разрядный адрес (220= 1.048.576), который формировался из двух 16-разрядных значений: сегмента и смещения внутри сегмента. Микропроцессор имел четыре сегментных регистра: кода (CS=Code Segment), данных (DS=Data Segment), стека (SS=Stack Segment) и дополнительный (ES=Extra Segment) сегментный регистр. 20-разрядный физический адрес получался сдвигом содержимого сегментного регистра влево на 4 разряда и добавлением к полученной величине смещения внутри сегмента: Прикладные программы становились более сложными и большими по объему, появлялась необходимость во множестве сегментов для кода и данных. Были определены близкие (near) указатели, имевшие величину 16 бит, и используемые для доступа к сегментам кода и данных по умолчанию, и дальние (far) указатели, которые имели ширину 32 бита, и состоявшие из смещения и сегмента. С помощью этого 32-разрядного адреса нельзя было адресовать память непосредственно. Нельзя было увеличить адрес на 1 без учета логики, обрабатывающей переполнение смещения и установку сегментного адреса. Были определены различные модели программирования: model tiny (один сегмент, совмещающий данные и код), model small (один сегмент для кода, один сегмент для данных), model medium (один сегмент данных, много сегментов кода), model compact (один сегмент кода, много сегментов данных), model large (много сегментов кода и данных), model huge (аналогично large, но со встроенной логикой обработки увеличения адреса). MS DOS не имела достаточно большой поддержки управления памятью. Программы определяли, какое количество памяти имеется в их распоряжении, и использовали ее всю целиком. Windows 1.0 была многозадачной средой, появилась необходимость расширить возможности управления памятью. Множество программ загружаются в память, позднее освобождают ее, память становится фрагментированной. Операционная система должна перемещать блоки памяти, чтобы объединить свободное пространство. Многозадачность без управления памятью существовать не может. Нельзя перемещать блоки кода и данных в памяти безотносительно прикладной программы. Так она будет содержать неправильный адрес. Но если программа использует только смещения, то сегменты могут быть изменены операционной системой. Программы для Windows были ограничены использованием только small и medium моделями памяти с одним 64-килобайтным сегментом для данных. Программы использовали близкие указатели для ссылок на свой сегмент данных; адрес сегмента данных для конкретного процесса устанавливался операционной системой при передаче управления программе. Это позволяло Windows перемещать сегмент данных программы и переустанавливать адрес сегмента. Все дальние вызовы функций, выполняемые программой (включая вызовы функций операционной системы), выполнялись когда сегменты кода, используемые программой, были перемещены в память. Программа, выполнявшаяся в 16-разрядной версии Windows, могла либо выделять память из собственного сегмента данных (локальная память, адресуемой с помощью 16-разрядного указателя смещения), либо за пределами своего сегмента данных (глобальная память адресуемая с помощью 32-разрядных адресов). В обоих случаях функции выделения памяти возвращали описатель блока памяти. Программы должны были фиксировать блок в памяти. Функции фиксации возвращали дальний указатель. После использования памяти следовало снять фиксацию с блока памяти. Это давало Windows возможность перемещать блоки памяти при необходимости. Процесс фиксации и снятия фиксации часто выполнялся в процессе обработки одного сообщения, и часто приводил к многочисленным ошибкам. Описатели памяти ― указатели на таблицу в ядре Windows. Ранние версии Windows содержали сегмент памяти, который содержал главную таблицу описателей памяти. В современной Windows содержатся некоторые части схемы адресации Windows 1.0. Архитектура Windows скрывает это, но это проявляется в структуре и синтаксисе вызовов функций. Проблемы с ближними и дальними адресами в 16-разрядных версиях Windows переносились и на файловый ввод/вывод. Во многих случаях программа получала от диалогового окна имя файла, которое являлось дальним указателем. Но в model small и modal medium моделях памяти WinAPI-функции файлового ввода/вывода ожидали ближнего указателя. Аналогично, программа, которая хранила данные в блоках глобальной памяти, должна была адресовать их с помощью дальних указателей и, как следствие, файловый ввод/вывод в эти блоки был затруднен. Возникала необходимость, чтобы Windows дублировала вызовы всех функций файлового ввода/вывода, написанных для MS DOS. Промежуточные решения Версия Windows 3.0 получила широкое распространение, у Microsoft появилась возможность поддерживать защищенный режим (protected mode) процессора Intel 286 (вместо реального режима), причем без существенных проблем для уже существовавших программ. В защищенном режиме сегментный адрес называется селектором (selector). Он также имеет ширину 16 бит, но внутри 286 процессора он ссылается на 24-разрядный базовый адрес (base address), который складывается затем с 16-разрядным смещением, и таким образом, формируется 24-разрядный физический адрес, с помощью которого можно адресовать до 224 = 16.777.216 байт = 16 Мб памяти: Чтобы обеспечить переход от более ранних версий Windows, описатель, возвращаемый функцией выделения глобальной памяти, был просто селектором. Такое использование защищенного режима отчасти облегчало управление памятью, но истинная цель еще не была достигнута. 32/64-разрядные Windows 32-разрядные Windows используют 32-разрядную адресацию памяти и могут реализовать доступ к 232 = 4.294.967.296 байтам (или 4 Гб) физической памяти. 64-разрядные Windows могут реализовать доступ к 264 = 18.446.744.073.709.551.616 байтам или 16 экзабайтам Несмотря на то, что стало возможным на аппаратном уровне использовать сегментную адресацию памяти, Windows сохраняет неизменными сегментные регистры и использует 32-разрядную плоскую (model flat) адресацию. Это означает, что адреса в приложении Windows хранятся как простые 32-разрядные величины, обеспечивая доступ к 4 Гб памяти. Используемые прикладными программами Windows 32-разрядные адреса для доступа к коду и данным, не являются 32-разрядными физическими адресами, которые микропроцессор использует для адресации физической памяти. Адрес, который используется приложением, называется виртуальным адресом (virtual address). Он преобразуется в физический адрес посредством таблицы страниц (page table). Этот процесс обычно прозрачен для прикладных программ. Программе кажется, что она расположена в 32-разрядном адресном пространстве, и для доступа к ней не требуется никаких особых усилий. Однако, в технической документации по Windows существуют ссылки на виртуальные адреса и таблицы страниц. Физическая память делится на страницы (pages) размером 4096 байт (4 Кб). Каждая страница начинается с адреса, в котором младшие 12 бит нулевые. 8 Мб памяти, содержат 2048 страниц. Операционная система Windows хранит набор таблиц страниц (каждая таблица сама представляет собой страницу) для преобразования виртуального адреса в физический. Каждый процесс, выполняемый в Windows, имеет свою собственную страницу каталога (directory page) таблиц страниц, которая содержит до 1024 32-разрядных дескриптора таблиц страниц. Физический адрес страницы каталога таблиц страниц хранится в регистре CR3. Содержимое этого регистра изменяется при переключении Windows между процессами. Старшие 10 бит виртуального адреса определяют один из 1024 возможных дескрипторов в каталоге таблиц страниц. В свою очередь, старшие 20 бит дескриптора таблицы страниц определяют физический адрес таблицы страниц (младшие 12 бит физического адреса равны нулю). Каждая таблица страниц содержит, в свою очередь, до 1024 32-разрядных дескриптора страниц. Выбор одного из этих дескрипторов определяется содержимым средних 10 битов исходного виртуального адреса. Старшие 20 бит дескриптора страницы определяют физический адрес начала страницы, а младшие 12 бит виртуального адреса определяют физическое смещение в пределах этой страницы. 32-разрядный виртуальный адрес с которым оперирует программа представлен в виде 10-разрядного индекса в таблице каталога таблиц страниц (Directory), 10-разрядного индекса в таблице страниц (Table), 12-разрядного смещения (Offset): Для каждого процесса в регистре CR3 хранятся (r) старшие 20 бит физического адреса таблицы каталога таблиц страниц:rrrr-rrrr-rrrr-rrrr-rrrrНачальный физический адрес каталога таблиц страниц определяется так:rrrr-rrrr-rrrr-rrrr-rrrr-0000-0000-0000каждая страница имеет размер 4 Кб и начинается с адреса, у которого 12 младших бит нулевые. Сначала микропроцессор получает физический адрес:rrrr-rrrr-rrrr-rrrr-rrrr-dddd-dddd-dd00По этому адресу содержится другое 20-разрядное значение (t-table):tttt-tttt-tttt-tttt-ttttсоответствующее начальному физическому адресу таблицы страниц:tttt-tttt-tttt-tttt-tttt-0000-0000-0000Затем, микропроцессор осуществляет доступ по физическому адресу:tttt-tttt-tttt-tttt-tttt-pppp-pppp-pp00Здесь хранится 20-битная величина, являющаяся основой для физического адреса начала страницы памяти (f-page frame):ffff-ffff-ffff-ffff-ffffРезультирующий 32-разрядный физический адрес получается в результате комбинирования основы физического адреса страницы и 12-разрядного смещения виртуального адреса:ffff-ffff-ffff-ffff-ffff-oooo-oooo-ooooЭто и есть результирующий физический адрес. Микропроцессоры имеют внутреннюю кэш-память, в которой могут храниться таблицы страниц. Фактически, преобразование адреса осуществляется очень быстро без каких-либо существенных потерь производительности. Такое двухступенчатое разделение памяти на страницы дает каждому приложению теоретическое ограничение по памяти в 1 миллион страниц по четыре килобайта каждая. Преимущества разделения памяти на страницы. приложения изолированы друг от друга. Никакой процесс не может случайно или преднамеренно использовать адресное пространство другого процесса, так как он не имеет возможности его адресовать без указания соответствующего значения регистра CR3 этого процесса, которое устанавливается только внутри ядра Windows. такой механизм разделения на страницы решает одну из основных проблем в многозадачной среде ― объединение свободной памяти. При более простых схемах адресации в то время, как множество программ выполняются и завершаются, память может стать фрагментированной. В случае, если память сильно фрагментирована, программы не могут выполняться из-за недостатка непрерывной памяти, даже если общего количества свободной памяти вполне достаточно. При использовании разделения на страницы нет необходимости объединять свободную физическую память, поскольку страницы необязательно должны быть расположены последовательно. Все управление памятью производится с помощью манипуляций с таблицами страниц. Потери связаны с самими таблицами страниц и с их 4 Кб размером. в 32-битных дескрипторах страниц существует еще 12 бит, кроме тех, которые используются для адреса страницы. "Бит доступа" (accessed bit) показывает возможность доступа к конкретной странице; "Бит мусора" (dirty bit) показывает, была ли произведена запись в эту страницу. Windows может использовать эти биты для того чтобы определить, можно ли сохранить эту страницу в файле подкачки для освобождения памяти. "Бит присутствия" (present bit) показывает, была ли страница сброшена на диск и должна ли быть подкачена обратно в память. "Бит чтения/записи" показывает, разрешена ли запись в данную страницу памяти. Этот бит обеспечивает защиту кода от "блуждающих" указателей. Например, если включить следующий оператор в программу для Windows: Код (C): *(int*) WinMain = 0; то на экран будет выведено следующее окно сообщения: This program has performed an illegal operation and will be shutdown. ("программа выполнила недопустимую операцию и будет завершена"). Этот бит не препятствует компилированной и загруженной в память программе быть запущенной на выполнение. Управление памятью в 32-разрядных Windows Виртуальные адреса имеют разрядность 32 бита. Для выявления нулевых указателей 0 - 0FFFFh Программа и данные имеют адреса в диапазоне от 0 до 7FFFFFFFh. Сама Windows использует адреса от 80000000h до 0FFFFFFFFh. В этой области располагаются точки входа в динамически подключаемые библиотеки Windows. Управление памятью в 64-разрядных Windows Для выявления нулевых указателей 0 - 0FFFFh Для кода и данных пользовательского режима 10000h- 3FFFFFEFFFFh Закрытый раздел размером 64 Кб 3FFFFFF0000h - 3FFFFFFFFFFh Для кода и данных режима ядра 40000000000h-0FFFFFFFFFFFFFFFFh Общее количество свободной памяти, доступной программе, определяется как количество свободной физической памяти плюс количество свободного места на жестком диске, доступного для свопинга страниц. Как правило, при управлении виртуальной памятью Windows использует алгоритм LRU (least recently used) для определения того, какие страницы будут сброшены на диск. Бит доступа и бит мусора помогают осуществить эту операцию. Страницы кода не должны сбрасываться на диск: поскольку запись в его страницы запрещена, они могут быть просто загружены из файла с расширением .EXE или из динамически подключаемой библиотеки. Иногда вы можете заметить, что происходит обращение к диску во время перемещения мыши из рабочей области одной программы в рабочую область другой программы. Почему это происходит? Windows должна посылать сообщения о передвижении мыши второму приложению. Если программа, выполняющая обработку этого сообщения, не находится в данный момент в памяти, то Windows загружает ее с диска. Если запущено несколько больших приложений одновременно, а в компьютере немного памяти, то вы можете стать свидетелем чрезмерного количества обращений к диску в моменты перехода от одной программы к другой, так как Windows вновь загружает с диска ранее удаленные страницы. Время от времени отдельные программы будут замедляться (или приостанавливаться на какое-то время), пока Windows осуществляет свопинг. Страницы кода могут разделяться между приложениями. Это особенно полезно для динамически подключаемых библиотек. Несколько программ, выполняемых одновременно, могут использовать одни и те же функции Windows, не требуя, чтобы один и тот же код загружался в память несколько раз. Достаточно одной копии. При динамическом выделении памяти каждый выделяемый блок необязательно получает свою собственную страницу. Последовательное выделение небольших объемов памяти производится в одной и той же физической странице с ближайшего 4-разрядного стартового адреса (то есть для выделения одного байта используется 16 бит). Если какой-либо блок расширяется, то он может быть физически перемещен в памяти, если следующий за ним блок памяти занят. Кроме разделения памяти на страницы по 4 Кб физическая память не может стать безнадежно фрагментированной, поскольку дефрагментация заключается только в манипуляциях с таблицами страниц. Однако виртуальная память конкретного приложения может стать фрагментированной, если приложение осуществляет выделение, освобождение, повторное выделение и освобождение слишком большого числа блоков памяти. Ограничения в 2 ГБ обычно достаточно для приложения и его данных. Но возможно, что программа столкнется с нехваткой физической памяти до того, как будет достигнут предел виртуальной памяти. Если вы считаете, что это может случиться с вашей программой, то вам стоит подумать об использовании перемещаемой (moveable) памяти. Выделение памяти WinAPI-функции Вы можете определить в программе указатель (например, на массив целых чисел) следующим образом: Код (ASM): pMem dq ? Указатель pMem ― 64-разрядное неинициализированое число. Вы можете выделить блок памяти, на который будет указывать pMem, следующим образом: Код (ASM): invoke malloc,1024 mov pMem,rax При этом выделяется блок памяти размером 1024 байта, который может хранить 256 32-разрядных целых. Указатель, равный NULL, показывает, что выделение памяти не было успешным. Можно также выделить такой блок памяти, используя следующий вызов: Код (ASM): invoke calloc,256, sizeof DWORD mov pMem,rax Два параметра функции calloc перемножаются и в результате получается 1024 байта. Кроме того, функция calloc производит обнуление блока памяти. Если вам необходимо увеличить размер блока памяти (например, удвоить его), то вы можете вызвать функцию: Код (ASM): invoke realloc,pMem, 2048 mov pMem,rax Указатель является параметром функции, и указатель (возможно, отличающийся по значению от первого, особенно, если блок увеличивается) является возвращаемым значением функции. Этот пример показывает, что Windows может перемещать блок в рамках виртуальной памяти. Например, если вы выделили блок размером 1024 байта, то его виртуальный адрес может быть равен 750100h. Вы можете выделить второй блок памяти и получить виртуальный адрес 750504h. Расширив первый блок памяти до 2048 байт, использовать тот же виртуальный адрес невозможно. В этом случае Windows должна переместить блок в физической памяти на новую страницу. При окончании работы с памятью, вызовите функцию: Код (ASM): invoke free,pMem Фундаментальное выделение памяти в Windows Всё, что вы можете делать с помощью WinAPI-функций, вы можете делать самостоятельно, или используя вызовы функций ядра Windows. Функция Windows для выделения блока памяти для указателя на целые: Код (ASM): invoke GlobalAlloc,uiFlags, dwSize mov pMem,rax Функция имеет два параметра: набор флагов размер выделяемого блока в байтах Функция возвращает виртуальный адрес, который может использоваться в программе для доступа к выделенной памяти. Значение NULL говорит о том, что имеющейся в распоряжении памяти для выделения недостаточно. Для каждой функции, начинающейся со слова Global, существует функция, начинающаяся со слова Local. Два набора функций в Windows идентичны. Два различных слова сохранены для совместимости с предыдущими версиями Windows, где функции Global возвращали дальние указатели, а функции Local ― ближние. Хотя определения параметров немного отличаются, они оба являются 64-разрядными беззнаковыми целыми. Если первый параметр задать нулевым, то это эквивалентно использованию флага GMEM_FIXED (=0). Такой вызов функции GlobalAlloc эквивалентен вызову функции malloc. В ранних версиях Windows присутствие флага GMEM_FIXED приводило к проблемам управления памятью, поскольку Windows не могла перемещать такие блоки в физической памяти. Сейчас флаг GMEM_FIXED вполне допустим, поскольку функция возвращает виртуальный адрес, и операционная система может перемещать блок в физической памяти, внося изменения в таблицу страниц. Можно использовать флаг GMEM_ZEROINIT для обнуления всех байтов выделяемого блока памяти. Флаг GPTR включает в себя флаги GMEM_FIXED и GMEM_ZEROINIT: Код (ASM): GPTR equ GMEM_FIXED or GMEM_ZEROINIT Функция изменения размера блока памяти: Код (ASM): invoke GlobalReAlloc,pMem, dwSize, uiFlags mov pMem,rax Можно использовать флаг GMEM_ZEROINIT для обнуления добавляющихся в блок памяти байтов, если блок расширяется. Функция, возвращающая размер блока памяти: Код (ASM): invoke GlobalSize,pMem mov dwSize,rax Функция освобождения памяти: Код (ASM): invoke GlobalFree,pMem Перемещаемая память В предыдущих версиях Windows наличие флага GMEM_FIXED приводило к проблемам при управлении памятью, поскольку Windows должна была сохранять блок памяти в фиксированном месте физической памяти. Сейчас блок памяти может быть перемещен в физической памяти при сохранении виртуального адреса неизменным. Функция GlobalAlloc, тем не менее, поддерживает флаг GMEM_MOVEABLE и комбинированный флаг для дополнительного обнуления блока памяти: Код (ASM): GHND equ GMEM_MOVEABLE or GMEM_ZEROINIT Флаг GMEM_MOVEABLE позволяет перемещать блок памяти в виртуальной памяти. Это не означает, что блок памяти будет перемещен в физической памяти, но адрес, которым пользуется программа для чтения и записи, может измениться. Если ваша программа выделяет, расширяет, освобождает память, то виртуальная память может сделаться фрагментированной. Может ли ваша программа дойти до предела в 2 ГБ виртуальной памяти до того, как будет достигнут предел 4, 8 или 16 Мб физической памяти? Проблема встает острее при непрерывном использовании компьютера. Программы, выполнение которых происходит в течение нескольких дней, в конце концов могут столкнуться с большей фрагментацией памяти, чем программы, разработанные для выполнения в течение одного-двух часов. Вы можете захотеть использовать перемещаемую память. Как это делается? определим указатель и переменную типа GLOBALHANDLE: Код (ASM): pMem dq ? hGlobal GLOBALHANDLE ? выделим память: Код (ASM): invoke GlobalAlloc,GHND, 1024 mov hGlobal,rax Когда вам необходимо обратиться к памяти, для фиксации блока используйте вызов: Код (ASM): invoke GlobalLock,hGlobal mov pMem,rax Эта функция преобразует описатель памяти в указатель. Пока блок зафиксирован, Windows не изменяет его виртуальный адрес. Когда вы заканчиваете работу с блоком, для снятия фиксации вызовите функцию: Код (ASM): invoke GlobalUnlock,hGlobal Этот вызов дает Windows свободу перемещать блок в виртуальной памяти. Для того чтобы правильно осуществлять этот процесс, вы должны фиксировать и снимать фиксацию блока памяти в ходе обработки одного сообщения. Когда вы хотите освободить память, вызовите функцию GlobalFree с параметром-описателем, а не указателем. Если в данный момент вы не имеете доступа к описателю, то вам необходимо использовать функцию: Код (ASM): invoke GlobalHandle,pMem mov hGlobal,rax Вы можете фиксировать блок памяти несколько раз до того, как снять с него фиксацию. Windows запоминает количество фиксаций, и каждое фиксирование требует снятия для того чтобы дать возможность блоку перемещаться. Перемещение блока в виртуальной памяти не есть перемещение байтов с одного места в другое ― производятся только манипуляции с таблицами страниц. Единственной причиной для выделения перемещаемой памяти служит предотвращение фрагментации виртуальной памяти. Удаляемая память Если вы используете опцию GMEM_MOVEABLE, то попробуйте использовать опцию GMEM_DISCARDABLE. Эта опция может быть использована только совместно с GMEM_MOVEABLE. Блок памяти, выделенный с этим флагом, может быть удален из физической памяти ядром Windows, когда необходима свободная память. Блоки памяти, содержащие код, являются удаляемыми. Они защищены от записи. Следовательно, быстрее загрузить код из исходного EXE-файла, чем записывать его на диск, а затем вновь загружать с диска. Если вы выделяете память для неизменяемых данных, которые могут быть легко регенерированы (обычно загрузкой из файла), то можно сделать этот блок удаляемым. О том, что данные были сброшены, вы узнаете, когда вызовите функцию GlobalLock и получите в ответ NULL. Теперь, вы восстанавливаете данные. Блок памяти не может быть удален до тех пор, пока счетчик фиксаций больше нуля. Для преднамеренного удаления блока памяти, вызовите: Код (ASM): invoke GlobalDiscard,hGlobal Другие функции и флаги Другим доступным для использования в функции GlobalAlloc является флаг GMEM_SHARE или GMEM_DDESHARE. Этот флаг предназначен для динамического обмена данными. Функции GlobalAlloc и GlobalReAlloc могут включать флаги GMEM_NODISCARD и GMEM_NOCOMPACT. Эти флаги указывают Windows не удалять и не перемещать блоки памяти для удовлетворения запросов памяти. Только излишне альтруистичные программисты используют эти флаги. Функция GlobalReAlloc может изменять флаги (например, преобразовывать фиксированный блок памяти в перемещаемый, и наоборот), если новый размер блока задан равным нулю, и указан флаг GMEM_MODIFY. Функция GlobalFlags возвращает комбинацию флагов GMEM_DISCARDABLE, GMEM_DISCARDED и GMEM_SHARE. Функция GlobalMemoryStatus с указателем на структуру типа MEMORYSTATUS для определения количества физической и виртуальной памяти, доступной приложению. Windows поддерживает функции: FreeMemory заполнение конкретным байтом, ZeroMemory обнуление памяти, CopyMemory и MoveMemory ― обе копируют данные из одной области памяти в другую. Если эти области перекрываются, то функция CopyMemory может работать некорректно. Вместо нее используйте функцию MoveMemory. Перед тем как осуществить доступ к памяти, вам придется проверить, возможен доступ или нет. Если указатель является недействительным, включается общая защита программы. Предварительная проверка указателя гарантирует, что этого не произойдет. Эту проверку выполняют функции IsBadCodePtr, IsBadReadPtr, IsBadWritePtr,IsBadStringPtr. IsBadCodePtr принимает указатель в качестве параметра и возвращает ненулевое значение (TRUE), если указатель действителен. IsBadReadPtr, IsBadWritePtr, IsBadStringPtr получают указатель в качестве первого параметра и длину блока памяти в качестве второго параметра. Функция IsBadStringPtr осуществляет проверку до тех пор, пока не встретит нулевой ограничитель строки.
Функции управления виртуальной памятью Windows поддерживает ряд функций, начинающихся с Virtual. Эти функции предоставляют значительно больше возможностей управления памятью. Однако, только очень необычные приложения требуют использования эти функции. Например, ваша программа читает исходный код и компилирует его, а результат компиляции заносится в память. Затем, вы хотите пометить этот блок памяти как "только для выполнения", то есть так, чтобы было невозможно случайно или преднамеренно прочитать (или того хуже, записать) в него что-либо из программы, которая была только что скомпилирована и готова к выполнению. Это одно из действий, которое должна осуществлять ваша программа для обработки ошибок при работе с указателями в пользовательской программе. Вы можете также пометить часть блоков памяти "только для чтения". Оба указанных действия выполняются только с помощью функций управления виртуальной памятью. Более часто возникает необходимость зарезервировать большой блок виртуальной памяти для данных, который может быть сильно увеличен в процессе выполнения программы. Действуя обычным путем ― то есть используя функции realloc или функцию GlobalReAlloc для динамического изменения размера выделенного блока памяти ― можно снизить производительность программы. Функции управления виртуальной памятью могут помочь избежать этого. В Windows любой блок виртуальной памяти может находиться в одном из трех состояний: "committed" (блок спроецирован в физическую память), "free" (свободен, доступен для будущего выделения), "reserved" (зарезервирован). Зарезервированный блок виртуальной памяти не отображается в физическую память. Адреса в пределах этого блока будут недоступны до тех пор, пока всему блоку или его какой-либо части не будет передан блок физической памяти. Таким образом, вы можете зарезервировать достаточно большой блок виртуальной памяти, не передавая ему физической памяти. Когда будет необходимо обратиться по какому-либо виртуальному адресу в пределах этого блока, вы передаете по этому адресу ровно столько физической памяти, сколько необходимо, то есть в зарезервированном блоке виртуальной памяти могут быть участки, как связанные, так и несвязанные с блоками физической памяти. Спроецировав физическую память на нужный участок зарезервированной области виртуальной памяти, программа может обращаться к нему, не вызывая при этом исключения нарушения доступа. Для того чтобы использовать функции работы с виртуальной памятью, вашей программе необходимо знать размер страницы памяти. Размер страницы всегда равен 4096 байт. Используйте функцию GetSystemInfo для получения размера страницы. Функция имеет один параметр ― указатель на структуру SYSTEM_INFO. Код (C): typedef struct _SYSTEM_INFO { union { DWORD dwOemId; struct { WORD wProcessorArchitecture; WORD wReserved; }; }; DWORD dwPageSize; LPVOID lpMinimumApplicationAddress; LPVOID lpMaximumApplicationAddress; DWORD_PTR dwActiveProcessorMask; DWORD dwNumberOfProcessors; DWORD dwProcessorType; DWORD dwAllocationGranularity; WORD wProcessorLevel; WORD wProcessorRevision; } SYSTEM_INFO; Поле dwPageSize этой структуры содержит размер страницы. Поля lpMinimumApplicationAddress и lpMaximumApplicationAddress содержат минимальный и максимальный адреса, имеющиеся в распоряжении приложения. Для Windows эти значения равны соответственно 400000h и 7FFFFFFFh. Функция VirtualAlloc выглядит следующим образом: Код (ASM): invoke VirtualAlloc,pAddr, dwSize, iAllocType, iProtect mov pMem,rax Первый параметр ― желаемый стартовый базовый адрес виртуальной памяти, его можно установить в NULL при первом вызове этой функции. Второй параметр ― размер. Третий параметр может быть MEM_RESERVE (для резервирования блока виртуальной памяти) или MEM_COMMIT (для резервирования и передачи физической памяти). Четвертый параметр ― константа, начинающаяся с префикса PAGE_ (PAGE_READONLY или PAGE_EXECUTE) для задания защиты блока памяти. Последовательные вызовы функции VirtualAlloc могут передавать или резервировать секции этого блока. Функция VirtualFree используется для освобождения виртуальной памяти. Функции работы с "кучей" Функции, имена которых начинаются со слова Heap (куча). Эти функции создают и поддерживают непрерывный блок виртуальной памяти, из которого можно выделять память более мелкими блоками. Работа начинается с вызова функции HeapCreate. Затем, используются функции HeapAllocate, HeapReAllocate и HeapFree для выделения и освобождения блоков памяти в рамках "кучи". "Куча" может быть уплотнена для объединения свободного пространства. Файловый ввод/вывод для файлового ввода/вывода используются WinAPI-функции fopen, fseek, fread, fwrite и fclose. Отличия Windows Можно читать и записывать файл большими блоками информации за один прием, используя однократный вызов функций fread и fwrite (или их эквивалентами). Отпадает необходимость в использовании циклов при работе с файлами большого размера. Windows поддерживает длинные имена файлов. Можно использовать данные, возвращаемые функцией GetVolumeInformation, для динамического выделения буферов для хранения имен файлов. Рекомендуется использовать константы MAX_PATH = 260 и MAX_FNAME = 256 для статического выделения памяти. Функции файлового ввода/вывода, поддерживаемые Windows Функция CreateFile Код (ASM): invoke CreateFile,szName, dwAccess, dwShare, NULL, dwCreate, dwFlags, 0 mov hFile,rax Функция используется для открытия существующего файла, для открытия каналов, используемых при обмене между процессами, а также коммуникационных устройств. первый параметр является именем файла. Второй имеет значение либо GENERIC_READ, либо GENERIC_WRITE, либо GENERIC_READ | GENERIC_WRITE. Использование нулевого значения позволяет получить информацию о файле без доступа к его содержимому. Параметр dwShare открывает файл с общими атрибутами, позволяя другим процессам читать из него (FILE_SHARE_READ), или записывать в него (FILE_SHARE_WRITE), или и то и другое вместе. Флаг dwCreate ― константа, показывающая, каким образом файл должен быть открыт. Флаг CREATE_NEW вызывает ошибку, если файл уже существует флаг CREATE_ALWAYS приводит к удалению содержимого существующего файла, флаг OPEN_EXISTING вызывает ошибку, если файл не существует, флаг OPEN_ALWAYS создает файл, если он не существует. Флаг TRUNCATE_EXISTING приводит к ошибке, если файл не существует, и удаляет все содержимое, если файл существует. Параметр dwFlags может быть комбинацией констант, начинающихся со слов FILE_ATTRIBUTE и FILE_FLAG, для установки атрибутов файла и других особенностей. Функция CreateFile возвращает переменную типа HANDLE. При завершении работы с файлом его необходимо закрыть, используя функцию CloseHandle с описателем файла в качестве параметра. Функции ReadFile и WriteFile похожи: Код (ASM): invoke ReadFile,hFile, pBuffer, dwToRead, &dwHaveRead, NULL invoke WriteFile,hFile, pBuffer, dwToWrite, &dwHaveWritten, NULL первый параметр ― описатель файла Второй параметр ― указатель на буфер, содержащий данные; третий параметр ― количество байтов для чтения или записи; четвертый параметр ― указатель на переменную, в которую при возврате из функции будет занесено количество байтов, которые были реально считаны или записаны. (Последний параметр используется только для файла, открываемого с флагом FILE_FLAG_OVERLAPPED Ввод/вывод с использованием файлов, проецируемых в память При работе в Windows существует возможность читать и записывать данные в файл так, как будто это блок памяти. Этот прием рекомендуется использовать также при разделении памяти между двумя и более процессами. Вот простейший подход к вводу/выводу с использованием файлов, проецируемых в память (memory mapped files): Сначала создается обычный файл с использованием функции CreateFile. Затем вызывается функция: Код (ASM): invoke CreateFileMapping,hFile, NULL, dwProtect, 0, 0, szName mov hMap,rax Параметр dwProtect принимает одно из следующих значений, и должен быть совместим с режимом разделения файла: PAGE_READONLY, PAGE_WRITECOPY, PAGE_READWRITE. Последний параметр функции ― необязательное имя, обычно используемое для разделения данных между процессами. В этом случае, функцияOpenFileMapping открывает тот же файл с указанным именем. Обе функции возвращают значение типа HANDLE. Если вам необходимо осуществить доступ к части файла, вызовите функцию MapViewOfFile: Код (ASM): invoke MapViewOfFile,hMap, dwAccess, dwHigh, dwLow, dwNumber mov pMem,rax Весь файл или его часть могут быть спроецированы в память, начиная с заданного 64-разрядного смещения, которое задается параметрами dwHigh и dwLow. (dwHigh = 0, если файл менее 4 ГБ.) Параметр dwNumber задает количество байтов, которое вы хотите спроецировать в память. Параметр dwAccess может быть равен FILE_MAP_WRITE (данные можно записывать и считывать) или FILE_MAP_READ (данные можно только считывать), и должен соответствовать параметру dwProtect функции CreateFileMapping. После этого вы можете использовать указатель, возвращаемый функцией, для доступа или модификации данных в файле. Функция FlushViewOfFile записывает на диск все измененные страницы файла, спроецированного в память. Функция UnmapViewOfFile делает недействительным указатель, возвращаемый функцией MapViewOfFile. Затем необходимо закрыть файл, используя функцию CloseHandle. Комментарий leo к 12 уроку Iczelion'а "Память и файлы" Забудьте раз и навсегда о GlobalAlloc с флагом GMEM_MOVEABLE!!! То, что было "традиционно" во времена перехода с Win16 на Win32, в наше время стало просто глупым и абсурдным. Никакого перемещения блоков памяти внутри кучи Win32 реально не поддерживают, так как им это не нужно ― все "перемещения" осуществляются на уровне подкачки и трансляции страниц. Но использовать флаг GMEM_MOVEABLE не только бессмысленно, но и накладно с точки зрения расхода памяти и быстродействия. Поясняю. Основными функциями для работы с кучей в Win32 являются kernel32.HeapAlloc\HeapFree, которые в NT+ просто мапятся в соответствующие функции ntdll.RtlAllocateHeap\RtlFreeHeap. Некоторым "неудобством" этих функций является то, что им нужно передавать хэндл кучи. Для основной кучи процесса этот хэндл возвращается функцией GetProcessHeap. Разумеется при многократных вызовах HeapAlloc имеет смысл один раз получить этот хэндл и сохранить его в переменной hHeap. Но если кому и один раз "в лом" вызвать GetProcessHeap, то можно использовать GlobalAlloc с флагом GMEM_FIXED. GlobalAlloc первым делом устанавливает SEH-обработчик и проверяет флаги. Если флаг GMEM_MOVEABLE не установлен, то просто берется хэндл основной кучи, вызывается HeapAlloc и затем удаляется SEH. Разумеется имеем дополнительные затраты времени на лишние call'ы, jmp'ы и mov'ы, но чего не сделаешь ради "простоты" С бестолковым флагом GMEM_MOVEABLE дела обстоят гораздо хуже. В итоге память точно также выделяется HeapAlloc, но дополнительно создается дескриптор HGLOBAL, хранящий ссылку на выделенный блок памяти. В Win 9x и NT дескрипторы создаются и хранятся по разному. В Win 9х память под дескриптор выделяется в той же основной куче процесса, то есть вместо одного HeapAlloc имеем два, плюс различные навороты типа блокировки кучи HeapLock\Unlock и т.п. В Win NT под дескрипторы HGLOBAL вообще выделяется отдельный регион памяти, который отъедает у процесса 512К(!) виртуальных адресов и как минимум 4К физической памяти у системы. Выделение памяти под дескрипторы производится при первом вызове GlobalAlloc с флагом GMEM_MOVEABLE и естественно сопровождается огромными потерями времени на обработку отказа страницы ― не менее 4-5 тысяч тактов процессора. Вот и спрашивается: насколько нужно любить и уважать "традиции", чтобы использовать совершенно бессмысленный флаг GMEM_MOVEABLE, зная, что в реальной ситуации блоки памяти в куче никуда не перемещаются бессмысленно вызывать GlobalLock\GlobalUnlock, которые только и делают, что увеличивают или уменьшают счетчик ссылок в HGLOBAL, а блок памяти как сидел на своем месте в куче так и сидит занимать 512К виртуальных адресов и 4К дополнительной физической памяти и напрягать процессор и систему, заставляя их выделять новую страницу только для того, что нам пришло в голову обращаться к выделенному блоку кучи не напрямую, а через через одно хорошо известное место PS: Еще пара замечаний до кучи: В GlobalUnlock положено передавать хэндл hMemory, а не указатель на блок pMemory. Хотя с pMemory тоже работает благодаря внутренним взаимным ссылкам hMemory ― pMemory задавать флаги GMEM_ZEROINIT или HEAP_ZERO_MEMORY во многих случаях не имеет смысла, как в рассматриваемом примере с ReadFile\WriteFile. При SendMessage+WriteFile вообще незачем занулять блок памяти, а при чтении файла достаточно занулить последний байт считанной строки и не тратить впустую время на обнуление всего блока размером MEMSIZE = 64K В чём разница между LocalAlloc и GlobalAlloc? Перевод фрагмента статьи "What was the difference between LocalAlloc and GlobalAlloc?" Реймонда Ченя. Когда-то во времена 16-ти битных Windows разница была огромной. В ранних версиях Windows доступ к памяти получали через значения, называемые селекторами (selectors), каждый из которых позволял адресовать память до 64 Кб. Существовал селектор по-умолчанию, который назывался селектором данных (data selector); операции с так называемыми ближними указателями (near pointers) осуществлялись относительно селектора данных. Например, если у вас был ближний указатель p со значением 1234h и ваш селектор данных был 012Fh, то тогда, когда вы писали p^, вы получали доступ к памяти по адресу 012F:1234 (когда вы объявляли указатель, он по-умолчанию считался ближним. Вам нужно было явно приписывать far, чтобы указать, что вам нужен дальний указатель). Важный момент: ближние указатели всегда относительны к селектору, обычно: к селектору данных. Функция GlobalAlloc выделяет селектор, который может быть использован для доступа к запрошенному вами количеству памяти (а если вы попросите выделить вам больше 64 Кб памяти, то произойдёт нечто волнующее, что, впрочем, сейчас не важно). Вы можете получить доступ к памяти по этому селектору через дальний указатель (far pointer). Дальний указатель ― это селектор, соединённый с ближним указателем (не забывайте: ближний указатель относителен к селектору; когда вы соединяете ближний указатель с соответствующим ему селектором, вы получаете дальний указатель). Каждый экземпляр (instance) программы и DLL имеет свой собственный селектор данных, известный как HINSTANCE. Селектор данных по-умолчанию для кода в программе был HINSTANCE этого экземпляра программы; селектор данных по-умолчанию для DLL был HINSTANCE этой DLL. Поэтому, если у вас был ближний указатель p, то разыменовывая его как p^ из кода программы, вы получали доступ к памяти относительно HINSTANCE экземпляра программы. Если вы разыменовывали его в DLL, то вы получали память, относительно HINSTANCE вашей DLL. Память, на которую ссылается селектор по-умолчанию, может быть превращена в локальную кучу (local heap) вызовом функции LocalInit. Инициализация локальной кучи обычно была одной из первых вещей, которые делала программа или DLL при запуске (для DLL это было обычно единственной вещью, которую она делала при загрузке!). Когда у вас есть локальная куча, вы можете вызывать LocalAlloc для выделения из неё памяти. Функция LocalAlloc возвращала ближний указатель, относительный к селектору по-умолчанию, так что если вы вызывали её из программного модуля, она выделяла память для HINSTANCE программы; если вы вызывали её из DLL, она выделяла память из HINSTANCE DLL. Если вы были сообразительны, вы могли понять, что вы можете использовать LocalAlloc для выделения памяти из других блоков, не только из HINSTANCE. Всё, что вам нужно было для этого сделать, так это изменить ваш селектор по-умолчанию на селектор для памяти, выделенной через GlobalAlloc, вызвать функцию LocalAlloc, а затем восстановить селектор по-умолчанию. Это давало вам ближний указатель, относительный к чему-то, отличному от селектора по-умолчанию, что было очень страшной вещью. Но если вы были умны и аккуратно всё делали, то вы могли избежать неприятностей. Заметьте, что в 16-ти битных Windows, функции LocalAlloc и GlobalAlloc были совершенно различными! LocalAlloc возвращала ближний указатель, в то время как GlobalAlloc вообще возвращала селектор. Указатели, которые вы хотели передавать между модулями, должны были иметь вид "дальних указателей", потому что у каждого модуля был свой селектор по-умолчанию. Если вы хотели передать владение памятью в другой модуль, вам нужно было использовать GlobalAlloc, потому что это позволяло вызывающему использовать потом вызов GlobalFree для его освобождения (вызывающий не мог использовать LocalFree, потому что LocalFree работает в локальной куче, которая будет кучей вызывающего ― это не то же самое, что ваша куча). Эта историческая разница между локальной и глобальной памятью до сих пор остаётся в Win32. Если у вас есть функция, которая пришла из 16-ти битных Windows, и она передаёт право на память, то она будет работать со значениями типа HGLOBAL. Классическим примером таких функций являются функции буфера обмена (clipboard). Если вы размещаете блок памяти в буфере обмена, он должен быть выделен через HGLOBAL, потому что вы передаёте память буферу обмена, и он вызовет GlobalFree, когда эта память станет ему не нужна. Память, передаваемая через STGMEDIUM, принимает форму HGLOBAL по той же причине. Даже в Win32, вам нужно быть осторожными и не перепутать локальную кучу и глобальную. Память, выделенная в одной, не может быть освобождена в другой. Хотя функциональные различия в основном исчезли; и семантика к этому моменту практически идентична. Все странности по ближним и дальним указателям тоже исчезли при переходе к Win32. Но функции локальной и глобальной куч, тем не менее, остаются двумя различными и не связанными интерфейсами куч.
Глава пятидесятая. Братец Кролик подключает к MessageBox кнопку Help Для добавления кнопки "Help" в стиль MessageBox достаточно установить в единицу 14-ый бит Код (ASM): MB_HELP equ 4000h Кнопка "Help" добавится к MessageBox с любым сочетанием клавиш, но на этом дело и остановится. Если вы нажимаете на любую клавишу кроме клавиши "Help" MessageBox всегда закрывается и программа переходит к обработке той клавиши которая только-что была нажата, но к кнопке "Help" это не относится. Вариант первый Проще всего обработку нажатия "Help" можно добавить в MessageBoxIndirect. Эта функция получает в качестве единственного параметра ссылку на структуру MSGBOXPARAMS где в поле lpfnMsgBoxCallback содержится адрес функции MsgBoxCallback. Код (ASM): MSGBOXPARAMS struc cbSize dd ?,?;UINT hwndOwner dq ? ;HWND hInstance dq ? ;HINSTANCE lpszText dq ? ;LPCTSTR lpszCaption dq ? ;LPCTSTR dwStyle dd ?,?;DWORD lpszIcon dq ? ;LPCTSTR dwContextHelpId dd ?,?;DWORD_PTR lpfnMsgBoxCallback dq ? ;MSGBOXCALLBACK dwLanguageId dd ?,?;DWORD MSGBOXPARAMS ends Для вывода на экран справки используется функция WinHelp или ShellExecute которая обработает расширение .hlp, .chm программой "по-умолчанию" tut_01.asm вывод справочной информации из MessageBoxIndirect Код (ASM): ; GUI # include win64a.inc .code capt db "Hello!",0 msg db "Customized message box",0 ButtonText db "Click here",0 hlpfile db "tut_01.hlp",0 WinMain proc local mbp:MSGBOXPARAMS mov mbp.cbSize,sizeof MSGBOXPARAMS and mbp.hwndOwner,0; mov mbp.hInstance,IMAGE_BASE movr mbp.lpszText,msg movr mbp.lpszCaption,capt mov mbp.dwStyle,MB_HELP or MB_USERICON mov mbp.lpszIcon,1000 mov mbp.dwContextHelpId,1 ;help topic ID mov eax,offset MsgBoxCallback mov mbp.lpfnMsgBoxCallback,rax; and mbp.dwLanguageId,0 invoke MessageBoxIndirect,&mbp invoke RtlExitUserProcess,NULL WinMain endp MsgBoxCallback proc x:qword invoke ShellExecute,0,0,&hlpfile,0,0,1 leave retn MsgBoxCallback endp end tut_01.rc Код (C): 1000 ICON DISCARDABLE "br_Bear4.ico" Здесь asm/rc/exe-файлы, файл с иконкой и hlp-файл Вариант второй tut_02.asm через создание нового потока Код (ASM): ; GUI # include win64a.inc .code capt db "Hello!",0 msg db "Customized message box",0 ButtonText db "OK",0 bclass db "Button",0 hlpfile db "imagedit.hlp",0 pOldProc dq ? HelpBtnID dq ? message dq ? caption dq ? button dq ? WinMain proc LOCAL ThreadID:QWORD LOCAL hWnd:QWORD LOCAL hThread:QWORD LOCAL hClick:QWORD movr message,msg movr caption,capt movr button,ButtonText invoke CreateThread,0,\ ;атрибуты безопасности по умолчанию 0,\ ;размер стека используется по умолчанию &ThreadProc1,\;функция потока &caption,\ ;аргумент функции потока 0,\ ;флажки создания используются по умолчанию &ThreadID ;возвращает идентификатор потока mov hThread,rax invoke GetCurrentProcess invoke WaitForInputIdle,rax,INFINITE invoke EnumWindows,&EnumWndProc,&ThreadID invoke FindWindowEx,hWnd,0,&bclass,0 mov hClick,rax invoke FindWindowEx,hWnd,eax,&bclass,0 invoke GetDlgCtrlID,eax mov HelpBtnID,rax invoke SetWindowText,hClick,&ButtonText invoke SetWindowLongPtr,hWnd,GWL_WNDPROC,&MsgboxProc mov pOldProc,rax invoke WaitForSingleObject,hThread,INFINITE invoke CloseHandle,hThread invoke RtlExitUserProcess,NULL WinMain endp ThreadProc1 PROC pParams:QWORD mov eax,ecx;ecx=pParams invoke MessageBox,QWORD PTR [rax-16],QWORD PTR [rax-8],QWORD PTR [rax],MB_HELP leave retn ThreadProc1 ENDP EnumWndProc PROC hWnd:QWORD,lParam:QWORD LOCAL pid:QWORD mov hWnd,rcx mov lParam,rdx invoke GetWindowThreadProcessId,,&pid mov rdx,lParam cmp eax,[rdx] mov eax,TRUE jne @f mov rax,hWnd mov [rdx-8],rax ; get the handle of the window xor eax,eax ; displaying the message box @@: leave retn EnumWndProc ENDP MsgboxProc PROC hWnd:HWND,uMsg:QWORD,wParam:WPARAM,lParam:LPARAM mov hWnd,rcx mov uMsg,rdx mov wParam,r8 mov lParam,r9 cmp edx,WM_COMMAND jne @f mov rax,r8;wParam mov edx,eax shr edx,16 ;cmp dx,BN_CLICKED=0 jne @f mov rcx,HelpBtnID cmp ax,cx jne @f invoke ShellExecute,0,0,&hlpfile,0,0,1 @@: invoke CallWindowProc,pOldProc,hWnd,uMsg,wParam,lParam leave retn MsgboxProc ENDP end Здесь asm/exe-файлы и hlp-файл Вариант третий tut_03.asm создаем окошко и обрабатываем сообщение WM_HELP Код (ASM): ; GUI # include win64a.inc HELPINFO struct cbSize dq ? ;размер структуры в байтах iContextType dq ?;тип справки iCtrlId dq ? ;идентификатор окна либо меню hItemHandle dq ? ;идентификатор дочернего окна либо связанного меню dwContextId dq ? ;контекстный идентификатор либо окна либо элемента управления MousePos POINT <> HELPINFO ends .code WinMain proc local msg:MSG xor ebx,ebx mov esi,IMAGE_BASE mov edi,offset ClassName mov ecx,offset FileName invoke LoadCursorFromFile push rax ;hIconSm push rdi ;lpszClassName push rbx ;lpszMenuName push COLOR_WINDOW+1;hbrBackground push 10003h ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassEx,esp ;addr WNDCLASSEX push rbx push rsi ;rsi=400000h shl esi,9 ;rsi=CW_USEDEFAULT push rbx push rbx push rsi push rsi push rsi push rsi sub esp,20h mov r9d,WS_OVERLAPPEDWINDOW or WS_VISIBLE invoke CreateWindowEx,0,edi,edi mov hwnd,rax lea edi,msg @@: invoke GetMessage,edi,NULL,0,0 invoke TranslateMessage,edi invoke DispatchMessage,edi jmp @b WinMain endp WndProc proc hWnd:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local ps:PAINTSTRUCT local expRect:RECT mov hWnd,rcx mov wParam,r8 mov lParam,r9 cmp edx,WM_DESTROY je wmDESTROY cmp edx,WM_PAINT je wmPAINT cmp edx,WM_HELP je wmHELP cmp edx,WM_KEYDOWN je wmF1 leave jmp NtdllDefWindowProc_ wmDESTROY: invoke RtlExitUserProcess,NULL wmPAINT:invoke BeginPaint,,addr ps invoke GetClientRect,hWnd,addr expRect invoke DrawText,ps.hdc,&message,-1,addr expRect,DT_SINGLELINE or DT_CENTER or DT_VCENTER invoke EndPaint,hWnd,addr ps jmp wmBYE wmHELP: mov rax,[r9+HELPINFO.iCtrlId] cmp rax,hwnd je wmBYE invoke ShellExecute,0,0,&hlpfile,0,0,1 jmp wmBYE wmF1: cmp r8,VK_F1 jne wmBYE invoke MessageBox,hwnd,&message2,&ClassName,MB_HELP wmBYE: leave ret WndProc endp ;----------------------------------- ClassName db "Uncle Remus tales:#3 Simple window",0 FileName db "..\Images\br_Rabbit3.cur",0 message db "Press on F1 button",0 message2 db "Press on 'Help'",0 hlpfile db "tut_01.hlp",0 hwnd dq ? end Здесь asm/exe-файлы и hlp-файл Вариант четвертый tut_04.asm обработка нажатия "Help" в SoftModalMessageBox. Код (ASM): ; GUI # include win64a.inc MSGBOXDATA struct params MSGBOXPARAMS <> pwndOwner QWORD ? wLanguageId DWORD ?,? pidButton QWORD ? ; // Array of button IDs ppszButtonText QWORD ? ; // Array of button text strings cButtons DWORD ? DefButton DWORD ? CancelId DWORD ? Timeout DWORD ? MSGBOXDATA ends .code hlpfile db 'ImagEdit.hlp',0 MsgBoxText1: du <Win64 Assembly is Great> MsgCaption8: du <SoftModalMessageBox> sBTN1: du <OK> sBTN2: du <Help> dwBtnIds dd 1,9 dwTxtTbl dq sBTN1,sBTN2 align 16 WinMain proc local mb:MSGBOXPARAMS local mbxData:MSGBOXDATA mov mbxData.params.cbSize,sizeof MSGBOXPARAMS and mbxData.params.hwndOwner,0 mov mbxData.params.hInstance,IMAGE_BASE movr mbxData.params.lpszText,MsgBoxText1;адрес текста в окне сообщений 8 movr mbxData.params.lpszCaption,MsgCaption8;адрес заголовка в окне сообщений 8 mov mbxData.params.dwStyle,MB_OK or MB_USERICON or MB_HELP;MB_YESNOCANCEL OR MB_ICONWARNING or MB_USERICON movr mbxData.params.lpszIcon,1000 mov mbxData.params.dwContextHelpId,1 mov eax,offset MsgBoxCallback mov mbxData.params.lpfnMsgBoxCallback,rax mov mbxData.params.dwLanguageId,0 and mbxData.pwndOwner,0 and mbxData.wLanguageId,0 movr mbxData.pidButton,dwBtnIds movr mbxData.ppszButtonText,dwTxtTbl mov mbxData.cButtons,2;16 and mbxData.DefButton,0 mov mbxData.CancelId,1 or mbxData.Timeout,-1 invoke SoftModalMessageBox,&mbxData invoke RtlExitUserProcess,NULL WinMain endp align 16 MsgBoxCallback proc lpHelpInfo:qword;(LPHELPINFO lpHelpInfo) invoke ShellExecute,0,0,&hlpfile,0,0,1 leave retn MsgBoxCallback endp end tut_04.rc Код (C): 1000 ICON DISCARDABLE "br_Bear4.ico" Здесь asm/rc/exe-файлы, иконка и hlp-файл © Mikl___ 2021
Глава пятьдесят вторая. Братец Кролик и просмотр видео-файлов Пишем плеер на основе Animate-класса Один из простейших проигрывателей. Создаем окно, в это окно загружаем дочернее окно с классом Animate с указанием файла, который хотим воспроизвести. Создаем дочернее окно с помощью функции CreateWindowEx. Нам не нужно регистрировать класс окна, поскольку он уже зарегистрирован Windows. При создании экрана указываем «SysAnimate32» в качестве имени класса CreateWindowsEx. Другие параметры, которые нужно указать, — это дескриптор родительского окна и идентификатор элемента управления. Идентификатор элемента управления должен быть уникальным, если мы использовали несколько элементов управления, чтобы отличить этот элемент управления от других. Установим ID = 104. Нам также необходимо указать размеры экрана и координаты его верхнего левого угла. Обычно дочернее окно создается во время обработки сообщения WM_CREATE главного окна. Мы создадим дочернее окно перед обработкой цикла сообщений. После создания инструмента управления анимацией мы отправляем сообщение ACM_OPEN через функцию SendDlgItemMessage, чтобы открыть AVI-файл и загрузить его в память. В сообщении указываем путь к AVI-файлу. Принцип действия органа управления Animation заключается в проигрывании несжатого AVI -файла. Для сжатия можно использовать алгоритм RLE, причем AVI-файл не должен содержать звуковой информации. Код (ASM): ; GUI # OPTION DOTNAME include win64a.inc ACM_OPEN = WM_USER + 100 ACS_AUTOPLAY = 4 ID_ANIMATE = 101 .code WinMain proc local msg:MSG local hwnd:QWORD xor ebx,ebx mov esi,IMAGE_BASE invoke LoadCursorFromFile,&FileNameCur mov edi,offset ClassName push rax ;hIconSm push rdi ;lpszClassName push rbx ;lpszMenuName push COLOR_WINDOW;hbrBackground push rax ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc ;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassEx,esp ;addr WNDCLASSEX push rbx push rsi ;rsi=400000h push rbx push rbx push 440 push 510 push 10 push 10 sub esp,20h invoke CreateWindowEx,0,edi,edi,WS_CAPTION or WS_SYSMENU or WS_VISIBLE mov hwnd,rax push rbx push rsi;IMAGE_BASE push ID_ANIMATE push rax;hwnd push rbx;ширина и высота кадра настроятся автоматически push rbx push 20 push 20 sub esp,20h invoke CreateWindowEx, WS_EX_STATICEDGE or WS_EX_CLIENTEDGE,&Animate32,&NoText, \ WS_CHILD or WS_VISIBLE or WS_TABSTOP or ACS_AUTOPLAY movr qword ptr [rsp+20h],AviFile invoke SendDlgItemMessage,hwnd,ID_ANIMATE,ACM_OPEN,IMAGE_BASE lea edi,msg @@: invoke GetMessage,edi,0,0,0 invoke DispatchMessage,edi jmp @b WinMain endp WndProc: cmp edx,WM_DESTROY je wmDESTROY jmp NtdllDefWindowProc_ wmDESTROY:invoke RtlExitUserProcess,NULL ;--------------------------------------- .data ClassName db 'Uncle Remus tales:#37a Animation',0 FileNameCur db "..\Images\br_Rabbit3.cur",0 AviFile db 'movie0.avi',0 Animate32 db "SysAnimate32" NoText db 0 end SendDlgItemMessageA( _In_ HWND hwnd, ;Дескриптор окна, оконная процедура которого получит сообщение _In_ int ID_ANIMATE, ;Идентификатор элемента Animate32 _In_ UINT ACM_OPEN, ;Сообщение, которое нужно отправить _In_ WPARAM IMAGE_BASE, ;Дескриптор экземпляра модуля _In_ LPARAM lpszName ;путь к AVI-файлу avi-файл для пробы movie0.avi Для Animation можно отправлять сообщения ЗначениесообщенияОписаниеhexbin46410001100100ACM_OPENAоткрывает AVI-клип и показывает на экране в аниматоре его первый кадр46510001100101ACM_PLAYпроигрывает AVI-клип46610001100110ACM_STOPостанавливает воспроизведениеAVI-клипа в аниматоре46710001100111ACM_OPENWЗначениеСтильОписаниеhexbin1001ACS_CENTERВидеоизображение центрировано в окне органа управления2010ACS_TRANSPARENTВидеоизображение транслируется в прозрачном режиме4100ACS_AUTOPLAYПроигрывание видеоизображения начинается автоматически сразу после открытия соответствующего AVI-файлаИзвещения от органа управления Animation ЗначениеСообщениеОписаниеhexbin1001ACN_STARTпроцесс проигрывания видеоролика начался2010ACN_STOPпроцесс завершилсяAnimation посылает в родительское окно извещения вместе с сообщением WM_COMMAND. Это извещения ACN_START и ACN_STOP.
Видео-плеер на основе интерфейса управляющих сообщений MCIУстройство открывается командой MCI_OPEN с использованием структуры MCI_OPEN_PARMS Поле lpstrDeviceType в этой структуре должно содержать указатель на строку имени устройства, или константный идентификатор устройства. hexКомандаНазначение804MCI_CLOSE эта команда закрывает устройство. Ее необходимо выдавать после завершения работы с устройством806MCI_PLAYКоманда предназначена для проигрывания809MCI_PAUSEКоманда приостанавливает выполнение операции проигрывания808MCI_STOPКоманда останавливает выполнение операции проигрывания807MCI_SEEKКоманда позволяет выполнять позиционирование в пределах файла811MCI_BREAKС помощью команды указывается виртуальный код клавиши, с помощью которой можно прервать выполнение операции80BMCI_GETDEVCAPSС помощью команды можно определить возможности устройства.80AMCI_INFOС помощью команды можно получить информацию об устройстве в виде текстовой строки, такую как описание аппаратуры, имя файла, связанного с устройством.810MCI_SYSINFOС помощью команды можно получить системную информацию об устройстве в виде текстовой строки814MCI_STATUSКоманда используется для определения текущего состояния устройства80DMCI_SETКоманда предназначена для установки режима работы устройства852MCI_COPYКоманда предназначена для копирования данных в универсальный буфер обмена Clipboard. Для нее используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT853MCI_PASTEКоманда вставляет данные из Clipboard в текущий буфер устройства. Для нее, как и для команды MCI_COPY, используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT851MCI_CUTКоманда удаляет данные из текущего буфера устройства и копирует их в универсальный буфер обмена Clipboard. Для нее используется блок параметров в формате структуры MCI_GENERIC_PARMS, флаги MCI_NOTIFY и MCI_WAIT856MCI_DELETEКоманда удаляет данные из текущего буфера устройства без копирования их в Clipboard850MCI_LOADКоманда предназначена для загрузки файлаНемного стараний и можно получить видио-плеер и редактор видео-файлов в одном флаконе. Код (ASM): ; GUI # include win64a.inc MCI_ANIM_OPEN_PARMS STRUCT dwCallback QWORD ? wDeviceID DWORD ? lpstrDeviceType QWORD ? lpstrElementName QWORD ? lpstrAlias QWORD ? dwStyle DWORD ? hWndParent QWORD ? MCI_ANIM_OPEN_PARMS ENDS MCI_ANIM_PLAY_PARMS STRUCT dwCallback QWORD ? dwFrom DWORD ? dwTo DWORD ? dwSpeed DWORD ? MCI_ANIM_PLAY_PARMS ENDS MCI_GENERIC_PARMS STRUCT dwCallback QWORD ? MCI_GENERIC_PARMS ENDS .code WinMain proc local msg:MSG local buffer[200h]:byte local hFile:qword local wid:qword local hei:qword local szReadWrite:QWORD xor ebx,ebx mov esi,IMAGE_BASE invoke LoadCursorFromFileA,&FileNameCur mov edi,offset ClassName push rax ;hIconSm push rdi ;lpszClassName push rbx ;lpszMenuName push COLOR_WINDOW;hbrBackground push rax ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc ;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassExA,esp ;addr WNDCLASSEX mov [rsp+30h],rbx ;hTemplateFile mov qword ptr [rsp+28h],FILE_ATTRIBUTE_ARCHIVE ;dwFlagsAndAttributes mov qword ptr [rsp+20h],OPEN_EXISTING ;dwCreationDisposition invoke CreateFile,&ElementName,GENERIC_READ,\ ;dwDesiredAccess FILE_SHARE_READ or FILE_SHARE_WRITE,0 ;lpSecurityAttributes mov hFile,rax;handle to file mov [rsp+20h],rbx invoke ReadFile,rax,&buffer,200h,&szReadWrite invoke CloseHandle,hFile mov eax,dword ptr buffer[102h] add eax,20 mov wid,rax mov eax,dword ptr buffer[106h] add eax,40 mov hei,rax mov rax,qword ptr buffer[66h] mov edx,429497 mul edx mov dur,rdx push rbx push rsi ;rsi=400000h shl esi,9 push rbx push rbx push hei push wid push rsi push rsi sub esp,20h invoke CreateWindowExA,0,edi,edi,WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_VISIBLE lea edi,msg @@: invoke GetMessageA,edi,0,0,0 invoke DispatchMessageA,edi jmp @b WinMain endp WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM local buff[96]:BYTE mov hWnd,rcx cmp edx,WM_DESTROY je wmDESTROY cmp edx,WM_CREATE je wmCREATE cmp edx,MM_MCINOTIFY je mmMCINOTIFY leave jmp NtdllDefWindowProc_ wmDESTROY:mov ecx,mciOP.wDeviceID invoke mciSendCommand,,MCI_CLOSE,0,&mciCP invoke RtlExitUserProcess,NULL wmCREATE:mov mciOP.hWndParent,rcx invoke mciSendCommand,0,MCI_OPEN,MCI_WAIT or MCI_OPEN_ELEMENT or \ MCI_ANIM_OPEN_WS or MCI_ANIM_OPEN_PARENT,&mciOP or eax,eax je @f invoke MCIError ;--------------------------------------------------------------------- EOF AVI mmMCINOTIFY:;invoke DestroyWindow,hWnd jmp wmBYE ;-------------------------------------------------------------------- Play AVI @@: mov rax,hWnd mov mciPP.dwCallback,rax mov ecx,mciOP.wDeviceID invoke mciSendCommand,,MCI_PLAY,MCI_NOTIFY,&mciPP or eax,eax je wmBYE MCIError:sub esp,28h invoke mciGetErrorString,eax,&buff,96 invoke MessageBoxA,hWnd,&buff,&aMCIError,MB_OK or MB_ICONSTOP wmBYE: leave ret WndProc endp .data ClassName db 'Uncle Remus tales:#37b Animation',0 FileNameCur db "..\Images\br_Rabbit3.cur",0 mciOP MCI_ANIM_OPEN_PARMS <0,0,DeviceType,ElementName,0,WS_CHILD,0> mciPP MCI_ANIM_PLAY_PARMS <0> mciCP MCI_GENERIC_PARMS <1> aMCIError db "MCI Error",0 DeviceType DB 'AVIVideo',0 ElementName DB 'movie2.wmv',0 dur dq ? end
Код (ASM): ; GUI # include win64a.inc .code WinMain proc sub esp,7*8 mov qword ptr [rsp+28h],SW_SHOWNORMAL and qword ptr [rsp+20h],0 invoke ShellExecute,0,&szNoTxt,&szFilm,0 invoke RtlExitUserProcess,NULL WinMain endp .data szFilm db 'movie3.wmv' szNoTxt db 0 end
У меня не получилось откомпилировать файл, нет масм64. Но я переделал под вин32 и UASM Код (ASM): ; Демка проигрывания avi файла на форме. .386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib include msvcrt.inc include macros.asm ACM_OPEN = WM_USER + 100 ACS_AUTOPLAY = 4 MAIN_FORM_ID = 0 ID_ANIMATE = 101 WS_MAIN_FORM = WS_CAPTION or WS_SYSMENU or WS_VISIBLE WS_AVI_FORM = WS_CHILD or WS_VISIBLE or WS_TABSTOP or ACS_AUTOPLAY .const ClassName db "Uncle Remus tales:#37a Animation",0 .code WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD local wc:WNDCLASSEX, msg:MSG ;edi - hWnd:HWND lea esi, wc xor ebx, ebx ASSUME esi:ptr WNDCLASSEX, edi:ptr MSG mov [esi].cbSize, sizeof WNDCLASSEX mov [esi].style, CS_BYTEALIGNCLIENT mov [esi].lpfnWndProc, offset WndProc mov [esi].cbClsExtra, ebx ;=NULL mov [esi].cbWndExtra, ebx ;=NULL mrm [esi].hInstance, hInst mov [esi].hbrBackground, COLOR_WINDOW mov [esi].lpszClassName, offset ClassName mov [esi].hIcon, LoadIcon(ebx, IDI_APPLICATION) mov [esi].hIconSm, eax mov [esi].hCursor, LoadCursor(ebx, IDC_ARROW) RegisterClassEx(esi) mov edi, CreateWindowEx(ebx,&ClassName,&ClassName,WS_MAIN_FORM,200,50,510,440,ebx,MAIN_FORM_ID,hInst,ebx) CreateWindowEx(WS_EX_STATICEDGE + WS_EX_CLIENTEDGE, CSTR("SysAnimate32"), ebx, WS_AVI_FORM, 20,20,99,99, edi, ID_ANIMATE, hInst, ebx) SendDlgItemMessage(edi, ID_ANIMATE, ACM_OPEN, hInst, CSTR("output.avi")) ; ShowWindow(edi, SW_SHOWNORMAL) ; UpdateWindow(edi) lea edi, msg .while (true) GetMessage(edi, ebx, ebx, ebx) .break .if (!eax) TranslateMessage(edi) DispatchMessage(edi) .endw mov eax, [edi].wParam ASSUME esi:nothing, edi:nothing ret WinMain endp align_proc WndProc proc uses esi edi ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .if (uMsg == WM_DESTROY) PostQuitMessage(NULL); ; .elseif (uMsg == WM_CREATE) .else DefWindowProc(hWnd, uMsg, wParam, lParam) ;//перемещаем форму за любую часть! .if (uMsg == WM_NCHITTEST && eax == HTCLIENT) mov eax, HTCAPTION .endif jmp EXIT .endif xor eax, eax EXIT: ret WndProc endp main_startup_Win32_END Для моих библиотечных файлов. Работает!
Современные сеньоры девелоперы не могут длину строки посчитать без подсказок от чата жпт, а вы им видеоплееры на ассемблере предлагаете
Структура SHELLEXECUTEINFOполетипописаниеcbSizedwordразмер структурыfMaskdwordКомбинация флагов, определяющих используемую часть структурыhwndqwordДескриптор окна, вызывающего функцию ShellExecuteExlpVerbqwordСтрока, определяющее действие с файлом lpFilelpFileqwordИмя файла для открытия, печати, запуска и других операций lpVerblpParametersqwordДополнительные параметры. Обычно используется как опции командной строкиlpDirectoryqwordПуть используемой папки. Если параметр не определен, то используется текущая папкаnShowdword, dwordПоказать открытое окно и активировать егоhInstAppqwordЕсли ShellExecuteEx отработала успешно, то параметр получает дескриптор экземпляра приложенияlpIDListqwordУказатель на структуру ITEMIDLIST (PIDL), определяющую файл для запуска. Данный параметр используется, если fMask содержит SEE_MASK_IDLIST или SEE_MASK_INVOKEIDLISTlpClassqwordИмя класса файла или глобальный идентификатор (GUID). Данный параметр используется, если fMask содержит SEE_MASK_CLASSNAMEhkeyClassqwordДескриптор ключа реестра. Данный параметр используется, если fMask содержит SEE_MASK_CLASSKEYdwHotKeyqword Горячая клавиша, ассоциированная с приложением. Младшее слово является виртуальным кодом клавиши, а старшее слово - комбинация флагов Данный параметр используется, если fMask содержит SEE_MASK_HOTKEYhIconqwordДескриптор значка, используемого для класса файла. Данный параметр используется, если fMask содержит SEE_MASK_ICONhProcessqwordДескриптор процесса. Данный параметр используется, если fMask содержит SEE_MASK_NOCLOSEPROCESShexfMask1SEE_MASK_CLASSNAME Используется lpClass3SEE_MASK_CLASSKEYИспользуется hkeyClass4SEE_MASK_IDLISTИспользуется lpIDListCSEE_MASK_INVOKEIDLISTИспользуется lpIDList10SEE_MASK_ICONИспользуется hIcon20SEE_MASK_HOTKEYИспользуется dwHotKey40SEE_MASK_NOCLOSEPROCESSИспользуется hProcess80SEE_MASK_CONNECTNETDRVПроверка на расшаренность файла и связь с диском. lpFile является универсальным именем (UNC) файла в сети100SEE_MASK_FLAG_DDEWAIT, SEE_MASK_NOASYNCIf ShellExecuteEx starts a DDE conversation, wait for that conversation to finish before the function returns.200SEE_MASK_DOENVSUBSTРасширяет среду переменных окружения в lpDirectory или lpFile400SEE_MASK_FLAG_NO_UIНе показывать диалоговое окно ошибки в случае ее возникновения4000SEE_MASK_UNICODE8000SEE_MASK_NO_CONSOLE10000SEE_MASK_ASYNCOK20000SEE_MASK_HMONITOR800000SEE_MASK_NOZONECHECKS1000000SEE_MASK_NOQUERYCLASSSTORE2000000SEE_MASK_WAITFORINPUTIDLE4000000SEE_MASK_FLAG_LOG_USAGElpVerb"explore"Если lpFile имя файла, то открывает его в Проводнике"open"Открывает lpFile с помощью связанной с этим типом файла программой. Открытие исполняемых файлов запускает файл"print"Печать lpFile с использованием связанной с печатью программойbindecnShow00000SW_HIDEСкрыть окно00011SW_SHOWNORMALПоказать открытое окно и активировать его (как обычно)00102SW_SHOWMINIMIZEDПоказать открытое окно свернутым00113SW_MAXIMIZE, SW_SHOWMAXIMIZEDПоказать открытое окно развернутым01004SW_SHOWNOACTIVATEПоказать открытое окно в текущих размерах и позиции, но не активировать его01015SW_SHOWПоказать окно01106SW_MINIMIZEСвернуть окно01117SW_SHOWMINNOACTIVEПоказать открытое окно свернутым, но не активировать его10008SW_SHOWNAПоказать открытое окно в текущем состоянии, но не активировать его10019SW_RESTOREВосстановить окноВ случае ошибки получает один из следующих флагов, определяющих тип ошибки: bindechInstApp0000012SE_ERR_FNFФайл не найден0000113SE_ERR_PNFПуть не найден0001015SE_ERR_ACCESSDENIEDВ доступе отказано0010008SE_ERR_OOMНедостаточно памяти01101026SE_ERR_SHAREРасшаренный файл не открывается01101127SE_ERR_ASSOCINCOMPLETEНет информации об ассоциации файла01110028SE_ERR_DDETIMEOUTThe DDE operation timed out01110129SE_ERR_DDEFAILОперация DDE не прошла01111030SE_ERR_DDEBUSYОперация DDE занята01111131SE_ERR_NOASSOCНет ассоциированной программы для требуемого действия10000032SE_ERR_DLLNOTFOUNDТребуемый DLL не найденГорячая клавиша, ассоциированная с приложением. Младшее слово является виртуальным кодом клавиши, а старшее слово - комбинация следующих флагов: bindecdwHotKey00011HOTKEYF_SHIFTКлавиша Shift как модификатор00102HOTKEYF_CONTROLКлавиша Ctrl как модификатор01004HOTKEYF_ALTКлавиша Alt как модификатор10008HOTKEYF_EXTДополнительная клавиша как модификатор Код (ASM): ; GUI # include win64a.inc SHELLEXECUTEINFO struct cbSize dd ? fMask dd ? hwnd dq ? lpVerb dq ? lpFile dq ? lpParameters dq ? lpDirectory dq ? nShow dd ?,? hInstApp dq ? lpIDList dq ? lpClass dq ? hkeyClass dq ? dwHotKey dq ? hIcon dq ? hProcess dq ? SHELLEXECUTEINFO ends .code WinMain proc sub esp,7*8 invoke ShellExecuteEx,&sei invoke RtlExitUserProcess,NULL WinMain endp .data sei label SHELLEXECUTEINFO dd sizeof SHELLEXECUTEINFO ;cbSize dd 1500h ;fMask dq 0 ;hwnd dq szNoTxt;lpVerb dq szFilm ;lpFile dq 0 ;lpParameters dq 0 ;lpDirectory dd SW_SHOWNORMAL,0;nShow dq 0 ;hInstApp dq 0 ;lpIDList dq 0 ;lpClass dq 0 ;hkeyClass dq 0 ;dwHotKey dq 0 ;hIcon dq 0 ;hProcess szFilm db 'movie4.wmv' szNoTxt db 0 end
Код (ASM): ; GUI # include win64a.inc .code SHELLEXECUTEINFO struct cbSize dd ? fMask dd ? hwnd dq ? lpVerb dq ? lpFile dq ? lpParameters dq ? lpDirectory dq ? nShow dd ?,? hInstApp dq ? lpIDList dq ? lpClass dq ? hkeyClass dq ? dwHotKey dq ? hIcon dq ? hProcess dq ? SHELLEXECUTEINFO ends WinMain proc sub esp,8 ;<--16bit align xor ebx,ebx push rbx ; hProcess push rbx ; hMonitor push rbx ; hIcon push rbx ; dwHotKey push rbx ; hkeyClass push rbx ; lpClass push rbx ; lpIDList push SW_SHOWNORMAL; nShow + hInstapp push rbx ; lpDirectory push rbx ; lpParameters pushaddr szFilm ; lpFile pushaddr szOpen ; lpVerb push rbx ; hWnd mov rax,((1500h shl 32) + (sizeof SHELLEXECUTEINFO)) ;cbSize=sizeof SHELLEXECUTEINFO fMask=1500h push rax ; cbSize fMask invoke ShellExecuteEx,esp invoke RtlExitUserProcess,NULL WinMain endp .data szFilm db 'movie6.mp4',0 szOpen db 'open' end
Видео-плеер на основе MCI-окнаСоздаем MCI-окно, вызывая для этого функцию MCIWndCreate со следующими параметрами параметрназначениеhwndParentидентификатор родительского окнаhInstanceидентификатор приложенияdwStyleстиль окнаszFileимя устройства или путь к файлуБолее подробно о параметрах функцииhwndParent Через этот параметр приложение передает функции идентификатор окна, создавшего MCI-окно. Если родительского окна нет, в качестве параметра указывают NULL. dwStyle Стиль создаваемого окна. Можно указывать стили, стандартные для функции CreateWindow, а также дополнительные, список которых приведен в таблице ниже. Если стандартные стили не указаны, то если есть родительское окно, используются стили WS_CHILD, WS_BORDER и WS_VISIBLE. Если параметр hwndParent указан как NULL, используются стили WS_OVERLAPPEDWINDOW и WS_VISIBLE. Для создания невидимого окна используют один из стандартных стилей, например, WS_CHILD szFile Указатель на текстовую строку, содержащую имя устройства или путь к файлу Функция MCIWndCreate позволяет определить обычные стили окна (WS_OVERLAPPEDWINDOW и WS_VISIBLE), а также специфические для окна MCI ― MCIWNDF_SHOWNAME и MCIWNDF_SHOWMODE. список дополнительных стилей, которые можно использовать при создании MCI-окна. dwStyleОписаниеMCIWNDF_NOAUTOSIZEWINDOWРазмер окна не изменяется при изменении размера изображенияMCIWNDF_NOAUTOSIZEMOVIEПри изменении размеров окна не следует выполнять масштабирование изображения для полного заполнения внутренней области окнаMCIWNDF_NOPLAYBARЕсли задан этот стиль, не отображается полоса просмотраMCIWNDF_NOMENUНе отображается кнопка для доступа к менюMCIWNDF_RECORDОтображается кнопка записи, в меню добавляется строка "New"MCIWNDF_NOERRORDLGПри возникновении ошибки на экран не выводится диалоговая панель с описанием этой ошибки. Приложение может получить описание самой последней возникшей ошибки при помощи функции MCIWndGetErrorMCIWNDF_NOTIFYMODEПри изменении режима родительское окно получит извещающее сообщение MCIWNDM_NOTIFYMODEMCIWNDF_NOTIFYPOSПри изменении текущей позиции приложение получит извещающее сообщение MCIWNDM_NOTIFYPOSMCIWNDF_NOTIFYMEDIAПри замене носителя данных (например, звукового компакт-диска) приложение получит извещающее сообщение MCIWNDM_NOTIFYMEDIAMCIWNDF_NOTIFYSIZEРодительское окно получит извещающее сообщение MCIWNDM_NOTIFYSIZE при изменении размераMCI-окнаMCIWNDF_NOTIFYERRORПри возникновении ошибки родительское окно получит сообщение MCIWNDM_NOTIFYERRORMCIWNDF_NOTIFYALL MCI-окно будет извещать родительское окно в случае возникновения любых событийMCIWNDF_SHOWNAMEВ заголовке окна будет отображаться имя устройства или путь к файлуMCIWNDF_SHOWPOSВ заголовке окна будет отображаться текущая позицияMCIWNDF_SHOWMODEВ заголовке окна будет отображаться текущий режим работыMCIWNDF_SHOWALLБудут использованы все возможности MCI-окна (все органы управления, отображение информации в заголовке и так далее)Другой способ создания MCI-окна ― регистрация класса окна MCIWND_WINDOW_CLASS функцией MCIWndRegisterClass, не имеющей параметров, и создании на базе этого класса окна функцией CreateWindow. После создания окна запускается цикл обработки сообщений. asm-файл Код (ASM): ; GUI # include win64a.inc include msvfw32.inc includelib msvfw32.lib MENU_ABOUT equ 5 MCIWNDF_NOPLAYBAR equ 2 MCIWNDF_NOMENU equ 8 MCIWNDM_CHANGESTYLES equ WM_USER+135 MCIWNDM_SENDSTRINGA equ WM_USER+101 MCIWNDM_SETZOOM equ WM_USER+108 MCIWNDM_OPENA equ WM_USER+153 IDC_ICON1 equ 500 MSGBOXPARAMS struct cbSize dd ?,? hwndOwner dq ? hInstance dq ? lpszText dq ? lpszCaption dq ? dwStyle dd ?,? lpszIcon dq ? dwContextHelpId dd ?,? lpfnMsgBoxCallback dq ? dwLanguageId dd ?,? MSGBOXPARAMS ends .code WinMain proc local buffer[128]:BYTE local msg:MSG xor ebx,ebx lea eax,buffer mov pBuffer,rax invoke MCIWndCreate,0,IMAGE_BASE,WS_VISIBLE or WS_OVERLAPPEDWINDOW or MCIWNDF_NOMENU,&MediaFileName;имя устройства или путь к файлу mov mcihWnd,rax invoke DragAcceptFiles,eax,TRUE ;----------------------------------------------- invoke GetSystemMenu,mcihWnd,FALSE mov hMenu,rax @@: mov rax,handler_menu[rbx*8] mov [rsp+20h],rax invoke InsertMenuA,hMenu,ebx,MF_BYPOSITION,ebx inc ebx cmp ebx,6 jb @b invoke SetWindowLongPtrA,mcihWnd,GWL_WNDPROC,&WndProc mov prevWindow,rax invoke LoadCursorFromFileA,&FileNameCur invoke SetClassLongPtrA,mcihWnd,GCL_HICON,rax lea edi,msg @@: invoke GetMessageA,edi,0,0,0 invoke DispatchMessageA,edi jmp @b handler_menu dq szFile,szCD,szVideoDisk,szVideoPlayer,szFullScreen,szAbout WinMain endp WndProc proc hWnd:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD mov hWnd,rcx mov uMsg,rdx mov wParam,r8 mov lParam,r9 xor ebx,ebx cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_SYSCOMMAND je wmSYSCOMMAND cmp edx,WM_DROPFILES je wmDROPFILES cmp edx,MM_MCINOTIFY je mmMCINOTIFY cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_DESTROY jne default wmDESTROY:invoke RtlExitUserProcess,NULL mmMCINOTIFY:invoke ShowWindow,hWnd,SW_MINIMIZE or SW_SHOWNORMAL invoke SendMessageA,hWnd, MCIWNDM_CHANGESTYLES,MCIWNDF_NOMENU,FALSE invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szStop jmp default wmDROPFILES:invoke DragQueryFile,wParam,0,pBuffer,128 invoke DragFinish,wParam jmp a1 wmSYSCOMMAND:cmp r8d,MENU_ABOUT ja default jmp [handle+r8*8] wmCLOSE:invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szStop invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szClose invoke DestroyWindow,hWnd jmp default mnuFILE:mov rdx,pBuffer and [rdx],rbx; ноль в начало буфера для названия файла ;заполняю OPENFILENAME push rbx ;<--- align 16 bytes push rbx;lpTemplateName push rbx;lpfnHook push rbx;lCustData push rbx;lpstrDefExt push OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or OFN_LONGNAMES \ or OFN_EXPLORER or OFN_HIDEREADONLY;Flags & nFileOffset & nFileExtension push rbx;lpstrTitle push rbx;lpstrInitialDir push rbx;nMaxFileTitle push rbx;lpstrFileTitle push 124;nMaxFile push rdx;lpstrFile push rbx;nMaxCustFilter & nFilterIndex push rbx;lpstrCustomFilter pushaddr LoadFileFilter;lpstrFilter push IMAGE_BASE;hInstance push hWnd;hwndOwner push sizeof OPENFILENAME;lStructSize ;---------------------------------------------------- mov edi,esp;адрес OPENFILENAME invoke GetOpenFileName,edi test eax,eax jz wmCLOSE; если не выбран никакой файл movzx edx,word ptr [rdi+OPENFILENAME.nFileExtension] add rdx,[rdi+OPENFILENAME.lpstrFile];offset LoadFileBuffer ;add esp,4Ch; удаляю OPENFILENAME mov eax,[rdx];в регистре еах расширение файла or eax,202020h;приводим к нижнему регистру cmp eax,"adc" jnz a1 mnuCD: mov r8d,offset cdaudioDev jmp @f mnuVIDEODISK: mov r8d,offset videodiscDev jmp @f;wmBYE mnuVIDEOPLAYER:mov r8d,offset szVideoPlayer @@: invoke lstrcpy,pBuffer a1: invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szStop invoke SendMessageA,hWnd,MCIWNDM_OPENA,0,pBuffer test eax,eax jnz NoMedia; .if eax = 0 invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szPlay test eax,eax jnz NoMedia; .if eax = 0 invoke ShowWindow,hWnd,SW_SHOWNORMAL invoke SendMessageA,hWnd,MCIWNDM_CHANGESTYLES,MCIWNDF_NOPLAYBAR,0 invoke SendMessageA,hWnd,MCIWNDM_SETZOOM,0,100 mov flag_fullscreen,TRUE jmp @f NoMedia:mov flag_fullscreen,rbx;FALSE invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szClose invoke SendMessageA,hWnd, MCIWNDM_CHANGESTYLES, MCIWNDF_NOPLAYBAR, MCIWNDF_NOPLAYBAR @@: invoke DragAcceptFiles,mcihWnd,TRUE jmp default mnuFULLSCREEN:invoke ShowWindow,hWnd,SW_SHOWNORMAL invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szPlayfullscreen test eax,eax;.if eax=0 jnz default invoke SendMessageA,hWnd,MCIWNDM_CHANGESTYLES, MCIWNDF_NOPLAYBAR, MCIWNDF_NOPLAYBAR jmp default mnuABOUT:invoke MessageBoxIndirectA,&mb default:mov rax,lParam mov [rsp+20h],rax invoke CallWindowProcA,prevWindow,hWnd,uMsg,wParam leave retn handle dq mnuFILE,mnuCD, mnuVIDEODISK, mnuVIDEOPLAYER, mnuFULLSCREEN, mnuABOUT WndProc endp ;--------------------------------------- .data FileNameCur db "br_Rabbit3.cur",0 mcihWnd dq ? prevWindow dq ? pBuffer dq ? hMenu dq ? flag_fullscreen dq FALSE szFile db "File",0 szCD db "Audio CD",0 szVideoDisk db "Video CD",0 szVideoPlayer db "vcr",0 szFullScreen db "Full screen",0 LoadFileFilter db "All files",0,"*",0,0; szAbout db "About program...",0 szPlay db "PLAY",0 szStop db "STOP",0 szClose db "CLOSE",0 szPlayfullscreen db "PLAY FULLSCREEN NOTIFY",0 szAboutText db "Mini-media player",0Dh,0Ah,\ "Based on JTG coding",0 cdaudioDev db "cdaudio",0 vcrExt db "UNKNOWN",0 videodiscDev db "videodisk",0 mb label MSGBOXPARAMS dd sizeof MSGBOXPARAMS,?;cbSize dq 0 ;hwndOwner dq IMAGE_BASE ;hInstance dq szAboutText ;lpszText dq szAbout ;lpszCaption dd MB_OK or MB_USERICON,?;dwStyle dq IDC_ICON1 ;lpszIcon dd 0,?;dwContextHelpId dq 0 ;lpfnMsgBoxCallback dd 0,?;dwLanguageId MediaFileName db "movie7.wmv" end rc-файл Код (C++): #define IDC_ICON1 500 IDC_ICON1 ICON "br_Fox1.ico"
Код (ASM): ; GUI # include win64a.inc include msvfw32.inc includelib msvfw32.lib MCIWNDF_NOMENU equ 8 MCIWNDM_GETLENGTH equ WM_USER+104 MCIWNDM_SENDSTRINGA equ WM_USER+101 .code WinMain proc local msg:MSG invoke MCIWndCreate,0,IMAGE_BASE,WS_VISIBLE or WS_OVERLAPPEDWINDOW or \ MCIWNDF_NOMENU,&MediaFileName;имя устройства или путь к файлу mov mcihWnd,rax invoke SetWindowLongPtrA,mcihWnd,GWL_WNDPROC,&WndProc mov prevWindow,rax invoke LoadCursorFromFileA,&FileNameCur invoke SetClassLongPtrA,mcihWnd,GCL_HICON,rax lea edi,msg @@: invoke GetMessageA,edi,0,0,0 invoke DispatchMessageA,edi jmp @b WinMain endp WndProc proc hWnd:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD mov hWnd,rcx mov uMsg,rdx mov wParam,r8 mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_DESTROY jne default wmDESTROY:invoke RtlExitUserProcess,NULL wmCLOSE:invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szStop invoke SendMessageA,hWnd,MCIWNDM_SENDSTRINGA,0,&szClose invoke DestroyWindow,hWnd default:mov rax,lParam mov [rsp+20h],rax invoke CallWindowProcA,prevWindow,hWnd,uMsg,wParam leave retn WndProc endp ;--------------------------------------- .data FileNameCur db "br_Rabbit3.cur",0 mcihWnd dq ? prevWindow dq ? szStop db "STOP",0 szClose db "CLOSE",0 MediaFileName db "movie9.wmv" end
Код (ASM): ; GUI # include win64a.inc include winmm.inc includelib winmm.lib .code WinMain proc local msg:MSG xor ebx,ebx mov esi,IMAGE_BASE mov edi,offset ClassName invoke LoadCursorFromFileA,&FileNameCur push rax ;hIconSm push rdi ;lpszClassName push rbx ;lpszMenuName push COLOR_WINDOWTEXT;hbrBackground push rax ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc ;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassExA,esp ;addr WNDCLASSEX push rbx push rsi ;rsi=400000h shl esi,9 ;rsi=CW_USEDEFAULT push rbx push rbx push 360 push 450 push rsi push rsi sub esp,20h invoke CreateWindowExA,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE lea edi,msg @@: invoke GetMessageA,edi,0,0,0 invoke DispatchMessageA,edi jmp @b WinMain endp WndProc proc hWnd:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD mov hWnd,rcx mov uMsg,rdx mov wParam,r8 mov lParam,r9 cmp edx,WM_CREATE je wmCREATE cmp edx,WM_DESTROY je wmDESTROY cmp edx,MM_MCINOTIFY je mmMCINOTIFY leave jmp NtdllDefWindowProc_ ;-------------- Close AVI wmDESTROY:mov ecx,mciOP.wDeviceID invoke mciSendCommand,,MCI_CLOSE,0,&mciCP invoke RtlExitUserProcess,NULL wmCREATE:mov mciOP.hWndParent,rcx;hWnd invoke mciSendCommand,0,MCI_OPEN,MCI_WAIT or MCI_OPEN_ELEMENT or \ MCI_ANIM_OPEN_WS or MCI_ANIM_OPEN_PARENT,&mciOP or eax,eax je @f invoke MCIError ;------------------------ EOF AVI mmMCINOTIFY:invoke DestroyWindow jmp wmBYE ;------------------------ Play AVI @@: mov rax,hWnd mov mciPP.dwCallback,rax mov ecx,mciOP.wDeviceID invoke mciSendCommand,,MCI_PLAY,MCI_NOTIFY,&mciPP or eax,eax jz wmBYE CALL MCIError wmBYE: leave retn WndProc endp ;-------------------------------------- MCIError proc local buffer[100]:BYTE;временный буфер для сообщения об ошибках invoke mciGetErrorString,rax,&buffer,100 invoke MessageBoxA,0,&buffer,&aMcierror,MB_OK or MB_ICONSTOP leave retn MCIError endp ;--------------------------------------- .data FileNameCur db "br_Rabbit3.cur",0 ;align 8 mciOP MCI_ANIM_OPEN_PARMS <0,0,DeviceType,ElementName,0,WS_CHILD,0> mciPP MCI_ANIM_PLAY_PARMS <0> mciCP MCI_GENERIC_PARMS <1> aMcierror db "MCI Error",0 DeviceType DB 'AVIVideo',0 ElementName db "movie10.wmv",0 ClassName db 'Uncle Remus tales:#37e-2 Animation',0 end
Используем GDI+ для просмотра анимированного gif-файла ФункцияОписаниеGdiplusStartup инициализирует Windows GDI+GdipLoadImageFromFileСоздает объект изображения на основе файлаGdipDisposeImageОсвобождает ресурсы, используемые объектомGdipImageGetFrameDimensionsCountВозвращает количество размеров кадра в этом объекте ImageGdipAllocвыделяет память для одного объекта Windows GDI+GdipImageGetFrameDimensionsListВозвращает идентификаторы размеров кадра этого объекта ImageGdipImageGetFrameCountвозвращает количество кадров выбранной размерности GdipGetPropertyItemSizeВозвращает размер (в байтах) указанного элемента свойства этого объекта ImageGdipGetPropertyItemВозвращает указанный элемент свойства (часть метаданных) из этого объекта ImageGdipFreeОсвобождает память для одного объекта Windows GDI+GdipGetImageWidthВозвращает ширину этого изображения (в пикселях)GdipGetImageHeightВозвращает высоту изображения (в пикселях) этого изображения.GdiplusShutdownочищает ресурсы, используемые Windows GDI+ Код (ASM): ; GUI # include win64a.inc include gdiplus.inc includelib gdiplus.lib PropertyTagFrameDelay equ 5100h cdXPos equ 127 cdYPos equ 127 cdXSize equ 200 cdYSize equ 100 cdVBarTipo equ 0 cdMainIcon equ 100 cdIdTimer equ 1 .code WinMain proc local msg:MSG local ptDiff:POINT local rctWnd:RECT local rctClient:RECT local nSize:qword local pDimensionIDs:qword local count:qword ;инициализация библиотеки GDIPLUS invoke GdiplusStartup,&gdiplusToken,&gsi,0 xor ebx,ebx mov edi,offset ClassName invoke LoadCursorFromFile,&FileName push rax ;hIconSm push rdi ;lpszClassName push rbx ;lpszMenuName push COLOR_WINDOWTEXT ;hbrBackground push rax ;hCursor push rax ;hIcon push IMAGE_BASE ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc ;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassExA,esp ;addr WNDCLASSEX invoke GdipLoadImageFromFile,&szfilename,&g_image ;TestForAnimatedGIF------------------------------- lea edx,count mov [rdx],ebx;count=0 mov ecx,g_image invoke GdipImageGetFrameDimensionsCount mov ecx,dword ptr count test ecx,ecx jz @f shl ecx,4 invoke GdipAlloc mov dword ptr pDimensionIDs,eax mov ecx,g_image invoke GdipImageGetFrameDimensionsList,,eax,count or eax,eax jnz @f mov ecx,g_image invoke GdipImageGetFrameCount,,pDimensionIDs,&nFrameCount or eax,eax jnz @f mov ecx,g_image invoke GdipGetPropertyItemSize,,PropertyTagFrameDelay,&nSize or eax,eax jnz @f mov ecx,dword ptr nSize shl ecx,2 invoke GdipAlloc mov pPropertyItem,eax mov ecx,g_image invoke GdipGetPropertyItem,,PropertyTagFrameDelay,nSize,eax or eax,eax jnz @f mov ecx,dword ptr pDimensionIDs invoke GdipFree @@:;------------------------------------ mov edx,offset nWidth mov ecx,g_image call GdipGetImageWidth mov edx,offset nHeight mov ecx,g_image call GdipGetImageHeight push rbx push IMAGE_BASE push rbx push rbx push cdYSize ; nHeight push cdXSize ; nWidth push cdYPos ; Y push cdXPos ; X sub esp,20h invoke CreateWindowExA,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE mov hMainWnd,eax invoke GetClientRect,eax,&rctClient ;lpRect mov ecx,hMainWnd invoke GetWindowRect,,&rctWnd ;lpRect mov eax,rctWnd.right sub eax,rctWnd.left sub eax,rctClient.right mov ptDiff.x,eax mov eax,rctWnd.bottom sub eax,rctWnd.top sub eax,rctClient.bottom mov ptDiff.y,eax mov qword ptr [rsp+28h],TRUE ; bRepaint mov ecx,nHeight add ecx,ptDiff.y mov [rsp+20h],rcx ;nHeight mov eax,nWidth add eax,ptDiff.x mov ecx,hMainWnd invoke MoveWindow,,cdXPos,cdYPos,rax lea edi,msg @@: invoke GetMessageA,edi,NULL,0,0 cmp msg.wParam,VK_ESCAPE;user press 'Esc'? je wmDESTROY invoke DispatchMessageA,edi jmp @b WinMain endp WndProc proc hWnd:qword,umsg:qword,wParam:qword,lParam:qword local ps:PAINTSTRUCT local graphics:qword mov hWnd,rcx mov wParam,r8 mov lParam,r9 cmp edx,WM_DESTROY je wmDESTROY cmp edx,WM_CREATE je wmCREATE cmp edx,WM_SIZE je wmSIZE cmp edx,WM_PAINT je wmPAINT cmp edx,WM_TIMER je wmTIMER leave jmp NtdllDefWindowProc_ wmDESTROY::invoke KillTimer,,cdIdTimer cmp g_image,0 jz @f mov ecx,g_image invoke GdipDisposeImage @@: cmp pPropertyItem,0 jz @f mov ecx,pPropertyItem invoke GdipFree @@:invoke GdiplusShutdown,gdiplusToken invoke RtlExitUserProcess,NULL wmPAINT:invoke BeginPaint,hWnd,&ps ;DrawFrameGIF--------------------- mov ecx,g_image invoke GdipGetImageWidth,,&nWidth or eax,eax jnz @f mov ecx,g_image invoke GdipGetImageHeight,,&nHeight or eax,eax jnz @f invoke GdipCreateFromHDC,ps.hdc,&graphics mov eax,dword ptr vdyClient mov [rsp+28h],rax mov eax,dword ptr vdxClient mov [rsp+20h],rax mov edx,g_image invoke GdipDrawImageRectI,graphics,,0,0 invoke GdipDeleteGraphics,graphics mov r8d,nFramePosition inc nFramePosition mov ecx,g_image invoke GdipImageSelectActiveFrame,,&pageGuid mov eax,nFramePosition sub eax,nFrameCount jnz @f mov nFramePosition,eax ;------------------------------- @@: invoke EndPaint,hWnd,&ps jmp wmBYE wmTIMER:invoke InvalidateRect,hWnd,0,0 jmp wmBYE wmCREATE:invoke SetTimer,hWnd,cdIdTimer,100,0 jmp wmBYE wmSIZE: mov rax,r9;lParam mov vdxClient,ax shr eax,10h mov vdyClient,ax jmp wmBYE wmBYE: leave retn WndProc endp ;----------------------------------- ClassName db "Uncle Remus tales:#37i gif animation",0 FileName db "br_Rabbit3.cur",0 gsi GdiplusStartupInput <1,0,0,0,0> pageGuid GUID <6AEDBD6Dh,3FB5h,418Ah,<83h,0A6h,7Fh,45h,22h,9Dh,0C8h,72h>> szfilename: du <movie.gif> gdiplusToken dq ? nWidth dd ? nHeight dd ? vdxClient dw 0,0 vdyClient dw 0,0 nFrameCount dd ? nFramePosition dd 0 pPropertyItem dd ? g_image dd ? hMainWnd dd ? end
Используем GDI+ для создания анимации из jpg-файлаЧтобы получить этот проигрыватель пришлось поиграть в Уолта Диснея. Из фильма "The song of south" был вырезан 8 секундный клип, из этого клипа вырезались фрагменты помеченные красным после склейки этих фрагментов был создан файл all3.jpg Перед тем как использовать GDI+, необходимо инициализировать Windows GDI+. Для этого используется функция GdiplusStartup. Функция имеет три параметра. Первый параметр — адрес учетверенного слова gdiplusToken, куда будет помещен маркер, который будет использован при закрытии библиотеки. Второй параметр — указатель на структуру gsi типа GdiplusStartupInput. Первый элемент структуры содержит версию библиотеки =1. Во втором элементе помещают указатель на процедуру, которая получит управление в случае возникновения ошибки, в третьем элементе — логическое значение, указывающее, следует ли подавлять фоновый поток, и в четвертом элементе — логическое значение, указывающее, следует ли подавлять внешние кодеки изображений. В простейшем случае (как у нас) эти поля заполняют нулям. В конце работы программы закрываем Windows GDI+ при помощи функции GdiplusShutdown. Единственный параметр этой функции — значение маркера gdiplusToken, который получаем после вызова функции GdiplusStartup. Отображение рисунка, хранящегося в файле, осуществляется по схеме: загрузить изображение из файла в память с помощью GdipCreateBitmapFromFile → создать изображение с помощью GdipCreateHBITMAPFromBitmap → освободить память с помощью GdipDisposeImage. Имя файла all3.jpg указано в формате Unicode. При загрузке изображения в память получают дескриптор pImage, который затем используется для вывода изображения в окно. Из одного изображения с помощью ImageList_Add создаем список изображений и далее, с помощью ImageList_Draw, рисуем элемент из списка изображений в указанный контекст устройства (на экран). всего 210 кадров, каждый кадр остается на экране 1/25 секунды (40 мСек), затем переходим к следующему кадру. По достижению 210 кадра счетчик обнуляется и все повторяется сначала Оцените! Код (ASM): ; GUI # include win64a.inc include gdiplus.inc includelib gdiplus.lib ILD_NORMAL equ 0 .code WinMain proc local msg:MSG local hwnd:qword ;inizialisation GDIPLUS invoke GdiplusStartup,&gdiplusToken,&gsi,0 xor ebx,ebx mov esi,IMAGE_BASE mov edi,offset ClassName invoke LoadCursorFromFileA,&FileName push rax ;hIconSm push rdi ;lpszClassName push rbx ;lpszMenuName push rbx ;COLOR_WINDOWTEXT;hbrBackground push rax ;hCursor push rax ;hIcon push rsi ;hInstance push rbx ;cbClsExtra & cbWndExtra pushaddr WndProc ;lpfnWndProc push sizeof WNDCLASSEX;cbSize & style invoke RegisterClassExA,esp ;addr WNDCLASSEX push rbx push rsi ;rsi=400000h shl esi,9 ;rsi=CW_USEDEFAULT push rbx push rbx push 180 push 170 push rsi push rsi sub esp,20h invoke CreateWindowExA,0,edi,edi,WS_CAPTION or WS_SYSMENU or WS_VISIBLE mov hwnd,rax and qword ptr [rsp+20h],0 invoke ImageList_Create,153,147,ILC_COLOR24,1 mov hList,rax invoke GdipCreateBitmapFromFile,&FilenameW,&pImage invoke GdipCreateHBITMAPFromBitmap,pImage,&hBmp,0 invoke GdipDisposeImage,pImage invoke ImageList_Add,hList,hBmp,0 invoke DeleteObject,hBmp invoke GetDC,hwnd mov hDC,rax invoke SetTimer,hwnd,1,40,&Draw lea edi,msg @@: invoke GetMessageA,edi,NULL,0,0 invoke DispatchMessageA,edi jmp @b WinMain endp WndProc: cmp edx,WM_DESTROY je wmDESTROY jmp NtdllDefWindowProc_ wmDESTROY:invoke GdiplusShutdown,gdiplusToken invoke RtlExitUserProcess,NULL ;------------------------------------- Draw proc enter 30h,0 and qword ptr [rbp-8],ILD_NORMAL; drawing style and qword ptr [rbp-10h],0;y-coordinate to draw at invoke ImageList_Draw,hList,count,hDC,0 inc count cmp count,210 jb @f and count,0 @@: leave ret Draw endp ;----------------------------------- .data align 2 FilenameW: du <all3.jpg> ClassName db "Uncle Remus tales:#37j Simple animation",0 gsi GdiplusStartupInput <1,0,0,0,0> FileName db "br_Rabbit3.cur",0 gdiplusToken dq ? hBmp dq ? count dq ? hList dq ? hDC dq ? pImage dq ? end