Ассемблер для Windows

Пример простой программы которая осуществляет поиск файлов и выводит их название на экран



Рисунок 2.5.1 Пример простой программы, которая осуществляет поиск файлов и выводит их название на экран.

Программа на Рисунок 2.5.1 довольно проста. Из нового здесь Вы обнаружите лишь то, как обращаться с функциями FindFirstFile и FindNextFile. Процедуры, которые используются для работы с параметрами командной строки, Вы уже встречали ранее. Вывод информации осуществляется в текущую консоль, с чем Вы тоже знакомы. Для получения дескриптора консоли используется функция GetStdHandle. Процедура WRITE позволила несколько упростить те участки программы, которые отвечают за вывод информации на экран. Ранее я обещал, что мы не обойдем вниманием строковые API-функции. В данной программе это обещание выполнено, и наряду со строковыми процедурами "собственного изготовления" используется строковая функция lstrcat, которая осуществляет сложение (конкатенацию) строк. По поводу параметра в командной строке замечу, что при наличии в имени каталога пробела Вам придется задавать имя в укороченном виде. Так, например, вместо C:\Program Files придется написать C:\Progra~1. Это должно быть понятно - пробелы отделяют параметры. Чтобы корректно решать проблему, необходимо ввести специальный разделитель для параметров, например "-" или "/".

Данная программа осуществляет поиск в указанном или текущем каталоге. Если бы программа была написана на языке высокого уровня, например Си, ее легко можно было бы видоизменить так, чтобы она осуществляла поиск по дереву каталогов. Собственно, небольшая модификация потребовалась бы только для процедуры FIND, которая должна была бы вызываться рекурсивно. Можно видеть, что эта легкость произрастает из наличия в языках высокого уровня такого элемента, как локальная переменная. Попробуем осуществить это, основываясь на материале Главы 1.2. А можно осуществить это без использования локальных переменных?

Программа на Рисунок 2.5.2 немного похожа на предыдущую программу. Но поиск она осуществляет по дереву каталогов, начиная с заданного каталога. Эта программа - одна из самых сложных в книге, поэтому советую читателю скрупулезно в ней разобраться. Может быть, Вам удастся ее усовершенствовать. Я могу дать и направление, в котором возможно такое усовершенствование. Дело в том, что вторым параметром командной строки можно указать маску поиска. Если, например, указать маску "*.ЕХЕ", по этой маске будет осуществляться поиск не только файлов, но и каталогов. Этот недостаток и следовало бы устранить в первую очередь.




Поиск по дереву каталогов оптимально производить рекурсивным образом, однако для этого необходимы локальные переменные35. Смысл использования локальной переменной в рекурсивном алгоритме заключается в том, что часть данных должна сохраняться при возврате из процедуры.
В данной программе я, ради простоты, отказался от процедуры LENSTR и использую функцию API lstrlen. Кроме того, я усовершенствовал вывод так, чтобы на экран выводилось полное имя файла.
35
Конечно, можно обойтись и без них, храня данные, например, в глобальном массиве, обращаясь к той или иной области массива в зависимости от уровня рекурсии.
; файл FILES.ASM
.386P ; плоская модель .MODEL FLAT, stdcall
; константы STD_OUTPUT_HANDLE equ -11 STD_INPUT_HANDLE equ -10
; прототипы внешних процедур EXTERN wsprintfA:NEAR EXTERN CharToOemA@8:NEAR EXTERN GetStdHandle@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN ReadConsoleA@20:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetCommandLineA@0:NEAR EXTERN lstrcatA@8:NEAR EXTERN lstrcpyA@8:NEAR EXTERN lstrlenA@4:NEAR EXTERN FindFirstFileA@8:NEAR EXTERN FindNextFileA@8:NEAR EXTERN FindClose@4:NEAR ;----------------------------
; структура, используемая для поиска файла ; при помощи функций FindFirstFile и FindNextFile
_FIND STRUC ; атрибут файла ATR DWORD ? ; время создания файла CRTIME DWORD ? DWORD ? ; время доступа к файлу ACTIME DWORD ? DWORD ? ; время модификации файла WRTIME DWORD ? DWORD ? ; размер файла SIZEH DWORD ? SIZEL DWORD ? ; резерв DWORD ? DWORD ? ; длинное имя файла NAM DB 260 DUP (0) ; короткое имя файла ANAM DB 14 DUP (0) _FIND ENDS ;------------------------------------------------- ; директивы компоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' BUF DB 0 DB 100 dup (0) LENS DWORD ? ; количество выведенных символов HANDL DWORD ? HANDL1 DWORD ? MASKA DB "*.*" DB 50 DUP (0) AP DB "\",0 FIN _FIND <0> TEXT DB "Нажмите клавишу ENTER",13, 10, 0 BUFIN DB 10 DUP (0) ; буфер ввода NUM DB 0 NUMF DWORD 0 ; счетчик файлов NUMD DWORD 0 ; счетчик каталогов FORM DB "Число найденных файлов: %lu",0 FORM1 DB "Число найденных каталогов: %lu",0 DIRN DB " <DIR>",0 PAR DWORD 0 PRIZN DB 0 _DATA ENDS


; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить HANDLE вывода PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL,EAX ; получить HANDL1 ввода PUSH STD_INPUT_HANDLE CALL GetStdHandle@4 MOV HANDL1,EAX ; преобразовать строки для вывода PUSH OFFSET TEXT PUSH OFFSET TEXT CALL CharToOemA@8 PUSH OFFSET FORM PUSH OFFSET FORM CALL CharToOemA@8 PUSH OFFSET FORM1 PUSH OFFSET FORM1 CALL CharToOemA@8 ; получить количество параметров CALL NUMPAR MOV PAR,EAX ; если параметр один, то искать в текущем каталоге CMP EAX, 1 JE NO_PAR ;---------------------------------- ; получить параметр номером EDI MOV EDI,2 LEA EBX,BUF CALL GETPAR CMP PAR,3 JB NO_PAR ; получить параметр - маску поиска MOV EDI,3 LEA EBX, MASKA CALL GETPAR NO_PAR: ;---------------------------------- PUSH OFFSET BUF CALL FIND ; вывести количество файлов PUSH NUMF PUSH OFFSET FORM PUSH OFFSET BUF CALL wsprintfA LEA EAX, BUF MOV EDX,1 CALL WRITE ;+++++++++++++++++ ; вывести количество каталогов PUSH NUMD PUSH OFFSET FORM1 PUSH OFFSET BUF CALL wsprintfA LEA EAX, BUF MOV EDX, 1 CALL WRITE _END: PUSH 0 CALL ExitProcess@4 ; область процедур ;************************************* ; вывести строку (в конце перевод строки) ; EAX - на начало строки ; EDX - с переводом строки или без WRITE PROC ; получить длину параметра PUSH EAX PUSH EAX CALL lstrlenA@4 MOV ESI,EAX POP EBX CMP EDX, 1 JNE NO_ENT ; в конце - перевод строки MOV BYTE PTR [EBX+ESI],13 MOV BYTE PTR [EBX+ESI+1],10 MOV BYTE PTR [EBX+ESI+2],0 ADD EAX,2 NO_ENT: ; вывод строки PUSH 0 PUSH OFFSET LENS PUSH EAX PUSH EBX PUSH HANDL CALL WriteConsoleA@20 RET WRITE ENDP
; процедура определения количества параметров в строке ; определить количество параметров (->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 ;----------------------------------- ; поиск в каталоге файлов и их вывод ; локальные переменные FINDH EQU [EBP-4] ; дескриптор поиска DIRS EQU [EBP-304] ; полное имя файла DIRSS EQU [EBP-604] ; для хранения каталога DIRV EQU [EBP-904] ; для временного хранения DIR EQU [EBP+8] ; параметр - имя каталога FIND PROC PUSH EBP MOV EBP,ESP SUB ESP,904 ; инициализация локальных переменных MOV ECX,300 MOV AL,0 MOV EDI,0 CLR: MOV BYTE PTR DIRS+[EDI],AL MOV BYTE PTR DIRSS+[EDI],AL MOV BYTE PTR DIRV+[EDI],AL INC EDI LOOP CLR ; определить длину пути PUSH DIR CALL lstrlenA@4 MOV EBX,EAX MOV EDI, DIR CMP BYTE PTR [EDI],0 JE _OK ;если в конце нет "\" - добавим CMP BYTE PTR [EDI+EBX-1],"\" JE _OK PUSH OFFSET AP PUSH DWORD PTR DIR CALL lstrcatA@8 _OK: ; запомним каталог PUSH DWORD PTR DIR LEA EAX,DIRSS PUSH EAX CALL lstrcpyA@8 ; путь с маской PUSH OFFSET MASKA PUSH DWORD PTR DIR CALL lstrcatA@8 ; здесь начало поиска PUSH OFFSET FIN PUSH DWORD PTR DIR CALL FindFirstFileA@8 CMP EAX,-1 JE _ERR ; сохранить дескриптор поиска MOV FINDH,EAX LF: ; исключить "файлы" "." и ".." CMP BYTE PTR FIN.NAM,"." JE _FF ;--------------------- LEA EAX,DIRSS PUSH EAX LEA EAX,DIRS PUSH EAX CALL lstrcpyA@8 ;--------------------- PUSH OFFSET FIN.NAM LEA EAX, DIRS PUSH EAX CALL lstrcatA@8 ; не каталог ли? TEST BYTE PTR FIN.ATR, 10H JE NO_DIR ; добавить в строку <DIR> PUSH OFFSET DIRN LEA EAX, DIRS PUSH EAX CALL lstrcatA@8 ; увеличим счетчики INC NUMD DEC NUMF ; установим признак каталога MOV PRIZN,1 ; вывести имя каталога LEA EAX, DIRS PUSH EAX CALL OUTF JMP _NO NO_DIR: ; вывести имя файла LEA EAX, DIRS PUSH EAX CALL OUTF ; признак файла (не каталога) MOV PRIZN,0 _NO: CMP PRIZN,0 JZ _F ; каталог, готовимся в рекурсивному вызову LEA EAX,DIRSS PUSH EAX LEA EAX, DIRV PUSH EAX CALL lstrcpyA@8 PUSH OFFSET FIN.NAM LEA EAX,DIRV PUSH EAX CALL lstrcatA@8 ; осуществляем вызов LEA EAX, DIRV PUSH EAX CALL FIND ; продолжение поиска _F: INC NUMF _FF: PUSH OFFSET FIN PUSH DWORD PTR FINDH CALL FindNextFileA@8 CMP EAX,0 JNE LF ; закрыть дескриптор поиска PUSH DWORD PTR FINDH CALL FindClose@4 _ERR: MOV ESP, EBP POP EBP RET 4 FIND ENDP ;---------------------------------- ; страничный вывод имен найденных файлов STRN EQU [EBP+8] OUTF PROC PUSH EBP MOV EBP,ESP ; преобразовать строку PUSH DWORD PTR STRN PUSH DWORD PTR STRN CALL CharToOemA@8 ; здесь вывод результата MOV EAX, STRN MOV EDX, 1 CALL WRITE INC NUM ; конец страницы? CMP NUM, 22 JNE NO MOV NUM, 0 ; ждать ввод строки MOV EDX, 0 LEA EAX, TEXT CALL WRITE PUSH 0 PUSH OFFSET LENS PUSH 10 PUSH OFFSET BUFIN PUSH HANDL1 CALL ReadConsoleA@20 NO: POP EBP RET 4 OUTF ENDP _TEXT ENDS END START

Содержание раздела