{
pascalx.osapi — модуль для взаимодействия с операционной системой.
Copyright © 2021 Малик Разработчик
Это свободная программа: вы можете перераспространять её и/или
изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она может быть полезна,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<http://www.gnu.org/licenses/>.
}
unit pascalx.osapi;
{$MODE DELPHI}
interface
uses
windows,
pascalx.lang,
pascalx.utils,
pascalx.io,
pascalx.io.vfs;
{$ASMMODE INTEL,CALLING REGISTER,TYPEINFO ON}
{%region public }
const { значения для MemoryRegion }
READABLE = int($01);
WRITEABLE = int($02);
EXECUTABLE = int($04);
type
MemoryRegion = class;
LocalFileSystem = class;
HandleInputStream = class;
HandleOutputStream = class;
FileInputStream = class;
FileOutputStream = class;
FileIOStream = class;
MemoryRegion = class(DynamicalyAllocatedObject)
public
class function getMinimumAddress(): long; static;
class function getMaximumAddress(): long; static;
class function allocate(address: long; size: long; protectionFlags: int): Pointer; static; overload;
class function allocate(address: Pointer; size: long; protectionFlags: int): Pointer; static; overload;
class procedure deallocate(address: Pointer; size: long); static; overload;
class procedure protect(address: long; size: long; protectionFlags: int); static; overload;
class procedure protect(address: Pointer; size: long; protectionFlags: int); static; overload;
class procedure getInfo(address: long; region: MemoryRegion); static; overload;
class procedure getInfo(address: Pointer; region: MemoryRegion); static; overload;
private
fProtectionFlags: int;
fAddress: long;
fSize: long;
fModule: UnicodeString;
function isModuleStored(): boolean;
function getProtectionFlag(flagMask: int): boolean;
protected
procedure setProtectionFlag(flagMask: int; flagValue: boolean); virtual;
procedure setAddress(address: long); virtual;
procedure setSize(size: long); virtual;
public
constructor create(); overload; override;
constructor create(address: long; size: long; protectionFlags: int; module: UnicodeString = ''); overload; virtual;
constructor create(address: Pointer; size: long; protectionFlags: int; module: UnicodeString = ''); overload; virtual;
function allocate(): Pointer; virtual; overload;
procedure deallocate(); virtual; overload;
procedure protect(); virtual; overload;
procedure setAddressSize(address, size: long); virtual;
published
property readable: boolean index pascalx.osapi.READABLE read getProtectionFlag write setProtectionFlag stored true;
property writeable: boolean index pascalx.osapi.WRITEABLE read getProtectionFlag write setProtectionFlag stored true;
property executable: boolean index pascalx.osapi.EXECUTABLE read getProtectionFlag write setProtectionFlag stored true;
property address: long read fAddress write setAddress stored true;
property size: long read fSize write setSize stored true;
property module: UnicodeString read fModule write fModule stored isModuleStored;
end;
LocalFileSystem = class(_Object, ReadOnlyVirtualFileSystem, WriteableVirtualFileSystem)
private
class var instance: LocalFileSystem;
class function isNameCorrect(const name: UnicodeString): boolean; static;
public
class function getInstance(): LocalFileSystem; static;
private
allowDestroy: boolean;
public
constructor create();
function getObjectNameMaximumLength(): int;
function findFirst(): FileEnumeration; overload;
function findFirst(const objectName: UnicodeString): FileEnumeration; overload;
function openFile(const fileName: UnicodeString): IOStream;
function openFileForReading(const fileName: UnicodeString): InputStream;
function openFileForAppending(const fileName: UnicodeString): OutputStream;
function createFile(const fileName: UnicodeString): OutputStream;
procedure createDirectory(const directoryName: UnicodeString);
procedure deleteFile(const fileName: UnicodeString);
procedure deleteDirectory(const directoryName: UnicodeString);
procedure move(const oldObjectName, newObjectName: UnicodeString);
procedure readAttributes(const objectName: UnicodeString; objectAttr: FileAttributes);
procedure writeAttributes(const objectName: UnicodeString; objectAttr: FileAttributes);
procedure beforeDestruction(); override;
end;
HandleInputStream = class(InputStream)
protected
handle: THandle;
public
constructor create(); overload;
constructor create(handle: THandle); overload;
function seekSupported(): boolean; override;
function seek(delta: long): long; override;
function reset(position: long = 0): long; override;
function position(): long; override;
function available(): long; override;
function size(): long; override;
function read(): int; override; overload;
function read(const dst: byte_Array1d; offset, length: int): int; override; overload;
end;
HandleOutputStream = class(OutputStream)
protected
handle: THandle;
public
constructor create(); overload;
constructor create(handle: THandle); overload;
procedure flush(); override;
procedure write(data: int); override; overload;
procedure write(const src: byte_Array1d; offset, length: int); override; overload;
end;
FileInputStream = class(HandleInputStream)
private
fileName: UnicodeString;
public
constructor create(const fileName: AnsiString); overload;
constructor create(const fileName: UnicodeString); overload;
function getFileName(): UnicodeString;
function hasOpenError(): boolean; virtual;
procedure checkOpenError(); virtual;
procedure close(); override;
end;
FileOutputStream = class(HandleOutputStream)
private
fileName: UnicodeString;
public
constructor create(const fileName: AnsiString; appending: boolean = false); overload;
constructor create(const fileName: UnicodeString; appending: boolean = false); overload;
function getFileName(): UnicodeString;
function hasOpenError(): boolean; virtual;
procedure checkOpenError(); virtual;
procedure close(); override;
end;
FileIOStream = class(IOStream)
private
fileName: UnicodeString;
inputPtr: HandleInputStream;
inputIntf: Input;
outputPtr: HandleOutputStream;
outputIntf: Output;
public
constructor create(const fileName: AnsiString); overload;
constructor create(const fileName: UnicodeString); overload;
function seekSupported(): boolean; override;
function truncateSupported(): boolean; override;
function seek(delta: long): long; override;
function reset(position: long = 0): long; override;
function truncate(): long; override;
function getInputStream(): InputStream; override;
function getOutputStream(): OutputStream; override;
function getFileName(): UnicodeString;
function hasOpenError(): boolean; virtual;
procedure checkOpenError(); virtual;
procedure close(); override;
end;
{%endregion}
implementation
{%region private }
type
LocalDiskInfo = class;
LocalDiskEnumeration = class;
LocalFileEnumeration = class;
FileIOStream0HandleInputStream = class;
FileIOStream0HandleOutputStream = class;
LocalDiskInfo = class(FileEnumeration)
public
constructor create(letter: wchar);
function findNext(): boolean; override;
end;
LocalDiskEnumeration = class(LocalDiskInfo)
private
letter: wchar;
public
constructor create(letter: wchar);
function findNext(): boolean; override;
end;
LocalFileEnumeration = class(FileEnumeration)
private
class function getFileTime(const fileTime: TFileTime): long; static;
class function getFileSize(const findData: TWin32FindDataW): long; static;
class function getFileName(const findData: TWin32FindDataW): UnicodeString; static;
class procedure toFileTime(const timeInMillis: long; var fileTime: TFileTime); static;
private
handle: THandle;
findData: TWin32FindDataW;
public
constructor create(handle: THandle; const findData: TWin32FindDataW);
function findNext(): boolean; override;
procedure close(); override;
end;
FileIOStream0HandleInputStream = class(HandleInputStream)
private
bufferedPosition: long;
public
function seek(delta: long): long; override;
function reset(position: long = 0): long; override;
function position(): long; override;
function available(): long; override;
function read(): int; override; overload;
function read(const dst: byte_Array1d; offset, length: int): int; override; overload;
end;
FileIOStream0HandleOutputStream = class(HandleOutputStream)
private
bufferedPosition: long;
function position(): long;
procedure reset(position: long);
public
procedure write(data: int); override; overload;
procedure write(const src: byte_Array1d; offset, length: int); override; overload;
end;
{%endregion}
{%region MemoryRegion }
class function MemoryRegion.getMinimumAddress(): long;
begin
result := $0000000000000000;
end;
class function MemoryRegion.getMaximumAddress(): long;
begin
result := $000007ffffffffff;
end;
class function MemoryRegion.allocate(address: long; size: long; protectionFlags: int): Pointer;
begin
result := allocate(Pointer((@address)^), size, protectionFlags);
end;
class function MemoryRegion.allocate(address: Pointer; size: long; protectionFlags: int): Pointer;
var
addr: long;
lim1: long;
lim2: long;
begin
if size < 0 then begin
raise IllegalArgumentException.create('MemoryRegion.allocate: ' + msgIllegalArgument + 'size');
end;
if size = 0 then begin
result := nil;
exit;
end;
addr := long((@address)^);
lim1 := getMinimumAddress();
lim2 := getMaximumAddress() + 1;
if (addr < lim1) or (addr > lim2) then begin
raise IllegalArgumentException.create('MemoryRegion.allocate: ' + msgIllegalArgument + 'address');
end;
inc(addr, size);
if (addr < lim1) or (addr > lim2) then begin
raise IllegalArgumentException.create('MemoryRegion.allocate: ' + msgIllegalArgument + 'address');
end;
protectionFlags := (protectionFlags or pascalx.osapi.READABLE) and $07;
case protectionFlags of
pascalx.osapi.READABLE:
protectionFlags := PAGE_READONLY;
pascalx.osapi.READABLE or pascalx.osapi.WRITEABLE:
protectionFlags := PAGE_READWRITE;
pascalx.osapi.READABLE or pascalx.osapi.EXECUTABLE:
protectionFlags := PAGE_EXECUTE_READ;
else
protectionFlags := PAGE_EXECUTE_READWRITE;
end;
result := virtualAlloc(address, size, MEM_COMMIT, protectionFlags);
end;
class procedure MemoryRegion.deallocate(address: Pointer; size: long);
var
addr: long;
lim1: long;
lim2: long;
begin
if size < 0 then begin
raise IllegalArgumentException.create('MemoryRegion.deallocate: ' + msgIllegalArgument + 'size');
end;
if size = 0 then exit;
addr := long((@address)^);
lim1 := getMinimumAddress();
lim2 := getMaximumAddress() + 1;
if (addr < lim1) or (addr > lim2) then begin
raise IllegalArgumentException.create('MemoryRegion.deallocate: ' + msgIllegalArgument + 'address');
end;
inc(addr, size);
if (addr < lim1) or (addr > lim2) then begin
raise IllegalArgumentException.create('MemoryRegion.deallocate: ' + msgIllegalArgument + 'address');
end;
virtualFree(address, size, MEM_DECOMMIT);
end;
class procedure MemoryRegion.protect(address: long; size: long; protectionFlags: int);
begin
protect(Pointer((@address)^), size, protectionFlags);
end;
class procedure MemoryRegion.protect(address: Pointer; size: long; protectionFlags: int);
var
addr: long;
lim1: long;
lim2: long;
oldp: int;
begin
if size < 0 then begin
raise IllegalArgumentException.create('MemoryRegion.protect: ' + msgIllegalArgument + 'size');
end;
if size = 0 then exit;
addr := long((@address)^);
lim1 := getMinimumAddress();
lim2 := getMaximumAddress() + 1;
if (addr < lim1) or (addr > lim2) then begin
raise IllegalArgumentException.create('MemoryRegion.protect: ' + msgIllegalArgument + 'address');
end;
inc(addr, size);
if (addr < lim1) or (addr > lim2) then begin
raise IllegalArgumentException.create('MemoryRegion.protect: ' + msgIllegalArgument + 'address');
end;
protectionFlags := (protectionFlags or pascalx.osapi.READABLE) and $07;
case protectionFlags of
pascalx.osapi.READABLE:
protectionFlags := PAGE_READONLY;
pascalx.osapi.READABLE or pascalx.osapi.WRITEABLE:
protectionFlags := PAGE_READWRITE;
pascalx.osapi.READABLE or pascalx.osapi.EXECUTABLE:
protectionFlags := PAGE_EXECUTE_READ;
else
protectionFlags := PAGE_EXECUTE_READWRITE;
end;
virtualProtect(address, size, protectionFlags, @oldp);
end;
class procedure MemoryRegion.getInfo(address: long; region: MemoryRegion);
begin
getInfo(Pointer((@address)^), region);
end;
class procedure MemoryRegion.getInfo(address: Pointer; region: MemoryRegion);
var
lim1: long;
lim2: long;
addr: long;
size: long;
queryAddress: long;
allocationBase: Pointer;
info: TMemoryBasicInformation;
basicProtection: int;
basicModule: UnicodeString;
currProtection: int;
currModule: UnicodeString;
moduleStorage: PWideChar;
begin
addr := long((@address)^);
lim1 := getMinimumAddress();
lim2 := getMaximumAddress();
if (addr < lim1) or (addr > lim2) then begin
raise IllegalArgumentException.create('MemoryRegion.getInfo: ' + msgIllegalArgument + 'address');
end;
if region = nil then begin
raise NullPointerException.create('MemoryRegion.getInfo: ' + msgNullPointerArgument + 'region');
end;
initialize(info);
virtualQuery(address, info, sizeOf(TMemoryBasicInformation));
addr := long((@info.baseAddress)^);
size := 0;
moduleStorage := getMemory((MAX_PATH + 1) shl 1);
try
moduleStorage^ := #$0000;
allocationBase := info.allocationBase;
if allocationBase <> nil then begin
getModuleFileNameW(HInst((@allocationBase)^), moduleStorage, MAX_PATH);
end;
basicProtection := int(info.protect) and $fe;
basicModule := UnicodeString(moduleStorage);
repeat
inc(size, long(info.regionSize));
queryAddress := addr + size;
if queryAddress > lim2 then break;
virtualQuery(Pointer((@queryAddress)^), info, sizeOf(TMemoryBasicInformation));
moduleStorage^ := #$0000;
allocationBase := info.allocationBase;
if allocationBase <> nil then begin
getModuleFileNameW(HInst((@allocationBase)^), moduleStorage, MAX_PATH);
end;
currProtection := int(info.protect) and $fe;
currModule := UnicodeString(moduleStorage);
until (currProtection <> basicProtection) or (currModule <> basicModule);
finally
freeMemory(moduleStorage);
end;
region.readable := basicProtection <> 0;
region.writeable := (basicProtection and (PAGE_EXECUTE_READWRITE or PAGE_READWRITE or PAGE_EXECUTE_WRITECOPY or PAGE_WRITECOPY)) <> 0;
region.executable := (basicProtection and (PAGE_EXECUTE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY)) <> 0;
region.address := addr;
region.size := size;
region.module := basicModule;
end;
function MemoryRegion.isModuleStored(): boolean;
begin
result := length(fModule) > 0;
end;
function MemoryRegion.getProtectionFlag(flagMask: int): boolean;
begin
result := (fProtectionFlags and flagMask) <> 0;
end;
procedure MemoryRegion.setProtectionFlag(flagMask: int; flagValue: boolean);
begin
if flagValue then begin
fProtectionFlags := (fProtectionFlags or flagMask) and $07;
end else begin
fProtectionFlags := (fProtectionFlags and not flagMask) and $07;
end;
end;
procedure MemoryRegion.setAddress(address: long);
begin
address := longBound(getMinimumAddress(), address, getMaximumAddress() - fSize + 1);
address := address and (-$1000);
self.fAddress := address;
end;
procedure MemoryRegion.setSize(size: long);
begin
inc(size, (-size) and $0fff);
size := longBound($1000, size, getMaximumAddress() - fAddress + 1);
self.fSize := size;
end;
constructor MemoryRegion.create();
begin
create(0, $1000, 0, '');
end;
constructor MemoryRegion.create(address: long; size: long; protectionFlags: int; module: UnicodeString);
var
min: long;
max: long;
begin
inherited create();
min := getMinimumAddress();
max := getMaximumAddress() + 1;
inc(size, (-size) and $0fff);
size := longBound($1000, size, max - min);
address := longBound(min, address, max - size);
address := address and (-$1000);
self.fProtectionFlags := protectionFlags and $07;
self.fAddress := address;
self.fSize := size;
self.fModule := module;
end;
constructor MemoryRegion.create(address: Pointer; size: long; protectionFlags: int; module: UnicodeString);
begin
create(long((@address)^), size, protectionFlags, module);
end;
function MemoryRegion.allocate(): Pointer;
begin
result := allocate(fAddress, fSize, fProtectionFlags);
address := long((@result)^);
end;
procedure MemoryRegion.deallocate();
begin
deallocate(Pointer((@fAddress)^), fSize);
end;
procedure MemoryRegion.protect();
begin
protect(fAddress, fSize, fProtectionFlags);
end;
procedure MemoryRegion.setAddressSize(address, size: long);
var
min: long;
max: long;
begin
min := getMinimumAddress();
max := getMaximumAddress() + 1;
inc(size, (-size) and $0fff);
size := longBound($1000, size, max - min);
address := longBound(min, address, max - size);
address := address and (-$1000);
self.fAddress := address;
self.fSize := size;
end;
{%endregion}
{%region LocalFileSystem }
class function LocalFileSystem.isNameCorrect(const name: UnicodeString): boolean;
begin
result := not stringEndsWith(wchar(DIRECTORY_SEPARATOR) + '.', name) and not stringEndsWith(wchar(DIRECTORY_SEPARATOR) + '..', name);
result := result and (stringIndexOf(wchar(DIRECTORY_SEPARATOR) + '..' + DIRECTORY_SEPARATOR, name) = 0);
result := result and (stringIndexOf(wchar(DIRECTORY_SEPARATOR) + '.' + DIRECTORY_SEPARATOR, name) = 0);
result := result and (stringIndexOf(wchar(DIRECTORY_SEPARATOR) + '' + DIRECTORY_SEPARATOR, name) = 0);
result := result and (stringIndexOf(wchar('|'), name) = 0) and (stringIndexOf(wchar('"'), name) = 0);
result := result and (stringIndexOf(wchar('<'), name) = 0) and (stringIndexOf(wchar('>'), name) = 0);
result := result and (stringIndexOf(wchar('*'), name) = 0) and (stringIndexOf(wchar('?'), name) = 0);
result := result and (stringLastIndexOf(wchar(':'), name) = 2);
end;
class function LocalFileSystem.getInstance(): LocalFileSystem;
begin
result := instance;
end;
constructor LocalFileSystem.create();
begin
inherited create();
self.allowDestroy := true;
end;
function LocalFileSystem.getObjectNameMaximumLength(): int;
begin
result := MAX_PATH - 1;
end;
function LocalFileSystem.findFirst(): FileEnumeration;
var
find: UnicodeString;
letter: wchar;
availableBytes: long;
totalFreeBytes: long;
totalSizeInBytes: long;
begin
for letter := 'A' to 'Z' do begin
find := letter + (':' + DIRECTORY_SEPARATOR);
if getDiskFreeSpaceExW(PWideChar(find), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
result := LocalDiskEnumeration.create(letter);
exit;
end;
end;
result := nil;
end;
function LocalFileSystem.findFirst(const objectName: UnicodeString): FileEnumeration;
var
name: UnicodeString;
find: UnicodeString;
found: UnicodeString;
letter: wchar;
len: int;
availableBytes: long;
totalFreeBytes: long;
totalSizeInBytes: long;
correctName: boolean;
handleInUse: boolean;
handle: THandle;
findData: TWin32FindDataW;
begin
name := stringReplace(objectName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
for letter := 'A' to 'Z' do begin
find := letter + (':' + DIRECTORY_SEPARATOR);
if getDiskFreeSpaceExW(PWideChar(find), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
result := LocalDiskEnumeration.create(letter);
exit;
end;
end;
result := nil;
exit;
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, length(name));
if not correctName then begin
raise DirectoryNotFoundException.create(name, 'LocalFileSystem.findFirst: ' + msgDirectoryNotFound);
end;
find := name + (DIRECTORY_SEPARATOR + '*');
initialize(findData);
handleInUse := false;
handle := findFirstFileW(PWideChar(find), findData);
if handle = THandle(-1) then begin
if int(getFileAttributesW(PWideChar(name))) <> -1 then begin
raise FileSystemSecurityException.create(name, 'LocalFileSystem.findFirst: ' + msgSecurity);
end;
raise DirectoryNotFoundException.create(name, 'LocalFileSystem.findFirst: ' + msgDirectoryNotFound);
end;
try
repeat
found := UnicodeString(PWideChar(@findData.cFileName));
if (found <> '.') and (found <> '..') then break;
if not findNextFileW(handle, findData) then begin
result := nil;
exit;
end;
until false;
handleInUse := true;
finally
if not handleInUse then findClose(handle);
end;
result := LocalFileEnumeration.create(handle, findData);
exit;
end;
if not correctName then begin
result := nil;
exit;
end;
if len = 2 then begin
letter := charToUpperCase(letter);
find := letter + (':' + DIRECTORY_SEPARATOR);
if getDiskFreeSpaceExW(PWideChar(find), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
result := LocalDiskInfo.create(letter);
exit;
end;
result := nil;
exit;
end;
initialize(findData);
handleInUse := false;
handle := findFirstFileW(PWideChar(name), findData);
if handle = THandle(-1) then begin
result := nil;
exit;
end;
try
repeat
found := UnicodeString(PWideChar(@findData.cFileName));
if (found <> '.') and (found <> '..') then break;
if not findNextFileW(handle, findData) then begin
result := nil;
exit;
end;
until false;
handleInUse := true;
finally
if not handleInUse then findClose(handle);
end;
result := LocalFileEnumeration.create(handle, findData);
end;
function LocalFileSystem.openFile(const fileName: UnicodeString): IOStream;
var
name: UnicodeString;
letter: wchar;
len: int;
correctName: boolean;
availableBytes: long;
totalFreeBytes: long;
totalSizeInBytes: long;
attributes: int;
handle: THandle;
stream: FileIOStream;
instrm: HandleInputStream;
outstrm: HandleOutputStream;
begin
name := stringReplace(fileName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFile: ' + msgFileNotFound);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFile: ' + msgFileNotFound);
end;
if len = 2 then begin
name := letter + (':' + DIRECTORY_SEPARATOR);
if getDiskFreeSpaceExW(PWideChar(name), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
raise FileCreationException.create(name, 'LocalFileSystem.openFile: ' + msgFileCreationError);
end;
raise FileNotFoundException.create(name, 'LocalFileSystem.openFile: ' + msgFileNotFound);
end;
attributes := getFileAttributesW(PWideChar(name));
if attributes = -1 then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFile: ' + msgFileNotFound);
end;
if (attributes and ATTR_DIRECTORY) <> 0 then begin
raise FileCreationException.create(name, 'LocalFileSystem.openFile: ' + msgFileCreationError);
end;
handle := createFileW(PWideChar(name), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, attributes, 0);
if handle = THandle(-1) then begin
raise FileSystemSecurityException.create(name, 'LocalFileSystem.openFile: ' + msgSecurity);
end;
stream := FileIOStream.create();
instrm := FileIOStream0HandleInputStream.create(handle);
outstrm := FileIOStream0HandleOutputStream.create(handle);
stream.fileName := name;
stream.inputPtr := instrm;
stream.inputIntf := instrm;
stream.outputPtr := outstrm;
stream.outputIntf := outstrm;
result := stream;
end;
function LocalFileSystem.openFileForReading(const fileName: UnicodeString): InputStream;
var
name: UnicodeString;
letter: wchar;
len: int;
correctName: boolean;
availableBytes: long;
totalFreeBytes: long;
totalSizeInBytes: long;
attributes: int;
handle: THandle;
stream: FileInputStream;
begin
name := stringReplace(fileName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFileForReading: ' + msgFileNotFound);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFileForReading: ' + msgFileNotFound);
end;
if len = 2 then begin
name := letter + (':' + DIRECTORY_SEPARATOR);
if getDiskFreeSpaceExW(PWideChar(name), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
raise FileCreationException.create(name, 'LocalFileSystem.openFileForReading: ' + msgFileCreationError);
end;
raise FileNotFoundException.create(name, 'LocalFileSystem.openFileForReading: ' + msgFileNotFound);
end;
attributes := getFileAttributesW(PWideChar(name));
if attributes = -1 then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFileForReading: ' + msgFileNotFound);
end;
if (attributes and ATTR_DIRECTORY) <> 0 then begin
raise FileCreationException.create(name, 'LocalFileSystem.openFileForReading: ' + msgFileCreationError);
end;
handle := createFileW(PWideChar(name), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, attributes, 0);
if handle = THandle(-1) then begin
raise FileSystemSecurityException.create(name, 'LocalFileSystem.openFileForReading: ' + msgSecurity);
end;
stream := FileInputStream.create(handle);
stream.fileName := name;
result := stream;
end;
function LocalFileSystem.openFileForAppending(const fileName: UnicodeString): OutputStream;
var
name: UnicodeString;
letter: wchar;
len: int;
correctName: boolean;
availableBytes: long;
totalFreeBytes: long;
totalSizeInBytes: long;
attributes: int;
position: int;
handle: THandle;
stream: FileOutputStream;
begin
name := stringReplace(fileName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFileForAppending: ' + msgFileNotFound);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFileForAppending: ' + msgFileNotFound);
end;
if len = 2 then begin
name := letter + (':' + DIRECTORY_SEPARATOR);
if getDiskFreeSpaceExW(PWideChar(name), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
raise FileCreationException.create(name, 'LocalFileSystem.openFileForAppending: ' + msgFileCreationError);
end;
raise FileNotFoundException.create(name, 'LocalFileSystem.openFileForAppending: ' + msgFileNotFound);
end;
attributes := getFileAttributesW(PWideChar(name));
if attributes = -1 then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.openFileForAppending: ' + msgFileNotFound);
end;
if (attributes and ATTR_DIRECTORY) <> 0 then begin
raise FileCreationException.create(name, 'LocalFileSystem.openFileForAppending: ' + msgFileCreationError);
end;
handle := createFileW(PWideChar(name), GENERIC_WRITE, 0, nil, OPEN_EXISTING, attributes, 0);
if handle = THandle(-1) then begin
raise FileSystemSecurityException.create(name, 'LocalFileSystem.openFileForAppending: ' + msgSecurity);
end;
position := 0;
setFilePointer(handle, 0, @position, FILE_END);
stream := FileOutputStream.create(handle);
stream.fileName := name;
result := stream;
end;
function LocalFileSystem.createFile(const fileName: UnicodeString): OutputStream;
var
name: UnicodeString;
letter: wchar;
len: int;
correctName: boolean;
attributes: int;
handle: THandle;
stream: FileOutputStream;
begin
name := stringReplace(fileName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise FileCreationException.create(name, 'LocalFileSystem.createFile: ' + msgFileCreationError);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName or (len = 2) then begin
raise FileCreationException.create(name, 'LocalFileSystem.createFile: ' + msgFileCreationError);
end;
attributes := int(getFileAttributesW(PWideChar(name)));
if (attributes <> -1) and ((attributes and ATTR_DIRECTORY) <> 0) then begin
raise FileCreationException.create(name, 'LocalFileSystem.createFile: ' + msgFileCreationError);
end;
if (attributes <> -1) and ((attributes and ATTR_READONLY) <> 0) then begin
raise FileSystemSecurityException.create(name, 'LocalFileSystem.createFile: ' + msgSecurity);
end;
handle := createFileW(PWideChar(name), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
if handle = THandle(-1) then begin
raise FileSystemSecurityException.create(name, 'LocalFileSystem.createFile: ' + msgSecurity);
end;
stream := FileOutputStream.create(handle);
stream.fileName := name;
result := stream;
end;
procedure LocalFileSystem.createDirectory(const directoryName: UnicodeString);
var
name: UnicodeString;
letter: wchar;
len: int;
correctName: boolean;
begin
name := stringReplace(directoryName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise DirectoryCreationException.create(name, 'LocalFileSystem.createDirectory: ' + msgDirectoryCreationError);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName or (len = 2) or (int(getFileAttributesW(PWideChar(name))) <> -1) or not createDirectoryW(PWideChar(name), nil) then begin
raise DirectoryCreationException.create(name, 'LocalFileSystem.createDirectory: ' + msgDirectoryCreationError);
end;
end;
procedure LocalFileSystem.deleteFile(const fileName: UnicodeString);
var
name: UnicodeString;
letter: wchar;
len: int;
attributes: int;
correctName: boolean;
begin
name := stringReplace(fileName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.deleteFile: ' + msgFileNotFound);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName or (len = 2) then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.deleteFile: ' + msgFileNotFound);
end;
attributes := int(getFileAttributesW(PWideChar(name)));
if (attributes = -1) or ((attributes and ATTR_DIRECTORY) <> 0) then begin
raise FileNotFoundException.create(name, 'LocalFileSystem.deleteFile: ' + msgFileNotFound);
end;
if not deleteFileW(PWideChar(name)) then begin
raise FileDeletionException.create(name, 'LocalFileSystem.deleteFile: ' + msgFileDeletionError);
end;
end;
procedure LocalFileSystem.deleteDirectory(const directoryName: UnicodeString);
var
name: UnicodeString;
letter: wchar;
len: int;
attributes: int;
correctName: boolean;
begin
name := stringReplace(directoryName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise DirectoryNotFoundException.create(name, 'LocalFileSystem.deleteDirectory: ' + msgDirectoryNotFound);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName then begin
raise DirectoryNotFoundException.create(name, 'LocalFileSystem.deleteDirectory: ' + msgDirectoryNotFound);
end;
if len = 2 then begin
raise DirectoryDeletionException.create(name, 'LocalFileSystem.deleteDirectory: ' + msgDirectoryDeletionError);
end;
attributes := int(getFileAttributesW(PWideChar(name)));
if (attributes = -1) or ((attributes and ATTR_DIRECTORY) = 0) then begin
raise DirectoryNotFoundException.create(name, 'LocalFileSystem.deleteDirectory: ' + msgDirectoryNotFound);
end;
if not removeDirectoryW(PWideChar(name)) then begin
raise DirectoryDeletionException.create(name, 'LocalFileSystem.deleteDirectory: ' + msgDirectoryDeletionError);
end;
end;
procedure LocalFileSystem.move(const oldObjectName, newObjectName: UnicodeString);
var
oldName: UnicodeString;
oldLetter: wchar;
oldLen: int;
oldCorrectName: boolean;
newName: UnicodeString;
newLetter: wchar;
newLen: int;
newCorrectName: boolean;
begin
oldName := stringReplace(oldObjectName, '/', DIRECTORY_SEPARATOR);
newName := stringReplace(newObjectName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), oldName) then begin
oldName := stringCopy(oldName, 2);
end;
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), newName) then begin
newName := stringCopy(newName, 2);
end;
oldLen := length(oldName);
newLen := length(newName);
if (oldLen <= 0) or (newLen <= 0) then begin
raise MoveOperationException.create(oldName, newName, 'LocalFileSystem.move: ' + msgMoveError);
end;
oldLetter := oldName[1];
newLetter := newName[1];
oldCorrectName := (oldLen >= 2) and ((oldLetter >= 'A') and (oldLetter <= 'Z') or (oldLetter >= 'a') and (oldLetter <= 'z')) and isNameCorrect(oldName);
newCorrectName := (newLen >= 2) and ((newLetter >= 'A') and (newLetter <= 'Z') or (newLetter >= 'a') and (newLetter <= 'z')) and isNameCorrect(newName);
if oldName[oldLen] = DIRECTORY_SEPARATOR then begin
oldName := stringCopy(oldName, 1, oldLen);
dec(oldLen);
end;
if newName[newLen] = DIRECTORY_SEPARATOR then begin
newName := stringCopy(newName, 1, newLen);
dec(newLen);
end;
if not oldCorrectName or not newCorrectName or (oldLen = 2) or (newLen = 2) or not moveFileW(PWideChar(oldName), PWideChar(newName)) then begin
raise MoveOperationException.create(oldName, newName, 'LocalFileSystem.move: ' + msgMoveError);
end;
end;
procedure LocalFileSystem.readAttributes(const objectName: UnicodeString; objectAttr: FileAttributes);
var
name: UnicodeString;
find: UnicodeString;
letter: wchar;
len: int;
attributes: int;
availableBytes: long;
totalFreeBytes: long;
totalSizeInBytes: long;
correctName: boolean;
handle: THandle;
creationTime: TFileTime;
lastWriteTime: TFileTime;
lastAccessTime: TFileTime;
findData: TWin32FindDataW;
begin
name := stringReplace(objectName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise ObjectNotFoundException.create(name, 'LocalFileSystem.readAttributes: ' + msgObjectNotFound);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName then begin
raise ObjectNotFoundException.create(name, 'LocalFileSystem.readAttributes: ' + msgObjectNotFound);
end;
if len = 2 then begin
letter := charToUpperCase(letter);
find := letter + (':' + DIRECTORY_SEPARATOR);
if not getDiskFreeSpaceExW(PWideChar(find), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
raise ObjectNotFoundException.create(name, 'LocalFileSystem.readAttributes: ' + msgObjectNotFound);
end;
if objectAttr <> nil then begin
objectAttr.setAttributes(ATTR_DIRECTORY, LONG_MIN_VALUE, LONG_MIN_VALUE, LONG_MIN_VALUE);
end;
exit;
end;
attributes := int(getFileAttributesW(PWideChar(name)));
if attributes = -1 then begin
raise ObjectNotFoundException.create(name, 'LocalFileSystem.readAttributes: ' + msgObjectNotFound);
end;
initialize(findData);
handle := findFirstFileW(PWideChar(name), findData);
if handle = THandle(-1) then begin
raise FileSystemSecurityException.create(name, 'LocalFileSystem.readAttributes: ' + msgSecurity);
end;
try
creationTime := findData.ftCreationTime;
lastWriteTime := findData.ftLastWriteTime;
lastAccessTime := findData.ftLastAccessTime;
finally
findClose(handle);
end;
if objectAttr <> nil then begin
objectAttr.setAttributes(attributes, LocalFileEnumeration.getFileTime(creationTime), LocalFileEnumeration.getFileTime(lastWriteTime), LocalFileEnumeration.getFileTime(lastAccessTime));
end;
end;
procedure LocalFileSystem.writeAttributes(const objectName: UnicodeString; objectAttr: FileAttributes);
const
ATTR_REWRITEABLE = ATTR_READONLY or ATTR_HIDDEN or ATTR_SYSTEM or ATTR_ARCHIVE;
var
name: UnicodeString;
find: UnicodeString;
letter: wchar;
len: int;
attributes: int;
availableBytes: long;
totalFreeBytes: long;
totalSizeInBytes: long;
correctName: boolean;
handle: THandle;
creationTime: TFileTime;
lastWriteTime: TFileTime;
lastAccessTime: TFileTime;
begin
name := stringReplace(objectName, '/', DIRECTORY_SEPARATOR);
if stringStartsWith(wchar(DIRECTORY_SEPARATOR), name) then begin
name := stringCopy(name, 2);
end;
len := length(name);
if len <= 0 then begin
raise ObjectNotFoundException.create(name, 'LocalFileSystem.writeAttributes: ' + msgObjectNotFound);
end;
letter := name[1];
correctName := (len >= 2) and ((letter >= 'A') and (letter <= 'Z') or (letter >= 'a') and (letter <= 'z')) and isNameCorrect(name);
if name[len] = DIRECTORY_SEPARATOR then begin
name := stringCopy(name, 1, len);
dec(len);
end;
if not correctName then begin
raise ObjectNotFoundException.create(name, 'LocalFileSystem.writeAttributes: ' + msgObjectNotFound);
end;
if len = 2 then begin
letter := charToUpperCase(letter);
find := letter + (':' + DIRECTORY_SEPARATOR);
if not getDiskFreeSpaceExW(PWideChar(find), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
raise ObjectNotFoundException.create(name, 'LocalFileSystem.writeAttributes: ' + msgObjectNotFound);
end;
raise FileSystemSecurityException.create(name, 'LocalFileSystem.writeAttributes: ' + msgSecurity);
end;
attributes := int(getFileAttributesW(PWideChar(name)));
if attributes = -1 then begin
raise ObjectNotFoundException.create(name, 'LocalFileSystem.writeAttributes: ' + msgObjectNotFound);
end;
handle := createFileW(PWideChar(name), GENERIC_WRITE, 0, nil, OPEN_EXISTING, attributes or FILE_FLAG_BACKUP_SEMANTICS, 0);
if handle = THandle(-1) then begin
raise FileSystemSecurityException.create(name, 'LocalFileSystem.writeAttributes: ' + msgSecurity);
end;
try
if objectAttr = nil then begin
raise NullPointerException.create('LocalFileSystem.writeAttributes: ' + msgNullPointerArgument + 'objectAttr');
end;
initialize(creationTime);
initialize(lastWriteTime);
initialize(lastAccessTime);
LocalFileEnumeration.toFileTime(objectAttr.getCreationTime(), creationTime);
LocalFileEnumeration.toFileTime(objectAttr.getLastWriteTime(), lastWriteTime);
LocalFileEnumeration.toFileTime(objectAttr.getLastAccessTime(), lastAccessTime);
setFileTime(handle, @creationTime, @lastAccessTime, @lastWriteTime);
finally
closeHandle(handle);
end;
setFileAttributesW(PWideChar(name), (attributes and not ATTR_REWRITEABLE) or (objectAttr.getAttributes() and ATTR_REWRITEABLE));
end;
procedure LocalFileSystem.beforeDestruction();
begin
if not allowDestroy then begin
raise RuntimeException.create('Error 204 (LocalFileSystem.getInstance() is indestructible)');
end;
end;
{%endregion}
{%region HandleInputStream }
constructor HandleInputStream.create();
begin
inherited create();
self.handle := THandle(-1);
end;
constructor HandleInputStream.create(handle: THandle);
begin
inherited create();
self.handle := handle;
end;
function HandleInputStream.seekSupported(): boolean;
begin
result := true;
end;
function HandleInputStream.seek(delta: long): long;
var
poshi: int;
poslo: int;
handle: THandle;
begin
handle := self.handle;
if handle = THandle(-1) then begin
raise IOException.create('HandleInputStream.seek: ' + msgFileReadError);
end;
poshi := int(delta shr 32);
poslo := setFilePointer(handle, int(delta), @poshi, FILE_CURRENT);
result := (long(poshi) shl 32) or (long(poslo) and $00000000ffffffff);
end;
function HandleInputStream.reset(position: long): long;
var
poshi: int;
poslo: int;
handle: THandle;
begin
handle := self.handle;
if handle = THandle(-1) then begin
raise IOException.create('HandleInputStream.reset: ' + msgFileReadError);
end;
poshi := int(position shr 32);
poslo := setFilePointer(handle, int(position), @poshi, FILE_BEGIN);
result := (long(poshi) shl 32) or (long(poslo) and $00000000ffffffff);
end;
function HandleInputStream.position(): long;
var
poshi: int;
poslo: int;
handle: THandle;
begin
handle := self.handle;
if handle = THandle(-1) then begin
raise IOException.create('HandleInputStream.position: ' + msgFileReadError);
end;
poshi := 0;
poslo := setFilePointer(handle, 0, @poshi, FILE_CURRENT);
result := (long(poshi) shl 32) or (long(poslo) and $00000000ffffffff);
end;
function HandleInputStream.available(): long;
var
sizhi: int;
sizlo: int;
poshi: int;
poslo: int;
handle: THandle;
begin
handle := self.handle;
if handle = THandle(-1) then begin
raise IOException.create('HandleInputStream.available: ' + msgFileReadError);
end;
sizhi := 0;
sizlo := getFileSize(handle, @sizhi);
poshi := 0;
poslo := setFilePointer(handle, 0, @poshi, FILE_CURRENT);
result := ((long(sizhi) shl 32) or (long(sizlo) and $00000000ffffffff)) - ((long(poshi) shl 32) or (long(poslo) and $00000000ffffffff));
end;
function HandleInputStream.size(): long;
var
sizhi: int;
sizlo: int;
handle: THandle;
begin
handle := self.handle;
if handle = THandle(-1) then begin
raise IOException.create('HandleInputStream.size: ' + msgFileReadError);
end;
sizhi := 0;
sizlo := getFileSize(handle, @sizhi);
result := (long(sizhi) shl 32) or (long(sizlo) and $00000000ffffffff);
end;
function HandleInputStream.read(): int;
var
buffer: int;
readed: int;
begin
buffer := 0;
readed := 0;
if not readFile(handle, buffer, 1, System.UInt32(readed), nil) then begin
raise IOException.create('HandleInputStream.read: ' + msgFileReadError);
end;
if readed = 0 then begin
result := -1;
exit;
end;
result := buffer and $ff;
end;
function HandleInputStream.read(const dst: byte_Array1d; offset, length: int): int;
var
readed: int;
begin
arrayCheckBounds('HandleInputStream.read', System.length(dst), offset, length);
readed := 0;
if not readFile(handle, dst[offset], length, System.UInt32(readed), nil) then begin
raise IOException.create('HandleInputStream.read: ' + msgFileReadError);
end;
if (length > 0) and (readed = 0) then begin
result := -1;
exit;
end;
result := readed;
end;
{%endregion}
{%region HandleOutputStream }
constructor HandleOutputStream.create();
begin
inherited create();
self.handle := THandle(-1);
end;
constructor HandleOutputStream.create(handle: THandle);
begin
inherited create();
self.handle := handle;
end;
procedure HandleOutputStream.flush();
begin
if not flushFileBuffers(handle) then begin
raise IOException.create('HandleOutputStream.flush: ' + msgFileWriteError);
end;
end;
procedure HandleOutputStream.write(data: int);
var
written: int;
begin
written := 0;
if not writeFile(handle, data, 1, System.UInt32(written), nil) or (written <> 1) then begin
raise IOException.create('HandleOutputStream.write: ' + msgFileWriteError);
end;
end;
procedure HandleOutputStream.write(const src: byte_Array1d; offset, length: int);
var
written: int;
begin
arrayCheckBounds('HandleOutputStream.write', System.length(src), offset, length);
written := 0;
if not writeFile(handle, src[offset], length, System.UInt32(written), nil) or (written <> length) then begin
raise IOException.create('HandleOutputStream.write: ' + msgFileWriteError);
end;
end;
{%endregion}
{%region FileInputStream }
constructor FileInputStream.create(const fileName: AnsiString);
begin
create(stringToUTF16(fileName));
end;
constructor FileInputStream.create(const fileName: UnicodeString);
begin
inherited create();
self.handle := createFileW(PWideChar(fileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, getFileAttributesW(PWideChar(fileName)), 0);
self.fileName := fileName;
end;
function FileInputStream.getFileName(): UnicodeString;
begin
result := fileName;
end;
function FileInputStream.hasOpenError(): boolean;
begin
result := handle = THandle(-1);
end;
procedure FileInputStream.checkOpenError();
begin
if handle = THandle(-1) then begin
raise FileCreationException.create(fileName, 'FileInputStream.checkOpenError: ' + msgFileCreationError);
end;
end;
procedure FileInputStream.close();
begin
closeHandle(handle);
handle := THandle(-1);
inherited close();
end;
{%endregion}
{%region FileOutputStream }
constructor FileOutputStream.create(const fileName: AnsiString; appending: boolean);
begin
create(stringToUTF16(fileName), appending);
end;
constructor FileOutputStream.create(const fileName: UnicodeString; appending: boolean);
var
handle: THandle;
pos: long;
begin
inherited create();
if appending then begin
handle := createFileW(PWideChar(fileName), GENERIC_WRITE, 0, nil, OPEN_EXISTING, getFileAttributesW(PWideChar(fileName)), 0);
if handle <> THandle(-1) then begin
pos := 0;
setFilePointer(handle, int(pos), Pointer(@pos) + 4, FILE_END);
end;
end else begin
handle := createFileW(PWideChar(fileName), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, 0, 0);
end;
self.handle := handle;
self.fileName := fileName;
end;
function FileOutputStream.getFileName(): UnicodeString;
begin
result := fileName;
end;
function FileOutputStream.hasOpenError(): boolean;
begin
result := handle = THandle(-1);
end;
procedure FileOutputStream.checkOpenError();
begin
if handle = THandle(-1) then begin
raise FileCreationException.create(fileName, 'FileOutputStream.checkOpenError: ' + msgFileCreationError);
end;
end;
procedure FileOutputStream.close();
begin
closeHandle(handle);
handle := THandle(-1);
inherited close();
end;
{%endregion}
{%region FileIOStream }
constructor FileIOStream.create(const fileName: AnsiString);
begin
create(stringToUTF16(fileName));
end;
constructor FileIOStream.create(const fileName: UnicodeString);
var
handle: THandle;
instrm: HandleInputStream;
outstrm: HandleOutputStream;
begin
inherited create();
handle := createFileW(PWideChar(fileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, getFileAttributesW(PWideChar(fileName)), 0);
instrm := FileIOStream0HandleInputStream.create(handle);
outstrm := FileIOStream0HandleOutputStream.create(handle);
self.fileName := fileName;
self.inputPtr := instrm;
self.inputIntf := instrm;
self.outputPtr := outstrm;
self.outputIntf := outstrm;
end;
function FileIOStream.seekSupported(): boolean;
begin
result := true;
end;
function FileIOStream.truncateSupported(): boolean;
begin
result := true;
end;
function FileIOStream.seek(delta: long): long;
var
outstrm: FileIOStream0HandleOutputStream;
begin
outstrm := FileIOStream0HandleOutputStream(outputPtr);
outstrm.reset(outstrm.bufferedPosition + delta);
result := outstrm.position();
outstrm.bufferedPosition := result;
end;
function FileIOStream.reset(position: long): long;
var
outstrm: FileIOStream0HandleOutputStream;
begin
outstrm := FileIOStream0HandleOutputStream(outputPtr);
outstrm.reset(position);
result := outstrm.position();
outstrm.bufferedPosition := result;
end;
function FileIOStream.truncate(): long;
var
instrm: FileIOStream0HandleInputStream;
outstrm: FileIOStream0HandleOutputStream;
begin
instrm := FileIOStream0HandleInputStream(inputPtr);
outstrm := FileIOStream0HandleOutputStream(outputPtr);
outstrm.reset(outstrm.bufferedPosition);
result := outstrm.position();
outstrm.bufferedPosition := result;
if instrm.bufferedPosition > result then begin
instrm.bufferedPosition := result;
end;
setEndOfFile(instrm.handle);
end;
function FileIOStream.getInputStream(): InputStream;
begin
result := inputPtr;
end;
function FileIOStream.getOutputStream(): OutputStream;
begin
result := outputPtr;
end;
function FileIOStream.getFileName(): UnicodeString;
begin
result := fileName;
end;
function FileIOStream.hasOpenError(): boolean;
begin
result := inputPtr.handle = THandle(-1);
end;
procedure FileIOStream.checkOpenError();
begin
if inputPtr.handle = THandle(-1) then begin
raise FileCreationException.create(fileName, 'FileIOStream.checkOpenError: ' + msgFileCreationError);
end;
end;
procedure FileIOStream.close();
begin
closeHandle(inputPtr.handle);
inputPtr.handle := THandle(-1);
outputPtr.handle := THandle(-1);
inherited close();
end;
{%endregion}
{%region LocalDiskInfo }
constructor LocalDiskInfo.create(letter: wchar);
begin
inherited create(ATTR_DIRECTORY, LONG_MIN_VALUE, LONG_MIN_VALUE, LONG_MIN_VALUE, 0, letter + ':');
end;
function LocalDiskInfo.findNext(): boolean;
begin
result := false;
end;
{%endregion}
{%region LocalDiskEnumeration }
constructor LocalDiskEnumeration.create(letter: wchar);
begin
inherited create(letter);
self.letter := letter;
end;
function LocalDiskEnumeration.findNext(): boolean;
var
letter: wchar;
availableBytes: long;
totalFreeBytes: long;
totalSizeInBytes: long;
find: UnicodeString;
begin
for letter := wchar(int(self.letter) + 1) to 'Z' do begin
find := letter + (':' + DIRECTORY_SEPARATOR);
if getDiskFreeSpaceExW(PWideChar(find), @availableBytes, @totalSizeInBytes, @totalFreeBytes) then begin
setAttributes(ATTR_DIRECTORY, LONG_MIN_VALUE, LONG_MIN_VALUE, LONG_MIN_VALUE, 0, letter + ':');
self.letter := letter;
result := true;
exit;
end;
end;
result := false;
end;
{%endregion}
{%region LocalFileEnumeration }
class function LocalFileEnumeration.getFileTime(const fileTime: TFileTime): long;
var
clnd: Calendar;
time: TSystemTime;
begin
initialize(time);
if not fileTimeToSystemTime(fileTime, time) then begin
result := LONG_MIN_VALUE;
exit;
end;
clnd := GregorianCalendar.create();
clnd.offset := 0;
clnd.year := time.year;
clnd.month := time.month;
clnd.day := time.day;
clnd.hour := time.hour;
clnd.minute := time.minute;
clnd.second := time.second;
clnd.millisecond := time.millisecond;
result := clnd.time;
end;
class function LocalFileEnumeration.getFileSize(const findData: TWin32FindDataW): long;
begin
result := long(findData.nFileSizeLow) or (long(findData.nFileSizeHigh) shl 32);
end;
class function LocalFileEnumeration.getFileName(const findData: TWin32FindDataW): UnicodeString;
begin
result := UnicodeString(PWideChar(@findData.cFileName));
end;
class procedure LocalFileEnumeration.toFileTime(const timeInMillis: long; var fileTime: TFileTime);
var
clnd: Calendar;
time: TSystemTime;
begin
fileTime.dwLowDateTime := 0;
fileTime.dwHighDateTime := 0;
clnd := GregorianCalendar.create();
clnd.offset := 0;
if timeInMillis < clnd.epochStart then exit;
clnd.time := timeInMillis;
time.year := System.UInt16(clnd.year);
time.month := System.UInt16(clnd.month);
time.dayOfWeek := System.UInt16(clnd.weekday);
time.day := System.UInt16(clnd.day);
time.hour := System.UInt16(clnd.hour);
time.minute := System.UInt16(clnd.minute);
time.second := System.UInt16(clnd.second);
time.millisecond := System.UInt16(clnd.millisecond);
systemTimeToFileTime(time, fileTime);
end;
constructor LocalFileEnumeration.create(handle: THandle; const findData: TWin32FindDataW);
begin
inherited create(int(findData.dwFileAttributes), getFileTime(findData.ftCreationTime), getFileTime(findData.ftLastWriteTime), getFileTime(findData.ftLastAccessTime), getFileSize(findData), getFileName(findData));
self.handle := handle;
self.findData := findData;
end;
function LocalFileEnumeration.findNext(): boolean;
var
handle: THandle;
findData: PWin32FindDataW;
found: UnicodeString;
begin
handle := self.handle;
findData := @self.findData;
while findNextFilew(handle, findData) do begin
found := getFileName(findData^);
if (found <> '.') and (found <> '..') then begin
setAttributes(int(findData.dwFileAttributes), getFileTime(findData.ftCreationTime), getFileTime(findData.ftLastWriteTime), getFileTime(findData.ftLastAccessTime), getFileSize(findData^), found);
result := true;
exit;
end;
end;
result := false;
end;
procedure LocalFileEnumeration.close();
begin
findClose(handle);
inherited close();
end;
{%endregion}
{%region PositionHandleInputStream }
function FileIOStream0HandleInputStream.seek(delta: long): long;
begin
inherited reset(bufferedPosition);
result := inherited seek(delta);
bufferedPosition := result;
end;
function FileIOStream0HandleInputStream.reset(position: long): long;
begin
result := inherited reset(position);
bufferedPosition := result;
end;
function FileIOStream0HandleInputStream.position(): long;
begin
result := bufferedPosition;
end;
function FileIOStream0HandleInputStream.available(): long;
begin
inherited reset(bufferedPosition);
result := inherited available();
end;
function FileIOStream0HandleInputStream.read(): int;
begin
inherited reset(bufferedPosition);
result := inherited read();
bufferedPosition := inherited position();
end;
function FileIOStream0HandleInputStream.read(const dst: byte_Array1d; offset, length: int): int;
begin
inherited reset(bufferedPosition);
result := inherited read(dst, offset, length);
bufferedPosition := inherited position();
end;
{%endregion}
{%region PositionHandleOutputStream }
function FileIOStream0HandleOutputStream.position(): long;
var
handle: THandle;
poshi: int;
poslo: int;
begin
handle := self.handle;
if handle = THandle(-1) then begin
raise IOException.create('HandleOutputStream: ' + msgFileWriteError);
end;
poshi := 0;
poslo := setFilePointer(handle, 0, @poshi, FILE_CURRENT);
result := (long(poshi) shl 32) or (long(poslo) and $00000000ffffffff);
end;
procedure FileIOStream0HandleOutputStream.reset(position: long);
var
handle: THandle;
poshi: int;
begin
handle := self.handle;
if handle = THandle(-1) then begin
raise IOException.create('HandleOutputStream: ' + msgFileWriteError);
end;
poshi := int(position shr 32);
setFilePointer(handle, int(position), @poshi, FILE_BEGIN);
end;
procedure FileIOStream0HandleOutputStream.write(data: int);
begin
reset(bufferedPosition);
inherited write(data);
bufferedPosition := position();
end;
procedure FileIOStream0HandleOutputStream.write(const src: byte_Array1d; offset, length: int);
begin
reset(bufferedPosition);
inherited write(src, offset, length);
bufferedPosition := position();
end;
{%endregion}
initialization
LocalFileSystem.instance := LocalFileSystem.create();
LocalFileSystem.instance.allowDestroy := false;
classInfoAdd([
typeInfo(MemoryRegion)
]);
finalization
LocalFileSystem.instance.allowDestroy := true;
LocalFileSystem.instance.free();
end.