Рейтинг@Mail.ru

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 звезда2 звезды3 звезды4 звезды5 звёзд (12 голосов, средний: 4,58 из 5)
Loading ... Loading ...

  1. Данько
    4 Август 2009 в 13:48 | #1

    Мне статья помогла. Я давно программирую и много шушары в инете.
    Но есть такие статьи которые открываешь и достаточно одну-две строчки прочитать и больше ничего не надо лишнего. Вот эта статья именно такая.

  2. Данько
    4 Август 2009 в 13:49 | #2

    Особенно то, что написано точно и чётко как употреблять имена в функциях и в том числе имена сетевые.

  3. 25 Март 2010 в 00:52 | #3

    В 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.

  4. fk__
    2 Май 2010 в 16:08 | #5

    >>Функция CloseFile
    мб CloseHandle?

  5. 18 Май 2010 в 00:11 | #6

    Есть такой вопрос:
    Использую функцию 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. Как глядя на это узнать какие атрибуты установлены. (интересует именно комбинация атрибутов)

  6. said brown
    6 Июль 2010 в 16:06 | #7

    В примерах использования функции WriteFile ошибка. Вызывается функция ReadFile и в коде на C, и в коде на Delphi.
    Также в примерах использования функций WriteFile и ReadFile на Delphi вместо NULL должно быть nil.

  7. said brown
    7 Июль 2010 в 08:53 | #8

    @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 — это архивный, скрытый, только для чтения.

  8. VovchiK
    10 Июль 2010 в 06:16 | #9

    Спасибо за информацию! Очень помогло)

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