mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-08 15:28:00 +02:00
1284 lines
37 KiB
ObjectPascal
1284 lines
37 KiB
ObjectPascal
{
|
|
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2010 by Sven Barth
|
|
member of the Free Pascal development team
|
|
|
|
Sysutils unit for NativeNT
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
{$IFNDEF FPC_DOTTEDUNITS}
|
|
unit sysutils;
|
|
{$ENDIF FPC_DOTTEDUNITS}
|
|
interface
|
|
|
|
{$MODE objfpc}
|
|
{$MODESWITCH OUT}
|
|
{$IFDEF UNICODERTL}
|
|
{$MODESWITCH UNICODESTRINGS}
|
|
{$ELSE}
|
|
{$H+}
|
|
{$ENDIF}
|
|
{$modeswitch typehelpers}
|
|
{$modeswitch advancedrecords}
|
|
|
|
{$IFDEF FPC_DOTTEDUNITS}
|
|
uses
|
|
NTApi.NDK;
|
|
{$ELSE FPC_DOTTEDUNITS}
|
|
uses
|
|
ndk;
|
|
{$ENDIF FPC_DOTTEDUNITS}
|
|
|
|
{$DEFINE HAS_SLEEP}
|
|
{$DEFINE HAS_CREATEGUID}
|
|
|
|
type
|
|
TNativeNTFindData = record
|
|
SearchSpec: UnicodeString;
|
|
NamePos: LongInt;
|
|
Handle: THandle;
|
|
IsDirObj: Boolean;
|
|
SearchAttr: LongInt;
|
|
Context: ULONG;
|
|
LastRes: NTSTATUS;
|
|
end;
|
|
|
|
{ used OS file system APIs use ansistring }
|
|
{$define SYSUTILS_HAS_UNICODESTR_FILEUTIL_IMPL}
|
|
{ OS has an ansistring/single byte environment variable API (actually it's
|
|
unicodestring, but that's not yet implemented) }
|
|
{$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL}
|
|
|
|
{ Include platform independent interface part }
|
|
{$i sysutilh.inc}
|
|
|
|
implementation
|
|
|
|
{$IFDEF FPC_DOTTEDUNITS}
|
|
uses
|
|
System.SysConst, NTApi.NDKUtils;
|
|
{$ELSE FPC_DOTTEDUNITS}
|
|
uses
|
|
sysconst, ndkutils;
|
|
{$ENDIF FPC_DOTTEDUNITS}
|
|
|
|
{$DEFINE FPC_NOGENERICANSIROUTINES}
|
|
|
|
{ Include platform independent implementation part }
|
|
{$i sysutils.inc}
|
|
|
|
{****************************************************************************
|
|
File Functions
|
|
****************************************************************************}
|
|
|
|
function FileOpen(const FileName : UnicodeString; Mode : Integer) : THandle;
|
|
const
|
|
AccessMode: array[0..2] of ACCESS_MASK = (
|
|
GENERIC_READ,
|
|
GENERIC_WRITE,
|
|
GENERIC_READ or GENERIC_WRITE);
|
|
ShareMode: array[0..4] of ULONG = (
|
|
0,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_SHARE_WRITE,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE);
|
|
var
|
|
ntstr: UNICODE_STRING;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
begin
|
|
UnicodeStrToNtStr(FileName, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
NtCreateFile(@Result, AccessMode[Mode and 3] or NT_SYNCHRONIZE, @objattr,
|
|
@iostatus, Nil, FILE_ATTRIBUTE_NORMAL, ShareMode[(Mode and $F0) shr 4],
|
|
FILE_OPEN, FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0);
|
|
FreeNtStr(ntstr);
|
|
end;
|
|
|
|
|
|
function FileCreate(const FileName : UnicodeString) : THandle;
|
|
begin
|
|
FileCreate := FileCreate(FileName, fmShareDenyNone, 0);
|
|
end;
|
|
|
|
|
|
function FileCreate(const FileName : UnicodeString; Rights: longint) : THandle;
|
|
begin
|
|
FileCreate := FileCreate(FileName, fmShareDenyNone, Rights);
|
|
end;
|
|
|
|
|
|
function FileCreate(const FileName : UnicodeString; ShareMode : longint; Rights: longint) : THandle;
|
|
const
|
|
ShareModeFlags: array[0..4] of ULONG = (
|
|
0,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_SHARE_WRITE,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE);
|
|
var
|
|
ntstr: UNICODE_STRING;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
res: NTSTATUS;
|
|
begin
|
|
UnicodeStrToNtStr(FileName, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
NtCreateFile(@Result, GENERIC_READ or GENERIC_WRITE or NT_SYNCHRONIZE,
|
|
@objattr, @iostatus, Nil, FILE_ATTRIBUTE_NORMAL,
|
|
ShareModeFlags[(ShareMode and $F0) shr 4], FILE_OVERWRITE_IF,
|
|
FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0);
|
|
FreeNtStr(ntstr);
|
|
end;
|
|
|
|
|
|
function FileRead(Handle : THandle; out Buffer; Count : longint) : Longint;
|
|
var
|
|
iostatus: IO_STATUS_BLOCK;
|
|
res: NTSTATUS;
|
|
begin
|
|
res := NtReadFile(Handle, 0, Nil, Nil, @iostatus, @Buffer, Count, Nil, Nil);
|
|
|
|
if res = STATUS_PENDING then begin
|
|
res := NtWaitForSingleObject(Handle, False, Nil);
|
|
if NT_SUCCESS(res) then
|
|
res := iostatus.union1.Status;
|
|
end;
|
|
|
|
if NT_SUCCESS(res) then
|
|
Result := LongInt(iostatus.Information)
|
|
else
|
|
Result := -1;
|
|
end;
|
|
|
|
|
|
function FileWrite(Handle : THandle; const Buffer; Count : Longint) : Longint;
|
|
var
|
|
iostatus: IO_STATUS_BLOCK;
|
|
res: NTSTATUS;
|
|
begin
|
|
res := NtWriteFile(Handle, 0, Nil, Nil, @iostatus, @Buffer, Count, Nil,
|
|
Nil);
|
|
|
|
if res = STATUS_PENDING then begin
|
|
res := NtWaitForSingleObject(Handle, False, Nil);
|
|
if NT_SUCCESS(res) then
|
|
res := iostatus.union1.Status;
|
|
end;
|
|
|
|
if NT_SUCCESS(res) then
|
|
Result := LongInt(iostatus.Information)
|
|
else
|
|
Result := -1;
|
|
end;
|
|
|
|
|
|
function FileSeek(Handle : THandle;FOffset,Origin : Longint) : Longint;
|
|
begin
|
|
Result := longint(FileSeek(Handle, Int64(FOffset), Origin));
|
|
end;
|
|
|
|
|
|
function FileSeek(Handle : THandle; FOffset: Int64; Origin: Longint) : Int64;
|
|
const
|
|
ErrorCode = $FFFFFFFFFFFFFFFF;
|
|
var
|
|
position: FILE_POSITION_INFORMATION;
|
|
standard: FILE_STANDARD_INFORMATION;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
res: NTSTATUS;
|
|
begin
|
|
{ determine the new position }
|
|
case Origin of
|
|
fsFromBeginning:
|
|
position.CurrentByteOffset.QuadPart := FOffset;
|
|
fsFromCurrent: begin
|
|
res := NtQueryInformationFile(Handle, @iostatus, @position,
|
|
SizeOf(FILE_POSITION_INFORMATION), FilePositionInformation);
|
|
if res < 0 then begin
|
|
Result := ErrorCode;
|
|
Exit;
|
|
end;
|
|
position.CurrentByteOffset.QuadPart :=
|
|
position.CurrentByteOffset.QuadPart + FOffset;
|
|
end;
|
|
fsFromEnd: begin
|
|
res := NtQueryInformationFile(Handle, @iostatus, @standard,
|
|
SizeOf(FILE_STANDARD_INFORMATION), FileStandardInformation);
|
|
if res < 0 then begin
|
|
Result := ErrorCode;
|
|
Exit;
|
|
end;
|
|
position.CurrentByteOffset.QuadPart := standard.EndOfFile.QuadPart +
|
|
FOffset;
|
|
end;
|
|
else begin
|
|
Result := ErrorCode;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
{ set the new position }
|
|
res := NtSetInformationFile(Handle, @iostatus, @position,
|
|
SizeOf(FILE_POSITION_INFORMATION), FilePositionInformation);
|
|
if res < 0 then
|
|
Result := ErrorCode
|
|
else
|
|
Result := position.CurrentByteOffset.QuadPart;
|
|
end;
|
|
|
|
|
|
procedure FileClose(Handle : THandle);
|
|
begin
|
|
NtClose(Handle);
|
|
end;
|
|
|
|
|
|
function FileTruncate(Handle : THandle;Size: Int64) : boolean;
|
|
var
|
|
endoffileinfo: FILE_END_OF_FILE_INFORMATION;
|
|
allocinfo: FILE_ALLOCATION_INFORMATION;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
res: NTSTATUS;
|
|
begin
|
|
// based on ReactOS' SetEndOfFile
|
|
endoffileinfo.EndOfFile.QuadPart := Size;
|
|
res := NtSetInformationFile(Handle, @iostatus, @endoffileinfo,
|
|
SizeOf(FILE_END_OF_FILE_INFORMATION), FileEndOfFileInformation);
|
|
if NT_SUCCESS(res) then begin
|
|
allocinfo.AllocationSize.QuadPart := Size;
|
|
res := NtSetInformationFile(handle, @iostatus, @allocinfo,
|
|
SizeOf(FILE_ALLOCATION_INFORMATION), FileAllocationInformation);
|
|
Result := NT_SUCCESS(res);
|
|
end else
|
|
Result := False;
|
|
end;
|
|
|
|
function NTToDosTime(const NtTime: LARGE_INTEGER): LongInt;
|
|
var
|
|
userdata: PKUSER_SHARED_DATA;
|
|
local, bias: LARGE_INTEGER;
|
|
fields: TIME_FIELDS;
|
|
zs: LongInt;
|
|
begin
|
|
userdata := SharedUserData;
|
|
repeat
|
|
bias.u.HighPart := userdata^.TimeZoneBias.High1Time;
|
|
bias.u.LowPart := userdata^.TimeZoneBias.LowPart;
|
|
until bias.u.HighPart = userdata^.TimeZoneBias.High2Time;
|
|
|
|
local.QuadPart := NtTime.QuadPart - bias.QuadPart;
|
|
|
|
RtlTimeToTimeFields(@local, @fields);
|
|
|
|
{ from objpas\datutil.inc\DateTimeToDosDateTime }
|
|
Result := - 1980;
|
|
Result := Result + fields.Year and 127;
|
|
Result := Result shl 4;
|
|
Result := Result + fields.Month;
|
|
Result := Result shl 5;
|
|
Result := Result + fields.Day;
|
|
Result := Result shl 16;
|
|
zs := fields.Hour;
|
|
zs := zs shl 6;
|
|
zs := zs + fields.Minute;
|
|
zs := zs shl 5;
|
|
zs := zs + fields.Second div 2;
|
|
Result := Result + (zs and $ffff);
|
|
end;
|
|
|
|
function DosToNtTime(aDTime: LongInt; var aNtTime: LARGE_INTEGER): Boolean;
|
|
var
|
|
fields: TIME_FIELDS;
|
|
local, bias: LARGE_INTEGER;
|
|
userdata: PKUSER_SHARED_DATA;
|
|
begin
|
|
{ from objpas\datutil.inc\DosDateTimeToDateTime }
|
|
fields.Second := (aDTime and 31) * 2;
|
|
aDTime := aDTime shr 5;
|
|
fields.Minute := aDTime and 63;
|
|
aDTime := aDTime shr 6;
|
|
fields.Hour := aDTime and 31;
|
|
aDTime := aDTime shr 5;
|
|
fields.Day := aDTime and 31;
|
|
aDTime := aDTime shr 5;
|
|
fields.Month := aDTime and 15;
|
|
aDTime := aDTime shr 4;
|
|
fields.Year := aDTime + 1980;
|
|
|
|
Result := RtlTimeFieldsToTime(@fields, @local);
|
|
if not Result then
|
|
Exit;
|
|
|
|
userdata := SharedUserData;
|
|
repeat
|
|
bias.u.HighPart := userdata^.TimeZoneBias.High1Time;
|
|
bias.u.LowPart := userdata^.TimeZoneBias.LowPart;
|
|
until bias.u.HighPart = userdata^.TimeZoneBias.High2Time;
|
|
|
|
aNtTime.QuadPart := local.QuadPart + bias.QuadPart;
|
|
end;
|
|
|
|
function FileAge(const FileName: UnicodeString): Int64;
|
|
begin
|
|
{ TODO }
|
|
Result := -1;
|
|
end;
|
|
|
|
|
|
function FileGetSymLinkTarget(const FileName: UnicodeString; out SymLinkRec: TUnicodeSymLinkRec): Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
|
|
function FileExists(const FileName: UnicodeString; FollowLink : Boolean): Boolean;
|
|
var
|
|
ntstr: UNICODE_STRING;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
res: NTSTATUS;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
h: THandle;
|
|
begin
|
|
UnicodeStrToNtStr(FileName, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
|
|
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
|
|
Result := NT_SUCCESS(res);
|
|
|
|
if Result then
|
|
NtClose(h);
|
|
FreeNtStr(ntstr);
|
|
end;
|
|
|
|
|
|
function DirectoryExists(const Directory : UnicodeString; FollowLink : Boolean) : Boolean;
|
|
var
|
|
ntstr: UNICODE_STRING;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
res: NTSTATUS;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
h: THandle;
|
|
begin
|
|
UnicodeStrToNtStr(Directory, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
|
|
{ first test wether this is a object directory }
|
|
res := NtOpenDirectoryObject(@h, DIRECTORY_QUERY, @objattr);
|
|
if NT_SUCCESS(res) then
|
|
Result := True
|
|
else begin
|
|
if res = STATUS_OBJECT_TYPE_MISMATCH then begin
|
|
{ this is a file object! }
|
|
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
|
|
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
|
|
Result := NT_SUCCESS(res);
|
|
end else
|
|
Result := False;
|
|
end;
|
|
|
|
if Result then
|
|
NtClose(h);
|
|
FreeNtStr(ntstr);
|
|
end;
|
|
|
|
{ copied from rtl/unix/sysutils.pp and adapted to UTF-16 }
|
|
Function FNMatch(const Pattern,Name:UnicodeString):Boolean;
|
|
Var
|
|
LenPat,LenName : longint;
|
|
|
|
function NameUtf16CodePointLen(index: longint): longint;
|
|
begin
|
|
{ see https://en.wikipedia.org/wiki/UTF-16#Description for details }
|
|
Result:=1;
|
|
{ valid surrogate pair? }
|
|
if (Name[index]>=#$D800) and
|
|
(Name[index]<=#$DBFF) then
|
|
begin
|
|
if (index+1<=LenName) and
|
|
(Name[index+1]>=#$DC00) and
|
|
(Name[index+1]<=#$DFFF) then
|
|
inc(Result)
|
|
else
|
|
exit;
|
|
end;
|
|
{ combining diacritics?
|
|
1) U+0300 - U+036F
|
|
2) U+1DC0 - U+1DFF
|
|
3) U+20D0 - U+20FF
|
|
4) U+FE20 - U+FE2F
|
|
}
|
|
while (index+Result+1<=LenName) and
|
|
((word(ord(Name[index+Result+1])-$0300) <= word($036F-$0300)) or
|
|
(word(ord(Name[index+Result+1])-$1DC0) <= word($1DFF-$1DC0)) or
|
|
(word(ord(Name[index+Result+1])-$20D0) <= word($20FF-$20D0)) or
|
|
(word(ord(Name[index+Result+1])-$FE20) <= word($FE2F-$FE20))) do
|
|
begin
|
|
inc(Result)
|
|
end;
|
|
end;
|
|
|
|
procedure GoToLastByteOfUtf16CodePoint(var j: longint);
|
|
begin
|
|
{ Take one less, because we have to stop at the last word of the sequence.
|
|
}
|
|
inc(j,NameUtf16CodePointLen(j)-1);
|
|
end;
|
|
|
|
{ input:
|
|
i: current position in pattern (start of utf-16 code point)
|
|
j: current position in name (start of utf-16 code point)
|
|
update_i_j: should i and j be changed by the routine or not
|
|
|
|
output:
|
|
i: if update_i_j, then position of last matching part of code point in
|
|
pattern, or first non-matching code point in pattern. Otherwise the
|
|
same value as on input.
|
|
j: if update_i_j, then position of last matching part of code point in
|
|
name, or first non-matching code point in name. Otherwise the
|
|
same value as on input.
|
|
result: true if match, false if no match
|
|
}
|
|
function CompareUtf16CodePoint(var i,j: longint; update_i_j: boolean): Boolean;
|
|
var
|
|
words,
|
|
new_i,
|
|
new_j: longint;
|
|
begin
|
|
words:=NameUtf16CodePointLen(j);
|
|
new_i:=i;
|
|
new_j:=j;
|
|
{ ensure that a part of an UTF-8 codepoint isn't interpreted
|
|
as '*' or '?' }
|
|
repeat
|
|
dec(words);
|
|
Result:=
|
|
(new_j<=LenName) and
|
|
(new_i<=LenPat) and
|
|
(Pattern[new_i]=Name[new_j]);
|
|
inc(new_i);
|
|
inc(new_j);
|
|
until not(Result) or
|
|
(words=0);
|
|
if update_i_j then
|
|
begin
|
|
i:=new_i;
|
|
j:=new_j;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function DoFNMatch(i,j:longint):Boolean;
|
|
Var
|
|
Found : boolean;
|
|
Begin
|
|
Found:=true;
|
|
While Found and (i<=LenPat) Do
|
|
Begin
|
|
Case Pattern[i] of
|
|
'?' :
|
|
begin
|
|
Found:=(j<=LenName);
|
|
GoToLastByteOfUtf16CodePoint(j);
|
|
end;
|
|
'*' : Begin
|
|
{find the next character in pattern, different of ? and *}
|
|
while Found do
|
|
begin
|
|
inc(i);
|
|
if i>LenPat then
|
|
Break;
|
|
case Pattern[i] of
|
|
'*' : ;
|
|
'?' : begin
|
|
if j>LenName then
|
|
begin
|
|
DoFNMatch:=false;
|
|
Exit;
|
|
end;
|
|
GoToLastByteOfUtf16CodePoint(j);
|
|
inc(j);
|
|
end;
|
|
else
|
|
Found:=false;
|
|
end;
|
|
end;
|
|
Assert((i>LenPat) or ( (Pattern[i]<>'*') and (Pattern[i]<>'?') ));
|
|
{ Now, find in name the character which i points to, if the * or
|
|
? wasn't the last character in the pattern, else, use up all
|
|
the chars in name }
|
|
Found:=false;
|
|
if (i<=LenPat) then
|
|
begin
|
|
repeat
|
|
{find a letter (not only first !) which maches pattern[i]}
|
|
while (j<=LenName) and
|
|
((name[j]<>pattern[i]) or
|
|
not CompareUtf16CodePoint(i,j,false)) do
|
|
begin
|
|
GoToLastByteOfUtf16CodePoint(j);
|
|
inc(j);
|
|
end;
|
|
if (j<LenName) then
|
|
begin
|
|
{ while positions i/j have already been checked, we have to
|
|
ensure that we don't split a code point }
|
|
if DoFnMatch(i,j) then
|
|
begin
|
|
i:=LenPat;
|
|
j:=LenName;{we can stop}
|
|
Found:=true;
|
|
Break;
|
|
end
|
|
{ We didn't find one, need to look further }
|
|
else
|
|
begin
|
|
GoToLastByteOfUtf16CodePoint(j);
|
|
inc(j);
|
|
end;
|
|
end
|
|
else if j=LenName then
|
|
begin
|
|
Found:=true;
|
|
Break;
|
|
end;
|
|
{ This 'until' condition must be j>LenName, not j>=LenName.
|
|
That's because when we 'need to look further' and
|
|
j = LenName then loop must not terminate. }
|
|
until (j>LenName);
|
|
end
|
|
else
|
|
begin
|
|
j:=LenName;{we can stop}
|
|
Found:=true;
|
|
end;
|
|
end;
|
|
#$D800..#$DBFF:
|
|
begin
|
|
{ ensure that a part of an UTF-16 codepoint isn't matched with
|
|
'*' or '?' }
|
|
Found:=CompareUtf16CodePoint(i,j,true);
|
|
{ at this point, either Found is false (and we'll stop), or
|
|
both pattern[i] and name[j] are the end of the current code
|
|
point and equal }
|
|
end
|
|
else {not a wildcard character in pattern}
|
|
Found:=(j<=LenName) and (pattern[i]=name[j]);
|
|
end;
|
|
inc(i);
|
|
inc(j);
|
|
end;
|
|
DoFnMatch:=Found and (j>LenName);
|
|
end;
|
|
|
|
Begin {start FNMatch}
|
|
LenPat:=Length(Pattern);
|
|
LenName:=Length(Name);
|
|
FNMatch:=DoFNMatch(1,1);
|
|
End;
|
|
|
|
|
|
function FindGetFileInfo(const s: UnicodeString; var f: TAbstractSearchRec; var Name: UnicodeString): Boolean;
|
|
var
|
|
ntstr: UNICODE_STRING;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
res: NTSTATUS;
|
|
h: THandle;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
attr: LongInt;
|
|
filename: UnicodeString;
|
|
isfileobj: Boolean;
|
|
objinfo: OBJECT_BASIC_INFORMATION;
|
|
fileinfo: FILE_BASIC_INFORMATION;
|
|
time: LongInt;
|
|
begin
|
|
UnicodeStrToNtStr(s, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
|
|
filename := ExtractFileName(s);
|
|
|
|
{ TODO : handle symlinks }
|
|
{ If Assigned(F.FindHandle) and ((((PUnixFindData(f.FindHandle)^.searchattr)) and faSymlink) > 0) then
|
|
FindGetFileInfo:=(fplstat(pointer(s),st)=0)
|
|
else
|
|
FindGetFileInfo:=(fpstat(pointer(s),st)=0);}
|
|
|
|
attr := 0;
|
|
Result := False;
|
|
|
|
if (faDirectory and f.FindData.SearchAttr <> 0) and
|
|
((filename = '.') or (filename = '..')) then begin
|
|
attr := faDirectory;
|
|
res := STATUS_SUCCESS;
|
|
end else
|
|
res := STATUS_INVALID_PARAMETER;
|
|
|
|
isfileobj := False;
|
|
|
|
if not NT_SUCCESS(res) then begin
|
|
{ first check whether it's a directory }
|
|
res := NtOpenDirectoryObject(@h, DIRECTORY_QUERY, @objattr);
|
|
if not NT_SUCCESS(res) then
|
|
if res = STATUS_OBJECT_TYPE_MISMATCH then begin
|
|
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
|
|
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
|
|
isfileobj := NT_SUCCESS(res);
|
|
end;
|
|
|
|
if NT_SUCCESS(res) then
|
|
attr := faDirectory;
|
|
end;
|
|
|
|
if not NT_SUCCESS(res) then begin
|
|
{ first try whether we have a file object }
|
|
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
|
|
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
|
|
isfileobj := NT_SUCCESS(res);
|
|
if res = STATUS_OBJECT_TYPE_MISMATCH then begin
|
|
{ is this an object? }
|
|
res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr,
|
|
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (res = STATUS_OBJECT_TYPE_MISMATCH)
|
|
and (f.FindData.SearchAttr and faSysFile <> 0) then begin
|
|
{ this is some other system file like an event or port, so we can only
|
|
provide it's name }
|
|
res := STATUS_SUCCESS;
|
|
attr := faSysFile;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
FreeNtStr(ntstr);
|
|
|
|
if not NT_SUCCESS(res) then
|
|
Exit;
|
|
|
|
time := 0;
|
|
|
|
if isfileobj then begin
|
|
res := NtQueryInformationFile(h, @iostatus, @fileinfo, SizeOf(fileinfo),
|
|
FileBasicInformation);
|
|
if NT_SUCCESS(res) then begin
|
|
time := NtToDosTime(fileinfo.LastWriteTime);
|
|
{ copy file attributes? }
|
|
end;
|
|
end else begin
|
|
res := NtQueryObject(h, ObjectBasicInformation, @objinfo, SizeOf(objinfo),
|
|
Nil);
|
|
if NT_SUCCESS(res) then begin
|
|
time := NtToDosTime(objinfo.CreateTime);
|
|
{ what about attributes? }
|
|
end;
|
|
end;
|
|
|
|
if (attr and not f.FindData.SearchAttr) = 0 then begin
|
|
Name := filename;
|
|
f.Attr := attr;
|
|
f.Size := 0;
|
|
{$ifndef FPUNONE}
|
|
if time = 0 then
|
|
{ for now we use "Now" as a fall back; ideally this should be the system
|
|
start time }
|
|
f.Time := DateTimeToFileDate(Now)
|
|
else
|
|
f.Time := time;
|
|
{$endif}
|
|
Result := True;
|
|
end else
|
|
Result := False;
|
|
|
|
NtClose(h);
|
|
end;
|
|
|
|
|
|
Procedure InternalFindClose (var Handle: THandle; var FindData: TFindData);
|
|
begin
|
|
if FindData.Handle <> 0 then
|
|
begin
|
|
NtClose(FindData.Handle);
|
|
FindData.Handle:=0;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function InternalFindNext (Var Rslt : TAbstractSearchRec; var Name: UnicodeString) : Longint;
|
|
{
|
|
re-opens dir if not already in array and calls FindGetFileInfo
|
|
}
|
|
Var
|
|
DirName : UnicodeString;
|
|
FName,
|
|
SName : UnicodeString;
|
|
Found,
|
|
Finished : boolean;
|
|
ntstr: UNICODE_STRING;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
buf: array of WideChar;
|
|
len: LongWord;
|
|
res: NTSTATUS;
|
|
i: LongInt;
|
|
dirinfo: POBJECT_DIRECTORY_INFORMATION;
|
|
filedirinfo: PFILE_DIRECTORY_INFORMATION;
|
|
pc: PAnsiChar;
|
|
filename: UnicodeString;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
begin
|
|
{ TODO : relative directories }
|
|
Result := -1;
|
|
{ SearchSpec='' means that there were no wild cards, so only one file to
|
|
find.
|
|
}
|
|
if Rslt.FindData.SearchSpec = '' then
|
|
Exit;
|
|
{ relative directories not supported for now }
|
|
if Rslt.FindData.NamePos = 0 then
|
|
Exit;
|
|
|
|
if Rslt.FindData.Handle = 0 then begin
|
|
if Rslt.FindData.NamePos > 1 then
|
|
filename := Copy(Rslt.FindData.SearchSpec, 1, Rslt.FindData.NamePos - 1)
|
|
else
|
|
if Rslt.FindData.NamePos = 1 then
|
|
filename := Copy(Rslt.FindData.SearchSpec, 1, 1)
|
|
else
|
|
filename := Rslt.FindData.SearchSpec;
|
|
UnicodeStrToNtStr(filename, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
|
|
res := NtOpenDirectoryObject(@Rslt.FindData.Handle,
|
|
DIRECTORY_QUERY or DIRECTORY_TRAVERSE, @objattr);
|
|
if not NT_SUCCESS(res) then begin
|
|
if res = STATUS_OBJECT_TYPE_MISMATCH then
|
|
res := NtOpenFile(@Rslt.FindData.Handle,
|
|
FILE_LIST_DIRECTORY or NT_SYNCHRONIZE, @objattr,
|
|
@iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT);
|
|
end else
|
|
Rslt.FindData.IsDirObj := True;
|
|
|
|
FreeNTStr(ntstr);
|
|
|
|
if not NT_SUCCESS(res) then
|
|
Exit;
|
|
end;
|
|
{ if (NTFindData^.SearchType = 0) and
|
|
(NTFindData^.Dirptr = Nil) then
|
|
begin
|
|
If NTFindData^.NamePos = 0 Then
|
|
DirName:='./'
|
|
Else
|
|
DirName:=Copy(NTFindData^.SearchSpec,1,NTFindData^.NamePos);
|
|
NTFindData^.DirPtr := fpopendir(PAnsiChar(pointer(DirName)));
|
|
end;}
|
|
SName := Copy(Rslt.FindData.SearchSpec, Rslt.FindData.NamePos + 1,
|
|
Length(Rslt.FindData.SearchSpec));
|
|
Found := False;
|
|
Finished := not NT_SUCCESS(Rslt.FindData.LastRes)
|
|
or (Rslt.FindData.LastRes = STATUS_NO_MORE_ENTRIES);
|
|
SetLength(buf, 200);
|
|
dirinfo := @buf[0];
|
|
filedirinfo := @buf[0];
|
|
while not Finished do begin
|
|
if Rslt.FindData.IsDirObj then
|
|
res := NtQueryDirectoryObject(Rslt.FindData.Handle, @buf[0],
|
|
Length(buf) * SizeOf(buf[0]), True, False,
|
|
@Rslt.FindData.Context, @len)
|
|
else
|
|
res := NtQueryDirectoryFile(Rslt.FindData.Handle, 0, Nil, Nil, @iostatus,
|
|
@buf[0], Length(buf) * SizeOf(buf[0]), FileDirectoryInformation,
|
|
True, Nil, False);
|
|
if Rslt.FindData.IsDirObj then begin
|
|
Finished := (res = STATUS_NO_MORE_ENTRIES)
|
|
or (res = STATUS_NO_MORE_FILES)
|
|
or not NT_SUCCESS(res);
|
|
Rslt.FindData.LastRes := res;
|
|
if dirinfo^.Name.Length > 0 then begin
|
|
SetLength(FName, dirinfo^.Name.Length div 2);
|
|
move(dirinfo^.Name.Buffer[0],FName[1],dirinfo^.Name.Length);
|
|
{$ifdef debug_findnext}
|
|
Write(FName, ' (');
|
|
for i := 0 to dirinfo^.TypeName.Length div 2 - 1 do
|
|
if dirinfo^.TypeName.Buffer[i] < #256 then
|
|
Write(AnsiChar(Byte(dirinfo^.TypeName.Buffer[i])))
|
|
else
|
|
Write('?');
|
|
Writeln(')');
|
|
{$endif debug_findnext}
|
|
end else
|
|
FName := '';
|
|
end else begin
|
|
SetLength(FName, filedirinfo^.FileNameLength div 2);
|
|
move(filedirinfo^.FileName[0],FName[1],filedirinfo^.FileNameLength);
|
|
end;
|
|
if FName = '' then
|
|
Finished := True
|
|
else begin
|
|
if FNMatch(SName, FName) then begin
|
|
Found := FindGetFileInfo(Copy(Rslt.FindData.SearchSpec, 1,
|
|
Rslt.FindData.NamePos) + FName, Rslt, Name);
|
|
if Found then begin
|
|
Result := 0;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function InternalFindFirst (Const Path : UnicodeString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name : UnicodeString) : Longint;
|
|
{
|
|
opens dir and calls FindNext if needed.
|
|
}
|
|
Begin
|
|
Result := -1;
|
|
if Path = '' then
|
|
Exit;
|
|
Rslt.FindData.SearchAttr := Attr;
|
|
{Wildcards?}
|
|
if (Pos('?', Path) = 0) and (Pos('*', Path) = 0) then begin
|
|
if FindGetFileInfo(Path, Rslt, Name) then
|
|
Result := 0;
|
|
end else begin
|
|
{Create Info}
|
|
Rslt.FindData.SearchSpec := Path;
|
|
Rslt.FindData.NamePos := Length(Rslt.FindData.SearchSpec);
|
|
while (Rslt.FindData.NamePos > 0)
|
|
and (Rslt.FindData.SearchSpec[Rslt.FindData.NamePos] <> DirectorySeparator)
|
|
do
|
|
Dec(Rslt.FindData.NamePos);
|
|
Result := InternalFindNext(Rslt,Name);
|
|
end;
|
|
if Result <> 0 then
|
|
InternalFindClose(Rslt.FindHandle,Rslt.FindData);
|
|
end;
|
|
|
|
|
|
function FileGetDate(Handle: THandle): Int64;
|
|
var
|
|
res: NTSTATUS;
|
|
basic: FILE_BASIC_INFORMATION;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
begin
|
|
res := NtQueryInformationFile(Handle, @iostatus, @basic,
|
|
SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
|
|
if NT_SUCCESS(res) then
|
|
Result := NtToDosTime(basic.LastWriteTime)
|
|
else
|
|
Result := -1;
|
|
end;
|
|
|
|
|
|
function FileSetDate(Handle: THandle;Age: Int64): Longint;
|
|
var
|
|
res: NTSTATUS;
|
|
basic: FILE_BASIC_INFORMATION;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
begin
|
|
res := NtQueryInformationFile(Handle, @iostatus, @basic,
|
|
SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
|
|
if NT_SUCCESS(res) then begin
|
|
if not DosToNtTime(Age, basic.LastWriteTime) then begin
|
|
Result := -1;
|
|
Exit;
|
|
end;
|
|
|
|
res := NtSetInformationFile(Handle, @iostatus, @basic,
|
|
SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
|
|
if NT_SUCCESS(res) then
|
|
Result := 0
|
|
else
|
|
Result := res;
|
|
end else
|
|
Result := res;
|
|
end;
|
|
|
|
|
|
function FileGetAttr(const FileName: UnicodeString): Longint;
|
|
var
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
info: FILE_NETWORK_OPEN_INFORMATION;
|
|
res: NTSTATUS;
|
|
ntstr: UNICODE_STRING;
|
|
begin
|
|
UnicodeStrToNtStr(FileName, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
|
|
res := NtQueryFullAttributesFile(@objattr, @info);
|
|
if NT_SUCCESS(res) then
|
|
Result := info.FileAttributes
|
|
else
|
|
Result := 0;
|
|
|
|
FreeNtStr(ntstr);
|
|
end;
|
|
|
|
|
|
function FileSetAttr(const Filename: UnicodeString; Attr: LongInt): Longint;
|
|
var
|
|
h: THandle;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
ntstr: UNICODE_STRING;
|
|
basic: FILE_BASIC_INFORMATION;
|
|
res: NTSTATUS;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
begin
|
|
UnicodeStrToNtStr(Filename, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
res := NtOpenFile(@h,
|
|
NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES or FILE_WRITE_ATTRIBUTES,
|
|
@objattr, @iostatus,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
FreeNtStr(ntstr);
|
|
|
|
if NT_SUCCESS(res) then begin
|
|
res := NtQueryInformationFile(h, @iostatus, @basic,
|
|
SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
|
|
|
|
if NT_SUCCESS(res) then begin
|
|
basic.FileAttributes := Attr;
|
|
Result := NtSetInformationFile(h, @iostatus, @basic,
|
|
SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation);
|
|
end;
|
|
|
|
NtClose(h);
|
|
end else
|
|
Result := res;
|
|
end;
|
|
|
|
|
|
function DeleteFile(const FileName: UnicodeString): Boolean;
|
|
var
|
|
h: THandle;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
ntstr: UNICODE_STRING;
|
|
dispinfo: FILE_DISPOSITION_INFORMATION;
|
|
res: NTSTATUS;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
begin
|
|
UnicodeStrToNtStr(Filename, ntstr);
|
|
InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
res := NtOpenFile(@h, NT_DELETE, @objattr, @iostatus,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
FILE_NON_DIRECTORY_FILE);
|
|
|
|
FreeNtStr(ntstr);
|
|
|
|
if NT_SUCCESS(res) then begin
|
|
dispinfo.DeleteFile := True;
|
|
|
|
res := NtSetInformationFile(h, @iostatus, @dispinfo,
|
|
SizeOf(FILE_DISPOSITION_INFORMATION), FileDispositionInformation);
|
|
|
|
Result := NT_SUCCESS(res);
|
|
|
|
NtClose(h);
|
|
end else
|
|
Result := False;
|
|
end;
|
|
|
|
|
|
function RenameFile(const OldName, NewName: UnicodeString): Boolean;
|
|
var
|
|
h: THandle;
|
|
objattr: OBJECT_ATTRIBUTES;
|
|
iostatus: IO_STATUS_BLOCK;
|
|
dest, src: UNICODE_STRING;
|
|
renameinfo: PFILE_RENAME_INFORMATION;
|
|
res: LongInt;
|
|
begin
|
|
{ check whether the destination exists first }
|
|
UnicodeStrToNtStr(NewName, dest);
|
|
InitializeObjectAttributes(objattr, @dest, 0, 0, Nil);
|
|
|
|
res := NtCreateFile(@h, 0, @objattr, @iostatus, Nil, 0,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE, Nil, 0);
|
|
if NT_SUCCESS(res) then begin
|
|
{ destination already exists => error }
|
|
NtClose(h);
|
|
Result := False;
|
|
end else begin
|
|
UnicodeStrToNtStr(OldName, src);
|
|
InitializeObjectAttributes(objattr, @src, 0, 0, Nil);
|
|
|
|
res := NtCreateFile(@h,
|
|
GENERIC_ALL or NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES,
|
|
@objattr, @iostatus, Nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT or FILE_OPEN_REMOTE_INSTANCE
|
|
or FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil,
|
|
0);
|
|
|
|
if NT_SUCCESS(res) then begin
|
|
renameinfo := GetMem(SizeOf(FILE_RENAME_INFORMATION) + dest.Length);
|
|
with renameinfo^ do begin
|
|
ReplaceIfExists := False;
|
|
RootDirectory := 0;
|
|
FileNameLength := dest.Length;
|
|
Move(dest.Buffer^, renameinfo^.FileName, dest.Length);
|
|
end;
|
|
|
|
res := NtSetInformationFile(h, @iostatus, renameinfo,
|
|
SizeOf(FILE_RENAME_INFORMATION) + dest.Length,
|
|
FileRenameInformation);
|
|
if not NT_SUCCESS(res) then begin
|
|
{ this could happen if src and destination reside on different drives,
|
|
so we need to copy the file manually }
|
|
{$message warning 'RenameFile: Implement file copy!'}
|
|
Result := False;
|
|
end else
|
|
Result := True;
|
|
|
|
NtClose(h);
|
|
end else
|
|
Result := False;
|
|
|
|
FreeNtStr(src);
|
|
end;
|
|
|
|
FreeNtStr(dest);
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Disk Functions
|
|
****************************************************************************}
|
|
|
|
function diskfree(drive: byte): int64;
|
|
begin
|
|
{ here the mount manager needs to be queried }
|
|
Result := -1;
|
|
end;
|
|
|
|
|
|
function disksize(drive: byte): int64;
|
|
begin
|
|
{ here the mount manager needs to be queried }
|
|
Result := -1;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Time Functions
|
|
****************************************************************************}
|
|
|
|
|
|
procedure GetLocalTime(var SystemTime: TSystemTime);
|
|
var
|
|
bias, syst: LARGE_INTEGER;
|
|
fields: TIME_FIELDS;
|
|
userdata: PKUSER_SHARED_DATA;
|
|
begin
|
|
// get UTC time
|
|
userdata := SharedUserData;
|
|
repeat
|
|
syst.u.HighPart := userdata^.SystemTime.High1Time;
|
|
syst.u.LowPart := userdata^.SystemTime.LowPart;
|
|
until syst.u.HighPart = userdata^.SystemTime.High2Time;
|
|
|
|
// adjust to local time
|
|
repeat
|
|
bias.u.HighPart := userdata^.TimeZoneBias.High1Time;
|
|
bias.u.LowPart := userdata^.TimeZoneBias.LowPart;
|
|
until bias.u.HighPart = userdata^.TimeZoneBias.High2Time;
|
|
syst.QuadPart := syst.QuadPart - bias.QuadPart;
|
|
|
|
RtlTimeToTimeFields(@syst, @fields);
|
|
|
|
SystemTime.Year := fields.Year;
|
|
SystemTime.Month := fields.Month;
|
|
SystemTime.Day := fields.Day;
|
|
SystemTime.Hour := fields.Hour;
|
|
SystemTime.Minute := fields.Minute;
|
|
SystemTime.Second := fields.Second;
|
|
SystemTime.Millisecond := fields.MilliSeconds;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Misc Functions
|
|
****************************************************************************}
|
|
|
|
procedure sysbeep;
|
|
begin
|
|
{ empty }
|
|
end;
|
|
|
|
procedure InitInternational;
|
|
begin
|
|
InitInternationalGeneric;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Target Dependent
|
|
****************************************************************************}
|
|
|
|
function SysErrorMessage(ErrorCode: Integer): String;
|
|
begin
|
|
Result := 'NT error code: 0x' + IntToHex(ErrorCode, 8);
|
|
end;
|
|
|
|
{****************************************************************************
|
|
Initialization code
|
|
****************************************************************************}
|
|
|
|
function wstrlen(p: PWideChar): SizeInt; external name 'FPC_PWIDECHAR_LENGTH';
|
|
|
|
function GetEnvironmentVariable(const EnvVar: String): String;
|
|
var
|
|
s, upperenvvar : UTF8String;
|
|
i : longint;
|
|
hp: pwidechar;
|
|
len: sizeint;
|
|
begin
|
|
{ TODO : test once I know how to execute processes }
|
|
Result:='';
|
|
hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment;
|
|
{ first convert to UTF-8, then uppercase in order to avoid potential data
|
|
loss }
|
|
upperenvvar:=EnvVar;
|
|
upperenvvar:=UpperCase(upperenvvar);
|
|
while hp^<>#0 do
|
|
begin
|
|
len:=UnicodeToUTF8(Nil, hp, 0);
|
|
SetLength(s,len);
|
|
UnicodeToUTF8(PAnsiChar(s), hp, len);
|
|
i:=pos('=',s);
|
|
if uppercase(copy(s,1,i-1))=upperenvvar then
|
|
begin
|
|
{ copy() returns a rawbytestring -> will keep UTF-8 encoding }
|
|
Result:=copy(s,i+1,length(s)-i);
|
|
break;
|
|
end;
|
|
{ next string entry}
|
|
hp:=hp+wstrlen(hp)+1;
|
|
end;
|
|
end;
|
|
|
|
function GetEnvironmentVariableCount: Integer;
|
|
var
|
|
hp : pwidechar;
|
|
begin
|
|
Result:=0;
|
|
hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment;
|
|
If (Hp<>Nil) then
|
|
while hp^<>#0 do
|
|
begin
|
|
Inc(Result);
|
|
hp:=hp+wstrlen(hp)+1;
|
|
end;
|
|
end;
|
|
|
|
function GetEnvironmentString(Index: Integer): {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif};
|
|
var
|
|
hp : pwidechar;
|
|
len: sizeint;
|
|
begin
|
|
Result:='';
|
|
hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment;
|
|
If (Hp<>Nil) then
|
|
begin
|
|
while (hp^<>#0) and (Index>1) do
|
|
begin
|
|
Dec(Index);
|
|
hp:=hp+wstrlen(hp)+1;
|
|
end;
|
|
If (hp^<>#0) then
|
|
begin
|
|
{$ifdef FPC_RTL_UNICODE}
|
|
Result:=hp;
|
|
{$else}
|
|
len:=UnicodeToUTF8(Nil, hp, 0);
|
|
SetLength(Result, len);
|
|
UnicodeToUTF8(PAnsiChar(Result), hp, len);
|
|
SetCodePage(RawByteString(Result),CP_UTF8,false);
|
|
{$endif}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function ExecuteProcess(const Path: RawByteString; const ComLine: RawByteString;
|
|
Flags: TExecuteFlags = []): Integer;
|
|
begin
|
|
{ TODO : implement }
|
|
Result := 0;
|
|
end;
|
|
|
|
function ExecuteProcess(const Path: RawByteString;
|
|
const ComLine: Array of RawByteString; Flags:TExecuteFlags = []): Integer;
|
|
var
|
|
CommandLine: RawByteString;
|
|
I: integer;
|
|
begin
|
|
Commandline := '';
|
|
for I := 0 to High (ComLine) do
|
|
if Pos (' ', ComLine [I]) <> 0 then
|
|
CommandLine := CommandLine + ' ' + '"' + ComLine [I] + '"'
|
|
else
|
|
CommandLine := CommandLine + ' ' + Comline [I];
|
|
ExecuteProcess := ExecuteProcess (Path, CommandLine,Flags);
|
|
end;
|
|
|
|
function ExecuteProcess(const Path: UnicodeString; const ComLine: UnicodeString;
|
|
Flags: TExecuteFlags = []): Integer;
|
|
begin
|
|
{ TODO : implement }
|
|
Result := 0;
|
|
end;
|
|
|
|
function ExecuteProcess(const Path: UnicodeString;
|
|
const ComLine: Array of UnicodeString; Flags:TExecuteFlags = []): Integer;
|
|
var
|
|
CommandLine: UnicodeString;
|
|
I: integer;
|
|
begin
|
|
Commandline := '';
|
|
for I := 0 to High (ComLine) do
|
|
if Pos (' ', ComLine [I]) <> 0 then
|
|
CommandLine := CommandLine + ' ' + '"' + ComLine [I] + '"'
|
|
else
|
|
CommandLine := CommandLine + ' ' + Comline [I];
|
|
ExecuteProcess := ExecuteProcess (Path, CommandLine,Flags);
|
|
end;
|
|
|
|
procedure Sleep(Milliseconds: Cardinal);
|
|
const
|
|
DelayFactor = 10000;
|
|
var
|
|
interval: LARGE_INTEGER;
|
|
begin
|
|
interval.QuadPart := - Milliseconds * DelayFactor;
|
|
NtDelayExecution(False, @interval);
|
|
end;
|
|
|
|
{****************************************************************************
|
|
Initialization code
|
|
****************************************************************************}
|
|
|
|
initialization
|
|
InitExceptions; { Initialize exceptions. OS independent }
|
|
InitInternational; { Initialize internationalization settings }
|
|
OnBeep := @SysBeep;
|
|
finalization
|
|
FreeTerminateProcs;
|
|
DoneExceptions;
|
|
end.
|