syswin.inc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2008 by Florian Klaempfl and Pavel Ozerski
  4. member of the Free Pascal development team.
  5. FPC Pascal system unit part shared by win32/win64.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {
  13. Error code definitions for the Win32 API functions
  14. Values are 32 bit values layed out as follows:
  15. 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
  16. 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
  17. +---+-+-+-----------------------+-------------------------------+
  18. |Sev|C|R| Facility | Code |
  19. +---+-+-+-----------------------+-------------------------------+
  20. where
  21. Sev - is the severity code
  22. 00 - Success
  23. 01 - Informational
  24. 10 - Warning
  25. 11 - Error
  26. C - is the Customer code flag
  27. R - is a reserved bit
  28. Facility - is the facility code
  29. Code - is the facility's status code
  30. }
  31. const
  32. SEVERITY_SUCCESS = $00000000;
  33. SEVERITY_INFORMATIONAL = $40000000;
  34. SEVERITY_WARNING = $80000000;
  35. SEVERITY_ERROR = $C0000000;
  36. const
  37. STATUS_SEGMENT_NOTIFICATION = $40000005;
  38. DBG_TERMINATE_THREAD = $40010003;
  39. DBG_TERMINATE_PROCESS = $40010004;
  40. DBG_CONTROL_C = $40010005;
  41. DBG_CONTROL_BREAK = $40010008;
  42. STATUS_GUARD_PAGE_VIOLATION = $80000001;
  43. STATUS_DATATYPE_MISALIGNMENT = $80000002;
  44. STATUS_BREAKPOINT = $80000003;
  45. STATUS_SINGLE_STEP = $80000004;
  46. DBG_EXCEPTION_NOT_HANDLED = $80010001;
  47. STATUS_ACCESS_VIOLATION = $C0000005;
  48. STATUS_IN_PAGE_ERROR = $C0000006;
  49. STATUS_INVALID_HANDLE = $C0000008;
  50. STATUS_NO_MEMORY = $C0000017;
  51. STATUS_ILLEGAL_INSTRUCTION = $C000001D;
  52. STATUS_NONCONTINUABLE_EXCEPTION = $C0000025;
  53. STATUS_INVALID_DISPOSITION = $C0000026;
  54. STATUS_ARRAY_BOUNDS_EXCEEDED = $C000008C;
  55. STATUS_FLOAT_DENORMAL_OPERAND = $C000008D;
  56. STATUS_FLOAT_DIVIDE_BY_ZERO = $C000008E;
  57. STATUS_FLOAT_INEXACT_RESULT = $C000008F;
  58. STATUS_FLOAT_INVALID_OPERATION = $C0000090;
  59. STATUS_FLOAT_OVERFLOW = $C0000091;
  60. STATUS_FLOAT_STACK_CHECK = $C0000092;
  61. STATUS_FLOAT_UNDERFLOW = $C0000093;
  62. STATUS_INTEGER_DIVIDE_BY_ZERO = $C0000094;
  63. STATUS_INTEGER_OVERFLOW = $C0000095;
  64. STATUS_PRIVILEGED_INSTRUCTION = $C0000096;
  65. STATUS_STACK_OVERFLOW = $C00000FD;
  66. STATUS_CONTROL_C_EXIT = $C000013A;
  67. STATUS_FLOAT_MULTIPLE_FAULTS = $C00002B4;
  68. STATUS_FLOAT_MULTIPLE_TRAPS = $C00002B5;
  69. STATUS_REG_NAT_CONSUMPTION = $C00002C9;
  70. { Exceptions raised by RTL use this code }
  71. FPC_EXCEPTION_CODE = $E0465043;
  72. EXCEPTION_EXECUTE_HANDLER = 1;
  73. EXCEPTION_CONTINUE_EXECUTION = -1;
  74. EXCEPTION_CONTINUE_SEARCH = 0;
  75. { exception flags (not everything applies to Win32!) }
  76. EXCEPTION_NONCONTINUABLE = $01;
  77. EXCEPTION_UNWINDING = $02;
  78. EXCEPTION_EXIT_UNWIND = $04;
  79. EXCEPTION_STACK_INVALID = $08;
  80. EXCEPTION_NESTED_CALL = $10;
  81. EXCEPTION_TARGET_UNWIND = $20;
  82. EXCEPTION_COLLIDED_UNWIND = $40;
  83. CONTEXT_X86 = $00010000;
  84. CONTEXT_CONTROL = CONTEXT_X86 or $00000001;
  85. CONTEXT_INTEGER = CONTEXT_X86 or $00000002;
  86. CONTEXT_SEGMENTS = CONTEXT_X86 or $00000004;
  87. CONTEXT_FLOATING_POINT = CONTEXT_X86 or $00000008;
  88. CONTEXT_DEBUG_REGISTERS = CONTEXT_X86 or $00000010;
  89. CONTEXT_EXTENDED_REGISTERS = CONTEXT_X86 or $00000020;
  90. CONTEXT_FULL = CONTEXT_CONTROL or CONTEXT_INTEGER or CONTEXT_SEGMENTS;
  91. MAXIMUM_SUPPORTED_EXTENSION = 512;
  92. type
  93. EXCEPTION_DISPOSITION=(
  94. ExceptionContinueExecution,
  95. ExceptionContinueSearch,
  96. ExceptionNestedException,
  97. ExceptionCollidedUnwind
  98. );
  99. TUnwindProc=procedure(frame: PtrUInt);
  100. PFilterRec=^TFilterRec;
  101. TFilterRec=record
  102. RvaClass: DWord;
  103. RvaHandler: DWord;
  104. end;
  105. TExceptObjProc=function(code: Longint; const rec: TExceptionRecord): Pointer; { Exception }
  106. TExceptClsProc=function(code: Longint): Pointer; { ExceptClass }
  107. function RunErrorCode(const rec: TExceptionRecord): longint;
  108. begin
  109. { negative result means 'FPU reset required' }
  110. case rec.ExceptionCode of
  111. STATUS_INTEGER_DIVIDE_BY_ZERO: result := 200; { reDivByZero }
  112. STATUS_FLOAT_DIVIDE_BY_ZERO: result := -208; { !!reZeroDivide }
  113. STATUS_ARRAY_BOUNDS_EXCEEDED: result := 201; { reRangeError }
  114. STATUS_STACK_OVERFLOW: result := 202; { reStackOverflow }
  115. STATUS_FLOAT_OVERFLOW: result := -205; { reOverflow }
  116. STATUS_FLOAT_DENORMAL_OPERAND,
  117. STATUS_FLOAT_UNDERFLOW: result := -206; { reUnderflow }
  118. STATUS_FLOAT_INEXACT_RESULT,
  119. STATUS_FLOAT_INVALID_OPERATION,
  120. STATUS_FLOAT_STACK_CHECK: result := -207; { reInvalidOp }
  121. STATUS_INTEGER_OVERFLOW: result := 215; { reIntOverflow }
  122. STATUS_ILLEGAL_INSTRUCTION: result := -216;
  123. STATUS_ACCESS_VIOLATION: result := 216; { reAccessViolation }
  124. STATUS_CONTROL_C_EXIT: result := 217; { reControlBreak }
  125. STATUS_PRIVILEGED_INSTRUCTION: result := 218; { rePrivilegedInstruction }
  126. STATUS_FLOAT_MULTIPLE_TRAPS,
  127. STATUS_FLOAT_MULTIPLE_FAULTS: result := -255; { indicate FPU reset }
  128. else
  129. result := 255; { reExternalException }
  130. end;
  131. end;
  132. procedure TranslateMxcsr(mxcsr: longword; var code: longint);
  133. begin
  134. { we can return only one value, further one's are lost }
  135. { InvalidOp }
  136. if (mxcsr and 1)<>0 then
  137. code:=-207
  138. { Denormal }
  139. else if (mxcsr and 2)<>0 then
  140. code:=-206
  141. { !!reZeroDivide }
  142. else if (mxcsr and 4)<>0 then
  143. code:=-208
  144. { reOverflow }
  145. else if (mxcsr and 8)<>0 then
  146. code:=-205
  147. { Underflow }
  148. else if (mxcsr and 16)<>0 then
  149. code:=-206
  150. { Precision }
  151. else if (mxcsr and 32)<>0 then
  152. code:=-207
  153. else { this should not happen }
  154. code:=-255
  155. end;
  156. function FilterException(var rec:TExceptionRecord; imagebase: PtrUInt; filterRva: DWord; errcode: Longint): Pointer;
  157. var
  158. ExClass: TClass;
  159. i: Longint;
  160. Filter: Pointer;
  161. curFilt: PFilterRec;
  162. begin
  163. result:=nil;
  164. if rec.ExceptionCode=FPC_EXCEPTION_CODE then
  165. ExClass:=TObject(rec.ExceptionInformation[1]).ClassType
  166. else if Assigned(ExceptClsProc) then
  167. ExClass:=TClass(TExceptClsProc(ExceptClsProc)(errcode))
  168. else
  169. Exit; { if we cannot determine type of exception, don't handle it }
  170. Filter:=Pointer(imagebase+filterRva);
  171. for i:=0 to PLongint(Filter)^-1 do
  172. begin
  173. CurFilt:=@PFilterRec(Filter+sizeof(Longint))[i];
  174. if (CurFilt^.RvaClass=$FFFFFFFF) or
  175. { TODO: exception might be coming from another module, need more advanced comparing }
  176. (ExClass.InheritsFrom({$if not defined(ver3_2)}PClass(imagebase+CurFilt^.RvaClass)^{$else}TClass(imagebase+CurFilt^.RvaClass){$endif})) then
  177. begin
  178. result:=Pointer(imagebase+CurFilt^.RvaHandler);
  179. exit;
  180. end;
  181. end;
  182. end;
  183. {*****************************************************************************
  184. Parameter Handling
  185. *****************************************************************************}
  186. function WideCharToMultiByte(CodePage:UINT; dwFlags:DWORD; lpWideCharStr:PWideChar; cchWideChar:longint;
  187. lpMultiByteStr:LPSTR;cchMultiByte:longint; lpDefaultChar:PAnsiChar; lpUsedDefaultChar:PLongBool):longint; stdcall; external 'kernel32' name 'WideCharToMultiByte';
  188. function GetCommandLineA : pansichar; stdcall;external KernelDLL name 'GetCommandLineA';
  189. type
  190. { nargs — argument count (without first and without null terminator),
  191. nchars — total widechar count in arguments (with null terminators),
  192. nachars — total ansichar count in arguments (with null terminators), counted only if args = chars = nil. }
  193. ParseCommandLineResult = record
  194. nargs, nchars, nachars: SizeInt;
  195. end;
  196. function ParseCommandLine(cmdLine: PWideChar; args: PPWideChar; chars: PWideChar): ParseCommandLineResult;
  197. var
  198. argsStartInCmdLine: PWideChar;
  199. nCharsUpToPrevArg, nCharsPlusQuotes: SizeInt;
  200. c, quote: WideChar;
  201. skippingFirstArg: boolean;
  202. begin
  203. argsStartInCmdLine:=cmdLine;
  204. nCharsUpToPrevArg:=0;
  205. FillChar(result,sizeof(result),0);
  206. skippingFirstArg:=true;
  207. quote:=' ';
  208. repeat
  209. c:=cmdLine^;
  210. inc(cmdLine);
  211. case c of
  212. #0..#32:
  213. if (quote=' ') or (c=#0) then
  214. begin
  215. if (result.nchars>nCharsUpToPrevArg) then
  216. begin
  217. // End of an argument found
  218. if Assigned(chars) then
  219. chars[result.nchars]:=#0;
  220. inc(result.nchars); { Null terminator. }
  221. nCharsUpToPrevArg:=result.nchars;
  222. end;
  223. skippingFirstArg:=false;
  224. if c = #0 then
  225. break;
  226. continue; // Skip whitespace
  227. end;
  228. '"', '''':
  229. if (c='"') and (quote<>'''') or (c='''') and (quote<>'"') then
  230. if cmdLine^<>c then
  231. begin
  232. if quote=c then
  233. quote:=' '
  234. else
  235. quote:=c;
  236. continue;
  237. end
  238. else
  239. inc(cmdLine);
  240. end;
  241. if skippingFirstArg then
  242. continue;
  243. if result.nchars=nCharsUpToPrevArg then
  244. begin
  245. if Assigned(args) then
  246. args[result.nargs]:=chars+result.nchars;
  247. inc(result.nargs);
  248. if result.nchars=0 then
  249. argsStartInCmdLine:=cmdLine-1;
  250. end;
  251. if Assigned(chars) then
  252. chars[result.nchars]:=c;
  253. inc(result.nchars);
  254. until false;
  255. if Assigned(chars) then
  256. exit;
  257. { Number of widechars in command line starting from argsStartInCmdLine, including markdown: cmdLine - 1 - argsStartInCmdLine. Avoid implicit signed div. }
  258. nCharsPlusQuotes:=SizeUint(pointer(cmdLine-1)-pointer(argsStartInCmdLine)) div sizeof(widechar);
  259. result.nachars:=
  260. { Count of ANSI characters, including markdown. }
  261. WideCharToMultiByte(DefaultSystemCodePage, 0, argsStartInCmdLine, nCharsPlusQuotes, nil, 0, nil, nil)
  262. { Assume each markdown character (quote, space) is ANSI. Subtract markdown, add null terminators; result.nchars already includes null terminators. }
  263. -(nCharsPlusQuotes-result.nchars);
  264. end;
  265. var
  266. argvw: PPWideChar; { Start of the memory region. Should very preferably be private as argv can (and WILL, by LazUTF8) be changed from outside. }
  267. procedure setup_arguments;
  268. var
  269. CmdLineW, wchars: PWideChar;
  270. buf: array[0..MaxPathLen] of WideChar;
  271. iarg, nArg0W, nArg0A: SizeInt;
  272. pc: ParseCommandLineResult;
  273. achars, acharse: PAnsiChar;
  274. begin
  275. CmdLine:=GetCommandLineA;
  276. CmdLineW:=GetCommandLineW;
  277. nArg0W:=GetModuleFileNameW(0, PWideChar(buf), Length(buf));
  278. nArg0A:=WideCharToMultiByte(DefaultSystemCodePage, 0, PWideChar(buf), nArg0W, nil, 0, nil, nil);
  279. pc:=ParseCommandLine(CmdLineW, nil, nil);
  280. argc:=pc.nargs+1;
  281. { Memory region layout:
  282. argc × PWideChar: argvw (internal, not terminated with nil).
  283. (argc + 1) × PAnsiChar: argv (terminated with nil).
  284. Nw × widechar: chars for argvw.
  285. Na × ansichar: chars for argv. }
  286. argvw:=nil;
  287. repeat { First iteration calculates region size (by adding to nil). Second iteration calculates pointers to region parts (by adding to region start). }
  288. argv:=PPAnsiChar(argvw+argc);
  289. wchars:=PWideChar(argv+argc+1);
  290. achars:=PAnsiChar(wchars+nArg0W+1+pc.nchars);
  291. acharse:=achars+nArg0A+1+pc.nachars;
  292. if Assigned(argvw) then
  293. break;
  294. argvw:=SysGetMem(PtrUint(acharse));
  295. until not Assigned(argvw); { If ReturnNilIfGrowHeapFails was customized to true, let it crash on allocation failure instead of looping endlessly. }
  296. Move(PWideChar(buf)^, wchars^, nArg0W*sizeof(widechar));
  297. wchars[nArg0W]:=#0;
  298. argvw[0]:=wchars;
  299. ParseCommandLine(CmdLineW, argvw+1, wchars+nArg0W+1);
  300. { Convert argvw to argv. }
  301. for iarg:=0 to pc.nargs do
  302. begin
  303. argv[iarg]:=achars;
  304. inc(achars, WideCharToMultiByte(DefaultSystemCodePage, 0, argvw[iarg], -1, achars, acharse-achars, nil, nil));
  305. end;
  306. argv[argc]:=nil;
  307. end;
  308. procedure finalize_arguments; inline;
  309. begin
  310. SysFreeMem(argvw);
  311. end;
  312. function paramcount : longint;
  313. begin
  314. paramcount := argc - 1;
  315. end;
  316. Function ParamStrU(l:Longint): UnicodeString; [public,alias:'_FPC_ParamStrU'];
  317. begin
  318. if (l >= 0) and (l < argc) then
  319. Result:=argvw[l]
  320. else
  321. Result:='';
  322. end;
  323. Function ParamStrA(l:Longint): AnsiString; [public,alias:'_FPC_ParamStrA'];
  324. begin
  325. Result:=AnsiString(ParamStrU(l));
  326. end;
  327. Function ParamStr(l:Longint): shortstring;
  328. begin
  329. if (l >= 0) and (l < argc) then
  330. Result:=argv[l]
  331. else
  332. Result:='';
  333. end;
  334. {*****************************************************************************}
  335. procedure randomize;
  336. begin
  337. randseed:=GetTickCount;
  338. end;
  339. Var
  340. DLLInitState : Longint = -1;
  341. DLLBuf : Jmp_buf;
  342. {$if defined(FPC_USE_WIN32_SEH) or defined(FPC_USE_WIN64_SEH)}
  343. {$define FPC_USE_SEH}
  344. {$endif}
  345. function Dll_entry{$ifdef FPC_HAS_INDIRECT_ENTRY_INFORMATION}(constref info : TEntryInformation){$endif FPC_HAS_INDIRECT_ENTRY_INFORMATION} : longbool; [public,alias:'_FPC_DLL_Entry'];
  346. begin
  347. {$ifdef FPC_HAS_INDIRECT_ENTRY_INFORMATION}
  348. SetupEntryInformation(info);
  349. {$endif FPC_HAS_INDIRECT_ENTRY_INFORMATION}
  350. IsLibrary:=true;
  351. DllInitState:=DLLreason;
  352. Dll_entry:=false; { return value is ignored, except when DLLreason=DLL_PROCESS_ATTACH }
  353. case DLLreason of
  354. DLL_PROCESS_ATTACH :
  355. begin
  356. MainThreadIdWin32 := Win32GetCurrentThreadId;
  357. If SetJmp(DLLBuf) = 0 then
  358. begin
  359. {$ifdef FPC_USE_SEH}
  360. try
  361. {$endif}
  362. {$ifdef FPC_HAS_INDIRECT_ENTRY_INFORMATION}
  363. EntryInformation.PascalMain();
  364. {$else FPC_HAS_INDIRECT_ENTRY_INFORMATION}
  365. PascalMain;
  366. {$endif FPC_HAS_INDIRECT_ENTRY_INFORMATION}
  367. Dll_entry:=true;
  368. {$ifdef FPC_USE_SEH}
  369. except
  370. DoUnHandledException;
  371. Dll_entry:=false;
  372. end;
  373. {$endif}
  374. end
  375. else
  376. Dll_entry:=(ExitCode=0);
  377. end;
  378. DLL_THREAD_ATTACH :
  379. begin
  380. { SysInitMultithreading must not be called here,
  381. see comments in exec_tls_callback below }
  382. { Allocate Threadvars }
  383. SysAllocateThreadVars;
  384. { NS : no idea what is correct to pass here - pass dummy value for now }
  385. { passing a dummy is ok, the correct value is read from the coff header of SysInstance (FK) }
  386. InitThread($1000000); { Assume everything is idempotent there, as the thread could have been created with BeginThread... }
  387. if assigned(Dll_Thread_Attach_Hook) then
  388. Dll_Thread_Attach_Hook(DllParam);
  389. end;
  390. DLL_THREAD_DETACH :
  391. begin
  392. if assigned(Dll_Thread_Detach_Hook) then
  393. Dll_Thread_Detach_Hook(DllParam);
  394. { Release Threadvars }
  395. if TlsGetValue(TLSKey^)<>nil then
  396. DoneThread; { Assume everything is idempotent there }
  397. end;
  398. DLL_PROCESS_DETACH :
  399. begin
  400. if MainThreadIDWin32=0 then // already been here.
  401. exit;
  402. If SetJmp(DLLBuf) = 0 then
  403. begin
  404. if assigned(Dll_Process_Detach_Hook) then
  405. Dll_Process_Detach_Hook(DllParam);
  406. InternalExit;
  407. end;
  408. SysReleaseThreadVars;
  409. { Free TLS resources used by ThreadVars }
  410. SysFiniMultiThreading;
  411. MainThreadIDWin32:=0;
  412. end;
  413. end;
  414. DllInitState:=-1;
  415. end;
  416. {****************************************************************************
  417. Error Message writing using messageboxes
  418. ****************************************************************************}
  419. function MessageBox(hWnd:THandle;lpText,lpCaption:PAnsiChar;uType:uint32):longint;
  420. stdcall;external 'user32' name 'MessageBoxA';
  421. const
  422. ErrorBufferLength = 1024;
  423. var
  424. ErrorBuf : array[0..ErrorBufferLength] of AnsiChar;
  425. ErrorLen : SizeInt;
  426. procedure ShowError(final: boolean);
  427. const
  428. IDCANCEL = 2;
  429. var
  430. showStart, showEnd, tailStart, errLen: SizeInt;
  431. begin
  432. errLen:=ErrorLen; { Local copy of ErrorLen, to soften (when multithreading) or avoid (with single thread) reenterancy issues. }
  433. { See e.g. comment in ErrorOpen about why not set ErrorLen := 0 there. }
  434. tailStart:=errLen;
  435. if tailStart=0 then
  436. exit;
  437. { Search for last line ending to show prettier message.
  438. line1 #13 #10 line2 #13 #10 line3
  439. ^ ^
  440. showEnd tailStart
  441. #0 is then written at showEnd (possibly overwriting EOL character). In the worst case of race, there always will be #0 at ErrorBufferLength. }
  442. if not final then
  443. begin
  444. while (tailStart>ErrorBufferLength div 2) and not (ErrorBuf[tailStart-1] in [#13,#10]) do
  445. dec(tailStart);
  446. if tailStart=ErrorBufferLength div 2 then
  447. tailStart:=errLen;
  448. end;
  449. if not NoErrMsg then
  450. begin
  451. { Strip trailing EOLs even if final. Required when not final (to have a spare character for #0), but even if final, they aren’t pretty and don’t add to anything. }
  452. showEnd:=tailStart;
  453. while (showEnd>0) and (ErrorBuf[showEnd-1] in [#13,#10]) do
  454. dec(showEnd);
  455. { Also strip starting EOLs. }
  456. showStart:=0;
  457. while (showStart<showEnd) and (ErrorBuf[showStart] in [#13,#10]) do
  458. inc(showStart);
  459. ErrorBuf[showEnd]:=#0;
  460. NoErrMsg:=NoErrMsg or (MessageBox(0,@ErrorBuf[showStart],nil,ord(not final) {MB_OK is 0 and MB_OKCANCEL is 1})=IDCANCEL);
  461. end;
  462. dec(errLen,tailStart);
  463. Move(ErrorBuf[tailStart],ErrorBuf[0],errLen*sizeof(ErrorBuf[0]));
  464. ErrorLen:=errLen;
  465. end;
  466. procedure ErrorWrite(Var F: TextRec);
  467. {
  468. An error message should always end with #13#10#13#10
  469. }
  470. var
  471. i,errLen : SizeInt;
  472. Begin
  473. while F.BufPos>0 do
  474. begin
  475. errLen:=ErrorLen; { Not required for single thread unlike in ShowError, but still prevents crashes on races. }
  476. i:=ErrorBufferLength-errLen;
  477. if i>F.BufPos then
  478. i:=F.BufPos;
  479. Move(F.BufPtr^,ErrorBuf[errLen],i);
  480. inc(errLen,i);
  481. ErrorLen:=errLen;
  482. if errLen=ErrorBufferLength then
  483. ShowError(false);
  484. Dec(F.BufPos,i);
  485. Move(PAnsiChar(F.BufPtr^)[i],F.BufPtr^[0],F.BufPos);
  486. end;
  487. End;
  488. procedure ErrorClose(Var F: TextRec);
  489. begin
  490. ShowError(true);
  491. end;
  492. procedure ErrorOpen(Var F: TextRec);
  493. Begin
  494. TextRec(F).InOutFunc:=@ErrorWrite;
  495. TextRec(F).FlushFunc:=@ErrorWrite;
  496. TextRec(F).CloseFunc:=@ErrorClose;
  497. { Better not to set ErrorLen := 0 here: MessageBox performed by ShowError might/will lead to TLS callbacks that might/will open their own stderrs... }
  498. End;
  499. procedure AssignError(Var T: Text);
  500. begin
  501. Assign(T,'');
  502. TextRec(T).OpenFunc:=@ErrorOpen;
  503. Rewrite(T);
  504. end;
  505. procedure SysInitStdIO;
  506. begin
  507. { Setup stdin, stdout and stderr, for GUI apps redirect stderr,stdout to be
  508. displayed in a messagebox }
  509. { WARNING: this should be done only once at startup,
  510. not for DLL entry code, as the standard handles might
  511. have been redirected }
  512. if StdInputHandle=0 then
  513. StdInputHandle:=THandle(GetStdHandle(cardinal(STD_INPUT_HANDLE)));
  514. if StdOutputHandle=0 then
  515. StdOutputHandle:=THandle(GetStdHandle(cardinal(STD_OUTPUT_HANDLE)));
  516. if StdErrorHandle=0 then
  517. StdErrorHandle:=THandle(GetStdHandle(cardinal(STD_ERROR_HANDLE)));
  518. if not IsConsole then
  519. begin
  520. AssignError(stderr);
  521. AssignError(StdOut);
  522. Assign(Output,'');
  523. Assign(Input,'');
  524. Assign(ErrOutput,'');
  525. end
  526. else
  527. begin
  528. OpenStdIO(Input,fmInput,StdInputHandle);
  529. OpenStdIO(Output,fmOutput,StdOutputHandle);
  530. OpenStdIO(ErrOutput,fmOutput,StdErrorHandle);
  531. OpenStdIO(StdOut,fmOutput,StdOutputHandle);
  532. OpenStdIO(StdErr,fmOutput,StdErrorHandle);
  533. end;
  534. end;
  535. function GetProcessID: SizeUInt;
  536. begin
  537. GetProcessID := GetCurrentProcessID;
  538. end;
  539. {******************************************************************************
  540. Unicode
  541. ******************************************************************************}
  542. const
  543. { MultiByteToWideChar }
  544. MB_PRECOMPOSED = 1;
  545. WC_NO_BEST_FIT_CHARS = $400;
  546. function MultiByteToWideChar(CodePage:UINT; dwFlags:DWORD; lpMultiByteStr:PAnsiChar; cchMultiByte:longint; lpWideCharStr:PWideChar;cchWideChar:longint):longint;
  547. stdcall; external 'kernel32' name 'MultiByteToWideChar';
  548. function WideCharToMultiByte(CodePage:UINT; dwFlags:DWORD; lpWideCharStr:PWideChar; cchWideChar:longint; lpMultiByteStr:PAnsiChar;cchMultiByte:longint; lpDefaultChar:PAnsiChar; lpUsedDefaultChar:pointer):longint;
  549. stdcall; external 'kernel32' name 'WideCharToMultiByte';
  550. function CharUpperBuff(lpsz:LPWSTR; cchLength:DWORD):DWORD;
  551. stdcall; external 'user32' name 'CharUpperBuffW';
  552. function CharLowerBuff(lpsz:LPWSTR; cchLength:DWORD):DWORD;
  553. stdcall; external 'user32' name 'CharLowerBuffW';
  554. procedure Win32Unicode2AnsiMove(source:punicodechar;var dest:RawByteString;cp : TSystemCodePage;len:SizeInt);
  555. var
  556. destlen: SizeInt;
  557. begin
  558. // retrieve length including trailing #0
  559. // not anymore, because this must also be usable for single characters
  560. destlen:=WideCharToMultiByte(cp, 0, source, len, nil, 0, nil, nil);
  561. // this will null-terminate
  562. setlength(dest, destlen);
  563. if destlen>0 then
  564. begin
  565. WideCharToMultiByte(cp, 0, source, len, @dest[1], destlen, nil, nil);
  566. PAnsiRec(pointer(dest)-AnsiFirstOff)^.CodePage:=cp;
  567. end;
  568. end;
  569. procedure Win32Ansi2UnicodeMove(source:PAnsiChar;cp : TSystemCodePage;var dest:UnicodeString;len:SizeInt);
  570. var
  571. destlen: SizeInt;
  572. dwflags: DWORD;
  573. begin
  574. // retrieve length including trailing #0
  575. // not anymore, because this must also be usable for single characters
  576. case cp of
  577. // Under https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar
  578. CP_UTF8, CP_UTF7, 50220, 50221, 50222, 50225, 50227, 50229, 57002..57011, 42:
  579. dwFlags:=0
  580. else
  581. dwFlags:=MB_PRECOMPOSED;
  582. end;
  583. destlen:=MultiByteToWideChar(cp, dwFlags, source, len, nil, 0);
  584. { destlen=0 means that Windows cannot convert, so call the default
  585. handler. This is similiar to what unix does and is a good fallback
  586. if rawbyte strings are passed }
  587. if destlen=0 then
  588. begin
  589. DefaultAnsi2UnicodeMove(source,DefaultSystemCodePage,dest,len);
  590. exit;
  591. end;
  592. // this will null-terminate
  593. setlength(dest, destlen);
  594. if destlen>0 then
  595. begin
  596. MultiByteToWideChar(cp, dwFlags, source, len, @dest[1], destlen);
  597. PUnicodeRec(pointer(dest)-UnicodeFirstOff)^.CodePage:=CP_UTF16;
  598. end;
  599. end;
  600. function Win32UnicodeUpper(const s : UnicodeString) : UnicodeString;
  601. begin
  602. result:=s;
  603. UniqueString(result);
  604. if length(result)>0 then
  605. CharUpperBuff(LPWSTR(result),length(result));
  606. end;
  607. function Win32UnicodeLower(const s : UnicodeString) : UnicodeString;
  608. begin
  609. result:=s;
  610. UniqueString(result);
  611. if length(result)>0 then
  612. CharLowerBuff(LPWSTR(result),length(result));
  613. end;
  614. {******************************************************************************
  615. Widestring
  616. ******************************************************************************}
  617. procedure Win32Ansi2WideMove(source:PAnsiChar;cp : TSystemCodePage;var dest:widestring;len:SizeInt);
  618. var
  619. destlen: SizeInt;
  620. dwFlags: DWORD;
  621. begin
  622. // retrieve length including trailing #0
  623. // not anymore, because this must also be usable for single characters
  624. if cp=CP_UTF8 then
  625. dwFlags:=0
  626. else
  627. dwFlags:=MB_PRECOMPOSED;
  628. destlen:=MultiByteToWideChar(cp, dwFlags, source, len, nil, 0);
  629. // this will null-terminate
  630. setlength(dest, destlen);
  631. if destlen>0 then
  632. MultiByteToWideChar(cp, dwFlags, source, len, @dest[1], destlen);
  633. end;
  634. function Win32WideUpper(const s : WideString) : WideString;
  635. begin
  636. result:=s;
  637. if length(result)>0 then
  638. CharUpperBuff(LPWSTR(result),length(result));
  639. end;
  640. function Win32WideLower(const s : WideString) : WideString;
  641. begin
  642. result:=s;
  643. if length(result)>0 then
  644. CharLowerBuff(LPWSTR(result),length(result));
  645. end;
  646. type
  647. PWStrInitEntry = ^TWStrInitEntry;
  648. TWStrInitEntry = record
  649. addr: PPointer;
  650. data: Pointer;
  651. end;
  652. PWStrInitTablesTable = ^TWStrInitTablesTable;
  653. TWStrInitTablesTable = packed record
  654. count : sizeint;
  655. tables : packed array [1..32767] of PWStrInitEntry;
  656. end;
  657. var
  658. {$ifdef FPC_HAS_INDIRECT_ENTRY_INFORMATION}
  659. WStrInitTablesTable: PWStrInitTablesTable;
  660. {$else FPC_HAS_INDIRECT_ENTRY_INFORMATION}
  661. WStrInitTablesTableVar: TWStrInitTablesTable; external name 'FPC_WIDEINITTABLES';
  662. WStrInitTablesTable: PWStrInitTablesTable = @WStrInitTablesTableVar;
  663. {$endif FPC_HAS_INDIRECT_ENTRY_INFORMATION}
  664. function GetACP:UINT; stdcall; external 'kernel32' name 'GetACP';
  665. function GetConsoleCP:UINT; stdcall; external 'kernel32' name 'GetConsoleCP';
  666. function Win32GetStandardCodePage(const stdcp: TStandardCodePageEnum): TSystemCodePage;
  667. begin
  668. case stdcp of
  669. scpAnsi,
  670. scpFileSystemSingleByte: Result := GetACP;
  671. scpConsoleInput: Result := GetConsoleCP;
  672. scpConsoleOutput: Result := GetConsoleOutputCP;
  673. end;
  674. end;
  675. { there is a similiar procedure in sysutils which inits the fields which
  676. are only relevant for the sysutils units }
  677. procedure InitWin32Widestrings;
  678. var
  679. i: longint;
  680. ptable: PWStrInitEntry;
  681. begin
  682. { assign initial values to global Widestring typed consts }
  683. for i:=1 to WStrInitTablesTable^.count do
  684. begin
  685. ptable:=WStrInitTablesTable^.tables[i];
  686. while Assigned(ptable^.addr) do
  687. begin
  688. fpc_widestr_assign(ptable^.addr^, ptable^.data);
  689. Inc(ptable);
  690. end;
  691. end;
  692. { Note: since WideChar=UnicodeChar and PWideChar=PUnicodeChar,
  693. Wide2AnsiMoveProc is identical to Unicode2AnsiStrMoveProc. }
  694. { Widestring }
  695. widestringmanager.Wide2AnsiMoveProc:=@Win32Unicode2AnsiMove;
  696. widestringmanager.Ansi2WideMoveProc:=@Win32Ansi2WideMove;
  697. widestringmanager.UpperWideStringProc:=@Win32WideUpper;
  698. widestringmanager.LowerWideStringProc:=@Win32WideLower;
  699. { Unicode }
  700. widestringmanager.Unicode2AnsiMoveProc:=@Win32Unicode2AnsiMove;
  701. widestringmanager.Ansi2UnicodeMoveProc:=@Win32Ansi2UnicodeMove;
  702. widestringmanager.UpperUnicodeStringProc:=@Win32UnicodeUpper;
  703. widestringmanager.LowerUnicodeStringProc:=@Win32UnicodeLower;
  704. { Codepage }
  705. widestringmanager.GetStandardCodePageProc:=@Win32GetStandardCodePage;
  706. DefaultSystemCodePage:=GetACP;
  707. DefaultUnicodeCodePage:=CP_UTF16;
  708. DefaultFileSystemCodePage:=CP_UTF8;
  709. DefaultRTLFileSystemCodePage:=DefaultSystemCodePage;
  710. end;
  711. type
  712. WINBOOL = longbool;
  713. PHANDLER_ROUTINE = function (dwCtrlType:DWORD):WINBOOL; stdcall;
  714. function SetConsoleCtrlHandler(HandlerRoutine:PHANDLER_ROUTINE; Add:WINBOOL):WINBOOL; stdcall;
  715. external 'kernel32' name 'SetConsoleCtrlHandler';
  716. function WinCtrlBreakHandler(dwCtrlType:DWORD): WINBOOL;stdcall;
  717. const
  718. CTRL_BREAK_EVENT = 1;
  719. begin
  720. if Assigned(CtrlBreakHandler) then
  721. Result:=CtrlBreakHandler((dwCtrlType and CTRL_BREAK_EVENT > 0))
  722. else
  723. Result:=false;
  724. end;
  725. function SysSetCtrlBreakHandler (Handler: TCtrlBreakHandler): TCtrlBreakHandler;
  726. begin
  727. (* Return either nil or previous handler *)
  728. if (Assigned(CtrlBreakHandler)) and (not Assigned(Handler)) then
  729. SetConsoleCtrlHandler(@WinCtrlBreakHandler, false)
  730. else if (not Assigned(CtrlBreakHandler)) and (Assigned(Handler)) then
  731. SetConsoleCtrlHandler(@WinCtrlBreakHandler, true);
  732. SysSetCtrlBreakHandler := CtrlBreakHandler;
  733. CtrlBreakHandler := Handler;
  734. end;
  735. procedure WinFinalizeSystem; inline;
  736. begin
  737. finalize_arguments;
  738. end;