system.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2009 by Sven Barth
  4. FPC Pascal system unit for the WinNT API.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit System;
  12. interface
  13. {$IFNDEF FPC_DISABLE_MONITOR}
  14. {$DEFINE SYSTEM_HAS_FEATURE_MONITOR}
  15. {$ENDIF}
  16. {$define FPC_IS_SYSTEM}
  17. {$ifdef SYSTEMDEBUG}
  18. {$define SYSTEMEXCEPTIONDEBUG}
  19. {$endif SYSTEMDEBUG}
  20. {$ifdef cpui386}
  21. {$define Set_i386_Exception_handler}
  22. {$endif cpui386}
  23. {$define DISABLE_NO_THREAD_MANAGER}
  24. {$ifdef KMODE}
  25. // in KernelMode we need use a memory manager that just wraps the routines
  26. // provided by the NT Executive and allows to select whether we want to use
  27. // paged or non-paged (use sparely!) memory
  28. {$define HAS_MEMORYMANAGER}
  29. {$endif KMODE}
  30. { include system-independent routine headers }
  31. {$I systemh.inc}
  32. var
  33. CurrentPeb: Pointer;
  34. IsDeviceDriver: Boolean = False;
  35. const
  36. LineEnding = #13#10;
  37. LFNSupport = true;
  38. DirectorySeparator = '\';
  39. DriveSeparator = '\';
  40. ExtensionSeparator = '.';
  41. PathSeparator = ';';
  42. AllowDirectorySeparators : set of AnsiChar = ['\'];
  43. AllowDriveSeparators : set of AnsiChar = [];
  44. { FileNameCaseSensitive and FileNameCasePreserving are defined separately below!!! }
  45. maxExitCode = High(ErrorCode);
  46. MaxPathLen = High(Word);
  47. AllFilesMask = '*';
  48. type
  49. PEXCEPTION_FRAME = ^TEXCEPTION_FRAME;
  50. TEXCEPTION_FRAME = record
  51. next : PEXCEPTION_FRAME;
  52. handler : pointer;
  53. end;
  54. var
  55. { C compatible arguments }
  56. argc: LongWord;
  57. argvw: PPWideChar;
  58. argv: PPAnsiChar;
  59. const
  60. { Default filehandles }
  61. UnusedHandle : THandle = 0;
  62. StdInputHandle : THandle = 0;
  63. StdOutputHandle : THandle = 0;
  64. StdErrorHandle : THandle = 0;
  65. {$ifndef kmode}
  66. type
  67. TDLL_Entry_Hook = procedure (dllparam : longint);
  68. const
  69. Dll_Process_Detach_Hook : TDLL_Entry_Hook = nil;
  70. Dll_Thread_Attach_Hook : TDLL_Entry_Hook = nil;
  71. Dll_Thread_Detach_Hook : TDLL_Entry_Hook = nil;
  72. {$endif}
  73. const
  74. // NT is case sensitive
  75. FileNameCaseSensitive : boolean = true;
  76. FileNameCasePreserving: boolean = true;
  77. // todo: check whether this is really the case on NT
  78. CtrlZMarksEOF: boolean = true; (* #26 is considered as end of file *)
  79. sLineBreak = LineEnding;
  80. DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsCRLF;
  81. System_exception_frame : PEXCEPTION_FRAME =nil;
  82. implementation
  83. { include system independent routines }
  84. {$I system.inc}
  85. function fpc_pwidechar_length(p: PWideChar): SizeInt; external name 'FPC_PWIDECHAR_LENGTH';
  86. { based on setup_arguments from Win32 RTL }
  87. procedure setup_arguments;
  88. var
  89. i,len,
  90. arglen,
  91. count : longint;
  92. argstart,
  93. pc,arg : pwidechar;
  94. pc2 : PAnsiChar;
  95. quote : Boolean;
  96. argvlen : longint;
  97. params : PRTLUserProcessParameters;
  98. procedure allocarg(idx,len:longint);
  99. var
  100. oldargvlen : longint;
  101. begin
  102. if idx>=argvlen then
  103. begin
  104. oldargvlen:=argvlen;
  105. argvlen:=(idx+8) and (not 7);
  106. sysreallocmem(argvw,argvlen*sizeof(pointer));
  107. fillchar(argvw[oldargvlen],(argvlen-oldargvlen)*sizeof(pointer),0);
  108. end;
  109. { use realloc to reuse already existing memory }
  110. { always allocate, even if length is zero, since }
  111. { the arg. is still present! }
  112. sysreallocmem(argvw[idx],len*sizeof(widechar)+2);
  113. end;
  114. begin
  115. { create commandline, it starts with the executed filename which is argvw[0] }
  116. { NativeNT passes inside the PEB which is passed on startup }
  117. argvw:=nil;
  118. argv:=nil;
  119. argvlen:=0;
  120. params:=PSimplePEB(CurrentPEB)^.ProcessParameters;
  121. ArgLen:=params^.ImagePathName.Length + 1;
  122. allocarg(0,arglen);
  123. move(params^.ImagePathName.Buffer^,argvw[0]^,arglen*sizeof(widechar)+1);
  124. { Setup cmdline variable }
  125. { cmdline is a PAnsiChar, but NT uses PWideChar... don't set cmdline for now }
  126. {$message warning 'cmdline is not set'}
  127. // cmdline:=GetCommandLine;
  128. { the first argument isn't the image file name, so start at 1 }
  129. count:=1;
  130. { process arguments }
  131. pc:=params^.CommandLine.Buffer;
  132. while pc^<>#0 do
  133. begin
  134. { skip leading spaces }
  135. while (Ord(pc^) >= 1) and (Ord(pc^) <= 32) {pc^ in [#1..#32]} do
  136. inc(pc);
  137. if pc^=#0 then
  138. break;
  139. { calc argument length }
  140. quote:=False;
  141. argstart:=pc;
  142. arglen:=0;
  143. while pc^<>#0 do
  144. begin
  145. case pc^ of
  146. #1..#32 :
  147. begin
  148. if quote then
  149. inc(arglen)
  150. else
  151. break;
  152. end;
  153. '"' :
  154. if pc[1]<>'"' then
  155. quote := not quote
  156. else
  157. inc(pc);
  158. else
  159. inc(arglen);
  160. end;
  161. inc(pc);
  162. end;
  163. { copy argument }
  164. { Don't copy the first one, it is already there.}
  165. If Count<>0 then
  166. begin
  167. allocarg(count,arglen);
  168. quote:=False;
  169. pc:=argstart;
  170. arg:=argvw[count];
  171. while (pc^<>#0) do
  172. begin
  173. case pc^ of
  174. #1..#32 :
  175. begin
  176. if quote then
  177. begin
  178. arg^:=pc^;
  179. inc(arg);
  180. end
  181. else
  182. break;
  183. end;
  184. '"' :
  185. if pc[1]<>'"' then
  186. quote := not quote
  187. else
  188. inc(pc);
  189. else
  190. begin
  191. arg^:=pc^;
  192. inc(arg);
  193. end;
  194. end;
  195. inc(pc);
  196. end;
  197. arg^:=#0;
  198. end;
  199. inc(count);
  200. end;
  201. { get argc }
  202. argc:=count;
  203. { free unused memory, leaving a nil entry at the end }
  204. sysreallocmem(argvw,(count+1)*sizeof(pointer));
  205. argvw[count] := nil;
  206. { now we need to fill argv with UTF8 encoded arguments }
  207. sysreallocmem(argv,(count+1)*sizeof(pointer));
  208. fillchar(argv^,(count+1)*sizeof(pointer),0);
  209. for i := 0 to count - 1 do begin
  210. len := fpc_pwidechar_length(argvw[i]);
  211. pc := argvw[i];
  212. argv[i]:=nil;
  213. sysreallocmem(argv[i],len+1);
  214. pc2 := argv[i];
  215. {$message warning 'Use UnicodeToUTF8 for argument conversion'}
  216. while Ord(pc^) > 0 do begin
  217. if word(pc^) < 127 then
  218. pc2^ := AnsiChar(word(pc^))
  219. else
  220. pc2^ := '?';
  221. Inc(pc);
  222. Inc(pc2);
  223. end;
  224. pc2^ := #0;
  225. end;
  226. end;
  227. function paramcount : longint;
  228. begin
  229. paramcount := argc - 1;
  230. end;
  231. function paramstr(l : longint) : shortstring;
  232. begin
  233. if (l>=0) and (l<argc) then
  234. paramstr:=strpas(argv[l])
  235. else
  236. paramstr:='';
  237. end;
  238. procedure KeQueryTickCount(TickCount: PLargeInteger); stdcall; external ntdll name 'KeQueryTickCount';
  239. procedure randomize;
  240. var
  241. tc: PLargeInteger;
  242. begin
  243. FillChar(tc, SizeOf(TLargeInteger), 0);
  244. KeQueryTickCount(@tc);
  245. // the lower part should differ most on system startup
  246. randseed := tc^.LowPart;
  247. end;
  248. {*****************************************************************************
  249. System Dependent Exit code
  250. *****************************************************************************}
  251. procedure PascalMain;external name 'PASCALMAIN';
  252. {$ifndef KMODE}
  253. function NtTerminateProcess(aProcess: THandle; aStatus: LongInt): LongInt; stdcall; external ntdll name 'NtTerminateProcess';
  254. {$endif KMODE}
  255. Procedure system_exit;
  256. begin
  257. if IsLibrary or IsDeviceDriver then
  258. Exit;
  259. {$ifndef KMODE}
  260. NtTerminateProcess(THandle(-1), ExitCode);
  261. {$endif KMODE}
  262. end;
  263. {$ifdef kmode}
  264. function FPCDriverStartup(aDriverObject: Pointer; aRegistryPath: Pointer): LongInt; [public, alias: 'FPC_DriverStartup'];
  265. begin
  266. IsDeviceDriver := True;
  267. IsConsole := True;
  268. IsLibrary := True;
  269. SysDriverObject := aDriverObject;
  270. SysRegistryPath := aRegistryPath;
  271. PASCALMAIN;
  272. SysDriverObject := Nil;
  273. SysRegistryPath := Nil;
  274. Result := ExitCode;
  275. end;
  276. {$else}
  277. const
  278. DLL_PROCESS_ATTACH = 1;
  279. DLL_THREAD_ATTACH = 2;
  280. DLL_PROCESS_DETACH = 0;
  281. DLL_THREAD_DETACH = 3;
  282. function FPCDLLEntry(aHInstance: Pointer; aDLLReason: LongInt; aDLLParam: LongInt): LongBool; [public, alias: 'FPC_DLLEntry'];
  283. begin
  284. IsLibrary := True;
  285. FPCDLLEntry := True;
  286. case aDLLReason of
  287. DLL_PROCESS_ATTACH: begin
  288. PascalMain;
  289. FPCDLLEntry := ExitCode = 0;
  290. end;
  291. DLL_THREAD_ATTACH: begin
  292. if Dll_Thread_Attach_Hook <> Nil then
  293. Dll_Thread_Attach_Hook(aDllParam);
  294. end;
  295. DLL_THREAD_DETACH: begin
  296. if Dll_Thread_Detach_Hook <> Nil then
  297. Dll_Thread_Detach_Hook(aDllParam);
  298. end;
  299. DLL_PROCESS_DETACH: begin
  300. if Dll_Process_Detach_Hook <> Nil then
  301. Dll_Process_Detach_Hook(aDllParam);
  302. // finalize units
  303. internal_do_exit;
  304. end;
  305. end;
  306. end;
  307. procedure FPCProcessStartup(aArgument: Pointer);[public, alias: 'FPC_ProcessStartup'];
  308. begin
  309. IsConsole := True;
  310. IsLibrary := False;
  311. CurrentPeb := aArgument;
  312. PASCALMAIN;
  313. system_exit;
  314. end;
  315. {$endif}
  316. {$ifdef kmode}
  317. // Kernel Mode Entry Point
  318. function NtDriverEntry( aDriverObject: Pointer; aRegistryPath: Pointer ): LongInt; stdcall; [public, alias: '_NtDriverEntry'];
  319. begin
  320. NtDriverEntry := FPCDriverStartup(aDriverObject, aRegistryPath);
  321. end;
  322. {$else}
  323. // User Mode Entry Points
  324. procedure NtProcessStartup( aArgument: Pointer ); stdcall; [public, alias: '_NtProcessStartup'];
  325. begin
  326. FPCProcessStartup(aArgument);
  327. end;
  328. function DLLMainStartup( aHInstance: Pointer; aDLLReason, aDLLParam: LongInt ): LongBool; stdcall; [public, alias: '_DLLMainStartup'];
  329. begin
  330. DLLMainStartup := FPCDLLEntry(aHInstance, aDLLReason, aDLLParam);
  331. end;
  332. {$endif}
  333. procedure SysInitStdIO;
  334. begin
  335. { This function is currently only called if the RTL is compiled for Usermode;
  336. one could think about adding a text driver that outputs using DbgPrint }
  337. {$ifndef KMODE}
  338. with PSimplePEB(CurrentPEB)^.ProcessParameters^ do begin
  339. StdInputHandle := StandardInput;
  340. StdOutputHandle := StandardOutput;
  341. StdErrorHandle := StandardError;
  342. end;
  343. if StdInputHandle <> 0 then
  344. OpenStdIO(Input, fmInput, StdInputHandle)
  345. else
  346. Assign(Input, '');
  347. if StdOutputHandle <> 0 then begin
  348. OpenStdIO(Output, fmOutput, StdOutputHandle);
  349. OpenStdIO(StdOut, fmOutput, StdOutputHandle);
  350. end else begin
  351. Assign(Output, '');
  352. Assign(StdOut, '');
  353. end;
  354. if StdErrorHandle <> 0 then begin
  355. OpenStdIO(ErrOutput, fmOutput, StdErrorHandle);
  356. OpenStdIO(StdErr, fmOutput, StdErrorHandle);
  357. end else begin
  358. Assign(ErrOutput, '');
  359. Assign(StdErr, '');
  360. end;
  361. {$endif}
  362. end;
  363. function GetProcessID: SizeUInt;
  364. begin
  365. {$ifdef kmode}
  366. // it might be that we can detect the user process that called us,
  367. // but that needs to be checked... so for now just return 0
  368. Result := 0;
  369. {$else}
  370. Result := NtCurrentTEB^.ClientID.UniqueProcess;
  371. {$endif}
  372. end;
  373. begin
  374. {$if not defined(KMODE) and not defined(HAS_MEMORYMANAGER)}
  375. { Setup heap }
  376. InitHeap;
  377. {$endif ndef KMODE and ndef HAS_MEMORYMANAGER}
  378. SysInitExceptions;
  379. { we do not use winlike widestrings and also the RTL can't be compiled with
  380. 2.2, so we can savely use the UnicodeString manager only. }
  381. initunicodestringmanager;
  382. {$ifndef KMODE}
  383. SysInitStdIO;
  384. { Arguments }
  385. setup_arguments;
  386. {$endif}
  387. InOutRes := 0;
  388. InitSystemThreads;
  389. errno := 0;
  390. end.