WinAPI: Работа с файлами (основные функции).
Категорически всех приветствую! На этот раз в наше поле зрения попала группа API-функций для работы с файлами. Ибо, как мне кажется, помимо меня многие программисты сталкиваются с необходимостью их использования в своих программах. Но откровенно скажу в голове все эти функции вместе с их возможными параметрами и не упомнишь, а иметь возможность «вспомнить все» ;) прочитав эту статью — очень неплохая затея. За сим и приступаю к рассмотрению оного =).
В этой статье мы рассмотрим следующие API-функции:
- CreateFile
- OpenFile
- ReadFile
- WriteFile
- CloseFile
- DeleteFile
- CopyFile
- FindFirstFile
- FindNextFile
- 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
На этом пока все, если вдруг возникли какие нибуть вопросы или замечания, прошу их высказывать на форуме.


(12 голосов, средний: 4,58 из 5)
Мне статья помогла. Я давно программирую и много шушары в инете.
Но есть такие статьи которые открываешь и достаточно одну-две строчки прочитать и больше ничего не надо лишнего. Вот эта статья именно такая.
Особенно то, что написано точно и чётко как употреблять имена в функциях и в том числе имена сетевые.
В CopyFile допущена ошибка! Правильный вариант ниже:
bFailIfExists
[in] If this parameter is TRUE and the new file specified by lpNewFileName already exists, the function fails. If this parameter is FALSE and the new file already exists, the function overwrites the existing file and succeeds.
Благодарю за внимательность, ошибка исправлена! =)
>>Функция CloseFile
мб CloseHandle?
Есть такой вопрос:
Использую функцию FindFirstFile программируя на ассемблере.
Так вот, создал эту структуру куда помещается ифомрация о файле,
structR1 struc
A1 dd (?)
A2 dd 2 dup (?)
A3 dd 2 dup (?)
A4 dd 2 dup (?)
A5 dd (?)
A6 dd (?)
A7 dd (?)
A8 dd (?)
A9 db 260 dup (?)
A10 db 14 dup (?)
structR1 ends
Происходит выполнение этой winapi функции, далее смотрю отладчик, и R1.А10, т.е краткое имя файла, почему то залезает в область памяти R1.A9, в те 260 байт которые я зарезервировал для отображения длинного имени файла. И по адресу R1.A10 пусто. Немогли бы объяснить почему это происходит?
И ещё немогли бы объяснить как «расшифровывать» комбинацию атрибутов файла, вот например отладчик показывает 00 00 00 23. Как глядя на это узнать какие атрибуты установлены. (интересует именно комбинация атрибутов)
В примерах использования функции WriteFile ошибка. Вызывается функция ReadFile и в коде на C, и в коде на Delphi.
Также в примерах использования функций WriteFile и ReadFile на Delphi вместо NULL должно быть nil.
@user
Атрибуты файла получаются сложением флагов:
FILE_ATTRIBUTE_READONLY = 00000001h
FILE_ATTRIBUTE_HIDDEN = 00000002h
FILE_ATTRIBUTE_SYSTEM = 00000004h
FILE_ATTRIBUTE_DIRECTORY = 00000010h
FILE_ATTRIBUTE_ARCHIVE = 00000020h
FILE_ATTRIBUTE_NORMAL = 00000080h
FILE_ATTRIBUTE_TEMPORARY = 00000100h
FILE_ATTRIBUTE_COMPRESSED = 00000800h
FILE_ATTRIBUTE_OFFLINE = 00001000h
00 00 00 23 — это архивный, скрытый, только для чтения.
Спасибо за информацию! Очень помогло)