III
Приемы работы с двоичными файлами*.
Манипуляция внешними файлами36 основывается на
нескольких функциях API, главной и наиболее сложной из которых является функция
CreateFile
.
В связи с ограниченностью объема книги, мы не можем подробно остановиться на
свойствах функции CreateFile
. Однако замечу, что с помощью этой
функции можно не только создавать или открывать файл, но и такие объекты как
каналы (PIPE
), консоли, устройства жесткого диска (disk device),
коммуникационный ресурс и др. Функция различает устройство по структуре имени. К
примеру, "C:\config.sys
" определяет файл, a "CONOUT$
"
- буфер вывода текущей консоли.
Сейчас я представлю две простые, но весьма важные программы (Рис. 2.5.3(1) и Рис. 2.5.3(2)). Обе программы выводят содержимое текстового файла37, имя которого указано в командной строке, в текущую консоль. В первом случае мы получаем дескриптор текущей консоли стандартным способом. Во втором случае - открываем консоль как файл и, соответственно, выводим туда информацию, как в файл. Хочу обратить Ваше внимание на роль буфера, в который читается содержимое файла. Поэкспериментируйте с размером буфера, взяв для пробы большой текстовый файл. Интересно, что в указанных программах никак не учитывается структура текстового файла. Для такого ввода-вывода это ни к чему. Ниже мы поговорим и о структуре текстового файла.
36 Имеется в виду файлами, расположенными на внешнем устройстве.
37 Точнее любого файла, но смысл выводить файл на консоль именно таким образом имеется только для текстового файла.
* Кажется, автор имеет ввиду текстовые файлы, а не бинарные, как это следует из дальнейшего повествования :)
;файл FILES1.ASM .386P ; плоская модель .MODEL FLAT, stdcall ; константы STD_OUTPUT_HANDLE equ -11 GENERIC_READ equ 80000000h GENERIC_WRITE equ 40000000h GEN = GENERIC_READ or GENERIC_WRITE SHARE = 0 OPEN_EXISTING equ 3 ; прототипы внешних процедур EXTERN GetStdHandle@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetCommandLineA@0:NEAR EXTERN CreateFileA@28:NEAR EXTERN CloseHandle@4:NEAR EXTERN ReadFile@20:NEAR ;------------------------------------------------- ; директивы компоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' HANDL DWORD ? HFILE DWORD ? BUF DB 100 DUP (0) BUFER DB 300 DUP (0) NUMB DWORD ? NUMW DWORD ? _DATA ENDS ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить HANDLE вывода PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL,EAX ; получить количество параметров CALL NUMPAR CMP EAX,1 JE NO_PAR ;------------------------------ ; получить параметр номером EDI MOV EDI,2 LEA EBX, BUF CALL GETPAR ; открыть файл PUSH 0 ; должен быть равен 0 PUSH 0 ; атрибут файла (если создаем) PUSH OPEN_EXISTING ; как открывать PUSH 0 ; указатель на security attr PUSH 0 ; режим общего доступа PUSH GEN ; режим доступа PUSH OFFSET BUF ; имя файла CALL CreateFileA@28 CMP EAX,-1 JE NO_PAR MOV HFILE, EAX L00: ; прочесть в буфер PUSH 0 PUSH OFFSET NUMB PUSH 300 PUSH OFFSET BUFER PUSH HFILE CALL ReadFile@20 ; вывести содержимое буфера на консоль PUSH 0 PUSH OFFSET NUMW PUSH NUMB PUSH OFFSET BUFER PUSH HANDL CALL WriteConsoleA@20 ; проверить, не последние ли байты прочитаны CMP NUMB, 300 JE L00 ; закрыть файл PUSH HFILE CALL CloseHandle@4 ; конец работы программы NO_PAR: PUSH 0 CALL ExitProcess@4 ; область процедур ; процедура определения количества параметров в строке ; определить количество параметров (->ЕАX) NUMPAR PROC CALL GetCommandLineA@0 MOV ESI,EAX ; указатель на строку XOR ECX,ECX ; счетчик MOV EDX,1 ; признак L1: CMP BYTE PTR [ESI],0 JE L4 CMP BYTE PTR [ESI],32 JE L3 ADD ECX.EDX ; номер параметра MOV EDX,0 JMP L2 L3: OR EDX,1 L2: INC ESI JMP L1 L4: MOV EAX, ECX RET NUMPAR ENDP ; получить параметр из командной строки ; EBX - указывает на буфер, куда будет помещен параметр ; в буфер помещается строка с нулем на конце ; EDI - номер параметра GETPAR PROC CALL GetCommandLineA@0 MOV ESI,EAX ; указатель на строку XOR ECX,ECX ; счетчик MOV EDX,1 ; признак L1: CMP BYTE PTR [ESI], 0 JE L4 CMP BYTE PTR [ESI],32 JE L3 ADD ECX,EDX ; номер параметра MOV EDX,0 JMP L2 L3: OR EDX,1 L2: CMP ECX,EDI JNE L5 MOV AL, BYTE PTR [ESI] MOV BYTE PTR [EBX], AL INC EBX L5: INC ESI JMP L1 L4: MOV BYTE PTR [EBX], 0 RET GETPAR ENDP _TEXT ENDS END STARTРис. 2.5.3(1). Вывод на консоль содержимого текстового файла. Первый способ.
; файл FILES2.ASM .386P ; плоская модель .MODEL FLAT, stdcall ; константы STD_OUTPUT_HANDLE equ -11 GENERIC_READ equ 80000000h GENERIC_WRITE equ 40000000h GEN = GENERIC_READ or GENERIC_WRITE SHARE = 0 OPEN_EXISTING equ 3 ; прототипы внешних процедур EXTERN ExitProcess@4:NEAR EXTERN GetCommandLineA@0:NEAR EXTERN CreateFileA@28:NEAR EXTERN CloseHandle@4:NEAR EXTERN ReadFile@20:NEAR EXTERN WriteFile@20:NEAR ;------------------------------------------------- ; директивы компоновщику для подключения библиотек ; includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' HANDL DWORD ? HFILE DWORD ? BUF DB 100 DUP (0) BUFER DB 300 DUP (0) NUMB DWORD ? NUMW DWORD ? NAMEOUT DB "CONOUT$" _DATA ENDS ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить HANDLE вывода (консоли) как файла PUSH 0 PUSH 0 PUSH OPEN_EXISTING PUSH 0 PUSH 0 PUSH GEN PUSH OFFSET NAMEOUT CALL CreateFileA@28 MOV HANDL,EAX ; получить количество параметров CALL NUMPAR CMP EAX, 1 JE NO_PAR ;--------------------------------------------- ; получить параметр номером EDI MOV EDI,2 LEA EBX,BUF CALL GETPAR ; открыть файл PUSH 0 PUSH 0 PUSH OPEN_EXISTING PUSH 0 PUSH 0 PUSH GEN PUSH OFFSET BUF CALL CreateFileA@28 CMP EAX,-1 JE NO_PAR MOV HFILE,EAX L00: ; прочесть в буфер PUSH 0 PUSH OFFSET NUMB PUSH 300 PUSH OFFSET BUFER PUSH HFILE CALL ReadFile@20 ; вывести на консоль как в файл PUSH 0 PUSH OFFSET NUMW PUSH NUMB PUSH OFFSET BUFER PUSH HANDL CALL WriteFile@20 CMP NUMB, 300 JE L00 ; закрыть файл PUSH HFILE CALL CloseHandle@4 ; конец работы программы NO_PAR: PUSH 0 CALL ExitProcess@4 ; область процедур ; процедура определения количества параметров в строке ; определить количество параметров (->EAX) NUMPAR PROC CALL GetCommandLineA@0 MOV ESI,EAX ; указатель на строку XOR ECX,ECX ; счетчик MOV EDX,1 ; признак L1: CMP BYTE PTR [ESI],0 JE L4 CMP BYTE PTR [ESI],32 JE L3 ADD ECX,EDX ; номер параметра MOV EDX,0 JMP L2 L3: OR EDX,1 L2: INC ESI JMP L1 L4: MOV EAX,ECX RET NUMPAR ENDP ; получить параметр из командной строки ; EBX - указывает на буфер, куда будет помещен параметр ; в буфер помещается строка с нулем на конце ; EDI - номер параметра GETPAR PROC CALL GetCommandLineA@0 MOV ESI,EAX ; указатель на строку XOR ECX,ECX ; счетчик MOV EDX,1 ; признак L1: CMP BYTE PTR [ESI],0 JE L4 CMP BYTE PTR [ESI],32 JE L3 ADD ECX,EDX ; номер параметра MOV EDX,0 JMP L2 L3: OR EDX,1 L2: CMP ECX,EDI JNE L5 MOV AL,BYTE PTR [ESI] MOV BYTE PTR [EBX], AL INC EBX L5: INC ESI JMP L1 L4: MOV BYTE PTR [EBX], 0 RET GETPAR ENDP _TEXT ENDS END START