Часть вторая. Третья на подходе. Однако до создания окна необходимо определить его самые основные параметры. Эти параметры записываются в специальную структуру, так наз. "класс окна". Вот она: WNDCLASS STRUCT style DWORD lpfnWndProc DWORD cbClsExtra DWORD cbWndExtra DWORD hInstance DWORD hIcon DWORD hCursor DWORD hbrBackground DWORD lpszMenuName DWORD lpszClassName DWORD WNDCLASS ENDS Если вы зарегистрируете свой класс окна - значит, вы по этому готовому "шаблону" сможете потом насоздавать в своей программе сколько угодно однотипных окон этого класса. Например, управляющие элементы типа "кнопка" - окна, созданные по образцу системного класса BUTTON. Если Вы знакомы с буржуйским языком, то советую лучше почитать насчёт этой структуры соответствующий раздел MSDN-а. Если же знания английского отсутствуют, то попытаюсь вкратце объяснить. style: так наз. "стиль" окна. Может являться комбинацией следующих флагов - CS_BYTEALIGNCLIENT - выравнивание массива пикселей внутренней области окна на двухбайтную границу. Облегчает работу видеокарте при перерисовке, однако в некоторых случаях бывает неприемлемо. CS_BYTEALIGNWINDOW - Практически то же самое, с теми же ограничениями, только применительно ко всему окну, а не только к внутренней области. CS_CLASSDC - Контекст Графического Устройства(КГУ, или, по-буржуйски, DC. Идентифицирует область рисования окна) создаётся для всех окон в единичном экземпляре, а не для каждого окна данного класса по отдельности. Т.е., теоретически, изменения картинки на всех окнах будут происходить синхронно. CS_DBLCLKS - Разрешает окну принимать сообщения о двойном щелчке мышой (почему окна создаются по умолчанию без этой возможности - тайна великая есть). CS_GLOBALCLASS - Делает класс доступным не только создавшему его процессу, но и всем остальным. CS_HREDRAW - Окно в случае перемещения/изменения размера по горизонтали должно перерисоваться. CS_NOCLOSE - Делает недоступной кнопку закрытия [x] в заголовке. CS_OWNDC - Присваивает каждому окну уникальный DC. CS_PARENTDC - Присваивает каждому окну DC окна, от имени которого произошло его создание. CS_SAVEBITS - Сохраняет область экрана, занятую окном. CS_VREDRAW - Окно в случае перемещения/изменения размера по вертикали должно перерисоваться. lpfnWndProc: указатель на первый байт оконной процедуры для данного окна. Это адрес, по которому будет передаваться управление при приходе сообщений. cbClsExtra: система зарезервирует указанное количество байт после оконной структуры для размещения некоторых специфических данных, определяемых программистом. Если этого не нужно, данный параметр должен быть выставлен в 0. cbWndExtra: то же самое, только применительно к самому окну. hInstance: базовый адрес процесса, запросившего создание класса. hIcon: иконка, отображаемая рядом со строкой заголовка. hCursor: курсор, на который будет заменёна стандартная "стрелка" при наведении на окно. Если этого не нужно, пишем сюда 0. hbrBackground: графический объект кисть, который система будет пользовать для закрски окна. lpszMenuName: указатель на ASCIZ-строку, определяющую имя "заготовки" главного меню, подгружаемого при необходимости из ресурсной секции программы. lpszClassName: и, наконец, главный параметр. Здесь должен быть записан указатель на ASCIZ-строку, содержащую имя класса, т.е. имя, под которым класс сопстно и будет регистрироваться, а потом - вызываться. После заполнения элементов данной структуры нужными значениями, следует вызвать API-функцию RegisterClass, которая прикажет системе создать класс окна с выбранными параметрами. Замечу, что, согласно МСДН, все созданные приложением классы автоматически освобождаются при его завершении. Теперь поговорим о том, как, собственно, организовать приёмопередачу сообщений. Для того, чтобы это реализовать, необходимо в теле программы запустить цикл приёма: .data msg MSG <>; .code ... @1: invoke GetMessage, ADDR msg, 0, 0, 0; CMP EAX, 0; JE @exit; invoke TranslateMessage, ADDR msg; invoke DispatchMessage, ADDR msg; JMP @1; Этот код начинает при работе постоянно опрашивать систему на предмет посылки окну сообщений, вынимая их последовательно из специальной "очереди сообщений", ассоциированной с текущим потоком команд. Этим занимается GetMessage. TranslateMessage отвечает за приведение сообщений виртуальных кодов к символьным (очевидно, в МСДН имеется в виду клавиатурный ввод), а DispatchMessage переправляет сообщение на обработку, и цикл повторяется заново. Возник вопрос: ну вот нам в процедуру направлено сообщение. А как его обработать? Для этого существует специальное соглашение, регламентирующее параметры вызова оконной процедуры и возвращаемый результат. Вот простейший шаблон: (hWnd - идентификатор окна; uMsg - присланное сообщение; wParam, lParam - сопутствующие параметры) WindowProc proc hWnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM; CMP uMsg, WM_CREATE; JE @wm_create; CMP uMsg, WM_DESTROY; JE @wm_destroy; @inherited: invoke DefWindowProc, hWnd, uMsg, wParam, lParam; @oops: RET; @wm_create: XOR EAX, EAX; RET; @wm_destroy: invoke PostQuitMessage, 0; RET; WindowProc endp; Алгоритм работы таков: параметр uMsg последовательно сравнивается с идентификаторами сообщений, обработка которых задана в программе явно, и если есть совпадение - управление передается на соответствующую метку. Если совпадений нет, в дело вступает стандартный системный обработчик - DefWindowProc, реализующий реакцию окна "по умолчанию". Вы, наверное, обратили внимание на метки @inherited: и @oops:, на которые нигде не задан переход. Это всего лишь дя удобства последующего дописывания процедуры: практика показала, что иногда вслед за собственным обработчиком сообщения, должен быть выполнен и системный, и тогда пользуется не RET, а JMP @inherited. Если же нужно реализовать экстренный выход из процедуры по условию, можно пользовать метку @oops. А теперь приступим к практике и сведём всё воедино. ЗЫ извиняюсь перед теми, кто не привык видеть после каждой строчки кода паскале-подобные точки с запятой. Так просто намного удобнее впоследствии комментить, имхо.
или sources.ru с CodeNet. для бегиннеров там всё. только статьи глупые. в этом треде хоть статья получается на удивление складная и грамотная. стало быть упячка отменяется.
Обьясните пожалуйста что такое прототип функции и зачем он ваще нужен. Это я читаю икзелионские уроки
Loginanton Это чтобы компилятор знал, что имя соответствует имени функции и какие/сколько у неё параметров. Используется для функций размещённых во внешних библиотеках или вызываемых до того как они описаны в тексте программы.
Третья, последняя, часть Итак, внимание код: Код (Text): .386; .model flat, stdcall; option casemap:none; include c:\progra~1\masm32\include\windows.inc; include c:\progra~1\masm32\include\user32.inc; include c:\progra~1\masm32\include\kernel32.inc; include c:\progra~1\masm32\include\gdi32.inc; include c:\progra~1\masm32\include\comdlg32.inc; include c:\progra~1\masm32\include\shell32.inc; includelib c:\progra~1\masm32\lib\user32.lib; includelib c:\progra~1\masm32\lib\kernel32.lib; includelib c:\progra~1\masm32\lib\gdi32.lib; includelib c:\progra~1\masm32\lib\comdlg32.lib; includelib c:\progra~1\masm32\lib\shell32.lib; .const CN db "WndClass",0; WN db "Test",0; IC db "MAIN",0; .data msg MSG <>; wc WNDCLASS <>; mWnd HWND 0; WX DWORD 0; WY DWORD 0; WW DWORD 300; WH DWORD 200; .code @prgm: invoke GetModuleHandle, 0; <-- *пояснения далее* MOV wc.hInstance, EAX; MOV wc.style, CS_HREDRAW or CS_VREDRAW; <-- нам нужно только лишь обеспечить перерисовку MOV wc.lpfnWndProc, OFFSET WindowProc; <-- передаём указательна точку входа в нашу оконную процедуру MOV wc.cbClsExtra, 0; <-- даём знать, что ничего не резервируем MOV wc.cbWndExtra, 0; <-- *аналогично* invoke LoadIcon, EAX, OFFSET IC; <-- *пояснения далее* MOV wc.hIcon, EAX; invoke GetSysColorBrush, COLOR_BTNFACE; <-- *пояснения далее* MOV wc.hbrBackground, EAX; MOV wc.lpszClassName, OFFSET CN; invoke LoadCursor, 0, IDC_HAND; <-- *пояснения далее* MOV wc.hCursor, EAX; invoke RegisterClass, ADDR wc; invoke GetSystemMetrics, SM_CXSCREEN; MOV WX, EAX; invoke GetSystemMetrics, SM_CYSCREEN; MOV WY, EAX; MOV EAX, WW; MOV EDX, WH; SUB WX, EAX; SHR WX, 1; SUB WY, EDX; SHR WY, 1; invoke CreateWindowEx, 0, OFFSET CN, OFFSET WN, WS_VISIBLE or WS_SYSMENU, WX, WY, EAX, EDX, 0, 0, wc.hInstance, 0; MOV mWnd, EAX; invoke ShowWindow, mWnd, SW_SHOWNORMAL; invoke UpdateWindow, mWnd; @getM: invoke GetMessage, ADDR msg, 0, 0, 0; CMP EAX, 0; JE @exit; invoke TranslateMessage, ADDR msg; invoke DispatchMessage, ADDR msg; JMP @getM; @exit: invoke ExitProcess, msg.wParam; WindowProc proc hWnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM; CMP uMsg, WM_CREATE; JE @wm_create; CMP uMsg, WM_DESTROY; JE @wm_destroy; invoke DefWindowProc, hWnd, uMsg, wParam, lParam; RET; @wm_create: XOR EAX, EAX; RET; @wm_destroy: invoke PostQuitMessage, 0; RET; WindowProc endp; end @prgm; Первое, что бросается в глаза - довольно значительное количество инклудов. Дело в том, что данный код - это готовый шаблон, нацеленный на создание начинающему WinAPI-программеру минимума проблем с различными еррорами на почве "незнания" компилятором каких-либо специфических функций типа SHGetSpecialFolderPath, раскиданных зачем-то по разным модулям. Ну тык вот. Далее - секция констант. Первая (CN) - имя класса, под которым будет региться окно. Второй (WN) - Текст, который будет отображаться в заголовке окна. Третий (IC) - имя иконки, которая должна после линковки находиться в секции ресурсов. Если вы не хотите линковать иконки, эту строку (и некоторые далее) смело можно удалить. Затем - данные. msg - это уже обговорённая переменная для выполнения приёма сообщений. wc - переменная для регистрации класса. mWnd - переменная, в которую будет записан идентификатор главного окна. WX, WY - изначальное положение окна на экране (рассчитывается вручную перед создании окна), WW и WH - соответственно ширина и высота окна. Теперь пошёл код. Кусок текста до строки > invoke RegisterClass, ADDR wc; включительно, составляет подготовку и регистрацию класса нашего главного окна. Давайте разберёмся с тем, что вызывается в процессе заполнения. Функция GetModuleHandle с параметром 0 возвращает нам базовый адрес текущего процесса. Он, в частности, понадобится для создания окон и обращения к ресурсам типа иконок, курсоров или заготовок меню, прилинкованным к проге (буде такие найдутся). Функция LoadIcon загружает (пытается загрузить) из ресурса с именем MAIN иконку для окна. Обратите внимание - мы пока что не трогали ЕАХ, поэтому в нем всё ещё содержится базовый адрес, чем мы и пользуемся. Функция GetSysColorBrush создаёт графический объект типа кисть, имеющий один из "системных" цветов (за полным списком возможных констант цветов обращайтесь к справке по GetSysColor!), в данном случае - цвет поверхности окна (бежевый для стандартных настроек WinXP и серый - для 2К и ниже). Далее - LoadCursor. Это предназначено для загрузки курсоров. Кстати, здесь курсор берётся не из ресурсов нашей программы, а из системных библиотек - на это указывает 0 в качестве базового адреса. Подобным функционалом обладает и LoadIcon. Более подробная инфа - в МСДН, ибо тут всё предельно просто, а список констант довольно длинный. Затем идёт GetSystemMetrics - это функция для определения различных параметров окон и экрана, как то размеров рабочего стола, высоты строки меню, ширины рамки окна, итд. В данном случае, определяются размеры монитора, причём берётся первый, "физический" монитор. Если в вашей системе установлен многомониторный режим и вы хотите получить ширину/высоту всего рабочего стола, юзайте SM_CX(или CY)VIRTUALSCREEN вместо SM_CX(CY)SCREEN. Опять же, список констант - в MSDN. И вот она, главная функция. > invoke CreateWindowEx, 0, OFFSET CN, OFFSET WN, WS_VISIBLE or WS_SYSMENU, WX, WY, EAX, EDX, 0, 0, wc.hInstance, 0; Первый параметр - это так называемый дополнительный набор флагов, дополнение к основному набору (4-ый с начала параметр). Поскольку битов в 4-х байтном числе всего 32, окно может в лучшем случае иметь набор из 32 уникально расшифровываемых атрибутов. Этого маловато, особенно учитывая, что могут попадаться несовместимые комбинации, тем более у окон разных классов могут оказаться разные требования к "внешнему виду" и "поведению" - всё это запихнуть в 32 бита невозможно. Поэтому для задания дополнительных свойств, число это "подняли" до 64, предоставив нам ещё один параметр. И как всегда, список констант огромен. Остановимся лишь на самых значительных: WS_EX_ACCEPTFILES: поддерживает Drag-n-Drop технологию применительно к файлам - окну отправляется сообщение WM_DROPFILES с полными путями к ним. WS_EX_CLIENTEDGE: делает рамку не выпуклой, а утопленной. WS_EX_MDICHILD: необходим для создания MDI-приложений. Заставляет окно полностью находиться в окне-"предке". WS_EX_TOOLWINDOW: убирает с таскбара "кнопку" окна (если это главное окно) и ужимает до минимума заголовок. WS_EX_TOPMOST: выводит окно поверх остальных, не обладающих этим флагом. WS_EX_LAYERED: позволяет применять к окну продвинутые графические эффекты типа прозрачности. Работает, начиная с Win2К. Более старыми Виндами игнорируется. Далее нужно указать имя класса, который будет наследовать наше будущее окно. Мы его только что создали, объяснять тут больше нечего. После этого идёт текст заголовка окна. Тут тоже особых трудностей возникнуть не должно. Затем идут основные флаги атрибутов. Основные: WS_BORDER: окно приобретает рамку WS_CAPTION: окно приобретает строку заголовка WS_CHILD: окно является потомком другого - четвёртый с конца параметр не игнорируется WS_CLIPCHILDREN: из области перерисовки исключаются все, порождённые данным, окна WS_CLIPSIBLINGS: из области перерисовки исключаются все окна того же класса WS_DISABLED: окно не реагирует на действия пользователя, являясь недоступным WS_MINIMIZE: окно с самого начала свёрнуто. Несовместимо с WS_MAXIMIZE WS_MAXIMIZE: окно с самого начала развёрнуто на весь экран WS_MINIMIZEBOX: в заголовок добавляется кнопочка [_] WS_MAXIMIZEBOX: в заголовок добавляется кнопочка [] WS_POPUP: окно без заголовка и кнопки в Панели Задач. Несовместимо с WS_CHILD WS_SYSMENU: в заголовке появляется иконка и кнопочка [x] WS_VISIBLE: окно с самого начала видимо Некоторые стили для многих системных классов типа BUTTON, EDIT, STATIC итп, имеют совсем иное значение (ибо, скажем, зачем кнопке строка заголовка?), а поэтому переопределены под другими именами: для BUTTON - с префиксом BS_, для STATIC - SS_, для EDIT - ES_. Полный список ищите в справке. Следующие четыре параметра - это позиция окна по ширине и высоте относительно левого верхнего угла родительского (для контролов и MDI-потомков), или от угла экрана для остальных типов окон, и сопстно его ширина с высотой. Четвёртый с конца параметр - родительское окно. Если родитель не нужен, пишем 0. Третий - идентификатор главного меню. Меню, заданное классу окна, перекрывает этот параметр. Второй - стартовый адрес родительского процесса. Последний - дополнительные данные, передаваемые окну при его создании. Мы пока никаких особых данных не пересылаем, поэтому тоже пишем 0. Связка ShowWindow+UpdateWindow "на глаз" никакого эффекта не имеет. Однако во многих руководствах (по-моему, даже в МСДН) пишут, что мол при создании главного окна "так надо". Ну надо значит надо... впрочем, оставляю на Ваше усмотрение И, наконец, функция ExitProcess, когда окно перестало принимать сообщения, вырубает процесс и заставляет систему снять его из памяти. Замечание. Обработчикам сообщений требуется возвращать в ЕАХ результат их обработки: принято/не_принято/принято_особым_образом. Для разных сообщений коды этих состояний могут различаться. Например, для WM_CREATE, EAX равное 0 означает успешное принятие. Как всегда, смотрим справку =) Функция DefWindowProc возвращает результат сама. Равно как и PostQuitMessage, и все функции, коды возврата которых намеренно (или случайно ;D) совпадают с кодами возврата для сообщений. Следите за тем, чтобы последняя вызванная Вами перед RET функция в обработчике выдавала то что нужно, иначе - возвращайте результат сами. Уф. Нате вот читайте =) Постарался разжевать всё что можно. Если кто-нибудь изъявит желание, то могу выложить следующие части на форум, когда напишу ещё пару-тройку.
Отформатируй текст. Разбей на пункты и подпункты. Выдели код и константы. Далее отправляй аквиле - пусть размещает на сайте.
Возникла проблема с функцией CreateFile. При установке третьего параметра GENERIC_READ не работает. А если сталю другой параметр - то не работает ункция CreateFileMapping. Подскажите, пожалуйста, в чем может быть дело?
Это означает, что объявлена меременная с именем msg, имеющая тип MSG. Это тип-структура, необходимый для принятия сообщений. Изначально структура пуста (ни один из её элементов не определён). На это указывают треугольные скобки, внутри которых ничего нет. ЗЫ Выкладываю окончательный вариант статьи. ЗЗЫ Обнаружил неточность в расположении констант: сначала приводится аббревиатура DC, а объясняется, что это такое - в самом конце Перезаливаю...
Я думаю, должно хватить win32.hlp и туториалов iczelion'a за глаза, чем больше сам поймешь, тем лучше. Разберешься с основами (как содать окно, система сообщений, ресурсы), так можешь прямо приступать к созданию блокнота. Я начинал так (на примере блокнота, у меня было другое): смотрю что мне нужно - едит контрол, меню, тулбар, статбар. Беру цепляю меню (самое простое имхо), цепляю статбар, располагаю Edit и тулбар(вообще можно и без него). Теперь приложение загружается и показывает мне окно с компонентами. Нужно чтобы оно выглядело всегда красиво, читаю про WM_SIZE и добавляю код ресайза контролов (гуглю если нужно). Создаю удобные обертки (функции) для статбара, чтобы туда легко можно было текст выводить(можно их не делать, толку и впрямь немного). Теперь нужно разобраться с edit'ом (можно юзать и RichEdit, благо в туториале он описан немного), беру и узнаю как с ним оперировать. Ввожу в поиск по win32.hlp "edit", кликаю edit control messages, по названиям большинства сообщений понятно, что они делают, узнаю как выделить текст, получить выделение, про управление скроллингом и др. Осталось разобраться с тулбаром, тут лучше погуглить, а лучше на первых порах без него обойтись. После этого просто прикручиваешь нужные обработчики к пунктам меню и все(звучит просто ). Есть еще такая страшная штука как MSDN, ею нужно научиться юзать, поелику оттуда все работает и написано правильно (хотя иногда через ж).
DEEP, хорошо бы приводить сорец ещё и без всех макросов, как у Пирогова. Если этого не сделать - вопросов потом не оберёшься. Хорошо когда .if, но надо понимать что работает оно также как и CMP.
Хранится код ошибки(NTSTATUS) возвращённый NtSetInformationThread(InfoClass=ThreadQuerySetWin32StartAddress, CONTEXT.regEax).
Люди помогите плиз нубику. Вот код проги: Код (Text): .386 .model flat, stdcall option casemap :none ; case sensitive includelib \masm32\lib\kernel32.lib includelib \masm32\lib\user32.lib .code start: ExitProcess proto:dword MessageBox proto:dword, :dword, :dword, :dword $Title db "Window", 0 $Text db "Assembler is cool !", 0 push 0 push offset $Text push offset $Title push 0 call MessageBox push 0 call ExitProcess end start Так вот ЭТО не компилится. Можете исправить ошибки.