Рис. 3.5.3. Пример взаимодействия с консольным процессом через PIPE.
Трансляция программы на Рис. 3.5.3.
MASM32:
Трансляция программы на Рис. 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.