{ This file is part of the Free Pascal run time library. Copyright (c) 1999-2005 by Florian Klaempfl and Pavel Ozerski member of the Free Pascal development team. FPC Pascal system unit for the Win32 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 {$ifdef SYSTEMDEBUG} {$define SYSTEMEXCEPTIONDEBUG} {$endif SYSTEMDEBUG} {$define FPC_HAS_INDIRECT_MAIN_INFORMATION} {$ifdef cpui386} {$define Set_i386_Exception_handler} {$endif cpui386} {$define DISABLE_NO_THREAD_MANAGER} {$define FPC_RTLSTRING_UTF16} {$define HAS_CMDLINE} { include system-independent routine headers } {$I systemh.inc} const LineEnding = #13#10; LFNSupport = true; DirectorySeparator = '\'; DriveSeparator = ':'; ExtensionSeparator = '.'; PathSeparator = ';'; AllowDirectorySeparators : set of char = ['\','/']; AllowDriveSeparators : set of char = [':']; { FileNameCaseSensitive is defined separately below!!! } maxExitCode = 65535; MaxPathLen = 260; AllFilesMask = '*'; type PEXCEPTION_FRAME = ^TEXCEPTION_FRAME; TEXCEPTION_FRAME = record next : PEXCEPTION_FRAME; handler : pointer; end; const { Default filehandles } UnusedHandle : THandle = THandle(-1); StdInputHandle : THandle = 0; StdOutputHandle : THandle = 0; StdErrorHandle : THandle = 0; FileNameCaseSensitive : boolean = true; CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *) sLineBreak = LineEnding; DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF; { Thread count for DLL } Thread_count : longint = 0; System_exception_frame : PEXCEPTION_FRAME =nil; type TStartupInfo=packed record cb : longint; lpReserved : Pointer; lpDesktop : Pointer; lpTitle : Pointer; dwX : longint; dwY : longint; dwXSize : longint; dwYSize : longint; dwXCountChars : longint; dwYCountChars : longint; dwFillAttribute : longint; dwFlags : longint; wShowWindow : Word; cbReserved2 : Word; lpReserved2 : Pointer; hStdInput : longint; hStdOutput : longint; hStdError : longint; end; var { Win32 Info } startupinfo : tstartupinfo; hprevinst, MainInstance, cmdshow : longint; DLLreason,DLLparam:longint; StartupConsoleMode : DWORD; type TDLL_Process_Entry_Hook = function (dllparam : longint) : longbool; TDLL_Entry_Hook = procedure (dllparam : longint); const Dll_Process_Attach_Hook : TDLL_Process_Entry_Hook = nil; Dll_Process_Detach_Hook : TDLL_Entry_Hook = nil; Dll_Thread_Attach_Hook : TDLL_Entry_Hook = nil; Dll_Thread_Detach_Hook : TDLL_Entry_Hook = nil; function CmdLine: PRtlChar; { C compatible arguments ANSI only} function argc: longint; function argv: PPAnsiChar; implementation function _W(const s: RtlString): PWideChar; inline; begin Result:=PWideChar(UnicodeString(s)); end; function _W(p: PWideChar): PWideChar; inline; begin Result:=p; end; var EntryInformation : TEntryInformation; SysInstance : Longint;public name '_FPC_SysInstance'; { used by wstrings.inc because wstrings.inc is included before sysos.inc this is put here (FK) } function SysAllocStringLen(psz:pointer;len:dword):pointer;stdcall; external 'oleaut32.dll' name 'SysAllocStringLen'; procedure SysFreeString(bstr:pointer);stdcall; external 'oleaut32.dll' name 'SysFreeString'; function SysReAllocStringLen(var bstr:pointer;psz: pointer; len:dword): Integer; stdcall;external 'oleaut32.dll' name 'SysReAllocStringLen'; { include system independent routines } {$I system.inc} {***************************************************************************** Parameter Handling *****************************************************************************} var FArgs: PRtlChar; FCmdLine: PRtlChar; Fargc: longint; Fargv: PPRtlChar; {$ifdef FPC_RTLSTRING_UTF16} FAnsiArgs: PAnsiChar; FAnsiArgv: PPAnsiChar; {$else} FCmdLineStr: RtlString; {$endif FPC_RTLSTRING_UTF16} procedure setup_arguments; var i, argvlen: longint; pc, dst, argstart: PRtlChar; quote: Boolean; buf: array[0..259] of WideChar; // need MAX_PATH bytes, not 256! {$ifndef FPC_RTLSTRING_UTF16} s: RtlString; {$endif FPC_RTLSTRING_UTF16} begin if FCmdLine <> nil then exit; // Alloc argv buffer argvlen:=20; Fargv:=SysGetMem(argvlen*SizeOf(pointer)); // Get command line {$ifdef FPC_RTLSTRING_UTF16} FCmdLine:=GetCommandLine; {$else} FCmdLineStr:=GetCommandLine; FCmdLine:=PRtlChar(FCmdLineStr); {$endif FPC_RTLSTRING_UTF16} // Get exe name i:=GetModuleFileName(0, buf, High(buf)-1); buf[i]:=#0; // be safe {$ifndef FPC_RTLSTRING_UTF16} s:=buf; i:=Length(s); {$endif FPC_RTLSTRING_UTF16} Inc(i); // Alloc space for arguments FArgs:=SysGetMem((i + strlen(FCmdLine) + 2)*SizeOf(RtlChar)); // Copy exe name Move(buf, FArgs^, i*SizeOf(RtlChar)); Fargv[0]:=FArgs; Fargc:=0; // Process arguments pc:=FCmdLine; dst:=FArgs + i; while pc^ <> #0 do begin { skip leading spaces } while (pc^ <> #0) and (pc^ <= ' ') do Inc(pc); if pc^ = #0 then break; argstart:=dst; { copy argument } quote:=False; while pc^ <> #0 do begin case pc^ of #1..#32 : if not quote then break; '"' : begin Inc(pc); if pc^ <> '"' then begin quote := not quote; continue; end; end; end; // don't copy the first argument. It is exe name if Fargc > 0 then begin dst^:=pc^; Inc(dst); end; Inc(pc); end; if Fargc > 0 then begin // null-terminate the argument dst^:=#0; Inc(dst); if Fargc >= argvlen then begin Inc(argvlen, 20); SysReAllocMem(Fargv, argvlen*SizeOf(pointer)); end; Fargv[Fargc]:=argstart; end; Inc(Fargc); end; // Terminate FArgs with double null dst^:=#0; Inc(dst); // Truncate buffers SysReAllocMem(FArgs, pointer(dst) - pointer(FArgs)); SysReAllocMem(Fargv, Fargc*SizeOf(pointer)); end; function CmdLine: PRtlChar; begin setup_arguments; Result:=FCmdLine; end; function argc: longint; begin setup_arguments; Result:=Fargc; end; function paramcount : longint; begin paramcount := argc - 1; end; function paramstr(l : longint) : RtlString; begin setup_arguments; if (l>=0) and (l 0) then dec(exceptLevel); eip:=exceptEip[exceptLevel]; error:=exceptError[exceptLevel]; {$ifdef SYSTEMEXCEPTIONDEBUG} if IsConsole then writeln(stderr,'In JumpToHandleErrorFrame error=',error); {$endif SYSTEMEXCEPTIONDEBUG} if resetFPU[exceptLevel] then SysResetFPU; { build a fake stack } asm {$ifdef REGCALL} movl ebp,%ecx movl eip,%edx movl error,%eax pushl eip movl ebp,%ebp // Change frame pointer {$else} movl ebp,%eax pushl %eax movl eip,%eax pushl %eax movl error,%eax pushl %eax movl eip,%eax pushl %eax movl ebp,%ebp // Change frame pointer {$endif} {$ifdef SYSTEMEXCEPTIONDEBUG} jmpl DebugHandleErrorAddrFrame {$else not SYSTEMEXCEPTIONDEBUG} jmpl HandleErrorAddrFrame {$endif SYSTEMEXCEPTIONDEBUG} end; end; function syswin32_i386_exception_handler(excep : PExceptionPointers) : Longint;stdcall; var res: longint; err: byte; must_reset_fpu: boolean; begin res := EXCEPTION_CONTINUE_SEARCH; if excep^.ContextRecord^.SegSs=_SS then begin err := 0; must_reset_fpu := true; {$ifdef SYSTEMEXCEPTIONDEBUG} if IsConsole then Writeln(stderr,'Exception ', hexstr(excep^.ExceptionRecord^.ExceptionCode, 8)); {$endif SYSTEMEXCEPTIONDEBUG} case excep^.ExceptionRecord^.ExceptionCode of STATUS_INTEGER_DIVIDE_BY_ZERO, STATUS_FLOAT_DIVIDE_BY_ZERO : err := 200; STATUS_ARRAY_BOUNDS_EXCEEDED : begin err := 201; must_reset_fpu := false; end; STATUS_STACK_OVERFLOW : begin err := 202; must_reset_fpu := false; end; STATUS_FLOAT_OVERFLOW : err := 205; STATUS_FLOAT_DENORMAL_OPERAND, STATUS_FLOAT_UNDERFLOW : err := 206; {excep^.ContextRecord^.FloatSave.StatusWord := excep^.ContextRecord^.FloatSave.StatusWord and $ffffff00;} STATUS_FLOAT_INEXACT_RESULT, STATUS_FLOAT_INVALID_OPERATION, STATUS_FLOAT_STACK_CHECK : err := 207; STATUS_INTEGER_OVERFLOW : begin err := 215; must_reset_fpu := false; end; STATUS_ILLEGAL_INSTRUCTION: { if we're testing sse support, simply set the flag and continue } if sse_check then begin os_supports_sse:=false; { skip the offending movaps %xmm7, %xmm6 instruction } inc(excep^.ContextRecord^.Eip,3); excep^.ExceptionRecord^.ExceptionCode := 0; res:=EXCEPTION_CONTINUE_EXECUTION; end else err := 216; STATUS_ACCESS_VIOLATION: { Athlon prefetch bug? } if is_prefetch(pointer(excep^.ContextRecord^.Eip)) then begin { if yes, then retry } excep^.ExceptionRecord^.ExceptionCode := 0; res:=EXCEPTION_CONTINUE_EXECUTION; end else err := 216; STATUS_CONTROL_C_EXIT: err := 217; STATUS_PRIVILEGED_INSTRUCTION: begin err := 218; must_reset_fpu := false; end; else begin if ((excep^.ExceptionRecord^.ExceptionCode and SEVERITY_ERROR) = SEVERITY_ERROR) then err := 217 else err := 255; end; end; if (err <> 0) and (exceptLevel < MaxExceptionLevel) then begin exceptEip[exceptLevel] := excep^.ContextRecord^.Eip; exceptError[exceptLevel] := err; resetFPU[exceptLevel] := must_reset_fpu; inc(exceptLevel); excep^.ContextRecord^.Eip := Longint(@JumpToHandleErrorFrame); excep^.ExceptionRecord^.ExceptionCode := 0; res := EXCEPTION_CONTINUE_EXECUTION; {$ifdef SYSTEMEXCEPTIONDEBUG} if IsConsole then begin writeln(stderr,'Exception Continue Exception set at ', hexstr(exceptEip[exceptLevel],8)); writeln(stderr,'Eip changed to ', hexstr(longint(@JumpToHandleErrorFrame),8), ' error=', error); end; {$endif SYSTEMEXCEPTIONDEBUG} end; end; syswin32_i386_exception_handler := res; end; procedure install_exception_handlers; {$ifdef SYSTEMEXCEPTIONDEBUG} var oldexceptaddr, newexceptaddr : Longint; {$endif SYSTEMEXCEPTIONDEBUG} begin {$ifdef SYSTEMEXCEPTIONDEBUG} asm movl $0,%eax movl %fs:(%eax),%eax movl %eax,oldexceptaddr end; {$endif SYSTEMEXCEPTIONDEBUG} SetUnhandledExceptionFilter(@syswin32_i386_exception_handler); {$ifdef SYSTEMEXCEPTIONDEBUG} asm movl $0,%eax movl %fs:(%eax),%eax movl %eax,newexceptaddr end; if IsConsole then writeln(stderr,'Old exception ',hexstr(oldexceptaddr,8), ' new exception ',hexstr(newexceptaddr,8)); {$endif SYSTEMEXCEPTIONDEBUG} end; procedure remove_exception_handlers; begin SetUnhandledExceptionFilter(nil); end; {$else not cpui386 (Processor specific !!)} procedure install_exception_handlers; begin end; procedure remove_exception_handlers; begin end; {$endif Set_i386_Exception_handler} const { MultiByteToWideChar } MB_PRECOMPOSED = 1; CP_ACP = 0; WC_NO_BEST_FIT_CHARS = $400; function MultiByteToWideChar(CodePage:UINT; dwFlags:DWORD; lpMultiByteStr:PChar; cchMultiByte:longint; lpWideCharStr:PWideChar;cchWideChar:longint):longint; stdcall; external 'kernel32' name 'MultiByteToWideChar'; function WideCharToMultiByte(CodePage:UINT; dwFlags:DWORD; lpWideCharStr:PWideChar; cchWideChar:longint; lpMultiByteStr:PChar;cchMultiByte:longint; lpDefaultChar:PChar; lpUsedDefaultChar:pointer):longint; stdcall; external 'kernel32' name 'WideCharToMultiByte'; function CharUpperBuff(lpsz:LPWSTR; cchLength:DWORD):DWORD; stdcall; external 'user32' name 'CharUpperBuffW'; function CharLowerBuff(lpsz:LPWSTR; cchLength:DWORD):DWORD; stdcall; external 'user32' name 'CharLowerBuffW'; {****************************************************************************** Widestring ******************************************************************************} procedure Win32Wide2AnsiMove(source:pwidechar;var dest:ansistring;len:SizeInt); var destlen: SizeInt; begin // retrieve length including trailing #0 // not anymore, because this must also be usable for single characters destlen:=WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, source, len, nil, 0, nil, nil); // this will null-terminate setlength(dest, destlen); WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, source, len, @dest[1], destlen, nil, nil); end; procedure Win32Ansi2WideMove(source:pchar;var dest:widestring;len:SizeInt); var destlen: SizeInt; begin // retrieve length including trailing #0 // not anymore, because this must also be usable for single characters destlen:=MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, source, len, nil, 0); // this will null-terminate setlength(dest, destlen); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, source, len, @dest[1], destlen); end; function Win32WideUpper(const s : WideString) : WideString; begin result:=s; if length(result)>0 then CharUpperBuff(LPWSTR(result),length(result)); end; function Win32WideLower(const s : WideString) : WideString; begin result:=s; if length(result)>0 then CharLowerBuff(LPWSTR(result),length(result)); end; {******************************************************************************} { include code common with win64 } {$I syswin.inc} {******************************************************************************} function CheckInitialStkLen(stklen : SizeUInt) : SizeUInt; type tdosheader = packed record e_magic : word; e_cblp : word; e_cp : word; e_crlc : word; e_cparhdr : word; e_minalloc : word; e_maxalloc : word; e_ss : word; e_sp : word; e_csum : word; e_ip : word; e_cs : word; e_lfarlc : word; e_ovno : word; e_res : array[0..3] of word; e_oemid : word; e_oeminfo : word; e_res2 : array[0..9] of word; e_lfanew : longint; end; tpeheader = packed record PEMagic : longint; Machine : word; NumberOfSections : word; TimeDateStamp : longint; PointerToSymbolTable : longint; NumberOfSymbols : longint; SizeOfOptionalHeader : word; Characteristics : word; Magic : word; MajorLinkerVersion : byte; MinorLinkerVersion : byte; SizeOfCode : longint; SizeOfInitializedData : longint; SizeOfUninitializedData : longint; AddressOfEntryPoint : longint; BaseOfCode : longint; BaseOfData : longint; ImageBase : longint; SectionAlignment : longint; FileAlignment : longint; MajorOperatingSystemVersion : word; MinorOperatingSystemVersion : word; MajorImageVersion : word; MinorImageVersion : word; MajorSubsystemVersion : word; MinorSubsystemVersion : word; Reserved1 : longint; SizeOfImage : longint; SizeOfHeaders : longint; CheckSum : longint; Subsystem : word; DllCharacteristics : word; SizeOfStackReserve : longint; SizeOfStackCommit : longint; SizeOfHeapReserve : longint; SizeOfHeapCommit : longint; LoaderFlags : longint; NumberOfRvaAndSizes : longint; DataDirectory : array[1..$80] of byte; end; begin result:=tpeheader((pointer(SysInstance)+(tdosheader(pointer(SysInstance)^).e_lfanew))^).SizeOfStackReserve; end; initialization { get some helpful informations } GetStartupInfo(@startupinfo); SysResetFPU; if not(IsLibrary) then SysInitFPU; { some misc Win32 stuff } hprevinst:=0; if not IsLibrary then SysInstance:=getmodulehandle(nil); MainInstance:=SysInstance; { pass dummy value } StackLength := CheckInitialStkLen($1000000); StackBottom := StackTop - StackLength; cmdshow:=startupinfo.wshowwindow; { Setup heap } InitHeap; SysInitExceptions; { setup fastmove stuff } fpc_cpucodeinit; SetupProcVars; { Reset IO Error } InOutRes:=0; ProcessID := GetCurrentProcessID; { threading } InitSystemThreads; { Reset internal error variable } errno:=0; initvariantmanager; initwidestringmanager; {$ifndef VER2_2} initunicodestringmanager; {$endif VER2_2} InitWin32Widestrings; DispCallByIDProc:=@DoDispCallByIDError; SysInitStdIO; finalization SysFreeMem(FArgs); SysFreeMem(FArgv); {$ifdef FPC_RTLSTRING_UTF16} SysFreeMem(FAnsiArgs); SysFreeMem(FAnsiArgv); {$endif FPC_RTLSTRING_UTF16} end.