Assembler для Windows

самое лучшее домашнее порно прямо сейчас|купить toyota chr



Глава 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 - текст всплывающей подсказки.

Давайте рассмотрим, как работает весь механизм. В случае минимизации окна на функцию окна приходит сообщение 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.res
TASM32:
tasm32 /ml tray.asm
brcc32 tray.rc
tlink32 -aa tray.obj,,,,,tray.res

В связи с программой на Рис. 3.5.1 хочу особо акцентировать Ваше внимание на сообщении WM_SIZE. Весьма полезное сообщение, я Вам скажу. Представьте, что у себя в окне Вы расположили какую-то информацию. Если окно допускает изменение размеров, то Вам придется решать проблему размещения информации в случае, если размер окна изменился. Так вот, аккуратно все перерисовать и отмасштабировать можно как раз, если использовать данное сообщение. Подчеркну, что сообщение посылается, когда размер окна уже изменился. При этом WPARAM содержит признак того, что произошло с окном, a LPARAM - новый размер окна (младшее слово - ширина, старшее - высота).

II

В. Есть ли дополнительные средства, упрощающие файловую обработку?

Да, таким средством, в частности, являются файлы, отображаемые в память. Побайтное чтение из файла действительно не всегда удобно. Конечно, можно подойти к этой проблеме несколько иначе. Можно выделить в памяти достаточно большой буфер и прочитать туда файл, а затем работать с ним, как с большим массивом. Так вот, файлы, отображаемые в память, - это нечто похожее, но здесь система берет на себя часть такой работы. Самое замечательное, однако, в этом механизме заключается в том, что файл, спроецированный в память, может быть разделенным между несколькими процессами, а это еще один способ обмена информацией между процессами.

У файлов, проецируемых в память, есть довольно существенный недостаток. После отображения их размер не может быть увеличен. В этом случае поступают следующим образом: заранее предполагая новый размер файла, проецируют его на большую область памяти.

Работу с файлами, отображаемыми в память, производят по следующему алгоритму:

  1. Открыть (или создать) файл с помощью обычной функции CreateFile. Функция, как известно, возвращает дескриптор открытого файла.
  2. Отобразить файл45 с помощью функции CreateFileMapping. Именно эта функция определяет размер отображения. Эта функция, как и предыдущая, возвращает дескриптор, только не обычного, а отображенного файла.
  3. Скопировать файл (или его часть) в созданную область при помощи функции MapViewOfFile. Эта функция возвращает указатель (вот оно!) на начало области, где будет расположен файл. После этого руки у Вас развязаны, и Вы можете делать с отображенным файлом все, что Вам заблагорассудится.
  4. При желании можно записать область памяти в файл при помощи функции FlushViewOfFile. Разумеется, сбрасываться на диск будет та область памяти, которую мы заказали при помощи функции CreateFileMapping. Записывать на диск можно, разумеется, и с помощью обычной функции WriteFile.
  5. Перед тем как закрывать отображенный файл, следует вначале сделать указатель недействительным. Это делается с помощью функции UnmapViewOfFile.
  6. Закрыть следует оба дескриптора. Вначале дескриптор, возвращенный функцией 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.obj
TASM32:
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.res
TASM32:
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. 1-й параметр. Указатель на структуру, определяющую атрибут доступа. Обычно NULL (0).
  2. 2-й параметр. Флаг. В случае ненулевого значения процесс требует немедленного владения объектом (!).
  3. 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. Вот основные положения, которые позволят Вам быстро разобраться в сути проблемы.

  1. Печать очень схожа с выводом на экран. При этом используются те же функции вывода, например TextOut или Ellipse. Как и в случае с экраном, для этого необходимо знать контекст принтера.
  2. Контекст принтера создается, а не получается, как в случае с экраном. Для этого используется функция CreateDC, у которой второй параметр - это указатель на строку, содержащую имя принтера. Все остальные три параметра, как правило, равны NULL.
  3. Функция PrintDlg позволяет выбрать название принтера в диалоге. При этом она возвращает тот контекст принтера.
  4. Если Вы все же решили использовать функцию CreateDC, Вам крайне необходимо узнать имена принтеров, которые есть в системе. Для этого Вам как раз подойдет функция EnumPrinters. Это весьма мощное средство может определить не только локальные, но и сетевые принтеры.
  5. Начало печати документа осуществляется функцией StartDoc. Конец печати - EndDoc. Эти две функции обрамляют блок, осуществляющий печать. В пределах этого блока можно выделять также страницы при помощи функций StartPage и EndPage.
  6. По окончании печати следует удалить контекст при помощи функции 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.res
TASM32:
tasm32 /ml proc.asm
brcc32 proc.rc
tlink32 -aa proc.obj,,,,,proc.res

Рис. 3.5.5. Пример работы программы на Рис. 3.5.4.