Использование функций windows api при работе с файлами
Иногда требуются стандартные функции работы с файлами Windows. Тем более, что возможности в них расширены. Каждый файл в Windows описывается не переменной, а дескриптором (Thandle, можно описывать Cardinal), который представляет собой 32-разрядную величину, идентифицирующую файл в операционной системе. Открывается файл при помощи следующей функции:
function CreateFileA(lpFileName: PAnsiChar;
dwDesiredAccess, dwShareMode: DWORD;
lpSecurityAttributes: PSecurityAttributes;
dwCreationDisposition, dwFlagsAndAttributes: DWORD;
hTemplateFile: THandle): THandle; stdcall;
Такое большое количество параметров функции связано с тем, что CreateFileA используется для открытия файлов на диске, устройств, каналов, портов и, вообще, любых источников ввода и вывода. Рассмотрим назначение параметров.
Здесь lpFileName - имя открываемого объекта; dwDesiredAccess – способ доступа к объекту (может иметь значение GENERIC_READ – для чтения, GENERIC_WRITE – для записи или комбинация этих значений). Если dwDesiredAccess =0, то можно получить атрибуты файла без фактического его открытия. Параметр dwShareMode открывает режим совместного с другими программами доступа (0 – совместный доступ запрещен; FILE_SHARE_READ – для чтения; FILE_SHARE_WRITE – для записи; комбинация этих значений – полный доступ). В рассматриваемой функции можно задавать атрибуты защиты объекта - lpSecurityAttributes (если равно nil, то устанавливаются атрибуты по умолчанию). Следующий параметр dwCreationDisposition отвечает за способ открытия объекта (CREATE_NEW – создается новый объект, если таковой существует, - иначе возвращается ошибка ERROR_ALREADY_EXISTS; CREATE_ALWAYS – создается новый объект, если таковой существует, или, если возможно, перезаписывается, - иначе выдается ошибка; OPEN_EXISTING – открывает существующий объект или, если таковой не найден, возвращается ошибка; OPEN_ALWAYS – открывает существующий объект, если таковой не найден, он создается). Набор атрибутов (скрытый, системный, сжатый) и флагов для открытия объекта задается параметром dwFlagsAndAttributes. Последний параметр hTemplateFile задает файл-шаблон, атрибуты которого используются для открытия объекта.
Функция возвращает дескриптор открытого объекта (некоторое число). Если окрыть объект невозможно, то возвращается код ошибки INVALID_HANDLE_VALUE. Более полные сведения об ошибке можно получить, вызвав функцию GetLastError.
Закрывается объект функцией function CloseHandle(hObject: THandle): BOOL; stdcall;.
Для чтения и записи данных используются следующие функции:
function ReadFile(hFile: THandle; var Buffer; nNumberOfBytesToRead: DWORD; var lpNumberOfBytesRead: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
function WriteFile(hFile: THandle; const Buffer; nNumberOfBytesToWrite: DWORD; var lpNumberOfBytesWritten: DWORD; lpOverlapped: POverlapped): BOOL; stdcall;
Здесь nNumberOfBytesToRead и nNumberOfBytesToWrite – количество байт, которое нужно прочитать или записать; lpNumberOfBytesRead и lpNumberOfBytesWritten - количество байт, которое фактически прочитано или записано. Параметр lpOverlapped – указатель на некоторый дескриптор структуры (типа TOverlapped) события отложенного ввода или вывода, например, для каких-то относительно “медленных” устройств. Создается событие следующей функцией:
function CreateEventA(lpEventAttributes: PSecurityAttributes; bManualReset, bInitialState: BOOL; lpName: PAnsiChar): THandle; stdcall;
Эта функция как раз и возвращает указанный выше дескриптор, на который ссылается параметр lpOverlapped. С помощью данного события автоматически включается отложенный ввод или вывод. Время в миллисекундах (параметр dwMilliseconds), которое разрешается ожидать при отложенном вводе или выводе указывается в функции function WaitForSingleObject(hHandle: THandle; dwMilliseconds: DWORD): DWORD; stdcall;, которая прерывает отложенную операцию чтения или записи. Можно задать бесконечный интервал INFINITE.
Функция CreateFileA полезна при работе с портами, например, следующая программа позволяет успешно открыть порт COM1 и выдать минимальные сведения о подключенном к этому порту модеме.
procedure TForm1.Button1Click(Sender: TObject);
var CommPort : string;
hDev : THandle;
ModemStat : DWord;
begin
CommPort := 'COM1';
hDev := CreateFile(PChar(CommPort),
GENERIC_READ,0,nil, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if hDev = INVALID_HANDLE_VALUE then begin
ShowMessage('Ошибка открытия порта '+ CommPort);
exit;
end;
if GetCommModemStatus(hDev,ModemStat)<>false
then begin
if ModemStat and MS_CTS_ON<> 0 then
ShowMessage(
'Управление CTS (clear-to-send) в порядке.');
if ModemStat and MS_DSR_ON <>0 then
ShowMessage(
'Управление DSR (data-set-ready) в порядке.');
if ModemStat and MS_RING_ON <> 0then
ShowMessage(
'Управление ring indicator в порядке.');
if ModemStat and MS_RLSD_ON <> 0 then
ShowMessage('Управление RLSD
(receive-line-signal-detect) в порядке.');
end;
CloseHandle(hDev);
end;
Еще один вариант использования рассматриваемой функции Windows API можно привести, когда требуется посекторное чтение, например, в дисководе A:.
Const SectorSize=512;
Var Buffer:Pointer;
FUNCTION TForm1.ReadSector(Head,Track,Sector:integer;
Buffer:pointer):boolean;
var
hDev:THandle;
DevName:string;
nb:cardinal;
begin
DevName:='\\.\A:';
hDev:=CreateFile(pChar(DevName),GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE,nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0);
if hDev=INVALID_HANDLE_VALUE then begin
Result:=false;
Exit;
end;
SetFilePointer(hDev,(Sector-1)*SectorSize,nil,
FILE_BEGIN);
Result:=ReadFile(hDev,Buffer,SectorSize,nb,nil)
and (nb=SectorSize);
CloseHandle(hDev);
end;
В данном примере для позиционирования файлового указателя применяется еще одна функция Windows API SetFilePointer,объявленная следующим образом:
function SetFilePointer(hFile: THandle;
lDistanceToMove: Longint; lpDistanceToMoveHigh:
Pointer; dwMoveMethod: DWORD): DWORD; stdcall;.
Как следует из примера функции ReadSector, позиционирование файлового указателя не учитывает значений параметров HeadиTrack (опущено для упрощения). При необходимости можно добавить взаимосвязь всех трех параметров.
Далее рассматривается небольшой пример, чтобы продемонстрировать указанные выше функции в работе.
ПРИМЕР 25
Пусть имеется текстовый файл, созданный по правилам Delphi. Требуется открыть этот файл указанной выше функцией Windows API и прочитать его содержимое. При выполнении этого примера встретятся некоторые трудности из-за того, что придется читать файл как совокупность байтов, а потом эти байты пытаться интерпретировать. Вариант решения примера приводится на рис. 61.
Рис. 61 Вариант решения примера 25.
Ниже приводится текст программы.
unit UMSfile;
Interface
uses Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls,
Buttons ;
Type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
BitBtn1: TBitBtn;
Memo2: TMemo;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormDeactivate(Sender: TObject);
public
hDev:THandle;
end;
var Form1: TForm1;
Buffer:pointer;
FilSize:cardinal;
Implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
type buf=array of byte;
var FilName:string;
nb:cardinal;
i,r:byte;
F:File;
sNumber:string[3];
begin
FilName:='InpFil.txt';
Buffer := Nil;
FilSize:=0;
{$I-}
AssignFile(F, FilName);
System.Reset(F, 1);
{$I+}
if IOResult<>0 then begin
memo1.Lines.Add('Ошибка открытия файла');
Exit;
End;
FilSize := FileSize(F);
closeFILE(F);
memo1.Lines.Add('FilSize= '+inttostr(FilSize));
memo2.Lines.LoadFromFile(FilName);
hDev:=CreateFileA(pChar(FilName),GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE,nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0);
if hDev=INVALID_HANDLE_VALUE then begin
memo1.Lines.Add(' Ошибка открытия файла Windows');
Exit;
end;
try
Getmem(Buffer,FilSize);
ReadFile(hDev,Buffer,FilSize,nb,nil);
memo1.Lines.Add('Прочитано байтов’+inttostr(nb));
memo1.Lines.Add('Прочитаны числа');
sNumber:='';
FOR I:=0 TO FilSize DO BEGIN
r:=buf(@buffer)[I];
if chr(r) in ['0'..'9'] then
sNumber:=sNumber+chr(r)
else begin
if length(sNumber)>0 then
memo1.Lines.Add(sNumber);
sNumber:='';
end;
END;
finally
CloseHandle(hDev);
end;
end;
procedure TForm1.FormDeactivate(Sender: TObject);
begin
FreeMem(Buffer,FilSize);
end;
end.
В данной программе вначале определяется количество байт в исходном файле, которое включает в себя все символы, содержащиеся в файле, в том числе и разделители, т.е. отдельные цифры чисел, пробелы, #13, #10. Далее этот файл открывается как файл Windows. Байты считываются в переменную Bufferи с помощью преобразования типа Pointer в тип Array of byte восстанавливаются в исходные числа. В примере показан один из вариантов работы с типом Pointer.