system.pp 11 KB

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