Главная > прикладное, Программирование, системное > WinAPI: Работа с файлами (основные функции).

WinAPI: Работа с файлами (основные функции).

Категорически всех приветствую! На этот раз в наше поле зрения попала группа API-функций для работы с файлами. Ибо как мне кажется что по мимо меня многие программисты сталкиваются с необходимостью их использования в своих программах. Но откровенно скажу в голове все эти функции в мести с их возможными параметрами и не упомнишь, и иметь возможность "вспомнить все" ;) прочитав эту статью очень не плохая затея. За семь и приступаю к рассмотрению оного =).

В этой статье мы рассмотрим следующие API-функции:

  1. CreateFile
  2. OpenFile
  3. ReadFile
  4. WriteFile
  5. CloseFile
  6. DeleteFile
  7. CopyFile
  8. FindFirstFile
  9. FindNextFile
  10. GetFileSize

Замечание
Но прежде чем  приступить к дело, замечу, для случая когда действия над файлами происходят удаленно.  За счет реализации в ОС Windows механизма прозрачного доступа к файлам,  данные функции работают и с удаленными файлами, отличием является задание сетевого пути к файлу, а именно для С/С++ необходимо указывать путь в следующем формате:   "\\\\имя_удаленного_компа\\путь_к_ файлу\\ имя_файла".
Пример:

DeleteFile("\\\\comp1\\user1\\test\\test1.txt");

Для Delphi необходимо указывать путь в следующем формате: "\\имя_удаленного_компа\путь_к_ файлу\имя_файла".

Пример:

DeleteFile("\\comp1\user1\test\test1.txt");

Функция CreateFile

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

  • Файл
  • Канал
  • mailslot (почтовый канал)
  • комуникационный ресурс (например COM порт)
  • дисковые устройства (только для Windows NT)
  • консоли
  • директории (открывает их)

Все эти функции описаны в одном файле. Для С/С++ в хедере (заголовочный файл) winbase.h, для Delphi в модуле windows.pas.  Чтобы использовать в своей программе эти функции, достаточно подключить их к своему проекту и вперед. Змечу, для тех кто кодит на С/С++ кроме winbase.h надо подключить еще windows.h.

Описание:
С/С++

HANDLE CreateFile(
LPCTSTR lpFileName,        // Указатель на имя файла (устройства)
DWORD dwDesiredAccess,  //Параметры доступа
DWORD dwShareMode,        //Разделяемый доступ
LPSECURITY_ATTRIBUTES lpSecurityAttributes,    //безопасность
DWORD dwCreationDistribution,// Описание
DWORD dwFlagsAndAttributes,     // Атрибуты файла
HANDLE hTemplateFile      // Файл шаблона
);

Delphi


function CreateFile(
lpFileName: PChar;   // Указатель на имя файла (устройства)
dwDesiredAccess,     //Параметры доступа
dwShareMode: DWORD;  //Разделяемый доступ
lpSecurityAttributes: PSecurityAttributes; //безопасность
dwCreationDisposition,       // Описание
dwFlagsAndAttributes: DWORD; // Атрибуты файла
hTemplateFile: THandle       // Файл шаблона
): THandle; stdcall;

Теперь к главному, как создать файл? Делается это так:
C/C++


{
HANDLE FileHandle;
FileHandle=CreateFile("file1.txt",GENERIC_READ |
GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0);
CloseHandle(FileHandle);
}

Delphi


var
FileHandle:THandle;
begin
FileHandle:=CreateFile(PChar('file1.txt'),GENERIC_READ or
GENERIC_WRITE, FILE_SHARE_WRITE or FILE_SHARE_READ, nil, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);
CloseHandle(FileHandle);
end;

Теперь рассмотрим используемые параметры:

  • параметры GENERIC_READ и GENERIC_WRITE определяют доступ на чтение и запись. Однако можно указать что то одно, в зависимости от ваших потребностей. Если в качестве этого параметра указать 0, то в этом случае функция отработает успешно, однако никакого доступа к файлу не получит. Этот вариант удобно использовать для теста существования файла.
  • параметры FILE_SHARE_WRITE or FILE_SHARE_READ общий доступ на чтение и запись к данному файлу. То есть файл будет доступен при одновременном использовании несколькими процессами.
  • следующий параметр - атрибут безопасности неопределен. То есть все дескрипторы будут доступны дочерним процессам вашего приложения.
  • параметр CREATE_NEW указывает на создание нового файла. Если файл с заданным именем существует, то будет возвращен код ошибки. Если использовать параметр CREATE_ALWAYS, то в этом случае, если файл существует, то он будет перезаписан.
  • параметр FILE_ATTRIBUTE_NORMAL определяет файлу стандартные атрибуты. А если указывать флаг FILE_FLAG_OVERLAPPED, то файл будет доступен в асинхронном режиме.
  • так как мы не используем шаблоны, то в качестве этого параметра указываем ноль.

Функция OpenFile
Данная функция, по сути своей, предназначена для открытия файлов или устройств. Функция OpenFile в случае успеха возвращает дискриптор (указатель) открываемого файла. В случае неудачи мы получим в качестве дискриптора значение INVALID_HANDLE_VALUE.
C/C++


HFILE WINAPI OpenFile(
LPCSTR lpFileName,
LPOFSTRUCT lpReOpenBuff,
UINT uStyle
);

Delphi


function OpenFile(
lpFileName: LPCSTR,   //имя файла (устройства)
lpReOpenBuff: TOFStruct,  //специальная структура открываемого файла
uStyle: UINT //флаги доступа к файлу
): HFILE; stdcall;

Теперь рассмотрим используемые параметры:

  • в качестве первого параметра указывается имя открываемого файла.
  • вторым параметром является специальная структура открываемого файла, в которую заносится полезная инфа о файле.
  • 3 параметр представляет собой совокупность флагов для работы с файлом.

Собственно, как открыть файл:
C/C++


{
HANDLE FileHandle;
FileHandle= OpenFile("Settings.ini", Buf, OF_Create |
OF_READWRITE | OF_SHARE_EXCLUSIVE);
CloseHandle(FileHandle);
}

Delphi


var
FileHandle:THandle;
begin
FileHandle:=OpenFile(PChar('Settings.ini'), Buf, OF_Create
Or OF_READWRITE Or OF_SHARE_EXCLUSIVE);
CloseHandle(FileHandle);
end;

В данном примере стоит уделить внимание флагам. Первые 2 из которых определяют доступ к файлу, а флаг OF_SHARE_EXCLUSIVE сообщает системе, что доступ к этому файлу запрещен до тех пор пока указатель на этот файл не освободится.

Функция ReadFile
Данная функция предназначена для чтения файлов или с устройств ввода/вывода. В случае успешного выполнения операции  чтения, возвращается логическое значение true, иначе false. А функция GetLastError вернет ERROR_HANDLE_EOF.
C/C++


BOOL ReadFile(HANDLE hFile, //собственно указатель на файл
LPVOID lpBuffer, // указатель на буфер - куда записываем считанные данные
DWORD nNumberOfBytesToRead, //объем считываемых данных, не может превышать размер буфера
LPDWORD lpNumberOfBytesRead, //фактический размер считанных данных
LPOVERLAPPED lpOverlapped // флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED)
//или синхронный(NULL)
);

Delphi


function ReadFile(
hFile: THandle; //собственно указатель на файл
var Buffer; // указатель на буфер - куда записываем считанные данные
nNumberOfBytesToRead: DWORD; //объем считываемых данных, не может превышать размер буфера
var lpNumberOfBytesRead: DWORD; //фактический размер считанных данных
lpOverlapped: POverlapped// флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED)
//или синхронный(nil)
): BOOL; stdcall;

Теперь поглядим рабочий пример:
C/C++


{
HANDLE FileHandle;
char Buf[1000];
...
ReadFile(FileHandle, Buf, sizeof(buf),NULL);
...
}

Delphi


var
FileHandle:THandle;
Buf:array[1..1000] of char;
begin
...
ReadFile(FileHandle, Buf, sizeof(buf),NULL);
...
end;

Функция WriteFile
Данная функция предназначена для записи в файл или устройство ввода/вывода. С ней все аналогично что и с функцией ReadFile(). В случае успешного выполнения операции  чтения, возвращается логическое значение true, иначе false. А функция GetLastError вернет ERROR_HANDLE_EOF.

C/C++


BOOL WriteFile(HANDLE hFile, //собственно указатель на файл
LPVOID lpBuffer, // указатель на буфер - откуда записываем данные в файл
DWORD nNumberOfBytesToWrite, //объем записываемых данных
LPDWORD lpNumberOfBytesWrite, //фактический размер записанных данных
LPOVERLAPPED lpOverlapped // флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED)
//или синхронный(NULL)
);

Delphi


function WriteFile(
hFile: THandle; //собственно указатель на файл
var Buffer; // указатель на буфер - откуда записываем данные в файл
nNumberOfBytesToWrite: DWORD; //объем записываемых данных
var lpNumberOfBytesWrite: DWORD; //фактический размер записанных данных
lpOverlapped: POverlapped// флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED)
//или синхронный(nil)
): BOOL; stdcall;

Теперь поглядим рабочий пример:
C/C++


{
HANDLE FileHandle;
char Buf[1000];
...
ReadFile(FileHandle, Buf, sizeof(buf),NULL);
...
}

Delphi


var
FileHandle:THandle;
Buf:array[1..1000] of char;
begin
...
ReadFile(FileHandle, Buf, sizeof(buf),NULL);
...
end;

Функция CloseFile
Эта функция достаточно проста. Выполняет действия по освобождению указателя на файл. Ее необходимость очевидна, чтобы не засорять память ненужными указателями и позволять работу с файлами другим процессам. В качестве ее единственного парамметра передается указатель на ранее открытый файл.

Теперь поглядим рабочий пример:
C/C++


{
HANDLE FileHandle;
FileHandle= OpenFile("Settings.ini", Buf, OF_Create |
OF_READWRITE | OF_SHARE_EXCLUSIVE);
...
CloseHandle(FileHandle);
}

Delphi


var
FileHandle:THandle;
begin
FileHandle:=OpenFile(PChar('Settings.ini'), Buf, OF_Create
Or OF_READWRITE Or OF_SHARE_EXCLUSIVE);
...
CloseHandle(FileHandle);
end;

Функция DeleteFile
Эта функция так же не отличается особой сложностью. Данная функция удаляет файл, который указан в парамметре.  Функция возвращает значение true если выполняется успешно. Иначе возвращает false, а код ошибки можно получить с помощью GetLastError.
C/C++


BOOL WINAPI DeleteFile(
LPCTSTR lpFileName // имя файла, который надо удалить
);

Delphi


function DeleteFile(
lpFileName: PChar; // имя файла, который надо удалить
): BOOL; stdcall;

Теперь поглядим рабочий пример:
C/C++


{
...
DeleteFile("C:\Temp\test.txt");// если указать только
// имя файла, то будет осуществлена попытка удалить файл из папки откуда была запущена программа

...
}

Delphi


begin
...
DeleteFile('C:\Temp\test.txt');// если указать только
// имя файла, то будет осуществлена попытка удалить файл из папки откуда была запущена программа
...
end;

Функция CopyFile
Основное назначение этой функции копирование файла. В случае успеха возвращает true, иначе - false. Код ошибки можно получить с помощью GetLastError.
C/C++


BOOL CopyFile(
LPCTSTR lpExistingFileName,// Указатель на файл, который надо копировать
LPCTSTR lpNewFileName, // Указатель на имя файла, куда надо копировать
BOOL bFailIfExists //если указать true, то в случае существования такого файла,
// он будет перезаписан, если false - то произойдет ошибка.
);

Delphi


function CopyFile(
lpExistingFileName, // Указатель на файл, который надо копировать
lpNewFileName: PChar; // Указатель на имя файла, куда надо копировать
bFailIfExists: BOOL //если указать true, то в случае существования такого файла,
// он будет перезаписан, если false - то произойдет ошибка.
): BOOL; stdcall;

Теперь поглядим рабочий пример:
C/C++


{
...
CopyFile("C:\Temp\test1.txt","C:\Temp\test2.txt", true); // если указать только
// имя файла, то все действия будут выполнены в папке из которой запущена прога
...
}

Delphi


begin
...
CopyFile('C:\Temp\test1.txt','C:\Temp\test2.txt', false);// если указать только
// имя файла, то все действия будут выполнены в папке из которой запущена прога...
end;

Функция FindFirstFile
Данная функция запускает поиск в указанной директории. Функция возвращает указатель на найденный файл, если нет то возврат будет типа ERROR_NO_MORE_FILES.
C/C++


HANDLE FindFirstFile(
LPCTSTR lpFileName, // Строка содержащая путь для поиска файлов.
LPWIN32_FIND_DATA lpFindFileData // Информация о найденном файле
);

Delphi

function FindFirstFile(
lpFileName: PChar; // Строка содержащая путь для поиска файлов.
var lpFindFileData: TWIN32FindData // Информация о найденном файле
): THandle; stdcall;

Теперь рассмотрим парамметры подробней:

  • lpFileName - строка содержащая путь для поиска файла. Эта строка может указывать наконкретный файл типа 'c:\filename.txt' или может хранить шаблон 'c:\*.*'. Если указывать шаблон, то это даёт возможность перечислить все файлы удовлетворяющие шаблону.
  • lpFindFileData - структура WIN32_FIND_DATA, в которую будет записана инфа о найденом файле.

Расмотрим структуру WIN32_FIND_DATA немного подробнее:


typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; // Атрибуты файла
FILETIME ftCreationTime; // Время создания
FILETIME ftLastAccessTime; //Время последнего доступа
FILETIME ftLastWriteTime; //Время последней записи в файл
DWORD nFileSizeHigh; //Верхний байт размера файла
DWORD nFileSizeLow; //Нижний байт размера файла
DWORD dwReserved0; //Зарезервировано
DWORD dwReserved1; //Зарезервировано
TCHAR cFileName[ MAX_PATH ]; //Имя файла
TCHAR cAlternateFileName[ 14 ]; //Имя файла для отображения в DOS (8:3)
} WIN32_FIND_DATA;

Атрибутами файла может быть комбинация из флагов:

  • FILE_ATTRIBUTE_ARCHIVE - архивный
  • FILE_ATTRIBUTE_COMPRESSED - сжатый
  • FILE_ATTRIBUTE_HIDDEN - скрытый
  • FILE_ATTRIBUTE_NORMAL - нормальный
  • FILE_ATTRIBUTE_OFFLINE - данные файла недоступны
  • FILE_ATTRIBUTE_READONLY - только для чтения
  • FILE_ATTRIBUTE_SYSTEM - системный
  • FILE_ATTRIBUTE_TEMPORARY - временный

Размер файла разложен на два байта. Чтоб получить полный размер файла нужно выполнить действие (FInfo.nFileSizeHigh * MAXDWORD) + FInfo.nFileSizeLow. Это не самый эффективный (эффективнее сдвигать), но самый понятный способ.

Теперь поглядим рабочий пример:
C/C++


{
WIN32_FIND_DATA sss;
HANDLE f;
f = FindFirstFile("C:\*.*", &sss);
...
FindClose(f);
}

Delphi


var
sss: TWIN32FindData;
f:THandle;
begin
...
f:=FindFirstFile(PChar('C:\*.*'), sss);
...
Windows.FindClose(f);
end;

Функция FindNextFile
Данная функция продолжает поиск начатый функцией FindFirstFile(). Если очередной файл найден, то функция возвращает значение true, иначе  возврат будет типа ERROR_NO_MORE_FILES.
C/C++


BOOL FindNextFile(
HANDLE hFindFile, // Указатель на файл из предыдущего поиска
LPWIN32_FIND_DATA lpFindFileData // Информация об очередном найденном файле
);

Delphi


function FindNextFile(
hFindFile: THandle; // Указатель на файл из предыдущего поиска
var lpFindFileData: TWIN32FindData // Информация об очередном найденном файле
): BOOL; stdcall;

Со вторым параметром всё ясно, из описания функции FindFirstFile. А вот первый - это указатель на файл из предыдущего поиска. Он нужен, чтобы функция FindNextFile знала на каком файле был остановлен поиск и какой надо найти следующим.

Теперь поглядим рабочий пример:
C/C++


{
WIN32_FIND_DATA sss;
HANDLE f;
f = FindFirstFile("C:\*.*", &sss);
if(f != INVALID_HANDLE_VALUE)
do{
...
}while(FindNextFile(f,&sss));
FindClose(f);
}

А теперь пример рекурсивного поиска файлов:
Delphi


function find(put: string):integer;
var
sss: TWIN32FindData;
f:THandle;
result:Boolean;
begin
...
f:=FindFirstFile(PChar('C:\*.*'), sss);
if f<>INVALID_HANDLE_VALUE then
begin
result:=true;
while(result)do
begin
if not((sss.cFileName = '.')or(sss.cFileName = '..')) then
if (sss.dwFileAttributes and faDirectory) > 0 then
find(put+'\'+sss.cFileName)
else
writeln(sss.cFileName);
...
result:=FindNextFile(f,sss);
end;
end;
Windows.FindClose(f);
end;

Функция GetFileSize
Данная функция определяет размер файла, на который ссылается переданный указатель. Если размер файла меньше 4 Гб, то функция возвращает размер файла. Если размер файла больше 4Гб, то размер файла записан во втором парамметре. В случае возникновения исключения, будет возвращено значение типа INVALID_FILE_SIZE.
C/C++


DWORD GetFileSize(
HANDLE hFile, // Указатель на файл
LPDWORD lpFileSizeHigh // старший байт размера файла, в качестве
// этого парамметра можно указать NULL
);

Delphi


function GetFileSize(
hFile: THandle; // Указатель на файл
lpFileSizeHigh: Pointer //старший байт размера файла, в качестве
// этого парамметра можно указать nil
): DWORD; stdcall;

Теперь поглядим рабочий пример:
C/C++


{
DWORD FileSize;
HANDLE f;
...
FileSize=GetFileSize(f,NULL);
...
}

Delphi

var
FileSize:DWORD;
f:THandle;
begin
...
FileSize:=GetFileSize(f,nil);
...
end

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

Пожалуйста, оцените полезность и качество данной статьи. Одна звезда - плохо, 5 - хорошо.
1/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.2/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.3/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.4/5.5/5. (7 голосов, средний: 4,00 из 5)
Загрузка...
  1. Freeeon
    3 октября 2013 в 00:22 | #1

    В примере WriteFile написано ReadFile =/

  2. Владимир
    22 октября 2013 в 21:31 | #2

    Здравствуйте!

    у меня такая проблема... есть файл 32 гига бинарный. записи строго по 8 байт....
    требуется в дельфи уметь читать любую запись 8 байт, при нужде ее изменить и записать
    назад. Стандартные процедуры не поддерживают такие размеры. помогите.

    Владимир.

  3. Кирилёв Сергей
    12 декабря 2013 в 22:13 | #3

    @ Владимир , я не думаю что кто то возьмётся за такую работу, тем более бесплатно.

  4. lizz
    12 декабря 2013 в 22:34 | #4

    @Кирилёв Сергей
    Ну почему сразу работу. Я думаю если кто может посоветовать в каком направлении копать, то здорово. Сам, увы, не сталкивался с таким, да и не дельфист вовсе.

    Кстати, у Яндекса в вопросах на должность python-разработчика тоже есть задание на работу с большими файлами. Я оптимального решения которое меня бы устраивало не придумал, но думаю ответ лежит где-то в смежной области с этим вопросом. :)

  5. Юрий Шерстянников
    8 марта 2014 в 21:27 | #5

    Пытаюсь процедурой WriteFile записать структуру диска на Flash (MBR, Boot, FAT и данные), ну, короче, практически образ. Все прекрасно до 63-го сектора (c 64 на диске начинается запись 1-го раздела), далее не пишет - процедура возвращает ошибку. Вероятно, это пространство оккупировано также какими-то процессами Windows. Есть ли способ все-же записать образ полностью?

  6. Юрий Шерстянников
    10 марта 2014 в 20:25 | #6

    Извиняюсь, уже нашел ответ на свой вопрос. ОС действительно не позволяет писать в области FAT, но позволяет в область MBR. Для того, чтобы писать в любое место необходимо демонтировать том, выполнить последовательность действий аналогичную той, которой меняют ОС на диске:
    1. Открыть том.
    2. Блокировать том.
    3. Форматировать том.
    4. Демонтировать том.
    5. Разблокировать том.
    6. Закрыть дескриптор тома.

    о чем явно сказано здесь:
    http://msdn.microsoft.com/en-us/library/windows/desktop/aa364562(v=vs.85).aspx
    (ну, так сказать, кому это интересно).

  7. Вороной Толик (Узбекистан)
    31 октября 2014 в 14:07 | #7

    Огромное спасибо!!!

  8. >hf
    12 марта 2017 в 13:13 | #8

    Автор долбоеб. Как мне, блять, новичку, понять, что тип данных "Handle" нужно писать в верхнем регистре, когда у автора написано в нижнем?

  9. Serg
    9 апреля 2017 в 16:24 | #9

    В примере для C/C++ и для Delphi не указан регистр куда будут записано количество считанных байт из файла или устройства.
    ReadFile(FileHandle, Buf, sizeof(buf),NULL); // так есть
    ReadFile(FileHandle, Buf, sizeof(buf), iBuf, NULL); // так надо, где iBuf: DWord

  10. Макс
    29 апреля 2017 в 15:11 | #10

    солидарен с >hf. минут 10 убил на то чтобы понять что Handle надо писать как HANDLE.отредактируйте пожалуйста во избежание непониманий новичков

  11. lizz
    10 июня 2017 в 17:41 | #11

    @>hf

    Отвечая на ваш вопрос: попробуйте включить мозг, например (если есть что включать, конечно). Если в учебнике по вождению будет написано что при виде стены надо втопить педаль газа в пол, как новичку понять что этого делать не надо?

    Так вот, если включить мозг удалось, то можно:
    1. Посмотреть официальные доки.
    2. Посмотреть соседние примеры в этой же статье и заметить, что handle в разных местах имеет разный регистр. Сделать предположение что это ошибка и воспользоваться правильным вариантом.

    Как-то так.

  12. lizz
    10 июня 2017 в 17:47 | #12

    @>hf

    Ах, да, забыл перейти на личности, простите. Комментатор, конечно же, мудак, раз не может без оскорблений указать на ошибку. ;)

  1. Пока что нет уведомлений.