{ $Id$ This file is part of the Free Pascal run time library. Copyright (c) 1999-2000 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. **********************************************************************} {$ifndef VER1_0} { $define MT} {$endif VER1_0} unit {$ifdef VER1_0}SysWin32{$else}System{$endif}; interface {$ifdef SYSTEMDEBUG} {$define SYSTEMEXCEPTIONDEBUG} {$endif SYSTEMDEBUG} {$ifdef cpui386} {$define Set_i386_Exception_handler} {$endif cpui386} { Ctrl-Z means EOF } {$DEFINE EOF_CTRLZ} { include system-independent routine headers } {$I systemh.inc} const LineEnding = #13#10; LFNSupport = true; DirectorySeparator = '\'; DriveSeparator = ':'; PathSeparator = ';'; { FileNameCaseSensitive is defined separately below!!! } maxExitCode = 65535; type PEXCEPTION_FRAME = ^TEXCEPTION_FRAME; TEXCEPTION_FRAME = record next : PEXCEPTION_FRAME; handler : pointer; end; const { Default filehandles } UnusedHandle : THandle = -1; StdInputHandle : THandle = 0; StdOutputHandle : THandle = 0; StdErrorHandle : THandle = 0; FileNameCaseSensitive : boolean = true; 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 { C compatible arguments } argc : longint; argv : ppchar; { Win32 Info } startupinfo : tstartupinfo; hprevinst, HInstance, MainInstance, cmdshow : longint; DLLreason,DLLparam:longint; Win32StackTop : 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; type HMODULE = THandle; implementation { include system independent routines } {$I system.inc} {***************************************************************************** Parameter Handling *****************************************************************************} var ModuleName : array[0..255] of char; function GetCommandFile:pchar; begin GetModuleFileName(0,@ModuleName,255); GetCommandFile:=@ModuleName; end; procedure setup_arguments; var arglen, count : longint; argstart, pc,arg : pchar; quote : char; argvlen : longint; procedure allocarg(idx,len:longint); var oldargvlen : longint; begin if idx>=argvlen then begin oldargvlen:=argvlen; argvlen:=(idx+8) and (not 7); sysreallocmem(argv,argvlen*sizeof(pointer)); fillchar(argv[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(argv[idx],len+1); end; begin { create commandline, it starts with the executed filename which is argv[0] } { Win32 passes the command NOT via the args, but via getmodulefilename} count:=0; argv:=nil; argvlen:=0; pc:=getcommandfile; Arglen:=0; repeat Inc(Arglen); until (pc[Arglen]=#0); allocarg(count,arglen); move(pc^,argv[count]^,arglen); { Setup cmdline variable } cmdline:=GetCommandLine; { process arguments } pc:=cmdline; {$IfDef SYSTEM_DEBUG_STARTUP} Writeln(stderr,'Win32 GetCommandLine is #',pc,'#'); {$EndIf } while pc^<>#0 do begin { skip leading spaces } while pc^ in [#1..#32] do inc(pc); if pc^=#0 then break; { calc argument length } quote:=' '; argstart:=pc; arglen:=0; while (pc^<>#0) do begin case pc^ of #1..#32 : begin if quote<>' ' then inc(arglen) else break; end; '"' : begin if quote<>'''' then begin if pchar(pc+1)^<>'"' then begin if quote='"' then quote:=' ' else quote:='"'; end else inc(pc); end else inc(arglen); end; '''' : begin if quote<>'"' then begin if pchar(pc+1)^<>'''' then begin if quote='''' then quote:=' ' else quote:=''''; end else inc(pc); end else inc(arglen); end; 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:=' '; pc:=argstart; arg:=argv[count]; while (pc^<>#0) do begin case pc^ of #1..#32 : begin if quote<>' ' then begin arg^:=pc^; inc(arg); end else break; end; '"' : begin if quote<>'''' then begin if pchar(pc+1)^<>'"' then begin if quote='"' then quote:=' ' else quote:='"'; end else inc(pc); end else begin arg^:=pc^; inc(arg); end; end; '''' : begin if quote<>'"' then begin if pchar(pc+1)^<>'''' then begin if quote='''' then quote:=' ' else quote:=''''; end else inc(pc); end else begin arg^:=pc^; inc(arg); end; end; else begin arg^:=pc^; inc(arg); end; end; inc(pc); end; arg^:=#0; end; {$IfDef SYSTEM_DEBUG_STARTUP} Writeln(stderr,'dos arg ',count,' #',arglen,'#',argv[count],'#'); {$EndIf SYSTEM_DEBUG_STARTUP} inc(count); end; { get argc and create an nil entry } argc:=count; allocarg(argc,0); { free unused memory } sysreallocmem(argv,(argc+1)*sizeof(pointer)); end; function paramcount : longint; begin paramcount := argc - 1; end; function paramstr(l : longint) : string; begin 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 asm fninit fldcw fpucw end; { 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 frame, res : longint; function SysHandleErrorFrame(error, frame : Longint; must_reset_fpu : Boolean) : Longint; begin if (frame = 0) then SysHandleErrorFrame:=EXCEPTION_CONTINUE_SEARCH else begin if (exceptLevel >= MaxExceptionLevel) then exit; exceptEip[exceptLevel] := excep^.ContextRecord^.Eip; exceptError[exceptLevel] := error; resetFPU[exceptLevel] := must_reset_fpu; inc(exceptLevel); excep^.ContextRecord^.Eip := Longint(@JumpToHandleErrorFrame); excep^.ExceptionRecord^.ExceptionCode := 0; SysHandleErrorFrame := 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; begin if excep^.ContextRecord^.SegSs=_SS then frame := excep^.ContextRecord^.Ebp else frame := 0; res := EXCEPTION_CONTINUE_SEARCH; {$ifdef SYSTEMEXCEPTIONDEBUG} if IsConsole then Writeln(stderr,'Exception ', hexstr(excep^.ExceptionRecord^.ExceptionCode, 8)); {$endif SYSTEMEXCEPTIONDEBUG} case cardinal(excep^.ExceptionRecord^.ExceptionCode) of STATUS_INTEGER_DIVIDE_BY_ZERO, STATUS_FLOAT_DIVIDE_BY_ZERO : res := SysHandleErrorFrame(200, frame, true); STATUS_ARRAY_BOUNDS_EXCEEDED : res := SysHandleErrorFrame(201, frame, false); STATUS_STACK_OVERFLOW : res := SysHandleErrorFrame(202, frame, false); STATUS_FLOAT_OVERFLOW : res := SysHandleErrorFrame(205, frame, true); STATUS_FLOAT_DENORMAL_OPERAND, STATUS_FLOAT_UNDERFLOW : res := SysHandleErrorFrame(206, frame, true); {excep^.ContextRecord^.FloatSave.StatusWord := excep^.ContextRecord^.FloatSave.StatusWord and $ffffff00;} STATUS_FLOAT_INEXACT_RESULT, STATUS_FLOAT_INVALID_OPERATION, STATUS_FLOAT_STACK_CHECK : res := SysHandleErrorFrame(207, frame, true); STATUS_INTEGER_OVERFLOW : res := SysHandleErrorFrame(215, frame, false); STATUS_ILLEGAL_INSTRUCTION: res := SysHandleErrorFrame(216, frame, true); 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 res := SysHandleErrorFrame(216, frame, true); STATUS_CONTROL_C_EXIT: res := SysHandleErrorFrame(217, frame, true); STATUS_PRIVILEGED_INSTRUCTION: res := SysHandleErrorFrame(218, frame, false); else begin if ((excep^.ExceptionRecord^.ExceptionCode and SEVERITY_ERROR) = SEVERITY_ERROR) then res := SysHandleErrorFrame(217, frame, true) else res := SysHandleErrorFrame(255, frame, true); 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} {$ifdef HASWIDESTRING} {**************************************************************************** OS dependend widestrings ****************************************************************************} function CharUpperBuff(lpsz:LPWSTR; cchLength:DWORD):DWORD; stdcall; external 'user32' name 'CharUpperBuffW'; function CharLowerBuff(lpsz:LPWSTR; cchLength:DWORD):DWORD; stdcall; external 'user32' name 'CharLowerBuffW'; function Win32WideUpper(const s : WideString) : WideString; begin result:=s; UniqueString(result); if length(result)>0 then CharUpperBuff(LPWSTR(result),length(result)); end; function Win32WideLower(const s : WideString) : WideString; begin result:=s; UniqueString(result); if length(result)>0 then CharLowerBuff(LPWSTR(result),length(result)); end; { there is a similiar procedure in sysutils which inits the fields which are only relevant for the sysutils units } procedure InitWin32Widestrings; begin widestringmanager.UpperWideStringProc:=@Win32WideUpper; widestringmanager.LowerWideStringProc:=@Win32WideLower; end; {$endif HASWIDESTRING} {**************************************************************************** Error Message writing using messageboxes ****************************************************************************} function MessageBox(w1:longint;l1,l2:pointer;w2:longint):longint; stdcall;external 'user32' name 'MessageBoxA'; const ErrorBufferLength = 1024; var ErrorBuf : array[0..ErrorBufferLength] of char; ErrorLen : longint; Function ErrorWrite(Var F: TextRec): Integer; { An error message should always end with #13#10#13#10 } var p : pchar; i : longint; Begin if F.BufPos>0 then begin if F.BufPos+ErrorLen>ErrorBufferLength then i:=ErrorBufferLength-ErrorLen else i:=F.BufPos; Move(F.BufPtr^,ErrorBuf[ErrorLen],i); inc(ErrorLen,i); ErrorBuf[ErrorLen]:=#0; end; if ErrorLen>3 then begin p:=@ErrorBuf[ErrorLen]; for i:=1 to 4 do begin dec(p); if not(p^ in [#10,#13]) then break; end; end; if ErrorLen=ErrorBufferLength then i:=4; if (i=4) then begin MessageBox(0,@ErrorBuf,pchar('Error'),0); ErrorLen:=0; end; F.BufPos:=0; ErrorWrite:=0; End; Function ErrorClose(Var F: TextRec): Integer; begin if ErrorLen>0 then begin MessageBox(0,@ErrorBuf,pchar('Error'),0); ErrorLen:=0; end; ErrorLen:=0; ErrorClose:=0; end; Function ErrorOpen(Var F: TextRec): Integer; Begin TextRec(F).InOutFunc:=@ErrorWrite; TextRec(F).FlushFunc:=@ErrorWrite; TextRec(F).CloseFunc:=@ErrorClose; ErrorOpen:=0; End; procedure AssignError(Var T: Text); begin Assign(T,''); TextRec(T).OpenFunc:=@ErrorOpen; Rewrite(T); end; procedure SysInitStdIO; begin { Setup stdin, stdout and stderr, for GUI apps redirect stderr,stdout to be displayed in and messagebox } StdInputHandle:=longint(GetStdHandle(cardinal(STD_INPUT_HANDLE))); StdOutputHandle:=longint(GetStdHandle(cardinal(STD_OUTPUT_HANDLE))); StdErrorHandle:=longint(GetStdHandle(cardinal(STD_ERROR_HANDLE))); if not IsConsole then begin AssignError(stderr); AssignError(stdout); Assign(Output,''); Assign(Input,''); Assign(ErrOutput,''); end else begin OpenStdIO(Input,fmInput,StdInputHandle); OpenStdIO(Output,fmOutput,StdOutputHandle); OpenStdIO(ErrOutput,fmOutput,StdErrorHandle); OpenStdIO(StdOut,fmOutput,StdOutputHandle); OpenStdIO(StdErr,fmOutput,StdErrorHandle); end; end; (* ProcessID cached to avoid repeated calls to GetCurrentProcess. *) var ProcessID: SizeUInt; function GetProcessID: SizeUInt; begin GetProcessID := ProcessID; end; const Exe_entry_code : pointer = @Exe_entry; Dll_entry_code : pointer = @Dll_entry; begin StackLength := InitialStkLen; StackBottom := Sptr - StackLength; { get some helpful informations } GetStartupInfo(@startupinfo); { some misc Win32 stuff } hprevinst:=0; if not IsLibrary then HInstance:=getmodulehandle(GetCommandFile); MainInstance:=HInstance; cmdshow:=startupinfo.wshowwindow; { Setup heap } InitHeap; SysInitExceptions; SysInitStdIO; { Arguments } setup_arguments; { Reset IO Error } InOutRes:=0; ProcessID := GetCurrentProcessID; { threading } InitSystemThreads; { Reset internal error variable } errno:=0; {$ifdef HASVARIANT} initvariantmanager; {$endif HASVARIANT} {$ifdef HASWIDESTRING} initwidestringmanager; InitWin32Widestrings {$endif HASWIDESTRING} end. { $Log$ Revision 1.72 2005-03-21 16:31:33 peter * fix crash under win32 with previous reallocmem fix Revision 1.71 2005/03/02 19:18:42 florian * fixed compilation with 1.0.10 Revision 1.70 2005/02/26 20:43:52 florian + WideCompareString and WideCompareText for win32 implemented Revision 1.69 2005/02/26 10:21:17 florian + implemented WideFormat + some Widestring stuff implemented * some Widestring stuff fixed Revision 1.68 2005/02/14 17:13:32 peter * truncate log Revision 1.67 2005/02/06 13:06:20 peter * moved file and dir functions to sysfile/sysdir * win32 thread in systemunit Revision 1.66 2005/02/01 20:22:50 florian * improved widestring infrastructure manager }