mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 14:48:18 +02:00
446 lines
11 KiB
ObjectPascal
446 lines
11 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2009 by Sven Barth
|
|
|
|
FPC Pascal system unit for the WinNT API.
|
|
|
|
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.
|
|
|
|
**********************************************************************}
|
|
unit System;
|
|
interface
|
|
|
|
{$IFNDEF FPC_DISABLE_MONITOR}
|
|
{$DEFINE SYSTEM_HAS_FEATURE_MONITOR}
|
|
{$ENDIF}
|
|
|
|
{$define FPC_IS_SYSTEM}
|
|
{$ifdef SYSTEMDEBUG}
|
|
{$define SYSTEMEXCEPTIONDEBUG}
|
|
{$endif SYSTEMDEBUG}
|
|
|
|
{$ifdef cpui386}
|
|
{$define Set_i386_Exception_handler}
|
|
{$endif cpui386}
|
|
|
|
{$define DISABLE_NO_THREAD_MANAGER}
|
|
|
|
{$ifdef KMODE}
|
|
// in KernelMode we need use a memory manager that just wraps the routines
|
|
// provided by the NT Executive and allows to select whether we want to use
|
|
// paged or non-paged (use sparely!) memory
|
|
{$define HAS_MEMORYMANAGER}
|
|
{$endif KMODE}
|
|
|
|
{ include system-independent routine headers }
|
|
{$I systemh.inc}
|
|
|
|
var
|
|
CurrentPeb: Pointer;
|
|
IsDeviceDriver: Boolean = False;
|
|
|
|
const
|
|
LineEnding = #13#10;
|
|
LFNSupport = true;
|
|
DirectorySeparator = '\';
|
|
DriveSeparator = '\';
|
|
ExtensionSeparator = '.';
|
|
PathSeparator = ';';
|
|
AllowDirectorySeparators : set of AnsiChar = ['\'];
|
|
AllowDriveSeparators : set of AnsiChar = [];
|
|
|
|
{ FileNameCaseSensitive and FileNameCasePreserving are defined separately below!!! }
|
|
maxExitCode = High(ErrorCode);
|
|
MaxPathLen = High(Word);
|
|
AllFilesMask = '*';
|
|
|
|
type
|
|
PEXCEPTION_FRAME = ^TEXCEPTION_FRAME;
|
|
TEXCEPTION_FRAME = record
|
|
next : PEXCEPTION_FRAME;
|
|
handler : pointer;
|
|
end;
|
|
|
|
var
|
|
{ C compatible arguments }
|
|
argc: LongWord;
|
|
argvw: PPWideChar;
|
|
argv: PPAnsiChar;
|
|
|
|
const
|
|
{ Default filehandles }
|
|
UnusedHandle : THandle = 0;
|
|
StdInputHandle : THandle = 0;
|
|
StdOutputHandle : THandle = 0;
|
|
StdErrorHandle : THandle = 0;
|
|
|
|
{$ifndef kmode}
|
|
type
|
|
TDLL_Entry_Hook = procedure (dllparam : longint);
|
|
|
|
const
|
|
Dll_Process_Detach_Hook : TDLL_Entry_Hook = nil;
|
|
Dll_Thread_Attach_Hook : TDLL_Entry_Hook = nil;
|
|
Dll_Thread_Detach_Hook : TDLL_Entry_Hook = nil;
|
|
{$endif}
|
|
|
|
const
|
|
// NT is case sensitive
|
|
FileNameCaseSensitive : boolean = true;
|
|
FileNameCasePreserving: boolean = true;
|
|
// todo: check whether this is really the case on NT
|
|
CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *)
|
|
|
|
sLineBreak = LineEnding;
|
|
DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF;
|
|
|
|
System_exception_frame : PEXCEPTION_FRAME =nil;
|
|
|
|
implementation
|
|
|
|
{ include system independent routines }
|
|
{$I system.inc}
|
|
|
|
function fpc_pwidechar_length(p: PWideChar): SizeInt; external name 'FPC_PWIDECHAR_LENGTH';
|
|
|
|
{ based on setup_arguments from Win32 RTL }
|
|
procedure setup_arguments;
|
|
var
|
|
i,len,
|
|
arglen,
|
|
count : longint;
|
|
argstart,
|
|
pc,arg : pwidechar;
|
|
pc2 : PAnsiChar;
|
|
quote : Boolean;
|
|
argvlen : longint;
|
|
params : PRTLUserProcessParameters;
|
|
|
|
procedure allocarg(idx,len:longint);
|
|
var
|
|
oldargvlen : longint;
|
|
begin
|
|
if idx>=argvlen then
|
|
begin
|
|
oldargvlen:=argvlen;
|
|
argvlen:=(idx+8) and (not 7);
|
|
sysreallocmem(argvw,argvlen*sizeof(pointer));
|
|
fillchar(argvw[oldargvlen],(argvlen-oldargvlen)*sizeof(pointer),0);
|
|
end;
|
|
{ use realloc to reuse already existing memory }
|
|
{ always allocate, even if length is zero, since }
|
|
{ the arg. is still present! }
|
|
sysreallocmem(argvw[idx],len*sizeof(widechar)+2);
|
|
end;
|
|
|
|
begin
|
|
{ create commandline, it starts with the executed filename which is argvw[0] }
|
|
{ NativeNT passes inside the PEB which is passed on startup }
|
|
argvw:=nil;
|
|
argv:=nil;
|
|
argvlen:=0;
|
|
params:=PSimplePEB(CurrentPEB)^.ProcessParameters;
|
|
ArgLen:=params^.ImagePathName.Length + 1;
|
|
allocarg(0,arglen);
|
|
move(params^.ImagePathName.Buffer^,argvw[0]^,arglen*sizeof(widechar)+1);
|
|
{ Setup cmdline variable }
|
|
{ cmdline is a PAnsiChar, but NT uses PWideChar... don't set cmdline for now }
|
|
{$message warning 'cmdline is not set'}
|
|
// cmdline:=GetCommandLine;
|
|
{ the first argument isn't the image file name, so start at 1 }
|
|
count:=1;
|
|
{ process arguments }
|
|
pc:=params^.CommandLine.Buffer;
|
|
while pc^<>#0 do
|
|
begin
|
|
{ skip leading spaces }
|
|
while (Ord(pc^) >= 1) and (Ord(pc^) <= 32) {pc^ in [#1..#32]} do
|
|
inc(pc);
|
|
if pc^=#0 then
|
|
break;
|
|
{ calc argument length }
|
|
quote:=False;
|
|
argstart:=pc;
|
|
arglen:=0;
|
|
while pc^<>#0 do
|
|
begin
|
|
case pc^ of
|
|
#1..#32 :
|
|
begin
|
|
if quote then
|
|
inc(arglen)
|
|
else
|
|
break;
|
|
end;
|
|
'"' :
|
|
if pc[1]<>'"' then
|
|
quote := not quote
|
|
else
|
|
inc(pc);
|
|
else
|
|
inc(arglen);
|
|
end;
|
|
inc(pc);
|
|
end;
|
|
{ copy argument }
|
|
{ Don't copy the first one, it is already there.}
|
|
If Count<>0 then
|
|
begin
|
|
allocarg(count,arglen);
|
|
quote:=False;
|
|
pc:=argstart;
|
|
arg:=argvw[count];
|
|
while (pc^<>#0) do
|
|
begin
|
|
case pc^ of
|
|
#1..#32 :
|
|
begin
|
|
if quote then
|
|
begin
|
|
arg^:=pc^;
|
|
inc(arg);
|
|
end
|
|
else
|
|
break;
|
|
end;
|
|
'"' :
|
|
if pc[1]<>'"' then
|
|
quote := not quote
|
|
else
|
|
inc(pc);
|
|
else
|
|
begin
|
|
arg^:=pc^;
|
|
inc(arg);
|
|
end;
|
|
end;
|
|
inc(pc);
|
|
end;
|
|
arg^:=#0;
|
|
end;
|
|
inc(count);
|
|
end;
|
|
{ get argc }
|
|
argc:=count;
|
|
{ free unused memory, leaving a nil entry at the end }
|
|
sysreallocmem(argvw,(count+1)*sizeof(pointer));
|
|
argvw[count] := nil;
|
|
{ now we need to fill argv with UTF8 encoded arguments }
|
|
sysreallocmem(argv,(count+1)*sizeof(pointer));
|
|
fillchar(argv^,(count+1)*sizeof(pointer),0);
|
|
for i := 0 to count - 1 do begin
|
|
len := fpc_pwidechar_length(argvw[i]);
|
|
pc := argvw[i];
|
|
argv[i]:=nil;
|
|
sysreallocmem(argv[i],len+1);
|
|
pc2 := argv[i];
|
|
{$message warning 'Use UnicodeToUTF8 for argument conversion'}
|
|
while Ord(pc^) > 0 do begin
|
|
if word(pc^) < 127 then
|
|
pc2^ := AnsiChar(word(pc^))
|
|
else
|
|
pc2^ := '?';
|
|
Inc(pc);
|
|
Inc(pc2);
|
|
end;
|
|
pc2^ := #0;
|
|
end;
|
|
end;
|
|
|
|
function paramcount : longint;
|
|
begin
|
|
paramcount := argc - 1;
|
|
end;
|
|
|
|
function paramstr(l : longint) : shortstring;
|
|
begin
|
|
if (l>=0) and (l<argc) then
|
|
paramstr:=strpas(argv[l])
|
|
else
|
|
paramstr:='';
|
|
end;
|
|
|
|
procedure KeQueryTickCount(TickCount: PLargeInteger); stdcall; external ntdll name 'KeQueryTickCount';
|
|
|
|
procedure randomize;
|
|
var
|
|
tc: PLargeInteger;
|
|
begin
|
|
FillChar(tc, SizeOf(TLargeInteger), 0);
|
|
KeQueryTickCount(@tc);
|
|
// the lower part should differ most on system startup
|
|
randseed := tc^.LowPart;
|
|
end;
|
|
|
|
{*****************************************************************************
|
|
System Dependent Exit code
|
|
*****************************************************************************}
|
|
|
|
procedure PascalMain;external name 'PASCALMAIN';
|
|
|
|
{$ifndef KMODE}
|
|
function NtTerminateProcess(aProcess: THandle; aStatus: LongInt): LongInt; stdcall; external ntdll name 'NtTerminateProcess';
|
|
{$endif KMODE}
|
|
|
|
Procedure system_exit;
|
|
begin
|
|
if IsLibrary or IsDeviceDriver then
|
|
Exit;
|
|
{$ifndef KMODE}
|
|
NtTerminateProcess(THandle(-1), ExitCode);
|
|
{$endif KMODE}
|
|
end;
|
|
|
|
{$ifdef kmode}
|
|
function FPCDriverStartup(aDriverObject: Pointer; aRegistryPath: Pointer): LongInt; [public, alias: 'FPC_DriverStartup'];
|
|
begin
|
|
IsDeviceDriver := True;
|
|
IsConsole := True;
|
|
IsLibrary := True;
|
|
|
|
SysDriverObject := aDriverObject;
|
|
SysRegistryPath := aRegistryPath;
|
|
|
|
PASCALMAIN;
|
|
|
|
SysDriverObject := Nil;
|
|
SysRegistryPath := Nil;
|
|
|
|
Result := ExitCode;
|
|
end;
|
|
{$else}
|
|
|
|
const
|
|
DLL_PROCESS_ATTACH = 1;
|
|
DLL_THREAD_ATTACH = 2;
|
|
DLL_PROCESS_DETACH = 0;
|
|
DLL_THREAD_DETACH = 3;
|
|
|
|
function FPCDLLEntry(aHInstance: Pointer; aDLLReason: LongInt; aDLLParam: LongInt): LongBool; [public, alias: 'FPC_DLLEntry'];
|
|
begin
|
|
IsLibrary := True;
|
|
FPCDLLEntry := True;
|
|
case aDLLReason of
|
|
DLL_PROCESS_ATTACH: begin
|
|
PascalMain;
|
|
FPCDLLEntry := ExitCode = 0;
|
|
end;
|
|
DLL_THREAD_ATTACH: begin
|
|
if Dll_Thread_Attach_Hook <> Nil then
|
|
Dll_Thread_Attach_Hook(aDllParam);
|
|
end;
|
|
DLL_THREAD_DETACH: begin
|
|
if Dll_Thread_Detach_Hook <> Nil then
|
|
Dll_Thread_Detach_Hook(aDllParam);
|
|
end;
|
|
DLL_PROCESS_DETACH: begin
|
|
if Dll_Process_Detach_Hook <> Nil then
|
|
Dll_Process_Detach_Hook(aDllParam);
|
|
// finalize units
|
|
internal_do_exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure FPCProcessStartup(aArgument: Pointer);[public, alias: 'FPC_ProcessStartup'];
|
|
begin
|
|
IsConsole := True;
|
|
IsLibrary := False;
|
|
CurrentPeb := aArgument;
|
|
|
|
PASCALMAIN;
|
|
|
|
system_exit;
|
|
end;
|
|
{$endif}
|
|
|
|
{$ifdef kmode}
|
|
|
|
// Kernel Mode Entry Point
|
|
|
|
function NtDriverEntry( aDriverObject: Pointer; aRegistryPath: Pointer ): LongInt; stdcall; [public, alias: '_NtDriverEntry'];
|
|
begin
|
|
NtDriverEntry := FPCDriverStartup(aDriverObject, aRegistryPath);
|
|
end;
|
|
{$else}
|
|
|
|
// User Mode Entry Points
|
|
|
|
procedure NtProcessStartup( aArgument: Pointer ); stdcall; [public, alias: '_NtProcessStartup'];
|
|
begin
|
|
FPCProcessStartup(aArgument);
|
|
end;
|
|
|
|
function DLLMainStartup( aHInstance: Pointer; aDLLReason, aDLLParam: LongInt ): LongBool; stdcall; [public, alias: '_DLLMainStartup'];
|
|
begin
|
|
DLLMainStartup := FPCDLLEntry(aHInstance, aDLLReason, aDLLParam);
|
|
end;
|
|
{$endif}
|
|
|
|
procedure SysInitStdIO;
|
|
begin
|
|
{ This function is currently only called if the RTL is compiled for Usermode;
|
|
one could think about adding a text driver that outputs using DbgPrint }
|
|
{$ifndef KMODE}
|
|
with PSimplePEB(CurrentPEB)^.ProcessParameters^ do begin
|
|
StdInputHandle := StandardInput;
|
|
StdOutputHandle := StandardOutput;
|
|
StdErrorHandle := StandardError;
|
|
end;
|
|
if StdInputHandle <> 0 then
|
|
OpenStdIO(Input, fmInput, StdInputHandle)
|
|
else
|
|
Assign(Input, '');
|
|
if StdOutputHandle <> 0 then begin
|
|
OpenStdIO(Output, fmOutput, StdOutputHandle);
|
|
OpenStdIO(StdOut, fmOutput, StdOutputHandle);
|
|
end else begin
|
|
Assign(Output, '');
|
|
Assign(StdOut, '');
|
|
end;
|
|
if StdErrorHandle <> 0 then begin
|
|
OpenStdIO(ErrOutput, fmOutput, StdErrorHandle);
|
|
OpenStdIO(StdErr, fmOutput, StdErrorHandle);
|
|
end else begin
|
|
Assign(ErrOutput, '');
|
|
Assign(StdErr, '');
|
|
end;
|
|
{$endif}
|
|
end;
|
|
|
|
function GetProcessID: SizeUInt;
|
|
begin
|
|
{$ifdef kmode}
|
|
// it might be that we can detect the user process that called us,
|
|
// but that needs to be checked... so for now just return 0
|
|
Result := 0;
|
|
{$else}
|
|
Result := NtCurrentTEB^.ClientID.UniqueProcess;
|
|
{$endif}
|
|
end;
|
|
|
|
begin
|
|
{$if not defined(KMODE) and not defined(HAS_MEMORYMANAGER)}
|
|
{ Setup heap }
|
|
InitHeap;
|
|
{$endif ndef KMODE and ndef HAS_MEMORYMANAGER}
|
|
SysInitExceptions;
|
|
{ we do not use winlike widestrings and also the RTL can't be compiled with
|
|
2.2, so we can savely use the UnicodeString manager only. }
|
|
initunicodestringmanager;
|
|
{$ifndef KMODE}
|
|
SysInitStdIO;
|
|
{ Arguments }
|
|
setup_arguments;
|
|
{$endif}
|
|
InOutRes := 0;
|
|
InitSystemThreads;
|
|
errno := 0;
|
|
end.
|
|
|