Категорически всех приветствую! На этот раз в наше поле зрения попала группа API-функций для работы с файлами. Ибо как мне кажется что по мимо меня многие программисты сталкиваются с необходимостью их использования в своих программах. Но откровенно скажу в голове все эти функции в мести с их возможными параметрами и не упомнишь, и иметь возможность «вспомнить все» прочитав эту статью очень не плохая затея. За семь и приступаю к рассмотрению оного =).
В этой статье мы рассмотрим следующие API-функции:
CreateFile
OpenFile
ReadFile
WriteFile
CloseFile
DeleteFile
CopyFile
FindFirstFile
FindNextFile
GetFileSize
Замечание
Но прежде чем приступить к дело, замечу, для случая когда действия над файлами происходят удаленно. За счет реализации в ОС Windows механизма прозрачного доступа к файлам, данные функции работают и с удаленными файлами, отличием является задание сетевого пути к файлу, а именно для С/С++ необходимо указывать путь в следующем формате: «\\\\имя_удаленного_компа\\путь_к_ файлу\\ имя_файла».
Пример:
1 | DeleteFile("\\\\comp1\\user1\\test\\test1.txt"); |
Для Delphi необходимо указывать путь в следующем формате: «\\имя_удаленного_компа\путь_к_ файлу\имя_файла».
Пример:
1 | DeleteFile("\\comp1\user1\test\test1.txt"); |
Функция CreateFile
Функция создаёт указатель на новое устройство типа:
- Файл
- Канал
- mailslot (почтовый канал)
- комуникационный ресурс (например COM порт)
- дисковые устройства (только для Windows NT)
- консоли
- директории (открывает их)
Все эти функции описаны в одном файле. Для С/С++ в хедере (заголовочный файл) winbase.h, для Delphi в модуле windows.pas. Чтобы использовать в своей программе эти функции, достаточно подключить их к своему проекту и вперед. Змечу, для тех кто кодит на С/С++ кроме winbase.h надо подключить еще windows.h.
Описание:
С/С++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | HANDLE CreateFile( LPCTSTR lpFileName, // Указатель на имя файла (устройства) DWORD dwDesiredAccess, //Параметры доступа DWORD dwShareMode, //Разделяемый доступ LPSECURITY_ATTRIBUTES lpSecurityAttributes, //безопасность DWORD dwCreationDistribution,// Описание DWORD dwFlagsAndAttributes, // Атрибуты файла HANDLE hTemplateFile // Файл шаблона ); |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function CreateFile( lpFileName: PChar; // Указатель на имя файла (устройства) dwDesiredAccess, //Параметры доступа dwShareMode: DWORD; //Разделяемый доступ lpSecurityAttributes: PSecurityAttributes; //безопасность dwCreationDisposition, // Описание dwFlagsAndAttributes: DWORD; // Атрибуты файла hTemplateFile: THandle // Файл шаблона ): THandle; stdcall; |
Теперь к главному, как создать файл? Делается это так:
C/C++
1 2 3 4 5 6 7 8 9 10 11 12 13 | { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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++
1 2 3 4 5 6 7 8 9 | HFILE WINAPI OpenFile( LPCSTR lpFileName, LPOFSTRUCT lpReOpenBuff, UINT uStyle ); |
Delphi
1 2 3 4 5 6 7 8 9 | function OpenFile( lpFileName: LPCSTR, //имя файла (устройства) lpReOpenBuff: TOFStruct, //специальная структура открываемого файла uStyle: UINT //флаги доступа к файлу ): HFILE; stdcall; |
Теперь рассмотрим используемые параметры:
- в качестве первого параметра указывается имя открываемого файла.
- вторым параметром является специальная структура открываемого файла, в которую заносится полезная инфа о файле.
- 3 параметр представляет собой совокупность флагов для работы с файлом.
Собственно, как открыть файл:
C/C++
1 2 3 4 5 6 7 8 9 10 11 | { Handle FileHandle; FileHandle= OpenFile("Settings.ini", Buf, OF_Create | OF_READWRITE | OF_SHARE_EXCLUSIVE); CloseHandle(FileHandle); } |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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++
1 2 3 4 5 6 7 8 9 10 11 12 13 | BOOL ReadFile(HANDLE hFile, //собственно указатель на файл LPVOID lpBuffer, // указатель на буфер - куда записываем считанные данные DWORD nNumberOfBytesToRead, //объем считываемых данных, не может превышать размер буфера LPDWORD lpNumberOfBytesRead, //фактический размер считанных данных LPOVERLAPPED lpOverlapped // флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED) //или синхронный(NULL) ); |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function ReadFile( hFile: THandle; //собственно указатель на файл var Buffer; // указатель на буфер - куда записываем считанные данные nNumberOfBytesToRead: DWORD; //объем считываемых данных, не может превышать размер буфера var lpNumberOfBytesRead: DWORD; //фактический размер считанных данных lpOverlapped: POverlapped// флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED) //или синхронный(nil) ): BOOL; stdcall; |
Теперь поглядим рабочий пример:
C/C++
1 2 3 4 5 6 7 8 9 10 11 12 13 | { Handle FileHandle; char Buf[1000]; ... ReadFile(FileHandle, Buf, sizeof(buf),NULL); ... } |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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++
1 2 3 4 5 6 7 8 9 10 11 12 13 | BOOL WriteFile(HANDLE hFile, //собственно указатель на файл LPVOID lpBuffer, // указатель на буфер - откуда записываем данные в файл DWORD nNumberOfBytesToWrite, //объем записываемых данных LPDWORD lpNumberOfBytesWrite, //фактический размер записанных данных LPOVERLAPPED lpOverlapped // флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED) //или синхронный(NULL) ); |
Delphi
f
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | unction WriteFile( hFile: THandle; //собственно указатель на файл var Buffer; // указатель на буфер - откуда записываем данные в файл nNumberOfBytesToWrite: DWORD; //объем записываемых данных var lpNumberOfBytesWrite: DWORD; //фактический размер записанных данных lpOverlapped: POverlapped// флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED) //или синхронный(nil) ): BOOL; stdcall; |
Теперь поглядим рабочий пример:
C/C++
1 2 3 4 5 6 7 8 9 10 11 12 13 | { Handle FileHandle; char Buf[1000]; ... ReadFile(FileHandle, Buf, sizeof(buf),NULL); ... } |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var FileHandle:THandle; Buf:array[1..1000] of char; begin ... ReadFile(FileHandle, Buf, sizeof(buf),NULL); ... end; |
Функция CloseFile
Эта функция достаточно проста. Выполняет действия по освобождению указателя на файл. Ее необходимость очевидна, чтобы не засорять память ненужными указателями и позволять работу с файлами другим процессам. В качестве ее единственного параметра передается указатель на ранее открытый файл.
Теперь поглядим рабочий пример:
C/C++
1 2 3 4 5 6 7 8 9 10 11 12 13 | { Handle FileHandle; FileHandle= OpenFile("Settings.ini", Buf, OF_Create | OF_READWRITE | OF_SHARE_EXCLUSIVE); ... CloseHandle(FileHandle); } |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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++
1 2 3 4 5 | BOOL WINAPI DeleteFile( LPCTSTR lpFileName // имя файла, который надо удалить ); |
Delphi
1 2 3 4 5 | function DeleteFile( lpFileName: PChar; // имя файла, который надо удалить ): BOOL; stdcall; |
Теперь поглядим рабочий пример:
C/C++
1 2 3 4 5 6 7 8 9 10 11 | { ... DeleteFile("C:\Temp\test.txt");// если указать только // имя файла, то будет осуществлена попытка удалить файл из папки откуда была запущена программа ... } |
Delphi
1 2 3 4 5 6 7 8 9 10 11 | begin ... DeleteFile('C:\Temp\test.txt');// если указать только // имя файла, то будет осуществлена попытка удалить файл из папки откуда была запущена программа ... end; |
Функция CopyFile
Основное назначение этой функции копирование файла. В случае успеха возвращает true, иначе — false. Код ошибки можно получить с помощью GetLastError.
C/C++
1 2 3 4 5 6 7 8 9 10 11 | BOOL CopyFile( LPCTSTR lpExistingFileName,// Указатель на файл, который надо копировать LPCTSTR lpNewFileName, // Указатель на имя файла, куда надо копировать BOOL bFailIfExists //если указать true, то в случае существования такого файла, // он будет перезаписан, если false - то произойдет ошибка. ); |
Delphi
1 2 3 4 5 6 7 8 9 10 11 | function CopyFile( lpExistingFileName, // Указатель на файл, который надо копировать lpNewFileName: PChar; // Указатель на имя файла, куда надо копировать bFailIfExists: BOOL //если указать true, то в случае существования такого файла, // он будет перезаписан, если false - то произойдет ошибка. ): BOOL; stdcall; |
Теперь поглядим рабочий пример:
C/C++
1 2 3 4 5 6 7 8 9 10 11 | { ... CopyFile("C:\Temp\test1.txt","C:\Temp\test2.txt", true); // если указать только // имя файла, то все действия будут выполнены в папке из которой запущена прога ... } |
Delphi
1 2 3 4 5 6 7 8 9 | begin ... CopyFile('C:\Temp\test1.txt','C:\Temp\test2.txt', false);// если указать только // имя файла, то все действия будут выполнены в папке из которой запущена прога... end; |
Функция FindFirstFile
Данная функция запускает поиск в указанной директории. Функция возвращает указатель на найденный файл, если нет то возврат будет типа ERROR_NO_MORE_FILES.
C/C++
1 2 3 4 5 6 7 | HANDLE FindFirstFile( LPCTSTR lpFileName, // Строка содержащая путь для поиска файлов. LPWIN32_FIND_DATA lpFindFileData // Информация о найденном файле ); |
Delphi
1 2 3 4 5 6 7 | function FindFirstFile( lpFileName: PChar; // Строка содержащая путь для поиска файлов. var lpFindFileData: TWIN32FindData // Информация о найденном файле ): THandle; stdcall; |
Теперь рассмотрим парамметры подробней:
- lpFileName — строка содержащая путь для поиска файла. Эта строка может указывать наконкретный файл типа ‘c:\filename.txt’ или может хранить шаблон ‘c:\*.*’. Если указывать шаблон, то это даёт возможность перечислить все файлы удовлетворяющие шаблону.
- lpFindFileData — структура WIN32_FIND_DATA, в которую будет записана инфа о найденом файле.
Расмотрим структуру WIN32_FIND_DATA немного подробнее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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++
1 2 3 4 5 6 7 8 9 10 11 12 13 | { WIN32_FIND_DATA sss; HANDLE f; f = FindFirstFile("C:\*.*", &sss); ... FindClose(f); } |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | var sss: TWIN32FindData; f:THandle; begin ... f:=FindFirstFile(PChar('C:\*.*'), sss); ... Windows.FindClose(f); end; |
Функция FindNextFile
Данная функция продолжает поиск начатый функцией FindFirstFile(). Если очередной файл найден, то функция возвращает значение true, иначе возврат будет типа ERROR_NO_MORE_FILES.
C/C++
1 2 3 4 5 6 7 | BOOL FindNextFile( HANDLE hFindFile, // Указатель на файл из предыдущего поиска LPWIN32_FIND_DATA lpFindFileData // Информация об очередном найденном файле ); |
Delphi
1 2 3 4 5 6 7 | function FindNextFile( hFindFile: THandle; // Указатель на файл из предыдущего поиска var lpFindFileData: TWIN32FindData // Информация об очередном найденном файле ): BOOL; stdcall; |
Со вторым параметром всё ясно, из описания функции FindFirstFile. А вот первый — это указатель на файл из предыдущего поиска. Он нужен, чтобы функция FindNextFile знала на каком файле был остановлен поиск и какой надо найти следующим.
Теперь поглядим рабочий пример:
C/C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | { WIN32_FIND_DATA sss; HANDLE f; f = FindFirstFile("C:\*.*", &sss); if(f != INVALID_HANDLE_VALUE) do{ ... }while(FindNextFile(f,&sss)); FindClose(f); } |
А теперь пример рекурсивного поиска файлов:
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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++
1 2 3 4 5 6 7 8 9 | DWORD GetFileSize( HANDLE hFile, // Указатель на файл LPDWORD lpFileSizeHigh // старший байт размера файла, в качестве // этого парамметра можно указать NULL ); |
Delphi
1 2 3 4 5 6 7 8 9 | function GetFileSize( hFile: THandle; // Указатель на файл lpFileSizeHigh: Pointer //старший байт размера файла, в качестве // этого парамметра можно указать nil ): DWORD; stdcall; |
Теперь поглядим рабочий пример:
C/C++
1 2 3 4 5 6 7 8 9 10 11 12 13 | { DWORD FileSize; Handle f; ... FileSize=GetFileSize(f,NULL); ... } |
Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var FileSize:DWORD; f:THandle; begin ... FileSize:=GetFileSize(f,nil); ... end |
На этом пока все, если вдруг возникли какие нибуть вопросы или замечания, прошу их высказывать в комментариях.
В примере WriteFile написано ReadFile =/
Здравствуйте!
у меня такая проблема… есть файл 32 гига бинарный. записи строго по 8 байт….
требуется в дельфи уметь читать любую запись 8 байт, при нужде ее изменить и записать
назад. Стандартные процедуры не поддерживают такие размеры. помогите.
Владимир.
я не думаю что кто то возьмётся за такую работу, тем более бесплатно.
Ну почему сразу работу. Я думаю если кто может посоветовать в каком направлении копать, то здорово. Сам, увы, не сталкивался с таким, да и не дельфист вовсе.
Кстати, у Яндекса в вопросах на должность python-разработчика тоже есть задание на работу с большими файлами. Я оптимального решения которое меня бы устраивало не придумал, но думаю ответ лежит где-то в смежной области с этим вопросом.
Пытаюсь процедурой WriteFile записать структуру диска на Flash (MBR, Boot, FAT и данные), ну, короче, практически образ. Все прекрасно до 63-го сектора (c 64 на диске начинается запись 1-го раздела), далее не пишет — процедура возвращает ошибку. Вероятно, это пространство оккупировано также какими-то процессами Windows. Есть ли способ все-же записать образ полностью?
Извиняюсь, уже нашел ответ на свой вопрос. ОС действительно не позволяет писать в области FAT, но позволяет в область MBR. Для того, чтобы писать в любое место необходимо демонтировать том, выполнить последовательность действий аналогичную той, которой меняют ОС на диске:
1. Открыть том.
2. Блокировать том.
3. Форматировать том.
4. Демонтировать том.
5. Разблокировать том.
6. Закрыть дескриптор тома.
о чем явно сказано здесь:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364562(v=vs.85).aspx
(ну, так сказать, кому это интересно).
Огромное спасибо!!!