system.pp 11 KB

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