]>Работа с файлами средствами WinAPI

Работа с файлами средствами WinAPI

Аватар пользователя mabu

Как ни хороши встроенные функции Open, Input, Print, Dir и прочие для работы с файлами, но всё же они имеют некоторые недостатки и не так гибки, как функции операционной системы.

  1. Функции
    1. CreateFile
    2. CreateDirectory
    3. ReadFile
    4. WriteFile
    5. CloseHandle
    6. GetFileSizeEx
  2. Примеры
    1. Запись текстовых файлов
    2. Чтение текстовых файлов
  3. Ссылки

Функции

CreateFile

Открывает или создаёт файл или устройство ввода‐вывода, такие как файл, директория, диск, том, буфер консоли (CONIN$ или CONOUT$), накопитель на магнитной ленте, почтовый слот, ресурс связи, именованный канал и прочее.

Код FreeBASIC
Declare Function CreateFile Alias "CreateFileW"( _
&t;ByVal lpFileName As LPCWSTR, _
&t;ByVal dwDesiredAccess As DWORD, _
&t;ByVal dwShareMode As DWORD, _
&t;ByVal lpSecurityAttributes As LPSECURITY_ATTRIBUTES, _
&t;ByVal dwCreationDisposition As DWORD, _
&t;ByVal dwFlagsAndAttributes As DWORD, _
&t;ByVal hTemplateFile As HANDLE) _
As HANDLE

Параметры

lpFileName
Указатель на строку с именем объекта, который необходимо создать или открыть. В Windows NT/2000/XP ANSI‐версия этой функции ограничивает число символов имени объекта значением MAX_PATH (что‐то около 260 символов). Чтобы выйти за это ограничение и использовать строку длиной 32767 символов, необходимо вызывать юникодную версию этой функции и присоединить слева от имени объекта «\\?\».
dwDesiredAccess
Тип доступа к объекту. Может принимать комбинацию следующих значений:
  • GENERIC_READ — объект будут использовать для чтения;
  • GENERIC_WRITE — объект будут использовать для записи;
  • 0 — открываемый объект нельзя читать или записывать, такой режим открытия объекта удобен для проверки его существования.
dwShareMode
Режим совместного доступа к файлу (из других процессов). Может принимать значения из списка или быть их кобминацией:
  • 0 — другим процессам запрещены операции чтения, записи или удаления объекта;
  • FILE_SHARE_DELETE — другим процессам можно удалять объект;
  • FILE_SHARE_READ — другие процессы могут выполнять операции чтения;
  • FILE_SHARE_WRITE — другие процессы могут выполнять операции записи.
lpSecurityAttributes
Указатель на структуру SECURITY_ATTRIBUTES, устанавливающую, может ли возвращённый дескриптор быть унаследован дочерними процессами. Если установлено в NULL, то дескриптор не может быть унаследован.
dwCreationDisposition
Выполняемые действия с файлами. Параметр может принимать одно из следующих значений без комбинаций:
  • CREATE_NEW — создание файла, но если такой уже существует, то выйдет ошибка;
  • CREATE_ALWAYS — создание файла или перезапись уже существующего;
  • OPEN_EXISTING — открытие существующего файла, а если такой файл не существует, выйдет ошибка;
  • OPEN_ALWAYS — открытие файла, а если его не существует, то он будет создан;
  • TRUNCATE_EXISTING — перезапись с нуля существующего файла, или возвращениние ошибки, если файла не существует.
Если открывается устройство, то необходимо использовать константу OPEN_EXISTING.
dwFlagsAndAttributes
Атрибуты файла, флаг FILE_ATTRIBUTE_NORMAL подходит для большинства операций. Все другие атрибуты переопределяют атрибут FILE_ATTRIBUTE_NORMAL. Этот параметр может быть любой комбинацией из следующих констант:
  • FILE_ATTRIBUTE_NORMAL;
  • FILE_ATTRIBUTE_ARCHIVE;
  • FILE_ATTRIBUTE_ENCRYPTED;
  • FILE_ATTRIBUTE_HIDDEN;
  • FILE_ATTRIBUTE_OFFLINE;
  • FILE_ATTRIBUTE_READONLY;
  • FILE_ATTRIBUTE_SYSTEM;
  • FILE_ATTRIBUTE_TEMPORARY.
Также можно использовать комбинацию из флагов, наиболее интересные из них:
  • FILE_FLAG_BACKUP_SEMANTICS — файл открывается для резервного копирования или восстановления, также этот флаг нужно указывать при открытии каталога;
  • FILE_FLAG_DELETE_ON_CLOSE — система немедленно удалит файл после того, как будут закрыты все его дескрипторы;
  • FILE_FLAG_NO_BUFFERING — запрещает кеширование файлов для операций чтения и записи;
  • FILE_FLAG_OVERLAPPED — файл открывается для асинхронных операций чтения и записи;
  • FILE_FLAG_RANDOM_ACCESS — файл открывается преимущественно для операций чтения и записи по случайным областям;
  • FILE_FLAG_SEQUENTIAL_SCAN — файл открывается преимущественно для операций последовательного чтения или записи;
  • FILE_FLAG_WRITE_THROUGH — запись в файл будет идти непосредственно на диск, минуя кеш.
hTemplateFile
Указатель на файл‐шаблон. Обычно не используется и может быть установлен в NULL. При открытии существующего файла этот параметр игнорируется.

Возвращаемое значение

Если функция завершается успешно, возвращаемое значение — открытый дескриптор заданного файла.

Если функция завершается с ошибкой, возвращаемое значение — INVALID_HANDLE_VALUE. Для получения кода ошибки можно вызвать функцию GetLastError.

CreateDirectory

Создаёт папку на диске.

Код FreeBASIC
Declare Function CreateDirectory Alias "CreateDirectoryW"( _
&t;ByVal lpPathName As LPCWSTR, _
&t;ByVal lpSecurityAttributes As LPSECURITY_ATTRIBUTES) _
As WINBOOL

Параметры

lpPathName
Указатель на строку с именем каталога, который необходимо создать. В Windows NT/2000/XP ANSI‐версия этой функции ограничивает число символов имени объекта значением MAX_PATH (что‐то около 260 символов). Чтобы выйти за это ограничение и использовать строку длиной 32767 символов, необходимо вызывать юникодную версию этой функции и присоединить слева от имени объекта «\\?\».
lpSecurityAttributes
Указатель на структуру SECURITY_ATTRIBUTES, устанавливающую атрибуты защиты созданного каталога. Если установлено в NULL, то атрибуты защиты наследуются от родительского каталога.

Возвращаемое значение

Если функция выполнилась успешно, то возвращаемое значение не равно нулю.

Если функция завершилась ошибкой, то возвращаемое значение равно нулю. Для получения кода ошибки можно вызвать функцию GetLastError.

ReadFile

Читает данные из файла или устройства ввода‐вывода.

Код FreeBASIC
Declare Function ReadFile( _
&t;ByVal hFile As HANDLE, _
&t;ByVal lpBuffer As LPVOID, _
&t;ByVal nNumberOfBytesToRead As DWORD, _
&t;ByVal lpNumberOfBytesRead As LPDWORD, _
&t;ByVal lpOverlapped As LPOVERLAPPED) _
As WINBOOL

Параметры

hFile
Идентификатор устройства, например, файл, диск, том, буфер консоли, сокет, почтовый слот, канал и прочее.
lpBuffer
Указатель на буфер, куда будут записаны данные.
nNumberOfBytesToRead
Количество требуемых для чтения байт.
lpNumberOfBytesRead
Указатель на переменную, куда будет записано количество действительно прочитанных байт. Число действительно прочитанных байт может отличаться от количества требуемых байт, например, когда требуется прочитать больше байт, чем осталось в файле. Если количество прочитанных байт равно нулю, то достигнут конец файла. Этот параметр можно оставлять в NULL только в случае асинхронных операций.
lpOverlapped
Указатель на структуру OVERLAPPED. Требуется только для асинхронных операций, для этого файл должен быть открыт с флагом FILE_FLAG_OVERLAPPED, в обычных случаях устанавливай в NULL.

Возвращаемое значение

Если функция выполнилась успешно, то возвращаемое значение не ноль.

Если функция выдала ошибку или вызвана в асинхронном режиме, то возвращаемое значение — ноль. Для получения кода ошибки можно вызвать функцию GetLastError.

Замечания

Функция немедленно возвращает значение при наступлении условий:

Также нужно заметить, что если функция выполняется успешно при чтении файла, если достигнут его конец.

WriteFile

Записывает данные в файл или устройство ввода‐вывода.

Код FreeBASIC
Declare Function WriteFile( _
&t;ByVal hFile As HANDLE, _
&t;ByVal lpBuffer As LPCVOID, _
&t;ByVal nNumberOfBytesToWrite As DWORD, _
&t;ByVal lpNumberOfBytesWritten As LPDWORD, _
&t;ByVal lpOverlapped As LPOVERLAPPED) _
As WINBOOL

Параметры

hFile
Идентификатор устройства, например, файл, диск, том, буфер консоли, сокет, почтовый слот, канал и прочее.
lpBuffer
Указатель на буфер, откуда будут записаны данные.
nNumberOfBytesToWrite
Количество записываемых байт.
lpNumberOfBytesWritten
Количество действительно записанных байт.
lpOverlapped
Указатель на структуру OVERLAPPED. Требуется только для асинхронных операций, для этого файл должен быть открыт с флагом FILE_FLAG_OVERLAPPED, в обычных случаях устанавливай в NULL.

Возвращаемое значение

Если функция завершилась успешно, то возвращаемое значение не ноль.

Если функция выдала ошибку или вызвана в асинхронном режиме, то возвращаемое значение — ноль. Для получения кода ошибки можно вызвать функцию GetLastError.

CloseHandle

Закрывает объект ядра и освобождает занимаемую им память.

Код FreeBASIC
Declare Function CloseHandle( _
&t;ByVal hObject As HANDLE) _
As WINBOOL

Параметры

hFile
Идентификатор объекта ядра, например, «файл».

Возвращаемое значение

Если функция завершилась успешно, то возвращаемое значение не ноль.

Если функция выдала ошибку, то возвращаемое значение — ноль. Для получения кода ошибки можно вызвать функцию GetLastError.

Замечания

Функция аннулирует заданный дескриптор объекта и уменьшает счётчик ссылок на объект. После того, как последний дескриптор объекта закрывается, объект удаляется из системы.

GetFileSizeEx

Получает размер файла.

Код FreeBASIC
Declare Function GetFileSizeEx( _
&t;ByVal hFile As HANDLE, _
&t;ByVal lpFileSize As PLARGE_INTEGER) _
As WINBOOL

Параметры

hFile
Идентификатор файла.
PLARGE_INTEGER
Указатель на структуру LARGE_INTEGER, куда будет записан размер файла.

Возвращаемое значение

Если функция завершилась успешно, то возвращаемое значение не равно нулю.

Если функция завершилась ошибкой, то возвращаемое значение — ноль.

Замечания

LARGE_INTEGER — это специальная структура для хранения больших чисел. Из неё нам требуется только поле QuadPart, представляющее знаковое 64‐битное число типа LongInt. В нём содержится размер файла в байтах. Обращаться к полю структуры, как обычно, нужно через точку.

Примеры

Запись текстовых файлов

Некоторое неудобство текстовых файлов в том, что всегда приходится иметь дело с кодировкой символов. Рассмотрим пример записи нескольких строк в текстовый файл.

Код FreeBASIC
#define unicode
#include once "windows.bi"

' Имя текстового файла, куда будет записана информация
Const FileName = "Привет.txt"

' Эта фраза будет записана в файл
' В файле она будет выглядеть как две строки
Const HelloWorld = !"Привет\r\nмир"

' Для начала нужно создать файла
Dim hFile As Handle = CreateFile( _
&t;@FileName, _ /' имя создаваемого файла '/
&t;GENERIC_WRITE, _ /' файл открывается для записи '/
&t;FILE_SHARE_READ, _ /' разрешено читать данные другим процессам '/
&t;NULL, _ /' используем атрибуты безопасности по умолчанию '/
&t;CREATE_ALWAYS, _ /' создадим файл или перезапишем уже существующий '/
&t;FILE_ATTRIBUTE_NORMAL, _ /' файл не будет иметь специальных атрибутов '/
&t;NULL _ /' без файлов‐шаблонов '/
)

If hFile = INVALID_HANDLE_VALUE Then
&t;' Произошла ошибка, нужно закрыть файл и выйти
&t;CloseHandle(hFile)
&t;End(1)
End If

' Теперь необходимо определиться с кодировкой символов
' Будем писать в UTF-16

' Но сначала необходимо записать в файл специальную метку,
' показывающую, что используется кодировка UTF-16
Dim BOM16LE As ZString * 2 = Any
BOM16LE[0] = 255
BOM16LE[1] = 254

Dim WritedBytesCount As DWORD = Any
Dim Result As Integer = WriteFile(hFile, @BOM16LE, 2, @WritedBytesCount, 0)

' Теперь можно записать в файл фразу
Result = WriteFile(hFile, @HelloWorld, Len(HelloWorld) * SizeOf(WString), @WritedBytesCount, 0)

' После всех манипуляций необходимо закрыть файл
CloseHandle(hFile)

После запуска программы рядом должен появиться файл «Привет.txt». Если открыть его блокнотом, то там будут две строки: «Привет» и «мир».

Чтение текстовых файлов

При чтении текстового файла хлопот больше, так как нам неизвестна длина строки. Чтобы хранить промежуточные результаты чтения придётся создавать буфер.

Для простоты будем считать, что текстовый файл будет в кодировке UTF-16 LE.

Код FreeBASIC
#define unicode
#include once "windows.bi"
' Для функции StrStr
#include once "win\shlwapi.bi"

' Имя текстового файла, откуда будет считываться текст
Const FileName = "Привет.txt"
' Символы перевода строки
Const vbCrLf = !"\r\n"

' Размер буфера для хранения данных 64 кибайт
Const MaxBufferLength As Integer = 64 * 1024

' Для начала нужно создать файла
Dim hFile As Handle = CreateFile( _
&t;@FileName, _ /' имя создаваемого файла '/
&t;GENERIC_READ, _ /' файл открывается для записи '/
&t;FILE_SHARE_READ, _ /' разрешено читать данные другим процессам '/
&t;NULL, _ /' используем атрибуты безопасности по умолчанию '/
&t;OPEN_EXISTING, _ /' открыть существующий файл '/
&t;FILE_ATTRIBUTE_NORMAL, _ /' файл не будет иметь специальных атрибутов '/
&t;NULL _ /' без файлов‐шаблонов '/
)

If hFile = INVALID_HANDLE_VALUE Then
&t;' Произошла ошибка, выйти
&t;Print "Ошибка открытия файла"
&t;End(1)
End If

' Буфер для хранения данных чтения
Dim Buffer As ZString * (MaxBufferLength + SizeOf(WString)) = Any

' Читаем данные файла
Dim ReadBytesCount As DWORD = Any
If ReadFile(hFile, @Buffer, MaxBufferLength, @ReadBytesCount, 0) = 0 Then
&t;' Ошибка чтения файла
&t;Print "Не могу прочитать данные файла"
&t;CloseHandle(hFile)
&t;End(0)
End If
' Ставим нулевой символ, чтобы строка была валидной
Buffer[ReadBytesCount] = 0
Buffer[ReadBytesCount + 1] = 0

' Будем считать, что кодировка текста UTF-16 с меткой BOM
If ReadBytesCount < 2 Then
&t;' Кодировка не та
&t;Print "Для чтения файла нужна кодировка UTF-16 LE"
&t;CloseHandle(hFile)
&t;End(0)
End If

If Buffer[0] <> 255 OrElse Buffer[1] <> 254 Then
&t;Print "Для чтения файла нужна кодировка UTF-16 LE"
&t;CloseHandle(hFile)
&t;End(0)
End If

Dim wLine As WString Ptr = CPtr(WString Ptr, @Buffer[2])
Dim ReadedBytesCount As Integer = 0
Dim Result2 As Integer = 1
Do
&t;' Найти в буфере CrLf
&t;Dim wCrLf As WString Ptr = StrStr(wLine, @vbCrLf)
&t;Do While wCrLf = NULL
&t;&t;' Проверить буфер на переполнение
&t;&t;If ReadedBytesCount >= MaxBufferLength Then
&t;&t;&t;' Буфер заполнен, будем читать данные в следующий раз
&t;&t;&t;Buffer[MaxBufferLength] = 0
&t;&t;&t;Buffer[MaxBufferLength + 1] = 0
&t;&t;&t;Exit Do
&t;&t;End If

&t;&t;' Если CrLf в буфере нет, то читать данные с файла
&t;&t;Result2 = ReadFile(hFile, @Buffer + ReadedBytesCount, MaxBufferLength - ReadedBytesCount, @ReadBytesCount, 0)
&t;&t;If Result2 = 0 OrElse ReadBytesCount = 0 Then
&t;&t;&t;' Ошибка или данные прочитаны, выйти
&t;&t;&t;Exit Do
&t;&t;End If
&t;&t;' Прочитанный байт всего
&t;&t;ReadedBytesCount += ReadBytesCount
&t;&t;' Ставим нулевой символ, чтобы строка была валидной
&t;&t;Buffer[ReadBytesCount] = 0
&t;&t;Buffer[ReadBytesCount + 1] = 0
&t;&t;' Искать CrLf заново
&t;&t;wCrLf = StrStr(wLine, @vbCrLf)
&t;Loop
&t;' CrLf найдено или произошла ошибка, его необходимо убрать
&t;If wCrLf <> 0 Then
&t;&t;wCrLf[0] = 0
&t;End If
&t;' Вывести на консоль что было в буфере
&t;Print *wLine
&t;If wCrLf <> 0 Then
&t;&t;' Переместить правее CrLf
&t;&t;wLine = wCrLf + 2
&t;&t;' Передвинуть данные в буфере влево
&t;&t;Dim tmpBuffer As ZString * (MaxBufferLength + SizeOf(WString)) = Any
&t;&t;lstrcpy(CPtr(WString Ptr, @tmpBuffer), wLine)
&t;&t;lstrcpy(CPtr(WString Ptr, @Buffer), CPtr(WString Ptr, @tmpBuffer))
&t;&t;wLine = CPtr(WString Ptr, @Buffer)
&t;End If
&t;ReadedBytesCount = 0
Loop While Result2 <> 0 And ReadBytesCount <> 0

' После всех манипуляций необходимо закрыть идентификатор
CloseHandle(hFile)

Ссылки

Поделись ссылочкой в социальных сетях