Добрый день. Читаю уторы Iczelion'а, дошел до третего. Там простое окно, привожу код: Code (Text): .386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc includelib \masm32\lib\user32.lib ; calls to functions in user32.lib and kernel32.lib include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib WinMain proto :DWORD,:DWORD,:DWORD,:DWORD .DATA ; initialized data ClassName db "SimpleWinClass",0 ; Имя нашего класса окна AppName db "Our First Window",0 ; Имя нашего окна .DATA? ; Hеиницилизиpуемые данные hInstance HINSTANCE ? ; Хэндл нашей пpогpаммы CommandLine LPSTR ? .CODE ; Здесь начинается наш код start: invoke GetModuleHandle, NULL ; Взять хэндл пpогpаммы ; Под Win32, hmodule==hinstance mov hInstance,eax mov hInstance,eax invoke GetCommandLine ; Взять командную стpоку. Вы не обязаны вызывать эту функцию ЕСЛИ ваша пpогpамма не обpабатывает командную стpоку. mov CommandLine,eax invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT ; вызвать основную функцию invoke ExitProcess, eax ; Выйти из пpогpаммы. ; Возвpащаемое значение, помещаемое в eax, беpется из WinMain'а. WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX ; создание локальных пеpеменных в стеке LOCAL msg:MSG LOCAL hwnd:HWND mov wc.cbSize,SIZEOF WNDCLASSEX ; заполнение стpуктуpы wc mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInstance pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc ; pегистpация нашего класса окна invoke CreateWindowEx,NULL,\ ADDR ClassName,\ ADDR AppName,\ WS_OVERLAPPEDWINDOW,\ CW_USEDEFAULT,\ CW_USEDEFAULT,\ CW_USEDEFAULT,\ CW_USEDEFAULT,\ NULL,\ NULL,\ hInst,\ NULL mov hwnd,eax invoke ShowWindow, hwnd,CmdShow ; отобpазить наше окно на десктопе invoke UpdateWindow, hwnd ; обновить клиентскую область .WHILE TRUE ; Enter message loop invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .ENDW mov eax,msg.wParam ; сохpанение возвpащаемого значения в eax ret WinMain endp WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg==WM_DESTROY ; если пользователь закpывает окно invoke PostQuitMessage,NULL ; выходим из пpогpаммы .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ; Дефаултная функция обpаботки окна ret .ENDIF xor eax,eax ret WndProc endp end start Так вот, в чем собственно вопрос. Code (Text): .WHILE TRUE ; Enter message loop invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .ENDW mov eax,msg.wParam ; сохpанение возвpащаемого значения в eax ret Это цикл обработки сообщений. GetMessage получает сообщение, если это квит - выходит. Дальше самое интересное. DispatchMessage передает сообщение нашему WndProc. Вот хоть убей не пойму, как? Ведь по сути DispatchMessage ничего не знает о этой ф-и. Ее адрес хранится только в структуре WNDCLASS, которая опять же в ф-ю не передается. Вопрос, как DispatchMessage переходит к WndProc? Нагуглил, что реализацию ее можно найти в исходниках win2k, но возможности их выкачать у меня нет. Может кто то подскажет, или приведет код реализации DispatchMessage? Заранее спасибо! Посмотрел в отладчике реализацию: Code (Text): CPU Disasm Address Hex dump Command Comments 7E3696B8 8BFF MOV EDI,EDI ; HEX USER32.DispatchMessageA(pMsg) 7E3696BA 55 PUSH EBP 7E3696BB 8BEC MOV EBP,ESP 7E3696BD 6A 01 PUSH 1 7E3696BF FF75 08 PUSH DWORD PTR SS:[EBP+8] 7E3696C2 E8 2AF2FFFF CALL 7E3688F1 7E3696C7 5D POP EBP 7E3696C8 C2 0400 RETN 4 Видно, что вызывается какая то непонятная ф-я, затем еще одна и ее одна, но в них я еще не могу разобраться никак.
По окну, вызвавшему сообщение. Относительно окна всегда же известно, какого оно класса windows.inc Code (Text): MSG STRUCT hwnd DWORD ? <--------- message DWORD ? wParam DWORD ? lParam DWORD ? time DWORD ? pt POINT <> MSG ENDS
Code (Text): WNDPROC fnWindowProc = (WNDPROC)GetWindowLong(msg.hwnd, GWL_WNDPROC); fnWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
Dmitry_Milk Тогда структуру MSG должна заполнить GetMessage. Но поскольку второй параметри у нас NULL, то Допустим GetMessage извлекла первое сообщение из очереди и поместила в структуру. Допустим это сообщение не относилось к нашему окну(в потоке ведь может быть несколько несвязанных друг с другом окон?). Но сообщение это было WM_QUIT, а значит наша программа завершится. Не сходится. Можете, пожалуйста, рассказать поподробнее про этот механизм? Хм, только что проверил, создал два окна. Закрываю одно - закрываются оба. Из этого делаю вывод: GetMessage получает сообщение из очереди и обрабатывает его. Сначала проверяет, не вм_квит ли это. если да - возвращает фальш. Далее ф-я DispatchMessage получает структуру MSG, которая УЖЕ заполнена(спасибо ф-е GetMessage), достает из этой структуры хэндл окна, которому предназначено сообщение. По этому хэндлу как то(пока не ясно, как) узнает адрес ф-и обработчика для нее и передает ей управление. Поправьте, где я не прав, пожалуйста.
Каждый поток процесса имеет собственную оконную очередь сообщений. Сообщения приходят потоку, тем самым и образуя очередь. Функции GetMessage и PeekMessage извлекают самое давнее сообщение из очереди (то, что пришло раньше остальных), заполняют структуру MSG и удаляет сообщение из очереди. Эти две функции отличаются по следующим пунктам: 1) Если очередь сообщений потока пуста, GetMessage будет ждать прихода хотя бы одного сообщения (т.е. будет "висеть"), в то время как PeekMessage возвращает управление программе сразу. Узнать пуста ли очередь сообщений в этом случае можно по возвращённому PeekMessage значению - если 0, то сообщений на момент вызова не было, если не 0, то сообщения были. 2) Кроме того, функции PeekMessage можно указать, следует ли удалять сообщение из очереди по завершении его обработки, GetMessage - нельзя, она всегда удаляет сообщение из очереди. 3) GetMessage возвращает 0, если очередным сообщением является WM_QUIT. PeekMessage возвращает 0, если очередь сообщений была пуста. Отсюда вывод - чтобы знать когда завершить цикл обработки сообщений, в котором используется PeekMessage, нужно проверять значение MSG.message - если оно равно WM_QUIT, цикл надо завершить. Сообщения могут посылаться как системой, так и самой программой с помощью, например, SendMessage и PostMessage (но есть и другие функции). Отличие функций SendMessage и PostMessage в том, что SendMessage добавляет сообщение в очередь и ждёт, пока его обработка завершится (при этом возвращая результат обработки), а PostMessage не ждёт. Т.е., например, если вызвать SendMessage(hWnd, WM_QUIT, 0, 0), то программа никогда не попадёт на следующую за вызовом SendMessage инструкцию (подумайте, почему), а если вызвать PostMessage(hWnd, WM_QUIT, 0, 0), то попадёт. Как правило, при нажатии на крестик в правом верхнем углу окна, в очередь сообщений помещается WM_CLOSE. Когда Вы его обрабатываете, Вы вызываете PostQuitMessage(hWnd, 0), что эквивалентно PostMessage(hWnd, WM_QUIT, 0, 0). В очередь сообщений кладётся WM_QUIT. Когда это сообщение будет извлечено из очереди, цикл обработки сообщений завершится; вскоре после этого завершится и сама программа. Естественно, все созданные окна при этом разрушаются. Всё верно. Из хендла можно получить адрес функции обработки сообщений с помощью GetClassName & GetClassInfo.
Поправка: Исключением являются низкоприоритетные сообщения WM_TIMER и WM_PAINT, которые всегда перемещаются в конец очереди и извлекаются последними. К тому же WM_PAINT может присутствовать в очереди только в единственном экземпляре Поправка: SendMessage из того же потока не ставит сообщение в очередь, а напрямую вызывает процедуру окна. Если же сообщение шлется из другого потока, то оно ставится в спец.очередь синхронных сообщений, которые обрабатываются внутри Get\PeekMessage до выборки\возврата асинхронных сообщений
Спасибо за ответы. Но у меня появился еще вопрос. Я ведь пишу все это на фасме и столкнулся с проблемой отсутствия таких типов как HFONT, DWORD и т.д. Есть ли файлик с ними для фасма? Или нужно писать его самому?
Такого файла, скорее всего, нет. И не нужен. В ассемблере переменные объявляются как dd, dw, db и т.п. и усложнять это не нужно. Относительно того, какой тип что из себя являет (dd, dw, db или что-то ещё), всё есть тут, нужно только знать основные типы языка C.
bug1z Делаем файл TYPES.INC: Code (Text): HWND equ dd WCHARS equ rw CHARS equ rb HFONT equ dd HANDLE equ dd Ну и далее, включаем в самом начале (перед остальными модулями): Code (Text): include 'TYPES.INC' ... include 'MODULE1.INC' include 'MODULE2.INC' include 'MODULE3.INC' ... Далее, в каком либо объявлении структуры: Code (Text): struc OBJECT1 { .UnicodePath WCHARS 256 .AnsiString CHARS 64 .HAllocator HANDLE ? .HMyDialog HWND ? } Наглядно.
Здравствуйте. У меня появился еще один вопрос. Можете, пожалуйста, обьяснить, что такое Регион Отсечения? В книге написано: "Ограничивает область, внутры которой система разрешает отображение графической информации." Так же написано: "Система устанавливает регион отсечения путем пересечения видимого региона и обновляемого региона". Но не пойму где тут этот регион отсечения и как это "путем пересечения видимого региона и обновляемого". Так же обновляемого региона не вижу. Заранее спасибо!
ну вот на примере красного окошка: оно должно пере рисоваться полностью - обновляемая область всё окно. наше окно загораживает белое, значит нет смысле закрашивать область белого. и мы вычитаем из из области красного область белого. далее мы тупо рисуем в красном окне, когда всё, по маске происходит блитинг(копирование) того, что в красном окне в видеоПамять
Точнее так: Видимый регион - это действительно то, что на рисунке видно нам красным цветом. А обновляемый регион - это запрос приложения на то, какую область окна оно хочет обновить. В обычном состоянии вся площадь окна считается валидной, то есть ничего не надо обновлять Запрос на то, какую именно область окна приложение хочет обновить, задается одним или несколькими последовательными вызовами функции InvalidateRect, или вызовом функции InvalidateRgn. В частном случае можно инвалидировать всю площадь площадь окна, не указывая аргумент-прямоугольник в InvalidateRect. В общем случае получается некоторая область проивольной формы - инвалидный(обновляемый) регион. Пересечение этого региона с видимым регионом дадут регион, в котором будут работать функции рисования. В других местах окна функции ничего не изменят, даже если эти места видимы (но не были заявлены инвалидными).
Объясню, на всякий случай, для чего эти сложности нужны. Все связано с тем, что рисование в окне обычно отделено от каких-либо действий и обычно выполняется в какой-то достаточно отложенный момент, когда наконец-то придет сообщение WM_PAINT. Поскольку реализации функции отклика на WM_PAINT может быть значительно отдалена в программе от действий, требующих каких-либо изменений в изображении, часто оказывается сложно передать в функцию рисования то, что и как именно надо перерисовать, тем более что WM_PAINT может прийти не только от программы, но и от системы из-за перераспределения окон на экране. Проще бывает нарисовать ВСЕ ЦЕЛИКОМ как должно быть видно в окне на момент поступления WM_PAINT. ДЛя того, чтоб не грузить систему лишним перерисовыванием того, что уже и так нарисовано на своих местах, как раз и придуман механизм инвалидации. Скажем, тащите вы какой-то спрайтик по окну, так зачем перерисовывать все, когда достаточно перерисовать прямоугольник, откуда спрайтик ушел, и прямоугольник, где он появился. Для этого объявляют эти два прямоугольника инвалидным регионом, и при вызове WM_PAINT графические функции Windows будут "стараться" только в этих прямоугольниках (причем, если они не загорожены ничем), а в остальных местах они будут "отлынивать", экономя процессрное время на более полезные вещи. Впрочем, регионом инвалидации можно воспользоваться и явно в функции рисования, вообще не давая команд рисования в тех местах, которые этим регионом не покрываются.