абота с каталогами Созданием папки в WinAPI занимается функция CreateDirectory BOOL CreateDirectory( LPCTSTR lpPathName,// указатель на строку пути LPSECURITY_ATTRIBUTES lpSecurityAttributes // указатель на SECURITY_ATTRIBUTES ); Структура SECURITY_ATTRIBUTES имеет вид: typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES; Кому охота в этом разбираться - пускай закуривают MSDN. Мне это тоже интересно, но требует тщательного разбора (что бы вы не думали про Windows, но к проблемам разделения доступа в MicroSoft подошли ответственно, и парой слов дескрипторы безопасности не описать), поэтому создавать папки мы будем с правами доступа по умолчанию, подставляя вместо указателя на SECURITY_ATTRIBUTES просто NULL У CreateDirectory есть "старшая сестра" - CreateDirectoryEx, отличающаяся тем, что принимает три параметра: указатель на путь к папке-шаблону, указатель на путь к создаваемой папке и указатель на SECURITY_ATTRIBUTES. Зачем? А чтобы можно было, не заморачиваясь, наделить новую папку теми же атрибутами, что и у шаблона (в т.ч. и атрибутами безопасности) Чтобы удалить папку, нужно вызвать функцию RemoveDirectory, принимающую единственный параметр - указатель на строку пути BOOL RemoveDirectory(LPCTSTR lpPathName); Единственное замечание: чтобы папку можно было удалить, она должна быть пустой, и удаление этой папки не должно быть запрещено атрибутами безопасности Кстати, в той статье мы изучили, как задать текущую папку, чтобы проводить в ней файловые операции. Но как узнать текущую папку, чтобы понять, откуда запустили наше приложение? Это делает API-функция GetCurrentDirectory DWORD GetCurrentDirectory( DWORD nBufferLength,// размер буфера в символах (оставьте место и под завершающий ноль) LPTSTR lpBuffer // указатель на буфер, куда положим путь к текущей директории ); В случае неудачи функция возвращает 0, если всё прошло успешно - количество скопированных в буфер символов Ещё мы запросто можем узнать пути к системным папкам: это делается функциями GetSystemDirectory и GetWindowsDirectory UINT GetSystemDirectory( LPTSTR lpBuffer,// указатель на буфер для пути к системной папке (на XP - system32) UINT uSize // размер буфера в символах ); UINT GetWindowsDirectory( LPTSTR lpBuffer,// указатель на буфер для пути к папке с Виндой (обычно C:\Windows) UINT uSize // размер буфера в символах ); Обе функи при удачном стечении обстоятельств возвращают длину полученного пути, а при облажании - 0 Чтобы получить полный путь к файлу, юзают GetFullPathName DWORD GetFullPathName( LPCTSTR lpFileName,// указатель на строку с именем файла DWORD nBufferLength,// размер буфера в символах LPTSTR lpBuffer,// указатель на буфер для полного пути LPTSTR *lpFilePart // непонятно... обычно NULL ); Если всё удачно - функция вернёт длину получившейся строки, если не получилось - 0 Копирование и перемещение файлов Для копирования файлов в Win32 используется функция CopyFile BOOL CopyFile( LPCTSTR lpExistingFileName,// указатель на имя старого файла LPCTSTR lpNewFileName,// указатель на имя нового файла BOOL bFailIfExists // типа флаг ); "Типа флаг" определяет поведение функции, если новое имя копируемого файла совпадает с именем уже существующего в этой папке файла. Если флаг равен 0, то происходит перезапись того файла, если 1 - копирование отменяется CopyFile была хороша для Win9x/Me. Но вот пришло следующее поколение - WinNT (NewTechnology), принесшее с собой многие новшества, в т.ч. и файловую систему NTFS, поддерживающую альтернативные потоки данных внутри файла. При обычном копировании эти потоки терялись, поэтому MicroSoft включили в системные библиотеки новую функцию CopyFileEx, поддерживающую альтернативные потоки и ещё кучу всего (но не копирующую атрибуты безопасности) BOOL CopyFileEx( LPCWSTR lpExistingFileName,// указатель на имя старого файла LPCWSTR lpNewFileName,// указатель на имя нового файла LPPROGRESS_ROUTINE lpProgressRoutine,// указатель на callback-функцию LPVOID lpData,// аргумент callback-функции LPBOOL pbCancel,// флаг отмены операции DWORD dwCopyFlags// флаги копирования ); Начнём объяснение параметров функции с конца. dwCopyFlags может быть комбинацией двух значений: COPY_FILE_FAIL_IF_EXISTS (прекратить копирование, если файл с таким именем уже существует) и COPY_FILE_RESTARTABLE (если копирование файла не удалось, повторить через некоторое время). Флаг pbCancel проверяется во время копирования, и, если он установлен в 1, копирование отменяется. И, наконец, самое интересное - callback-функция Callback-функция выполняется каждый раз, как скопировалась очередная порция данных. Её называют CopyProgressRoutine DWORD WINAPI CopyProgressRoutine( LARGE_INTEGER TotalFileSize,// общий размер файла в байтах LARGE_INTEGER TotalBytesTransferred,// общее количество скопированных байт LARGE_INTEGER StreamSize,// общий размер данного потока LARGE_INTEGER StreamBytesTransferred,// общее количество скопированных из этого потока байт DWORD dwStreamNumber,// номер текущего потока DWORD dwCallbackReason,// причина callback'а HANDLE hSourceFile,// хэндл исходного файла HANDLE hDestinationFile,// хэндл конечного файла LPVOID lpData// аргумент, переданный CopyFileEx ); Причинами callback'а могут быть очередная скопированная порция данных (CALLBACK_CHUNK_FINISHED) или требование очередного потока его скопировать (CALLBACK_STREAM_SWITCH - именно по этой причине CopyProgressRoutine вызывается в первый раз с номером потока 1). Возвращает функция одно из следующих значений: PROGRESS_CONTINUE (продолжить копирование), PROGRESS_CANCEL (отменить его нахрен и удалить конечный файл), PROGRESS_STOP (приостановить копирование и продолжить его попозже), PROGRESS_QUIET (продолжить копирование, но больше не вызывать callback-функцию) В общем, задумка хорошая, но можно обойтись и без этого, попросту передав вместо указателя на callback-функцию и её аргумента NULL'ы Для перемещения файлов существует функция MoveFile BOOL MoveFile( LPCTSTR lpExistingFileName,// указатель имени файла LPCTSTR lpNewFileName // указатель на новое имя файла ); Эта функция способна переносить (а заодно и переименовывать) файлы и папки, причём файлы - куда угодно, а папки - только в пределах одного диска Попутно майкрософтовцы создали ещё одну функцию - MoveFileEx, снабдив её расширенными возможностями BOOL MoveFileEx( LPCTSTR lpExistingFileName,// указатель на имя файла LPCTSTR lpNewFileName,// указатель на новое имя файла DWORD dwFlags // флаги ); MoveFileEx позволяет подстановку во второй параметр NULL'а, что приводит к удалению файла. Опять же: файлы перемещайте, куда угодно, а папки - в пределах диска. dwFlags может быть комбинацией следующих флагов: MOVEFILE_COPY_ALLOWED (устанавливается, если нужно перемещать на другой диск, тогда вызов MoveFileEx распадается на CopyFile и DeleteFile), MOVEFILE_DELAY_UNTIL_REBOOT (этот флаг поддерживают только NT: файл будет перемещён после перезагрузки - после работы autochk, но до создания файла подкачки), MOVEFILE_REPLACE_EXISTING (если файл с таким именем уже существует, затереть его нахрен:)), MOVEFILE_WRITE_THROUGH (поддерживают только NT: функция вернёт управление только тогда, когда удостоверится, что файл в натуре перемещён) Специальной команды переименования в WinAPI нет, ведь перемещение с другим именем в пределах одной папки - и есть переименование Работа с атрибутами файла Каждый файл в Windows имеет свои атрибуты, определяющие его доступность для чтения/записи и не только. Для работы с ними существуют функции SetFileAttributes и GetFileAttributes. Первая из них устанавливает атрибуты файла, а вторая позволяет их просмотреть BOOL SetFileAttributes( LPCTSTR lpFileName,// указатель на имя файла DWORD dwFileAttributes // указатель на атрибуты ); А атрибуты мы можем установить вот такие: FILE_ATTRIBUTE_ARCHIVE (архивный: готов для архивирования или удаления), FILE_ATTRIBUTE_HIDDEN (скрытый), FILE_ATTRIBUTE_NORMAL (просто файл - ничего особенного; этот атрибут не комбинируется с другими), FILE_ATTRIBUTE_READONLY (только для чтения), FILE_ATTRIBUTE_SYSTEM (системный), FILE_ATTRIBUTE_TEMPORARY (временный), FILE_ATTRIBUTE_OFFLINE (непосредственный доступ к данным невозможен) DWORD GetFileAttributes( LPCTSTR lpFileName // указатель на имя файла или папки ); Эта функция в случае провала вернёт в eax -1, а в случае успешного выполнения - атрибуты файла или папки. Атрибуты те же самые, что и в случае SetFileAttributes, но добавляются ещё два: FILE_ATTRIBUTE_DIRECTORY (а файл-то - папка!) и FILE_ATTRIBUTE_COMPRESSED (сжатый) Кроме атрибутов файлы обладают размером, который можно определить функцией GetFileSize DWORD GetFileSize( HANDLE hFile,// хэндл файла LPDWORD lpFileSizeHigh // указатель на переменную ); В случае неудачи функция возвращает в eax -1, при удачном стечении обстоятельств - младшие 32 бита результата в eax и старшие 32 бита - в указанной переменной А ещё файл характеризуется временем создания, временем последнего доступа и временем последней записи. Чтобы их получить, нужно заюзать функцию GetFileTime BOOL GetFileTime( HANDLE hFile,// хэндл файла LPFILETIME lpCreationTime,// указатель на время создания LPFILETIME lpLastAccessTime,// указатель на время последнего доступа LPFILETIME lpLastWriteTime // указатель на время последней записи ); Каждый указатель адресует 64-битное расположение в памяти, где первые 32 бита представляют собой младший dword времени, а следующие 32 - старший dword Чтобы установить свои значения времени для файла, нужно заюзать API-шку SetFileTime, которая удивительно похожа на GetFileTime: также принимает хэндл файла и три указателя на время (с единственным отличием - здесь время уже задано). Кстати, и в той, и в другой функции, если какое-то из времён вас не интересует, можно задать вместо указателя на него NULL Создание, открытие и закрытие файла Создание и открытие файла в Win32 производится одной функцией CreateFile HANDLE CreateFile( LPCTSTR lpFileName,// указатель на имя файла DWORD dwDesiredAccess,// режим доступа DWORD dwShareMode,// режим разделения LPSECURITY_ATTRIBUTES lpSecurityAttributes,// указатель на атрибуты безопасности DWORD dwCreationDistribution,// параметры открытия DWORD dwFlagsAndAttributes,// атрибуты файла и флаги HANDLE hTemplateFile // хэндл шаблонного файла ); Теперь о параметрах подробнее: режим доступа может быть GENERIC_READ (доступен для чтения), GENERIC_WRITE (доступен для записи) или GENERIC_READ+GENERIC_WRITE Режим разделения: NULL - доступ к файлу полностью монополизирован открывшим его процессом, FILE_SHARE_READ - другие процессы могут читать файл, но запись в него монополизирована, FILE_SHARE_WRITE - другие процессы могут записывать в файл, но чтение из него монополизировано, FILE_SHARE_READ+FILE_SHARE_WRITE - тут понятно без слов. Под WinNT есть ещё режим FILE_SHARE_DELETE, когда и чтение, и запись монополизированы открывшим файл процессом, но другие процессы могут его удалить dwCreationDistribution определяет, что делать в "спорных ситуациях". CREATE_NEW - создать новый файл (если файл с таким именем уже существует, то функция выдаёт ошибку), CREATE_ALWAYS - создать новый файл (если уже существует - затереть его), OPEN_EXISTING - открыть существующий файл (если не существует - ошибка), OPEN_ALWAYS - открыть существующий файл (если не существует - создать), TRUNCATE_EXISTING - открыть файл с усечением его до нулевой длины (если не существует - ошибка) Ну, про атрибуты файла сказано было уже много. Здесь они те же: FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_COMPRESSED, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_OFFLINE, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_TEMPORARY и их комбинации. Интереснее флаги: FILE_FLAG_OVERLAPPED - асинхронные чтение и запись в файл разрешены с определённого смещения, FILE_FLAG_WRITE_THROUGH - не использовать промежуточное кэширование при записи на диск, FILE_FLAG_NO_BUFFERING - не использовать средства буферизации ОС, FILE_FLAG_RANDOM_ACCESS и FILE_FLAG_SEQUENTIAL_SCAN - помогают системе оптимизировать кэширование, FILE_FLAG_DELETE_ON_CLOSE - удалить файл после закрытия, FILE_FLAG_DELETE_ON_CLOSE - файл открыт для резервного копирования или восстановления (только на NT), FILE_FLAG_DELETE_ON_CLOSE - доступ к файлу возможен при использовании правил POSIX Если CreateFile открывает клиентскую часть именованного канала, то среди флагов могут содержаться данные о безопасности от службы QoS. Если вызывающее приложение установило флаг SECURITY_SQOS_PRESENT, то в dwFlagsAndAttributes могут быть SECURITY_ANONYMOUS, SECURITY_IDENTIFICATION, SECURITY_IMPERSONATION, SECURITY_DELEGATION, SECURITY_CONTEXT_TRACKING, SECURITY_EFFECTIVE_ONLY - я хреново понимаю, что это значит, кому интересно - курите MSDN Хэндл шаблонного файла используется для создания нового файла с теми же атрибутами, что и у шаблона Функция CreateFile возвращает хэндл файла или INVALID_HANDLE_VALUE в случае неудачи Закрыть файл можно булевой функцией CloseHandle, принимающей только один параметр - закрываемый хэндл Чтение и запись осуществляются уже знакомыми вам ReadFile и WriteFile, а чтобы установить позицию указателя в файле, используется функция SetFilePointer DWORD SetFilePointer( HANDLE hFile,// хэндл файла LONG lDistanceToMove,// на сколько байт продвинуть указатель? PLONG lpDistanceToMoveHigh,// указатель на старшее слово дистанции продвижения указателя DWORD dwMoveMethod // точка отсчёта ); Точка отсчёта может быть задана, как FILE_BEGIN (отсчитывать дистанцию от начала файла), FILE_CURRENT (отсчитывать от текущей позиции курсора), FILE_END (отсчитывать от конца файла). Дистанцию можно задавать как положительным, так и отрицательным числом байт - указатель ведь должен двигаться в обе стороны. Если мы ставим NULL вместо адреса старшего слова дистанции, то ограничиваем дальнобойность указателя 2^32-2 байт. Если пропишем адрес, то диапазон расширяется до 2^64 - 2 Кое-что можно упростить... Итак, мы перечислили основные API для работы с файлами. Согласитесь, всё довольно просто и понятно, но как-то громоздко: узнать атрибуты файла - одна функция, размер - другая, время создания - третья, полный путь - четвёртая... Долго и неэлегантно. Вот потому и существует функция GetFileInformationByHandle BOOL GetFileInformationByHandle( HANDLE hFile, // хэндл файла LPBY_HANDLE_FILE_INFORMATION lpFileInformation // указатель на BY_HANDLE_FILE_INFORMATION ); Ну а структура BY_HANDLE_FILE_INFORMATION в свою очередь выглядит так: typedef struct _BY_HANDLE_FILE_INFORMATION { DWORD dwFileAttributes; FILETIME ftCreationTime; FILETIME ftLastAccessTime; FILETIME ftLastWriteTime; DWORD dwVolumeSerialNumber; DWORD nFileSizeHigh; DWORD nFileSizeLow; DWORD nNumberOfLinks; DWORD nFileIndexHigh; DWORD nFileIndexLow; } BY_HANDLE_FILE_INFORMATION; Эта структура содержит в себе тотальное досье на файл: атрибуты; время создания, доступа и записи; серийный номер дискового тома, на котором он расположен; размер; количество ссылок на файл (на FAT всегда 1, на NTFS можно и больше), идентификатор файла ShellAPI Ещё одно упрощение можно сделать для базовых операций с файлами (удаление, копирование, переименование, перемещение) - использовать всего одну функцию для всех этих действий Дело в том, что все вышеперечисленные функции экспортируются kernel32.dll. Эта либа содержит базовые API-шки, без которых не сможет работать ни одно приложение (а даже если и сможет, системный загрузчик не запустит программу, в секции импорта которой не значится kernel32.dll), я даже больше скажу: импортируя функи из одной только kernel32.dll, можно получить доступ ко всему многообразию WinAPI-функций Однако, кроме базовых, есть специальные API-шки для файлов и папок - ShellAPI, функции которого сосредоточены в библиотеке shell32.dll. Начиная с четвёртой версии, в этой библе появилась функция SHFileOperation, предоставляющая почти неограниченные возможности: WINSHELLAPI int WINAPI SHFileOperation( LPSHFILEOPSTRUCT lpFileOp //указатель на структуру _SHFILEOPSTRUCT ); Ну а структура _SHFILEOPSTRUCT уже и содержит детальные указания: какой файл имеется в виду, что с ним делать и т.д. Эта структура состоит из следующих полей: typedef struct _SHFILEOPSTRUCT { HWND hwnd; //хэндл окна, заказавшего файловые операции (туда мы прогресс-бар покажем) UINT wFunc; //Что делать с файлом? LPCSTR pFrom; //где находится файл? LPCSTR pTo; //и куде его перемещать, переносить и т.д. FILEOP_FLAGS fFlags; //поле флагов BOOL fAnyOperationsAborted; //это поле сообщит: не прервал ли юзер выполнение функции LPVOID hNameMappings; //сам ещё не разобрался LPCSTR lpszProgressTitle; //заголовок к прогресс-бару } SHFILEOPSTRUCT, FAR *LPSHFILEOPSTRUCT; В фасмовских инклудах есть понятие об SHFileOperation, но об _SHFILEOPSTRUCT там и не слыхивали (по крайней мере, в версии 1.67.12, которую юзаю я), поэтому инклуду придётся варганить самим. Вот что наколдовал я: ;описуха структуры, ;взята из масмовских инклудов struct _SHFILEOPSTRUCT hwnd dd ? wFunc dd ? pFrom dd ? pTo dd ? fFlags dd ? fAnyOperationsAborted dd ? hNameMappings dd ? lpszProgressTitle dd ? ends ;числовые коды символьных имён ;функций и флагов FO_MOVE = 1h;переместить FO_COPY = 2h;копировать FO_DELETE = 3h;удалить FO_RENAME = 4h;переименовать FOF_MULTIDESTFILES = 1h;в поле pTo - несколько имён FOF_CONFIRMMOUSE = 2h FOF_SILENT = 4h;не выводить прогресс-бар FOF_RENAMEONCOLLISION = 8h;переименовать при совпадении FOF_NOCONFIRMATION = 10h;не требовать подтверждения FOF_WANTMAPPINGHANDLE = 20h FOF_ALLOWUNDO = 40h;сохранять информацию для отката операции FOF_FILESONLY = 80h;обрабатывать только файлы FOF_SIMPLEPROGRESS = 100h;показывать прогресс-бар, но не светить имена файлов FOF_NOCONFIRMMKDIR = 200h;создавать новую папку без подтверждения Теперь закатайте всё это в текстовый файл, обзовите его, например, shellapiA.inc и положите в папку с фасмовскими инклудами. Этот файлик нам очень пригодитсяЧто ж, быстренько напишем консольную программку, реализующую удаление файлов с помощью функции SHFileOperation. Большая часть кода вам уже знакома, комментировать буду то, что для вас ново: format PE console include 'win32axp.inc' ;подключаем инклуду win32axp.inc ;и нашу самописную shellapiA.inc include 'shellapiA.inc' section '.data' data readable writeable ns dd ? hout dd ? buffer db 261 dup (?) help db 'Using: shdel.exe path_to_file',0 Retry db 'Program fails. Please, retry',0 shf _SHFILEOPSTRUCT 0,FO_DELETE,0,0,FOF_NOCONFIRMATION,0,0 ;объявляем переменную shf типа _SHFILEOPSTRUCT ;и заполняем поля hwnd,wFunc,fFlags section '.code' code readable executable fuck: invoke GetStdHandle,STD_OUTPUT_HANDLE mov [hout],eax invoke GetCommandLine mov esi,eax cycle1: cmp byte [esi],20h je parameter cmp byte [esi],0Dh je najobka inc esi jmp cycle1 parameter: mov edi,buffer mov ecx,260 cycle2: inc esi mov al,byte [esi] cmp al,0Dh je konets mov byte [edi],al inc edi loop cycle2 konets: mov byte [edi],0 invoke lstrlen,buffer test eax,eax jz najobka ;пропарсив командную строку, ;получили в buffer путь и имя удаляемого файла mov eax,buffer mov [shf.pFrom],eax ;заполняем в shf поле pFrom ;и вызываем функцию invoke SHFileOperation,shf exit: invoke ExitProcess,0 najobka: invoke WriteConsole,[hout],help,29,ns,NULL jmp exit .end fuck
Как видите, по сравнению с DeleteFile SHFileOperation позволяет сократить размер кода. Правда, её использование сопряжено с немалым умственным геморроем: у меня (на WinXP SP1 Pro) приведенный выше код нормально компилировался и запускался, но попытка удаления файла с именем короче 3-х символов приводила к ошибке чтения с диска. Удаление по маске тоже не всегда удаётся... Не в этом ли причина того, что при всём удобстве ShellAPI-функций программисты юзают их довольно редко? Поэтому, уважаемые читатели, мне было бы очень интересно узнать, каковы ваши впечатления от этой API-шки: работает ли она в вашей системе, не глючит ли? Что ж, до новых встреч, уважаемые читатели! Надеюсь, вам с нами интересно:)