Как открыть файл в winapi
Описание:
function CreateFile(lpFileName: PChar; dwDesiredAccess, dwShareMode: DWORD;
lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD;
hTemplateFile: THandle): THandle;
Создает или открывает следующие объекты и возвращает Хендл (handle), для получения доступа к объекту:
· файлам
· каналам (pipes)
· mailslots
· коммуникационные ресурсы (communications resources)
· Дисковым устройствам (только для Windows NT)
· консолям (consoles)
· папкам (только открытие)
lpFileName: Строка которая определяет название объекта для создания или открытия. Если lpFileName - путь, то максимум символов в строке определено константой MAX_PATH.
Windows NT: Можно использовать пути более MAX_PATH знаков, вызывая расширенную (W) версию CreateFile и подставив " \? \ " в Путь. " \? \ " говорит функции выключать парсинг пути. Это позволяет вам использовать пути, которые имеют почти 32 000 Unicode знаков. Функция также работает с именами internet-протокола. " \? \ " игнорируется как часть пути. Например, " \? \C:\myworld\private" будет трансформирован в "C:\myworld\private", а " \? \UNC\tom_1\hotstuff\coolapps" будет трансформирован в " \ tom_1\hotstuff\coolapps".
dwDesiredAccess: Определяет тип доступа к объекту. Приложение может получить доступ по чтению, записи, записи-чтению. Этим параметром может быть любая комбинация следующих флагов:
-0 - Специальный флаг доступа к обьекту. Определяет аттрибуты файла не подсоединяясь к нему, не подсоединяясь к нему.
-GENERIC_READ- определяет доступ по чтению к устройству. Данные могут читаться из файла, а указатель файла может быть перемещен. Объединяется с флагом GENERIC_WRITE для доступа по чтению-записи.
-GENERIC_WRITE - определяет, доступ по записи к устройству. Данные могут быть записываться в файл, и указатель файла может быть перемещен. Объединяется с флагом GENERIC_WRITE для доступа по чтению-записи.
dwShareMode: Набор флагов который определяет, как объект может быть разделен(shared). Если dwShareMode - 0, объект не может быть разделен. Последующие операции открытия объекта будут отклонены, пока указатель не будет закрыт.
Чтобы разделить объект, используйте комбинацию следующих флагов:
-FILE_SHARE_DELETE - только для Windows NT : Доступ закрыт для всех кроме открывшего, после закрытия указателя файл будет удален.
- FILE_SHARE_READ - Доступ для чтения.
-FILE_SHARE_WRITE - Доступ для записи.
lpSecurityAttributes: Указатель на структуру TSecurityAttributes, которая определяет, может ли возвращенный указатель быть использован процессами-потомками. Если lpSecurityAttributes является пустым, указатель не может быть унаследован.
Windows NT: поле структуры lpSecurityDescriptor определяет описатель безопасности для объекта. Если lpSecurityAttributes является пустым, объект получает описатель по умолчанию. Файловая система должна поддерживать ружим безопасности.
Windows 95: поле lpSecurityDescriptor игнорируется.
dwCreationDistribution: Определяет действие которое надо совершить в случае когда файлы существуют, и какое когда файлы не существуют. Дополнительную информацию см. в Примечаниях. Этим параметром должна быть один из следующих флагов:
-CREATE_NEW создает новый файл. Функция терпит неудачу, если указанный файл уже существует.
-CREATE_ALWAYS создает новый файл. Функция переписывает файл, если он существует.
-OPEN_EXISTING открывает файл. Функция терпит неудачу, если файл не существует.
-OPEN_ALWAYS открывает файл, если он существует. Если файл не существует, функция создает файл.
-TRUNCATE_EXISTING открывает файл. После того файл открывается его размер обнуляется. Процесс запроса должен открыть файл с по крайней мере с GENERIC_WRITE доступом. Функция терпит неудачу, если файл не существует.
dwFlagsAndAttributes: Определяет признаки файла и флаги для файла.
Любая комбинация следующих признаков приемлема. Все признаки файла перекрывают FILE_ATTRIBUTE_NORMAL. См. FILE_ATTRIBUTE_.
Любая комбинация следующих флагов приемлема для использования.
- FILE_FLAG_WRITE_THROUGH - говорит системе чтобы при записи в кэш сразу шла запись на диск.
- FILE_FLAG_OVERLAPPED - говорит системе возвращать указатель даже если действие еще не завершено. Без этого флага функции ReadFile, WriteFile, ConnectNamedPipe, и TransactNamedPipe, которые занимают существенное время, возвращают ERROR_IO_PENDING. Когда действие закончено, вызывается событие указывающее об этом.Когда Вы определяете FILE_FLAG_OVERLAPPED, ReadFile, и функции WriteFile должны определить структуру TOverlapped.Когда FILE_FLAG_OVERLAPPED определен, операционная система не не обслуживает указатель файла. Позицию указателя файла нужно передать как часть параметра lpOverlapped (указывающий на структуру TOverlapped ) к функциям WriteFile и ReadFile.
Этот флаг также позволяет выполняться более чем одному действию с указателем файла ( например одновременный читать и писать).
- FILE_FLAG_NO_BUFFERING - Инструктирует операционную систему открывать файл без промежуточного буфера или кэширования.
- FILE_FLAG_RANDOM_ACCESS - Указывает, что к файлу обращаются беспорядочно. Windows может использовать это как намек, чтобы оптимизировать кэширование файла.
- FILE_FLAG_SEQUENTIAL_SCAN - Указывает, что к файлу нужно обратиться последовательно с начала до конца. Windows может использовать это как намек, чтобы оптимизировать кэширование файла.
- FILE_FLAG_DELETE_ON_CLOSE - Указывает, что операционная система должна удалить файл немедленно после того, как все его указатели будут закрыты.
- FILE_FLAG_BACKUP_SEMANTICS - только для Windows NT: Указывает, что файл открывается или создается для резервирования или восстанавления. Операционная система гарантирует, что процесс запроса отвергает проверку безопасности файла. Уместные разрешения - SE_BACKUP_NAME, и SE_RESTORE_NAME.
FILE_FLAG_POSIX_SEMANTICS - Указывает, что к файлу нужно обратиться согласно правилам POSIX.
Если функция CreateFile открывает клиент канала, параметр dwFlagsAndAttributes может также содержать Security Quality of Service information. Когда приложение запрашивает флаг SECURITY_SQOS_PRESENT, параметр dwFlagsAndAttributes может содержать один или больше следующих флагов:
- SECURITY_ANONYMOUS - определяет Анонимнго клиента(Anonymous).
- SECURITY_IDENTIFICATION - определяет Идентифицируемого клиента(Identification).
- SECURITY_IMPERSONATION определяет клиента уровня IMPERSONATION.
- SECURITY_DELEGATION - определяет клиента уровня DELEGATION.
- SECURITY_CONTEXT_TRACKING - определяет, что способ отслеживания безопасности является динамическим.
- SECURITY_EFFECTIVE_ONLY - определяет, что только позволенные аспекты содержания безопасности клиента доступны серверу. Если Вы не определяете этот флаг, все аспекты содержания безопасности клиента доступны. Этот флаг позволяет клиенту ограничивать группы и привилегии которые сервер может использовать при исполнении запроса клиента.
hTemplateFile - Определяет указатель с доступом GENERIC_READ к файлу шаблона. Файл шаблона задает признаки файла и расширенные признакаки для создаваемого файла.
Windows 95: Этот флаг должен быть равен NULL. Если Вы установливаете указатель под Windows 95, запрос терпит неудачу, и GetLastError возвращает ERROR_NOT_SUPPORTED.
Возвращаемые значения
Если функция преуспевает - открытый указатель к файлу. Если указанный файл существовал до запроса функции и dwCreationDistribution - CREATE_ALWAYS или OPEN_ALWAYS, запрос к GetLastError возвращает ERROR_ALREADY_EXISTS. Если файл не существовал перед запросом, GetLastError возвращает ноль.
Если функция терпит неудачу - INVALID_HANDLE_VALUE. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError.
Примечания
Используйте функцию CloseHandle, чтобы закрыть указатель объекта, возвращенную CreateFile.
Как отмечено выше, определяя ноль для dwDesiredAccess функция позволяет узнать признаки устройства, фактически не обращаясь к устройству. Этоо полезено, например, если функция хочет узнать размер дискеты, и поддерживаемые форматы, при этом не имея дискеты в дисководе.
Файлы
При создании нового файла, функция CreateFile исполняет следующие действия:
· объединяет признаки файла и флаги, указанные dwFlagsAndAttributes с FILE_ATTRIBUTE_ARCHIVE.
· устанавливает длину файла на ноль.
· копирует расширенные признаки, заданные файлом шаблона к новому файлу, если параметр hTemplateFile определен.
При открытии существующего файла, CreateFile исполняет следующие действия:
· объединяет флаги файла, указанные dwFlagsAndAttributes с существующими признаками файла. CreateFile игнорирует признаки файла, указанные dwFlagsAndAttributes.
· устанавливает длину файла согласно флагу dwCreationDistribution.
· игнорирует параметр hTemplateFile.
· игнорирует параметр lpSecurityDescriptor структуры TSecurityAttributes, если lpSecurityAttributes параметр не является NULL. Другие параметры структуры используются. Параметр bInheritHandle - единственный способ указать, может ли указатель файла быть унаследованным.
Каналы(Pipes)
Если CreateFile открывает клиента, функция использует любой открытый клиент, которая находится в состоянии приема. Процесс открытия может дублировать указатель так много раз как требуется, но, когда-то открытый клиент не может быть открыт на другого клиента.
Mailslots
Если CreateFile открывает клиента-mailslot, функция возвращает INVALID_HANDLE_VALUE, если mailslot-клиент пытается открывать локальный mailslot прежде, чем mailslot-сервер создал его с функцией CreateMailSlot.
Ресурсы Коммуникаций
Функция CreateFile может создать указатель к ресурсу коммуникаций, типа последовательного порта COM1. Для ресурсов коммуникаций, dwCreationDistribution параметром должен быть OPEN_EXISTING, и hTemplate параметр должен быть NULL. Указатель можен быть открыть для записи, чтения, чтения-записи.
Дисковое устройство
Windows NT: Вы можете использовать функцию CreateFile, чтобы открыть дисковод. Функция возвращает указатель на дисковые устройства. Указатель может использоваться с функцией DeviceIOControl. Следующие требования должны быть выполнены:
· вызывающий должен иметь привилегии администратора.
· строка lpFileName должна иметь форму \.\PHYSICALDRIVEx, чтобы открыть жесткий диск x. Номера жесткого диска начинаются с нуля. Например: \.\PHYSICALDRIVE2 - получает указатель на третий физический диск на компьютере пользователя.
· строка lpFileName должна быть \.\x: чтобы открыть накопитель на гибких дисках. Например: \.\A: - получает указатель,на диск А. \.\C: - получает указатель,на диск C.
Windows 95: Эта функция не открывает логическое устройство. В Windows 95 это вызовет ошибку.
· параметр dwCreationDistribution должен иметь флаг OPEN_EXISTING.
· при открытии дискеты или жесткого диска, Вы должны установить флаг FILE_SHARE_WRITE в параметре dwShareMode .
Функция CreateFile может создать указатель ввод консоли (CONIN$). Если процесс имеет открытый указатель к нему в результате наследования или дублирования, то функция может также создать указатель на активный буфер экрана (CONOUT$). Процесс запроса должен быть присоединен к унаследованной консоли или один размещенный функцией AllocConsole. Для указателей консоли, установите параметры CreateFile следующим образом:
lpFileName - используют CONIN$, чтобы определить ввод консоли и CONOUT$, чтобы определить вывод консоли.
CONIN$ получает указатель на входной буфер консоли, даже если функция SetStdHandle переадресовывала стандартный указатель. Чтобы получить стандартный указатель, используйте функцию GetStdHandle.
CONOUT$ получает указатель на активный буфер экрана, даже если SetStdHandle переадресовывала стандартный указадель вывода. Чтобы получить стандартный указатель выводо используйте GetStdHandle.
dwDesiredAccess лучше устанавливать GENERIC_READ | GENERIC_WRITE, но любой может ограничить доступ.
dwShareMode, если процесс запроса унаследовал консоль или если дочерний процесс способен получить доступ, этот параметр должен быть FILE_SHARE_READ | FILE_SHARE_WRITE.
lpSecurityAttributes, если Вы хотите, чтобы консоль была унаследована,параметр bInheritHandle структуры TSecurityAttributes должен быть True.
dwCreationDistribution Вы должны определить OPEN_EXISTING при использовании CreateFile, чтобы открыть консоль.
dwFlagsAndAttributes игнорируется.
hTemplateFile игнорируется.
Следующий список показывает эффекты различных назначений fwdAccess и lpFileName.
lpFileName fwdAccess Результат
CON GENERIC_READ Открывает консоль для ввода
CON GENERIC_WRITE Открывает консоль для вывода
CON GENERIC_READ\GENERIC_WRITE Windows 95:Терпит неудачу; GetLastError возвращает ERROR_PATH_NOT_FOUND.
Windows NT: Терпит неудачу; GetLastError возвращает ERROR_FILE_NOT_FOUND.
Папки
Вызов функции CreateFile не может создать папку, чтобы создать папку надо вызвать CreateDirectory или CreateDirectoryEx.
Windows NT:Вы можете получить указатель к папку, подставив флаг FILE_FLAG_BACKUP_SEMANTICS . Указатель папки можно передать некоторым функциям Win32 вместо указателя файла. Некоторые файловые системы, типа NTFS, поддерживают сжатие для индивидуальных файлов и папок. На устройствах, форматированных для такой файловой системы, новая папка унаследует признак сжатия ее родительской папки.
В лекции рассматривается использование функций библиотеки (C++): CreateFile(), CopyFile(), MoveFile(), DeleteFile(), ReadFile(), WriteFile(), UnlockFile(), LockFile() и CloseHandle().
Материалы по данной теме имеются так же в лекции про файлы.
Создание файлов
Для создания нового или открытия существующего файла используется функция CreateFile. При использовании функции CreateFile необходимо указать, предполагается чтение или запись в файл или и то и другое. Также необходимо указать, необходимые действия в случае наличия файла на диске или его отсутствия (например, перезаписывать файл, если он существует и создавать новый, если – нет). Также функция CreateFile позволяет указать возможность разделения файла с другими приложениями (одновременного чтения/записи нескольких приложений в файл). Если некоторое приложение монополизировало доступ к файлу на чтение и/или запись, то никакое другое приложение не сможет читать и/или писать в файл, пока первое его не закроет.
Функция CreateFile имеет следующий прототип:
HANDLE CreateFile(
LPCTSTR lpFileName, // file name
DWORD dwDesiredAccess, // access mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to template file
); dwDesiredAccess:
[in] Описание желаемого режима доступа к файлу. Существует возможность передачи большого количества различных флагов. Нас интересуют флаги GENERIC_READ, GENERIC_WRITE и их объединение. При доступе к каналам следует учитывать режим создания канала сервером. Если сервер создал канал для записи, то клиент открывает его для чтения и наоборот. Если сервер создал канал для чтения и записи, то клиент может открыть его как для чтения, так и для записи.
dwShareMode:
[in] Определяет режим разделения объекта
0 – приложение открывает файл для монопольного доступа. Последующие обращения на открытие данного файла будут безуспешными, пока данных дескриптор не будет закрыт. Для разделения доступа к файлу могут использоваться следующие ключи (один или вместе):
FILE_SHARE_READ – разрешены лишь последующие операции открытия только на чтение.
FILE_SHARE_WRITE – разрешены лишь последующие операции открытия только на запись.
lpSecurityAttributes
[in] Указатель на сруктуру SECURITY_ATTRIBUTES, которая определяет возможность наследования дескриптора дочерними процессами. Можно передавать NULL – это значит, что дескриптор не может быть наследован (для наших приложений этого достаточно).
dwCreationDisposition:
[in] Определяет то, какие действия необходимо предпринять в случаях, если файл существует и если файл не существует. Этот параметр должен иметь одно из следующих заначений:
Значение Пояснение
CREATE_NEW Создаёт файл. Вызов заканчивается неудачей, если файл существует.
CREATE_ALWAYS Создаёт новый файл. Если файл существует, то его содержимое и атрибуты будут стёрты.
OPEN_EXISTING Открытие файла. Если файл не существует, то вызов закончится неудачей.
OPEN_ALWAYS Открывает файл. Если файл не существует, то он будет создан.
TRUNCATE_EXISTING Открывает файл, размер которого обнуляется. Файл должен открываться как минимум с режимом доступа GENERIC_WRITE. Если файл не существует, то вызов будет неудачен.
dwFlagsAndAttributes:
[in] Позволяет задавать файловые атрибуты (только для чтения, скрытый, системный и пр.). Также позволяет сообщать операционной системе желаемое поведение при работе с файлами. Например, запись в файл без буферизации (FILE_FLAG_NO_BUFFERING и FILE_FLAG_WRITE_THROUGH); оптимизация для неупорядоченного доступа (FILE_FLAG_RANDOM_ACCESS); открытие для асинхронного ввода/вывода (FILE_FLAG_OVERLAPPED).
Возвращаемое значение:
Если вызов успешен, возвращается дескриптор открытого файла. Если вызов неудачен, возвращается константа INVALID_HANDLE_VALUE. Код ошибки можно получить вызовом функции GetLastError. Подробную информацию об ошибке (по её коду) можно получить вызовом функции FormatMessage.
Для копирования файлов используется функция CopyFile :
LPCTSTR lpExistingFileName, // имя существующего файла
LPCTSTR lpNewFileName, // имя нового файла
BOOL bFailIfExists // действие, если файл существует TRUE – ошибка
// FALSE - перезаписывать
);
В случае успеха возвращается ненулевое значение.
Для переименования файлов и директорий используется функция MoveFile :
LPCTSTR lpExistingFileName, // имя файла
LPCTSTR lpNewFileName // новое имя файла
);
В случае успеха возвращается ненулевое значение.
Для удаления существующих файлов используется функция DeleteFile:
LPCTSTR lpFileName // имя удаляемого файла
);
Чтение/запись в файл:
Каждый открытый файл имеет файловый указатель (file pointer), который указывает позицию следующего файла, который будет записан/прочтен. При открытии файла его файловый указатель перемещается на начало файла. После прочтения/записи очередного файла система перемещает файловый указатель. Файловый указатель можно перемещать, используя функцию SetFilePointer.
Для чтения/записи в файл используются функции ReadFile и WriteFile, при этом необходимо, чтобы файл был открыт на чтение и на запись соответственно.
Функция ReadFile читает из файла указанное количество символов, начиная с позиции, обозначенной файловым указателем. При синхронном (в противоположность асинхронному) чтении файловый указатель сдвигается на фактически прочитанное количество байт.
HANDLE hFile, // дескриптор файла
LPVOID lpBuffer, // буфер данных
DWORD nNumberOfBytesToRead, // количество байт для прочтения
LPDWORD lpNumberOfBytesRead, // количество фактически прочитанных байт
LPOVERLAPPED lpOverlapped // указатель на структуру, используемую при
// асинхронном чтении
); hFile:
[in] Дескриптор читаемого файла. Должен быть открыт с доступом GENERIC_READ.
lpBuffer:
[out] Указатель на буфер, принимающий данные из файла.
nNumberOfBytesToRead:
[in] Задаёт количество байт, которые необходимо прочитать из файла.
lpNumberOfBytesRead:
[out] Указатель на переменную, которая принимает количество реально прочитанных байт.
lpOverlapped:
[in] Укзатель на структуру OVERLAPPED. При не асинхронном доступе следует передавать NULL.
Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение. Если возвращено ненулевое значение, но прочитано 0 байт, значит файловый указатель стоял на конце файла перед операцией чтения.
Функция WriteFile записывает в файл данные, начиная с позиции, обозначенной файловым указателем. При синхронной (в противоположность асинхронному) записи файловый указатель сдвигается на фактически записанное количество байт.
HANDLE hFile, // дескриптор файла
LPCVOID lpBuffer, // буфер данных
DWORD nNumberOfBytesToWrite, // количество байт для записи
LPDWORD lpNumberOfBytesWritten,// количество фактически записанных байт
LPOVERLAPPED lpOverlapped // указатель на структуру, используемую при
// асинхронном чтении
);
hFile:
[in] Дескриптор файла, в который производится запись. Должен быть открыт с доступом GENERIC_WRITE.
lpBuffer:
[in] Указатель на буфер, содержащий данные, которые необходимо записать.
nNumberOfBytesToWrite:
[in] Задаёт количество байт, которые необходимо записать в файл.
lpNumberOfBytesWritten:
[out] Указатель на переменную, которая принимает количество реально записанных байт.
lpOverlapped:
[in] Укзатель на структуру OVERLAPPED. При не асинхронном доступе следует передавать NULL.
Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение.
Блокировка файлов:
Т.к. система позволяет более чем одному приложению открывать файл и писать в него, приложения не должны одновременно писать в одну область файла. Эта проблема может быть решена путем временного блокирования части файла. Функция LockFile позволяет приложению получить в монопольное пользования определённое количество байт в файле. Отрезок заблокированных байт может выходить за текущий конец файла. Все попытки сторонних приложений обратиться к заблокированному участку файла потерпят неудачу.
Приложение может файл с помощью функции UnlockFile .
BOOL LockFile(
HANDLE hFile, // дескриптор файла
DWORD dwFileOffsetLow, // младшее слово смещения начала отрезка
DWORD dwFileOffsetHigh, // старшее слово смещения начала отрезка
DWORD nNumberOfBytesToLockLow, // младшее слово длины отрезка
DWORD nNumberOfBytesToLockHigh // старшее слово длины отрезка
);
hFile:
[in] Дескриптор файла, в который производится запись. Должен быть открыт с доступом GENERIC_READ или GENERIC_WRITE (или обоими).
dwFileOffsetLow:
[in] Определяет младшее слово смещения начала блокируемого отрезка относительно начала файла.
dwFileOffsetHigh:
[in] Определяет старшее слово смещения начала блокируемого отрезка относительно начала файла. Не равно нулю, если смещение относительно начала более чем 232 байт.
nNumberOfBytesToLockLow:
[in] Определяет младшее слово длины блокируемого отрезка.
nNumberOfBytesToLockHigh:
[in] Определяет старшее слово длины блокируемого отрезка. Не равно нулю, если длина отрезка более 232 байт или 4 ГБ.
Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение.
Блокирование предоставляет процессу монопольный доступ к отрезку файла. Файловые блокировки не наследуются. Остальные процессы не могут ни читать, ни писать в заблокированную часть файла.
Функция UnlockFile позволяет разблокировать участок файла, ранее заблокированный функцией LockFile .
HANDLE hFile, // дескриптор файла
DWORD dwFileOffsetLow, // младшее слово смещения начала отрезка
DWORD dwFileOffsetHigh, // старшее слово смещения начала отрезка
DWORD nNumberOfBytesToUnlockLow, // младшее слово длины отрезка
DWORD nNumberOfBytesToUnlockHigh // старшее слово длины отрезка
);
Параметры данной функции аналогичны параметрам функции LockFile.
Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение.
Отрезок файла, который разблокируется функцией UnlockFile должен в точности соответствовать отрезку, заблокированному функцией LockFile. Например, две соседних части файла не могут быть заблокированы по отдельности, а разблокированы как единое целое. Процесс не должен завершать выполнение, имея заблокированные части файлов. Файловый дескриптор, для которого есть заблокированные отрезки, не должен закрываться.
Для оптимального использования ресурсов операционной системы приложение должно закрывать ненужные файлы с помощью функции CloseHandle. Файлы, открытые на момент завершения приложения, закрываются автоматически.
HANDLE hObject // Дескриптор объекта (файла, канала и пр.)
);
Возвращаемое значение:
Если выполнение функции произошло успешно, то возвращается ненулевое значение.
Работа с двоичными файлами с использованием функций WinApi
Для создания файла используется функция WinApi CreateFile().
FILE_ATTRIBUTE определены следующим образом:
С помощью этой функции можно открывать уже существующие файлы и консоли для консольных приложений, усекать их, открывать каталоги. Это все задается пятым параметром. Для создания файла этот параметр задается как CREATE_NEW. Вновь создаваемый файл открывается как для чтения, так и для записи, о чем свидетельствует второй параметр функции. Третий и четвертый параметры редко имеют какое-либо практическое применение в Windows 9x и требуются в основном при создании систем с разделением доступа на базе Windows NT. Шестой параметр определяет, какие атрибуты будут установлены для создаваемого файла. В данном случае будет присвоен атрибут Read-Only, позволяющий только чтение файла без записи в него. Windows самостоятельно закрывает все файлы и освобождает дескрипторы, как только завершается выполнение программы, но правило чистоты программирования требует не заставлять думать систему, когда сам знаешь как выполнить то или иное действие.
Открытие существующего файла
Функция WinApi OpenFile() открывает файл. Помимо этого, OpenFile() умеет создавать и удалять файлы. Во многом назначения этой функции пересекаются с CreateFile(), и существует в последних версиях WinApi для совместимости с ранними версиями Windows. Поэтому везде, где это возможно, лучше пользовать CreateFile().
Функция OpenFile() принимает всего три аргумента: имя открываемого файла, указатель на структуру OFSTRUCT и флаг режима открытия файла. Структура OFSTRUCT заполняется данными об открытом файле. Она предоставляет такую информацию, как свойства файла, его размер и т. д.
Удаление файлов
Для удаления файлов используется функция WinApi DeleteFile().
DeleteFile() не удаляет защищенные от записи файлы (READONLY).
Копирование и перемещение файлов
Для копирования используется функция WinApi CopyFile, для перемещения MoveFile().
Определим имена файлов:
Аналогично CopyFile() действует функция API для переноса файла MoveFile().
Единственное отличие MoveFile() от CopyFile() - отсутствие третьего параметра, отвечающего за блокировку процесса переноса в случае, если файл уже существует.
Чтение информации из файла
Для чтения информации из файла используется функцию Win API ReadFile(), и ряд функций, обеспечивающих ее информацией:
В тексте объявляются две структуры. Первая, BY_HANDLE_FILE_INFORMATION, нужна для хранения полезной информации о файле. Вторая, OFSTRUCT требуется для работы функции API OpenFile(). Далее идет инициализация поля размера этой структуры.
Далее - создание буфера, в который будут скопированы данные, считанные из файла. Можно проделать расчет, основанный на размере файла, но можно поступить проще: задать размер принудительно, например 64 Кбайт. Для создания такого буфера можно использовать задание типа new char[0xfffe], выделяющей блок памяти подходящего размера. В конце обработчика необходимо освободить блок памяти операцией delete[].
Само чтение происходит в несколько этапапов: открытие файла, получение его размера, сдвиг на нужную позицию и собственно чтение.
Полученный дескриптор функцией OpenFile() открытого файла необходимо передать в качестве параметра для другой функции API - GetFileInformationByHandle(), которая заполняет структуру bhFileInformation типа BY_HANDLE_FILE_INFORMATION, переданную в качестве второго параметра, данными об открытом файле. В этой структуре имеются два поля, хранящие старшие и младшие четыре байта размера файла. Для малого файла хватает и младших четырех байтов.
Первые два параметра - это дескриптор читаемого файла и адрес буфера, в который будут считаны данные. Четвертый параметр функции - счетчик байтов, в который ReadFile() записывает количество байтов, считанных из файла.
Пятый параметр игнорируется.
Обратим внимание на то, что для правильной работы дескриптор файла надо преобразовывать к типу HANDLE.
При работе в переменной появятся данные из файла.
Запись информации в файл
Функция WinApi WriteFile() записывает данные в файл. Она полностью аналогична по используемым параметрам функции ReadFile().
Пример универсальной функции работы с файлами
Следующая функция читает информацию типа int или char из файла, имя которого и директория передается в функцию, или записывает информацию в файл в соответствии с передаваемыми в функцию параметрами. Функция не предполагает чтение всего файла, и написана для случаев когда известно сколько байт предполагается прчитать или записать.
Язык MQL4 спроектирован таким образом, чтобы даже неправильно написанные программы не могли случайно удалить данные с жесткого диска компьютера. Функции операций чтения и записи в файлы могут работать только в следующих директориях(цитирую):
- /HISTORY/<текущий брокер> - специально для функции FileOpenHistory;
- /EXPERTS/FILES - общий случай;
- /TESTER/FILES - специально для тестирования.
Работа с файлами из других каталогов пресекается.
Если все же вам необходимо работать вне заданных (из соображений безопасности) папок, то вы можете обратиться к функциям операционной системы Windows. Для этого широко используются функции API, представленные в библиотеке kernel32.dll.
Файловые функции библиотеки kernel32.dll
За основу был взят скрипт, выложенный в ветке Файловые операции в MQL4 без ограничений. Это хороший пример импорта функций в программу на MQL4.
Эти функции объявлены в msdn как устаревшие, но использовать их можно, см. Obsolete Windows Programming Elements. Описание функций и параметров я приведу прямо из той ветки, как это сделал автор скрипта - mandor:
Функция чтения из файла
Рассмотрим предложенную фукнцию для чтения из файла. Есдинственным ее параметром является переменная типа string, в которой содержится имя файла. Импортированная функция _lopen(path,0) возвращает указатель на открытый файл, и по назначению очень похожа на функцию FileOpen() из MQL4.
Функция _lseek() также имеет аналог в MQL4 - это FileSeek(). Для закрытия файла служит функция _lclose, которая используется также, как и FileClose(). Единственной новой функцией является _lread(handle, buffer, bytes), которая читает из указанного файла (указатель на который должен быть предварительно получен функцией _lopen() ) в переменную buffer указанное количество байт. В качестве переменой buffer необходимо использовать строковую константу необходимой длины. В данном пример мы видим:
указана строковая константа char, которая имеет единичную длину, то есть позволяет считать в нее только один байт. Причем значение этой константы не влияет, это может быть не "х", а "Z" или даже " "(знак пробела). Вы не сможете считать в нее большее количество байт, чем было распределено для этой константы изначально. В данном случае, попытки считать 2 или более байт успехом не увенчаются. Кроме того, результатом функции _lread() является количество реально считанных байтов. Если файл имеет длину в 20 байт, а вы попытаетесь считать в переменную длиной 30 байт более 20 байт, что функция вернет значение 20. Если применять эту функцию последовательно, то мы будем двигаться по файлу, читая один блок байтов за другим. Например, файл имеет размер 22 байта, мы начинаем его считывать блоками по 10 байт, тогда после двух вызовов функции __lread(handle, buff, 10) останутся непрочитанными два байта в конце файла.
При третьем вызове __lread(handle, buff, 10) вернет значение 2, то есть, прочитанными будут последние 2 байта. При четвертом вызове получим нулевое значение - ни один байт не прочитан, указатель находится в конце файла. Именно на этом простроена процедура считывания символов из файла в цикле:
До тех пор, пока result (количество прочитанных байтов) больше нуля, происходит циклический вызов функции _lread(handle, char1, 1). Как видите, ничего сложного в этих функциях нет. Значение считанного символа сохраняется в переменной char1 и на следующей итерации этот символ дописывается с строковой переменной buffer. По окончании работы пользовательская функция ReadFile() возвращает содержимое прочитанного файла в этой переменной. Как видите, ничего сложного в этом нет.
Функция записи в файл
Запись в некотором смысле даже проще, чем чтение. Необходимо открыть файл и записать в него байтовый массив функцией _lwrite (int handle, string buffer, int bytes). Здесь handle - файловый указатель, полученный функцией _lopen(), параметр buffer - строковая переменная, параметр bytes указывает сколько байт необходимо записать. После записи файл закрывается функцией _lclose(). Рассмотрим авторскую функцию WriteFile():
Правда, требуется проводить проверку на ошибки. Сначала делается попытка открыть файл на запись:
(функция _lopen() вызывается с параметром OF_WRITE).
Если попытка оказалась неудачной (handle < 0), то делается попытка создать файл с заданным именем:
Если же и эта функция вернет отрицательный указатель, то происходит досрочное завершение функции WriteFile(). Остальной код в этой функции понятен без объяснений. Простейшая функция start() позволяет проверить работу скрипта File_Read_Write.mq4 .
Обратите внимание, что символ обратной косой черты "\" (back slash) написан дважды, хотя на самом деле должен быть один. Дело в том, что некоторые спецсимволы, такие символ перевода строки ("\n") или символ табуляции ("\t") пишутся с использованием обратного слеша, . Если вы забудете об этом, то при написании пути в тестовой переменной у вас будут проблемы во время исполнения программы.
Старайтесь всегда в строковой константе писать не один обратный слеш, а два идущих друг за другом. В этом случае компилятор однозначно воспримет его правильно.
Все работает, только есть одна ложка дегтя - для больших файлов скрипт функция ReadFile() работает слишком долго.
Причина столь медленного считывания файла кроется в том, что мы считываем информацию по одному байту (символу). На рисунке видно, что файл размером 280 324 байта потребовал 103 секунды. Столько времени потребовалось чтобы 280 324 раза считать по одному символу. Вы можете самостоятельно проверить время работы скрипта File Read Write.mq4, который приложен к статье. Как мы можем ускорить время чтения из файла? Ответ напрашивается сам собой - нужно считывать не по одному символу, а, например, по 50 символов за раз. Тогда количество обращений к функции _lread() сократиться в 50 раз. Соответственно, и время чтения должно сократиться в 50 раз. Проверим это.
Новая функция чтения файла блоками по 50 байт.
Изменяем код, назовем новый вариант как xFiles,mq4. Компилируем и запускаем его на исполнение. Тут необходимо напомнить, что в настройках (Ctrl+O) необходимо разрешить импорт функций из DLL.
Итак, время выполнения модифицированного скрипта xFiles.mq4 составило 2047 миллисекунд, что составляет примерно 2 секунды. Делим 103 секунды (время исполнения первоначального скрипта) на 2 секунды и получаем 103 / 2 = 51.5 раза. Таким образом, время исполнения программы действительно изменилось примерно в 50 раз, как и ожидалось. Как же был модифицирован код для этого?
Обратите внимание, что строковая переменная char50 теперь иницилизированна константой в 50 символов (символ "x" и 49 пробелов).
Теперь мы можем производить чтение из файла таким образом, чтобы считывать в эту переменную 50 байт(символов) за один раз:
Здесь read_size = 50. Разумеется, что размер считываемого файла очень редко будет кратен 50 байтам, а значит в какой-то момент результатом выполнения этой функции будет значение, отличное от 50-ти. Это является сигналом для прекращения цикла и последний прочитанный в переменную блок символов усекается до размера реально прочитанного количества байтов.
Вы можете изменить размер буфера для считывания с помощью функции lread() до размера N, только не забудьте сделать две модификации:
- установите значение read_size = N;
- инициализируйте строковую переменную char50 константой длины N(N<256).
Операцию чтения мы ускорили, осталась последняя задача - обработка ошибки с несуществующим путем при попытке записи файла. В функции WriteFile() производится попытка создания файла, но не обрабатывается ситуация, когда папка, содержащая путь к имени файла, отсутствует. Значит нам необходима еще одна функция -
Функция создания папок
Функция для создания папок также доступна в библиотеке kernel32.dll - CreateDirectory Function. Необходимо только отметить, что эта функция делает попытку создания только папки самого нижнего уровня, она не создает все промежуточные папки в пути в случае их отсутствия. Например, если мы попробуем с помощью этой функции создать папку "С:\folder_A\folder_B", то попытка будет успешна в том случае, если путь "С:/folder_A" уже существует до вызова функции. В противном случае папка folder_B создана не будет. Добавим в секцию импорта новую функцию:
Первый параметр содержит путь, по которому необходимо создать новую папку, а второй параметр atrr[] служит для указания прав для создаваемой папки и должен иметь тип _SECURITY_ATTRIBUTES. Мы не будем указывать никакой информации для второго параметра, а просто будем передавать пустой массив int. В этом случае создаваемая папка будет наследовать все права от родительской папки. Но прежде чем попытаться применить эту функцию, нам понадобиться произвести такую операцию, как -
Разбор пути на составляющие
В самом деле, пусть нам необходимо создать патку четвертого уровня, например такую:
здесь папку folder_D назовем папкой четвертого уровня, потому что над ней есть еще три папки вышестоящих уровней. Диск "С:" содержит папку "folder_A", папка "С:\folder_A\" содержит папку "folder_B", папка "С:\folder_A\folder_B\" содержит папку "folder_С"; и так далее. Значит нам необходимо разбить весь путь к файлу на массив вложенных друг в друга файлов. Назовем нужную функцию как ParsePath():
Разделителем между папками служит символ "\", который имеет в кодировке ANSI значение 92. Функция получает в качестве аргумента путь path, и заполняет переданный в нее по ссылке массив folder[] именами найденных путей, начиная с самого нижнего и заканчивая самым верхним. Для нашего примера массив folder будет содержать следующие значения:
folder[0] = "С:\folder_A\folder_B\folder_С\folder_D";
folder[1] = "С:\folder_A\folder_B\folder_С";
folder[2] = "С:\folder_A\folder_B";
folder[3] = "С:\folder_A";
folder[4] = "С:";
Если нам необходимо записать файл с именем С:\folder_A\folder_B\folder_С\folder_D\test.txt", то указанный путь мы можем разбить на имя файла test.txt и струтуру вложенных папок "С:\folder_A\folder_B\folder_С\folder_D", которая и содержит данный файл. При неудачной попытке создания файла по этому пути, в первую очередь необходимо попытаться создать папку самого нижнего уровня "С:\folder_A\folder_B\folder_С\folder_D".
Теперь мы можем собрать и саму функцию, которая создает по указанному пути все необходимые промежуточные папки:
Теперь осталось только изменить немного функцию WriteFile() с учетом возможности создать новую папку.
Логика работы измененной функции представлена на рисунке.
Обратите внимание, что после закрытия вновь созданного файла, мы устанавливаем переменную дескриптора файла handle в отрицательное значение.
Это сделано для того, чтобы строчкой ниже проверить значение handle и открыть файл на чтение только в том случае, если первое открытие было неудачным.
Это позволит нам избежать ситуации, когда по ошибке происходят множественные открытия файлов, но закрывать их забывают. В таких случаях операционная система сообщает о превышении допустимого максимального числа открытых файлов и не позволяет открывать новые файлы.
Изменим функцию start() для проверки новых возможностей
и запустим скрипт xFiles.mq4 на исполнение.
Заключение
Использовать функции WinAPI не так сложно, но не нужно забывать и об обратной стороне выхода за песочницу:
Прежде чем запустить незнакомую программу в исполняемом виде c расширением ex4 (без исходного кода на MQL4), которая требует права на импорт функций из внешних DLL, подумайте хорошенько о возможных последствиях.
Читайте также: