Глава 5. Разрешение некоторых проблем программирования в Windows
Признаться, данная глава стала для меня некоторым компромиссом. Вопросов программирования в Windows еще так много, что не хотелось бы их опускать. А если писать на каждый вопрос целую главу - книга станет непомерно большой. И я решил отвести на все оставшееся (но далеко не все, однако) одну главу, где постараюсь, насколько это возможно, подробно осветить ряд интересных вопросов. Глава будет построена в виде вопросов гипотетического собеседника и ответов на них Вашего покорного слуги. Итак, приступим.
I
В. Как сделать так, чтобы при минимизации окна значок его помещался бы на системную панель?
Эта проблема решается с использованием системной функции Shell_NotifyIcon. Причем
решение это столь просто и прозрачно, что приходится удивляться тому, что большинство для этих
целей привлекают какие-то библиотеки. Вот параметры этой функции:
- 1-й параметр. Действие, которое необходимо произвести:
- NIM_ADD equ 0h ; добавить иконку на системную панель
- NIM_MODIFY equ 1h ; удалить иконку
- NIM_DELETE equ 2h ; модифицировать иконку
- 2-й параметр. Указатель на структуру, где содержится информация, необходимая для реализации
указанного действия.
NOTI_ICON STRUC cbSize DWORD ? hWnd DWORD ? uID DWORD ? uFlags DWORD ? uCallbackMessage DWORD ? hIcon DWORD ? szTip DB 64 DUP (?) NOTI_ICON ENDS
- cbSize - размер структуры.
- hWnd — дескриптор окна, куда будет посылаться сообщение (см. ниже)
- uID - идентификатор иконки
- uFlags - комбинация следующих флагов
NIF_MESSAGE equ 1h; использовать поле hIcon
NIF_ICON equ 2h; использовать поле uCallbackMessage
NIF_TIP equ 4h; использовать поле szTip
- uCallbackMessage - сообщение, которое приходит на окно, определяемое дескриптором
- hWnd, в случае возникновения некого события вблизи иконки на системной панели.
Значение сообщения должно быть больше, чем 1024. При приходе этого сообщения WPARAM
содержит идентификатор иконки, a LPARAM - событие, т.е. то, что произошло с иконкой.
- hIcon - дескриптор иконки.
- szTip - текст всплывающей подсказки.
- cbSize - размер структуры.
Давайте рассмотрим, как работает весь механизм. В случае минимизации окна на функцию окна приходит сообщение WM_SIZE. Причем wParam должен содержать значение SIZE_MINIMIZED. Вот тогда то и следует воспользоваться функцией Shell_NotifyIcon, которая поместит иконку на системную панель. Любые же события с мышью, когда ее курсор находится на иконке, вызовут приход на функцию окна сообщения uCallbackMessage, которое, естественно, мы сами и определи. При этом младшее слово wParam будет содержать идентификатор иконки, а по младшему слову lParam можно определить, что же приключилось (см. ниже). Ниже (Рис. 3.5.1) Вы увидите, как все это работает.
// файл tray.rc // определение констант #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x0001OOOOL #define DS_3DLOOK 0x0004L // идентификаторы #define IDI_ICON1 1 // определили иконку IDI_ICON1 ICON "ico1.ico" //определение диалогового окна DIAL1 DIALOG 0, 0, 250, 110 STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | DS_3DLOOK CAPTION "Поместить иконку на системную панель" FONT 8, "Arial" { } ; файл tray.inc ; константы NIM_ADD equ 0h ; добавить иконку на системную панель NIM_MODIFY equ 1h ; удалить иконку NIM_DELETE equ 2h ; модифицировать иконку NIF_MESSAGE equ 1h ; использовать поле hIcon NIF_ICON equ 2h ; использовать поле uCallbackMessage NIF_TIP equ 4h ; использовать поле szTip FLAG equ NIF_MESSAGE or NIF_ICON or NIF_TIP SIZE_MINIMIZED equ 1h SW_HIDE equ 0 SW_SHOWNORMAL equ 1 ; сообщение приходит при закрытии окна WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_SIZE equ 5h WM_LBUTTONDBLCLK equ 203h WM_LBUTTONDOWN equ 201h ; прототипы внешних процедур IFDEF MASM EXTERN ShowWindow@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN lstrcpy@8:NEAR EXTERN Shell_NotifyIconA@8:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN SendDlgItemMessageA@20:NEAR ELSE EXTERN ShowWindow:NEAR EXTERN LoadIconA:NEAR EXTERN lstrcpy:NEAR EXTERN Shell_NotifyIconA:NEAR EXTERN ExitProcess:NEAR EXTERN GetModuleHandleA:NEAR EXTERN DialogBoxParamA:NEAR EXTERN EndDialog:NEAR EXTERN SendDlgItemMessageA:NEAR ShowWindow@8 = ShowWindow LoadIconA@8 = LoadIconA lstrcpy@8 = lstrcpy Shell_NotifyIconA@8 = Shell_NotifyIconA ExitProcess@4 = ExitProcess GetModuleHandleA@4 = GetModuleHandleA DialogBoxParamA@20 = DialogBoxParamA EndDialog@8 = EndDialog SendDlgItemMessageA@20 = SendDlgItemMessageA ENDIF ; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ; структура для функции Shell_NotifyIcon NOTI_ICON STRUC cbSize DWORD ? hWnd DWORD ? uID DWORD ? uFlags DWORD ? uCallbackMessage DWORD ? hIcon DWORD ? szTip DB 64 DUP (?) NOTI_ICON ENDS ; файл tray.asm .386P ; плоская модель .MODEL FLAT, stdcall include tray.inc ; директивы компоновщику для подключения библиотек IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\shell32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;————————----------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' MSG MSGSTRUCT <?> HINST DD 0 ; дескриптор приложения PA DB "DIAL1",0 NOTI NOTI_ICON <0> TIP DB "Пример использования функции" DB "Shell_NotifyIcon",0 _DATA ENDS;сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ;-------------------------------------- PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX,-1 JNE KOL ; сообщение об ошибке KOL: ;-------------------------------------- PUSH 0 CALL ExitProcess@4 ;-------------------------------------- ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10Н] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI ;--------------- CMP DWORD PTR [EBP+0CH],WM_CLOSE JNE L1 PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH],WM_INITDIALOG JNE L2 JMP FINISH L2: CMP DWORD PTR [EBP+0CH],WM_SIZE JNE L3 CMP DWORD PTR [EBP+10H],SIZE_MINIMIZED JNE L3 ; здесь работа по установке иконки на системную панель MOV NOTI.cbSize,88 MOV EAX,DWORD PTR [EBP+8H] MOV NOTI.hWnd,EAX MOV NOTI.uFlags,FLAG MOV NOTI.uID,12 ; идентификатор иконки MOV NOTI.uCallbackMessage,2000 ; сообщение ; загрузить иконку из ресурсов PUSH 1 PUSH [HINST] CALL LoadIconA@8 ; скопировать текст всплывающего сообщения MOV NOTI.hIcon,EAX PUSH OFFSET TIP PUSH OFFSET NOTI.szTip CALL lstrcpy@8 ; поместить иконку PUSH OFFSET NOTI PUSH NIM_ADD CALL Shell_NotifyIconA@8 ; спрятать минимизированное окно PUSH SW_HIDE PUSH DWORD PTR [EBP+08H] CALL ShowWindow@8 JMP FINISH L3: ; сообщение от иконки на системной панели? CMP DWORD PTR [EBP+0CH],2000 JNE FINISH ; идентификатор нашей иконки? CMP WORD PTR [EBP+10H],12 JNE FINISH ; что произошло? двойной щелчок? CMP WORD PTR [EBP+14H],WM_LBUTTONDBLCLK JNE FINISH ; заполнить структуру MOV NOTI.cbSize,88 MOV EAX,DWORD PTR [EBP+8H] MOV NOTI.hWnd,EAX MOV NOTI.uFlags,NIF_ICON MOV NOTI.uID,12 ; идентификатор иконки MOV NOTI.uCallbackMessage,1000 ; сообщение ; удалить иконку PUSH OFFSET NOTI PUSH NIM_DELETE CALL Shell_NotifyIconA@8 ; восстановить окно PUSH SW_SHOWNORMAL PUSH DWORD PTR [EBP+08H] CALL ShowWindow@8 FINISH: MOV EAX,0 POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END START
Рис. 3.5.1. Демонстрация процедуры помещения иконки на системную панель.
Трансляция программы на Рис. 3.5.1.
MASM32:
ml /c /coff /DMASM tray.asm rc tray.rc link /subsystem:windows tray.obj tray.resTASM32:
tasm32 /ml tray.asm brcc32 tray.rc tlink32 -aa tray.obj,,,,,tray.res
В связи с программой на Рис. 3.5.1 хочу особо акцентировать Ваше внимание на сообщении WM_SIZE. Весьма полезное сообщение, я Вам скажу. Представьте, что у себя в окне Вы расположили какую-то информацию. Если окно допускает изменение размеров, то Вам придется решать проблему размещения информации в случае, если размер окна изменился. Так вот, аккуратно все перерисовать и отмасштабировать можно как раз, если использовать данное сообщение. Подчеркну, что сообщение посылается, когда размер окна уже изменился. При этом WPARAM содержит признак того, что произошло с окном, a LPARAM - новый размер окна (младшее слово - ширина, старшее - высота).
II
В. Есть ли дополнительные средства, упрощающие файловую обработку?
Да, таким средством, в частности, являются файлы, отображаемые в память. Побайтное чтение из файла действительно не всегда удобно. Конечно, можно подойти к этой проблеме несколько иначе. Можно выделить в памяти достаточно большой буфер и прочитать туда файл, а затем работать с ним, как с большим массивом. Так вот, файлы, отображаемые в память, - это нечто похожее, но здесь система берет на себя часть такой работы. Самое замечательное, однако, в этом механизме заключается в том, что файл, спроецированный в память, может быть разделенным между несколькими процессами, а это еще один способ обмена информацией между процессами.
У файлов, проецируемых в память, есть довольно существенный недостаток. После отображения их размер не может быть увеличен. В этом случае поступают следующим образом: заранее предполагая новый размер файла, проецируют его на большую область памяти.
Работу с файлами, отображаемыми в память, производят по следующему алгоритму:
- Открыть (или создать) файл с помощью обычной функции CreateFile. Функция, как известно, возвращает дескриптор открытого файла.
- Отобразить файл45 с помощью функции CreateFileMapping. Именно эта функция определяет размер отображения. Эта функция, как и предыдущая, возвращает дескриптор, только не обычного, а отображенного файла.
- Скопировать файл (или его часть) в созданную область при помощи функции MapViewOfFile. Эта функция возвращает указатель (вот оно!) на начало области, где будет расположен файл. После этого руки у Вас развязаны, и Вы можете делать с отображенным файлом все, что Вам заблагорассудится.
- При желании можно записать область памяти в файл при помощи функции FlushViewOfFile. Разумеется, сбрасываться на диск будет та область памяти, которую мы заказали при помощи функции CreateFileMapping. Записывать на диск можно, разумеется, и с помощью обычной функции WriteFile.
- Перед тем как закрывать отображенный файл, следует вначале сделать указатель недействительным. Это делается с помощью функции UnmapViewOfFile.
- Закрыть следует оба дескриптора. Вначале дескриптор, возвращенный функцией CreateFileMapping, а затем дескриптор, созданный функцией CreateFile.
Как видите, алгоритм работы с отображаемыми файлами весьма прост. Рассмотрим теперь новые для Вас функции.
Функция CreateFileMapping. Возвращает дескриптор отображаемого файла.
- 1-й параметр. Дескриптор открытого файла.
- 2-й параметр. Атрибут доступа, обычно полагают равным нулю.
- 3-й параметр. Может принимать одно из следующих значений, и должен быть совместим с режимом разделения файла: PAGE_READONLY, PAGE_WRITECOPY, PAGE_READWRITE. Как Вы понимаете, этот атрибут определяет защиту отображаемого файла и не должен противоречить атрибуту файла, открытого с помощью CreateFile.
- 4-й параметр. Старшая часть (32 бита) размера отображаемого файла.
- 5-й параметр. Младшая часть размера отображаемого файла (как Вы понимаете, размер может и не совпадать с размером файла). Если оба параметра равны нулю, то размер полагается равным размеру открытого файла (1-й параметр).
- 6-й параметр. Имя отображаемого файла. Необходимо только в том случае, если предполагается, что отображаемый файл будет использоваться несколькими процессами. В этом случае повторный вызов функции CreateFileMapping другими процессами с тем же именем приведет не к созданию нового отображаемого файла, а к возвращению уже созданного дескриптора.
Функция MapViewOfFile. Возвращает указатель на область памяти, где помещается файл или его часть.
- 1-й параметр. Дескриптор, возвращенный функцией CreateFileMapping.
- 2-й параметр. Определяет операцию, которую мы будем делать. Например, FILE_MAP_READ означает - только чтение, a FILE_MAP_WRITE - чтение и запись.
- 3-й параметр. Старшая часть (32-байта) смещения в файле, откуда начинается копирование в память.
- 4-й параметр. Младшая часть смещения в файле, откуда начинается копирование.
- 5-й параметр. Определяет количество копируемых байт. Если вы хотите скопировать весь файл, то положите три последних параметра равными 0.
Функция FlushViewOfFile.
- 1-й параметр. Указывает на область, записываемую в файл.
- 2-й параметр. Определяет количество записываемых байт.
Функция UnmapViewOfFile.
- 1-й параметр. Дескриптор отображаемого файла.
Вот, собственно, и вся теория отображаемых файлов. Материал весьма прост для программирования, и мы не приводим здесь программы. Я думаю, что читатель, добравшийся до данной главы сего повествования, без труда напишет свою программу, воспользовавшись изложенными выше материалами.
45 Возможно, несколько правильнее сказать, что функция создает объект, под названием отображаемый файл.
III
В.Можно ли контролировать ввод информации в окне редактирования?
Да, в главе 2.4 мы видели, как можно это корректно делать при помощи горячих клавиш. Однако с помощью горячих клавиш легко блокировать приход в окно редактирования некоторых символов. Речь же в некоторых случаях может идти о том, чтобы преобразовывать вводимую информацию, что называется, "налету". Таким механизмом может быть использование подклассов. Для демонстрации его мы возьмем программу на Рис. 1.3.2, видоизменим ее так, чтобы можно было контролировать ввод информации.
На самом деле в этом механизме нет ничего нового. Еще в операционной системе MS DOS можно было перехватывать прерывание и встраивать туда свою процедуру. В результате при вызове данного прерывания вызывалась вначале Ваша процедура, а потом уже та, которая была установлена ранее. Впрочем, можно было поступать и по-другому: вначале вызывается процедура, существовавшая ранее, а потом, в последнюю очередь, вызывается Ваша процедура (см. [1]). Поскольку перехватывать прерывание можно было многократно, в результате могла образоваться целая цепочка процедур, выполняющихся одна за другой. Мне уже приходилось сравнивать вызов процедур окна с вызовом прерываний. Это очень похоже, не правда ли? С точки зрения объектного программирования это является ни чем иным как созданием класса-родителя.
В основе рассматриваемого механизма лежит использование функции SetWindowLong, которая может менять атрибуты уже созданного окна. Вот параметры этой функции.
- 1-й параметр. Дескриптор окна, которое было создано текущим процессом.
- 2-й параметр. Величина, определяющая, какой атрибут следует изменить. Вообще говоря, это всего лишь смещение в некоторой области памяти. Нас будет интересовать только величина, определяющая адрес процедуры окна. Эта величина определяется константой DWL_DLGPROC = 4 для диалогового окна и GWL_WNDPROC = -4 для обычного окна. Отсюда, кстати, следует, что данное действо можно производить не только над простыми, но и над диалоговыми окнами.
- 3-й параметр. Новое значение атрибута окна.
Следует также отметить, что данная функция возвращает старое значение атрибута. Здесь я должен еще обратить внимание читателя на то, что все элементы, создаваемые на окне, являясь тоже окнами, имеют, разумеется, и свои функции окна. В повседневной практике мы довольствуемся только сообщениями, приходящими на процедуру основного окна.
Вопрос следующий: как вызвать старую процедуру окна. Обычная команда CALL не подходит. Нужно использовать специальную функцию CallWindowProc. Параметры этой функции следующие:
- 1-й параметр. Адрес вызываемой процедуры.
- 2-й параметр. Дескриптор окна.
- 3-й параметр. Код сообщения.
- 4-й параметр. Параметр WPARAM.
- 5-й параметр. Параметр LPARAM.
; файл edit.inc ; константы WM_CHAR equ 102h WM_SETFOCUS equ 7h ; сообщение приходит при закрытии окна WM_DESTROY equ 2 ; сообщение приходит при создании окна WM_CREATE equ 1 ; сообщение, если что-то происходит с элементами на окне WM_COMMAND equ 111h ; сообщение, позволяющее получить строку WM_GETTEXT equ 0Dh ; константа для функции SetWindowLong GWL_WNDPROC equ -4 ; свойства окна CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_TABSTOP equ 10000h WS_SYSMENU equ 80000h WS_OVERLAPPEDWINDOW equ 0 + WS_TABSTOP + WS_SYSMENU STYLE equ CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS ;CS_HREDRAW equ 2h BS_DEFPUSHBUTTON equ 1h WS_VISIBLE equ 10000000h WS_CHILD equ 40000000h WS_BORDER equ 800000h STYLBTN equ WS_CHILD + BS_DEFPUSHBUTTON + WS_VISIBLE + WS_TABSTOP STYLEDT equ WS_CHILD + WS_VISIBLE + WS_BORDER + WS_TABSTOP ; идентификатор стандартной иконки IDI_APPLICATION equ 32512 ; идентификатор курсора IDC_ARROW equ 32512 ; режим показа окна - нормальный SW_SHOWNORMAL equ 1 ; прототипы внешних процедур IFDEF MASM EXTERN CallWindowProcA@20:NEAR EXTERN SetWindowLongA@12:NEAR EXTERN SetFocus@4:NEAR EXTERN SendMessageA@16:NEAR EXTERN MessageBoxA@16:NEAR EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4:NEAR EXTERN UpdateWindow@4:NEAR ELSE EXTERN CallWindowProcA:NEAR EXTERN SetWindowLongA:NEAR EXTERN SetFocus:NEAR EXTERN SendMessageA:NEAR EXTERN MessageBoxA:NEAR EXTERN CreateWindowExA:NEAR EXTERN DefWindowProcA:NEAR EXTERN DispatchMessageA:NEAR EXTERN ExitProcess:NEAR EXTERN GetMessageA:NEAR EXTERN GetModuleHandleA:NEAR EXTERN LoadCursorA:NEAR EXTERN LoadIconA:NEAR EXTERN PostQuitMessage:NEAR EXTERN RegisterClassA:NEAR EXTERN ShowWindow:NEAR EXTERN TranslateMessage:NEAR EXTERN UpdateWindow:NEAR CallWindowProcA@20 = CallWindowProcA SetWindowLongA@12 = SetWindowLongA SetFocus@4 = SetFocus SendMessageA@16 = SendMessageA MessageBoxA@16 = MessageBoxA CreateWindowExA@48 = CreateWindowExA DefWindowProcA@16 = DefWindowProcA DispatchMessageA@4 = DispatchMessageA ExitProcess@4 = ExitProcess GetMessageA@16 = GetMessageA GetModuleHandleA@4 = GetModuleHandleA LoadCursorA@8 = LoadCursorA LoadIconA@8 = LoadIconA PostQuitMessage@4 = PostQuitMessage RegisterClassA@4 = RegisterClassA ShowWindow@8 = ShowWindow TranslateMessage@4 = TranslateMessage UpdateWindow@4 = UpdateWindow ENDIF ; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ;----структура класса окон WNDCLASS STRUC CLSSTYLE DD ? CLWNDPROC DD ? CLSCBCLSEX DD ? CLSCBWNDEX DD ? CLSHINST DD ? CLSHICON DD ? CLSHCURSOR DD ? CLBKGROUND DD ? CLMENNAME DD ? CLNAME DD ? WNDCLASS ENDS ; файл edit.asm .386P ; плоская модель .MODEL FLAT, stdcall include edit.inc ; директивы компоновщику для подключения библиотек IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> HINST DD 0 ; дескриптор приложения TITLENAME DB 'Контроль окна редактирования',0 CLASSNAME DB 'CLASS32',0 CPBUT DB 'Выход',0 ; выход CPEDT DB ' ',0 CLSBUTN DB 'BUTTON',0 CLSEDIT DB 'EDIT',0 HWNDBTN DWORD 0 HWNDEDT DWORD 0 CAP BYTE 'Сообщение',0 MES BYTE 'Конец работы программы',0 TEXT DB 100 DUP (0) OLDWND DD 0 CHAR DD ? _DATA ENDS ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ; заполнить структуру окна ; стиль MOV [WC.CLSSTYLE],STYLE ; процедура обработки сообщений MOV [WC.CLWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEX],0 MOV [WC.CLSCBWNDEX],0 MOV EAX, [HINST] MOV [WC.CLSHINST], EAX ;----------иконка окна PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ;----------курсор окна PUSH IDC_ARROW PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ;—————————— MOV [WC.CLBKGROUND], 17 ; цвет окна MOV DWORD PTR [WC.CLMENNAME],0 MOV DWORD PTR [WC.CLNAME],OFFSET CLASSNAME PUSH OFFSET WC CALL RegisterClassA@4 ; создать окно зарегистрированного класса PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH 150 ; DY - высота окна PUSH 400 ; DX - ширина окна PUSH 100 ; Y - координата левого верхнего угла PUSH 100 ; X - координата левого верхнего угла PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET TITLENAME ; имя окна PUSH OFFSET CLASSNAME ; имя класса PUSH 0 CALL CreateWindowExA@48 ; проверка на ошибку CMP EAX,0 JZ _ERR MOV [NEWHWND], EAX ; дескриптор окна ;----------------------------------------- PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ; показать созданное окно ;----------------------------------------- PUSH [NEWHWND] CALL UpdateWindow@4 ; команда перерисовать видимую ; часть окна, сообщение WM_PAINT ; петля обработки сообщений MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP AX, 0 JE END_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: ; выход из программы (закрыть процесс) PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP ;----------------------------------------- ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI CMP DWORD PTR [EBP+0CH],WM_DESTROY JE WMDESTROY CMP DWORD PTR [EBP+0CH],WM_CREATE JE WMCREATE CMP DWORD PTR [EBP+0CH],WM_COMMAND JE WMCOMMND JMP DEFWNDPROC WMCOMMND: MOV EAX,HWNDBTN CMP DWORD PTR [EBP+14H],EAX JNE NODESTROY JMP WMDESTROY NODESTROY: MOV EAX, 0 JMP FINISH WMCREATE: ; создать окно-кнопку PUSH 0 PUSH [HINST] PUSH 0 PUSH DWORD PTR [EBP+08H] PUSH 20 ; DY PUSH 60 ; DX PUSH 10 ; Y PUSH 10 ; X PUSH STYLBTN PUSH OFFSET CPBUT ; имя окна PUSH OFFSET CLSBUTN ; имя класса PUSH 0 CALL CreateWindowExA@48 MOV HWNDBTN,EAX ; запомнить дескриптор кнопки ; создать окно редактирования PUSH 0 PUSH [HINST] PUSH 0 PUSH DWORD PTR [EBP+08H] PUSH 20 ; DY PUSH 350; DX PUSH 50 ; Y PUSH 10 ; X PUSH STYLEDT PUSH OFFSET CPEDT ; имя окна PUSH OFFSET CLSEDIT ; имя класса PUSH 0 CALL CreateWindowExA@48 MOV HWNDEDT,EAX ; установить фокус на окне редактирования PUSH HWNDEDT CALL SetFocus@4 ; установить свою собственную процедуру обработки PUSH OFFSET WNDEDIT PUSH GWL_WNDPROC PUSH [HWNDEDT] CALL SetWindowLongA@12 MOV OLDWND,EAX MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: ; получить отредактированную строку PUSH OFFSET TEXT PUSH 150 PUSH WM_GETTEXT PUSH HWNDEDT CALL SendMessageA@16 ; показать эту строку PUSH 0 PUSH OFFSET CAP PUSH OFFSET TEXT PUSH DWORD PTR [EBP+08H] ; дескриптор окна CALL MessageBoxA@16 ; на выход PUSH 0 CALL PostQuitMessage@4 ; сообщение WM_QUIT MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP ;----------------------------------------------- ; новая процедура обработки сообщений окну редактирования WNDEDIT PROC PUSH EBP MOV EBP,ESP MOV EAX,DWORD PTR [EBP+10H] MOV CHAR,EAX CMP DWORD PTR [EBP+0CH],WM_CHAR JNE _OLD ; проверка вводимого символа CMP AL,13 JNE _OLD ; послать сообщение о закрытии основного окна PUSH 0 PUSH 0 PUSH WM_DESTROY PUSH [NEWHWND] CALL SendMessageA@16 _OLD: ; вызвать старую процедуру PUSH DWORD PTR [EBP+014H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+8H] PUSH [OLDWND] CALL CallWindowProcA@20 FIN: POP EBP RET 16 WNDEDIT ENDP _TEXT ENDS END START
Рис. 3.5.2. Пример использования подклассов.
Трансляция программы на Рис. 3.5.2.
MASM32:
ml /c /coff /DMASM edit.asm link /subsystem:windows edit.objTASM32:
tasm32 /ml edit.asm tlink32 -aa edit.obj
Разбирая программу, представленную на Рис. 3.2.2, обратите внимание, что представленная схема позволяет делать с окном редактирования практически любые трюки. К примеру. Вы можете заблокировать любой символ, послав вместо него код 0, или вместо одного символа подставить другой и т.д.
IV
В. Возможны ли какие-либо способы взаимодействия и обмен информацией между запущенными приложениями?
Мы уже говорили о различных способах синхронизации, о разделяемой памяти. Есть еще один интересный подход, реализованный в Windows, - это анонимные каналы (pipes)46. Этот подход наиболее эффективен для обмена информацией с консольным процессом, порождаемым данным процессом. Представьте себе, что Вам необходимо, чтобы запускаемый Вами из приложения консольный процесс (например, какой-нибудь строковый компилятор) выводил информацию не в консоль, а в окно редактирования основного процесса. Пример такого приложения представлен на Рис. 3.5.3.
Идея использования каналов очень проста. Канал - как труба: с одной стороны в него втекает информация, а с другой вытекает. Создавая процесс, можно передать ему в качестве дескриптора ввода или вывода соответствующий дескриптор канала. После этого можно обмениваться информацией между двумя процессами при помощи уже известных Вам функций WriteFile и ReadFile.
// файл pipe.rc // определение констант #define WS_SYSMENU 0x00080000L #define WS_VISIBLE 0x10000000L #define WS_TABSTOP 0x00010000L #define DS_3DLOOK 0x0004L #define ES_LEFT 0x0000L #define WS_CHILD 0x40000000L #define WS_BORDER 0x00800000L #define ES_MULTILINE 0x0004L #define WS_VSCROLL 0x00200000L #define WS_HSCROLL 0x00100000L MENUP MENU { POPUP "&Запуск программы" { MENUITEM "&Запустить", 200 MENUITEM "Выход из &программы", 300 } } // определение диалогового окна DIAL1 DIALOG 0, 0, 200, 140 STYLE WS_SYSMENU | DS_3DLOOK CAPTION "Пример использования PIPE" FONT 8, "Arial" { CONTROL "", 101, "edit", ES_LEFT | ES_MULTILINE | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_HSCROLL, 24, 20, 128, 70 } ; файл pipe.inc ; константы SW_HIDE equ 0 SW_SHOWNORMAL equ 1 STARTF_USESHOWWINDOW equ 1h STARTF_USESTDHANDLES equ 100h STARTF_ADD = STARTF_USESHOWWINDOW + STARTF_USESTDHANDLES ; сообщение приходит при закрытии окна WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_COMMAND equ 111h EM_REPLACESEL equ 0C2h ; прототипы внешних процедур IFDEF MASM EXTERN ReadFile@20:NEAR EXTERN CloseHandle@4:NEAR EXTERN CreatePipe@16:NEAR EXTERN SetMenu@8:NEAR EXTERN LoadMenuA@8:NEAR EXTERN CreateProcessA@40:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN SendDlgItemMessageA@20:NEAR ELSE EXTERN ReadFile:NEAR EXTERN CloseHandle:NEAR EXTERN CreatePipe:NEAR EXTERN TerminateProcess:NEAR EXTERN WaitForSingieObject:NEAR EXTERN SetMenu:NEAR EXTERN LoadMenuA:NEAR EXTERN CreateProcessA:NEAR EXTERN ExitProcess:NEAR EXTERN GetModuleHandleA:NEAR EXTERN DialogBoxParamA:NEAR EXTERN EndDialog:NEAR EXTERN SendDlgItemMessageA:NEAR ReadFile@20 = ReadFile CloseHandle@4 = CloseHandle CreatePipe@16 = CreatePipe TerminateProcess@8 = TerminateProcess WaitForSingleObject@8 = WaitForSingleObject SetMenu@8 = SetMenu LoadMenuA@8 = LoadMenuA CreateProcessA@40 = CreateProcessA ExitProcess@4 = ExitProcess GetModuleHandleA@4 = GetModuleHandleA DialogBoxParamA@20 = DialogBoxParamA EndDialog@8 = EndDialog SendDlgItemMessageA@20 = SendDlgItemMessageA ENDIF ; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ; структура для CreateProcess STARTUP STRUC cb DD 0 lpReserved DD 0 lpDesktop DD 0 lpTitle DD 0 dwX DD 0 dwY DD 0 dwXSize DD 0 dwYSize DD 0 dwXCountChars DD 0 dwYCountChars DD 0 dwFillAttribute DD 0 dwFlags DD 0 wShowWindow DW 0 cbReserved2 DW 0 lpReserved2 DD 0 hStdInput DD 0 hStdOutput DD 0 hStdError DD 0 STARTUP ENDS ; структура - информация о процессе PROCINF STRUC hProcess DD ? hThread DD ? Idproc DD ? idThr DD ? PROCINF ENDS ; файл pipe.asm .386P ; плоская модель .MODEL FLAT, stdcall include pipe.inc ; директивы компоновщику для подключения библиотек IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;---------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' STRUP STARTUP <?> INF PROCINF <?> MSG MSGSTRUCT <?> HINST DD 0 ; дескриптор приложения PA DB "DIAL1",0 CMD DB "c:\tasm32\bin\tlink32.exe",0 PMENU DB "MENUP",0 HW DD ? HR DD ? BUFER DB 3000 DUP (0) BYT DD ? _DATA ENDS ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ;------------------------------------------- PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX,-1 JNE KOL ; сообщение об ошибке KOL: ;--------------------------------- PUSH 0 CALL ExitProcess@4 ;--------------------------------- ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10Н] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI ;------------------------ CMP DWORD PTR [EBP+0CH],WM_CLOSE JNE L1 L3: PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH],WM_INITDIALOG JNE L2 ; загрузить меню PUSH OFFSET PMENU PUSH [HINST] CALL LoadMenuA@8 ; установить меню PUSH EAX PUSH DWORD PTR [EBP+08H] CALL SetMenu@8 JMP FINISH L2: CMP DWORD PTR [EBP+0CH],WM_COMMAND JNE FINISH CMP WORD PTR [EBP+10H],300 JE L3 CMP WORD PTR [EBP+10H],200 JNE FINISH ; здесь запуск ; в начале PIPE PUSH 0 PUSH 0 PUSH OFFSET HW PUSH OFFSET HR CALL CreatePipe@16 MOV EAX,HW ; здесь запуск консольного приложения MOV STRUP.cb,68 MOV STRUP.lpReserved,0 MOV STRUP.lpDesktop,0 MOV STRUP.lpTitle,0 MOV STRUP.dwFlags,STARTF_ADD MOV STRUP.cbReserved2,0 MOV STRUP.lpReserved2,0 MOV STRUP.wShowWindow,SW_HIDE ; окно процесса невидимо MOV STRUP.hStdOutput,EAX MOV STRUP.hStdError,EAX ;---------------------------------------------------------- PUSH OFFSET INF PUSH OFFSET STRUP PUSH 0 PUSH 0 PUSH 0 PUSH 1 ; наследует дескрипторы PUSH 0 PUSH 0 PUSH OFFSET CMD PUSH 0 CALL CreateProcessA@40 ; здесь чтение информации PUSH 0 PUSH OFFSET BYT PUSH 3000 PUSH OFFSET BUFER PUSH HR CALL ReadFile@20 PUSH OFFSET BUFER PUSH 0 PUSH EM_REPLACESEL PUSH 101 PUSH DWORD PTR [EBP+08H] CALL SendDlgItemMessageA@20 ; закрыть HANDLE на запись PUSH HW CALL CloseHandle@4 ; закрыть HANDLE на чтение PUSH HR CALL CloseHandle@4 FINISH: MOV EAX,0 POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END START
Рис. 3.5.3. Пример взаимодействия с консольным процессом через PIPE.
Трансляция программы на Рис. 3.5.3.
MASM32:
ml /c /coff /DMASM pipe.asm rc pipe.rc link /subsystem:windows pipe.obj pipe.resTASM32:
tasm32 /ml pipe.asm brcc32 pipe.rc tlink32 -aa pipe.obj,,,,,pipe.res
Комментарий к программе на Рис. 3.5.3.
Вообще, запуск консольного приложения - дело довольно запутанное. Мы не будем вдаваться в детали. В нашей программе этот запуск почти не отличается от запуска программы Word.exe в главе 3.2. Отмечу новое для Вас в этой программе. Обратите внимание, что управляющий элемент EditBox выступает в несколько новой ипостаси. По сути, этот элемент играет роль консоли вывода. Для этого мы указали свойство ES_MULTILINE, что дает возможность помещать в окно целый текст, который отправляется в окно при помощи сообщения EM_REPLACESEL. Для чтения информации мы используем довольно большой буфер. В принципе, как и в случае с файлами, можно читать несколькими порциями, проверяя количество считанных байт.
46 Так называемые именованные каналы реализованы в Windows NT.
V
В. Можно ли не допустить многократный запуск одного и того же приложения?
Да. Наиболее часто употребляемым для этого средством является создание объекта Mutex. Этот объект как раз и предназначен для того, чтобы координировать разные процессы. Создается данный объект при помощи функции CreateMutex. Рассмотрим параметры этой функции.
- 1-й параметр. Указатель на структуру, определяющую атрибут доступа. Обычно NULL (0).
- 2-й параметр. Флаг. В случае ненулевого значения процесс требует немедленного владения объектом (!).
- 3-й параметр. Указатель на имя объекта.
При запуске программы она создает Mutex. Второй параметр должен быть ненулевым. При вторичном запуске программы попытка создания Mutex вызовет ошибку, что и может расцениваться как повод немедленного выхода из программы.
К тому же результату можно прийти, используя семафор или файл, отображаемый в память. В данном случае все достаточно тривиально.
Еще один подход основан на разделяемой памяти. Определим область разделяемой памяти и там - переменную. При запуске приложение проверяет значение переменной, и если она равна нулю, то засылает туда единицу. Если переменная уже равна единице, то - выход (или действия, предусмотренные в этом случае).
Все способы, указанные в данном разделе столь просты, что мы больше не будем на них останавливаться.
VI
В. Имеет ли операционная система Windows средства, упрощающие операции над группами файлов и каталогами?
Да, имеется функция SHFileOperation, которая умеет выполнять копирование, перенос, переименование или удаление файловых объектов (т.е. файлов и каталогов, в том числе и вложенных). Данная функция имеет всего один параметр - указатель на структуру, которая и определяет, какую операцию следует произвести, над чем и как. Вот эта структура.
SH STRUCT hwnd DWORD ? wFunc DWORD ? pFrom DWORD ? pTo DWORD ? fFlags DWORD ? fAnyOperationsAborted DWORD ? hNameMappings DWORD ? lpszProgressTitle DWORD ? SH ENDS
Рассмотрим значение этих полей.
- hwnd - дескриптор окна, куда будет выводиться статус операции.
- wFunc - код операции. Может принимать следующие значения: FO_COPY, FO_DELETE, FO_MOVE, FO_RENAME. Смысл этих значений, я думаю, Вам понятен.
- pFrom - название файла, каталога или группы файлов или каталогов, над которыми будет производиться операция. Если несколько объектов, то имена отделяются символами с кодом 0. Можно выделять списки, которые отделяются друг от друга двумя нулевыми символами.
- pTo - имя или группа имен - результат операции, например копирование.
- fFlags - флаг, определяет характер операции. Может являться комбинацией
следующих констант:
- FOF_ALLOWUNDO - сохранить, если возможно, информацию для возвращения в исходное состояние.
- FOF_CONFIRMMOUSE - данное значение не реализовано.
- FOF_FILESONLY - выполнять только над файлами, если определен шаблон.
- FOF_MULTIDESTFILES - указывает, что pTo содержит несколько результирующих файлов или каталогов. Например, можно копировать сразу в несколько каталогов. Если pFrom состоит из нескольких файлов, то каждый файл будет копироваться в свой каталог.
- FOF_NOCONFIRMATION - отвечать утвердительно на все запросы.
- FOF_NOCONFIRMMKDIR- не подтверждать создание каталога, если это требуется.
- FOF_RENAMEONCOLLISION - давать файлам новые имена, если файлы с такими именами уже существуют.
- FOF_SILENT - не показывать окно-статус.
- FOF_SIMPLEPROGRESS - показывать окно-статус, но не показывать имена файлов.
- FOF_WANTMAPPINGHANDLE - заполнять отображаемый файл (см. ниже).
- fAnyOperationsAborted - переменная, по которой после операции можно определить, была ли прервана операция (<>0) или нет (0).
- hNameMappings - дескриптор отображаемого в памяти файла, содержащего массив, состоящий из новых и старых имен файлов, участвующих в операции.
- lpszProgressTitle - указывает на строку-заголовок для диалогового окна-статуса.
Кроме описанной функции, есть еще целая группа функций, начинающихся на SH. Среди них особенно полезна функция SHGetDesktopFolder, осуществляющая вывод диалогового окна для выбора нужной папки каталога.
VII
В. Как отправить данные на печатающее устройство?
Вопрос весьма обширен, и за подробностями отсылаю читателей к книге [12]. Здесь же приведу общий алгоритм, который используется при печати в Windows. Вот основные положения, которые позволят Вам быстро разобраться в сути проблемы.
- Печать очень схожа с выводом на экран. При этом используются те же функции вывода, например TextOut или Ellipse. Как и в случае с экраном, для этого необходимо знать контекст принтера.
- Контекст принтера создается, а не получается, как в случае с экраном. Для этого используется функция CreateDC, у которой второй параметр - это указатель на строку, содержащую имя принтера. Все остальные три параметра, как правило, равны NULL.
- Функция PrintDlg позволяет выбрать название принтера в диалоге. При этом она возвращает тот контекст принтера.
- Если Вы все же решили использовать функцию CreateDC, Вам крайне необходимо узнать имена принтеров, которые есть в системе. Для этого Вам как раз подойдет функция EnumPrinters. Это весьма мощное средство может определить не только локальные, но и сетевые принтеры.
- Начало печати документа осуществляется функцией StartDoc. Конец печати - EndDoc. Эти две функции обрамляют блок, осуществляющий печать. В пределах этого блока можно выделять также страницы при помощи функций StartPage и EndPage.
- По окончании печати следует удалить контекст при помощи функции DeleteDC.
VIII
В. Может ли приложение узнать, какие программы в настоящее время запущены?
Да. В основу метода положено использование функции EnumWindows (т.е. пересчитать окна). Вот параметры этой функции.
- 1-й параметр. Адрес процедуры, которая будет вызываться автоматически, если будет найдено окно.
- 2-й параметр. Произвольное значение, которое будет передаваться в процедуру.
Сама вызываемая процедура получает два параметра: дескриптор найденного окна и определенный выше параметр. По известному дескриптору с помощью функции GetWindowThreadProcessId можно определить уникальный идентификатор процесса или потока, который владеет данным окном. Имея же идентификатор, мы можем, в частности, удалить данный процесс из памяти с помощью функции TerminateProcess (впрочем, будьте осторожнее).
Ниже на Рис. 3.5.4 представлен пример программы, выдающей список работающих процессов. Обратите внимание на использование функции GetWindowText, которая определяет текст заголовка окна. Не пугайтесь, что в списке будут представлены и те процессы, окна которых мы не видим. Окна, как известно, могут быть скрытыми. Заметьте, что в список попадают и консольные приложения. Программа определяет также уникальные идентификаторы процессов.
// файл proc.rc // определение констант #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_VISIBLE 0x10000000L #define WS_TABSTOP 0x00010000L #define WS_VSCROLL 0x00200000L #define WS_HSCROLL 0x00100000L #define DS_3DLOOK 0x0004L #define LBS_NOTIFY 0x000lL #define LBS_SORT 0x0002L // идентификаторы #define LIST1 101 // определение диалогового окна DIAL1 DIALOG 0, 0, 220, 110 STYLE WS_SYSMENU | WS_MINIMIZEBOX | DS_3DLOOK CAPTION "Поиск процессов" FONT 8, "Arial" { CONTROL "ListBox1", LIST1, "listbox", WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | WS_HSCROLL | LBS_NOTIFY, 16, 16, 190, 75 } ; файл proc.inc ; константы ; значения, возвращаемые функцией GetDriveType ; значения 0 и 1 можно считать признаком отсутствия устройства DRIVE_REMOVABLE equ 2 ; накопитель на гибком диске DRIVE_FIXED equ 3 ; устройство жесткого диска DRIVE_REMOTE equ 4 ; сетевой диск DRIVE_CDROM equ 5 ; накопитель на лазерном диске DRIVE_RAMDISK equ 6 ; электронный диск ; сообщение приходит при закрытии окна WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_COMMAND equ 111h LB_ADDSTRING equ 180h LB_RESETCONTENT equ 184h WM_LBUTTONDOWN equ 201h ; прототипы внешних процедур IFDEF MASM EXTERN wsprintfA:NEAR EXTERN GetWindowThreadProcessId@8:NEAR EXTERN GetWindowTextA@12:NEAR EXTERN EnumWindows@8:NEAR EXTERN lstrcat@8:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN SendDlgItemMessageA@20:NEAR ELSE EXTERN _wsprintfA:NEAR EXTERN GetWindowThreadProcessId:NEAR EXTERN GetWindowTextA@12:NEAR EXTERN EnumWindows:NEAR EXTERN lstrcat:NEAR EXTERN ExitProcess:NEAR EXTERN GetModuleHandleA:NEAR EXTERN DialogBoxParamA:NEAR EXTERN EndDialog:NEAR EXTERN SendDlgItemMessageA:NEAR wsprintfA = _wsprintfA GetWindowThreadProcessId@8 = GetWindowThreadProcessId GetWindowTextA@12 = GetWindowTextA EnumWindows@8 = EnumWindows lstrcat@8 = lstrcat ExitProcess@4 = ExitProcess GetModuleHandleA@4 = GetModuleHandleA DialogBoxParamA@20 = DialogBoxParamA EndDialog@8 = EndDialog SendDlgItemMessageA@20 = SendDlgItemMessageA ENDIF ; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ; файл proc.asm .386P ; плоская модель .MODEL FLAT, stdcall include proc.inc ; директивы компоновщику для подключения библиотек IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' MSG MSGSTRUCT <?> HINST DD 0 ; дескриптор приложения PA DB "DIAL1",0 BUFER DB 200 DUP (0) BUF DB 20 DUP (0) FORM DB ";%lu",0 IDP DD ? HWN DD ? _DATA ENDS ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ;--------------------------------------- PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX,-1 JNE KOL ; сообщение об ошибке KOL: ;--------------------------------------- PUSH 0 CALL ExitProcess@4 ;--------------------------------------- ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ;LPARAM ; [EBP+10Н] ;WAPARAM ; [EBP+0CH] ;MES ; [EBP+8] ;HWND WNDPROC PROC PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI ;------------------------- CMP DWORD PTR [EBP+0CH],WM_CLOSE JNE L1 PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH],WM_INITDIALOG JNE FINISH ; запомним дескриптор окна MOV EAX,DWORD PTR [EBP+08H] MOV HWN,EAX ; вызвать функцию EnumWindows PUSH 1 ; неиспользуемый параметр PUSH OFFSET PENUM CALL EnumWindows@8 FINISH: MOV EAX,0 POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP ; процедура обратного вызова, вызываемая при поиске окон ; [EBP+0CH] ; параметр ; [EBP+8] ; дескриптор окна PENUM PROC PUSH EBP MOV EBP,ESP ; получить заголовок окна PUSH 200 PUSH OFFSET BUFER PUSH DWORD PTR [EBP+8] CALL GetWindowTextA@12 ; получить идентификатор процесса или потока, ; владеющего окном PUSH OFFSET IDP PUSH DWORD PTR [EBP+8] CALL GetWindowThreadProcessId@8 ; сформировать строку для списка PUSH OFFSET IDP PUSH OFFSET FORM PUSH OFFSET BUF CALL wsprintfA ADD ESP,12 PUSH OFFSET BUF PUSH OFFSET BUFER CALL lstrcat@8 ; добавить в список PUSH OFFSET BUFER PUSH 0 PUSH LB_ADDSTRING PUSH 101 PUSH [HWN] CALL SendDlgItemMessageA@20 POP EBP MOV EAX,1 RET 8 PENUM ENDP _TEXT ENDS END START
Рис. 3.5.4. Программа поиска процессов.
Трансляция программы на Рис. 3.5.4.
MASM32:
ml /c /coff /DMASM PROC.asm rc proc.rc link /subsystem:windows PROC.obj proc.resTASM32:
tasm32 /ml proc.asm brcc32 proc.rc tlink32 -aa proc.obj,,,,,proc.res
Рис. 3.5.5. Пример работы программы на Рис. 3.5.4.