/jcl/source/common/JclSysUtils.pas

https://github.com/project-jedi/jcl · Pascal · 4540 lines · 3632 code · 377 blank · 531 comment · 174 complexity · 00573b12b18ba02a8212f58df1790e9a MD5 · raw file

  1. {**************************************************************************************************}
  2. { }
  3. { Project JEDI Code Library (JCL) }
  4. { }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the }
  7. { License at http://www.mozilla.org/MPL/ }
  8. { }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights }
  11. { and limitations under the License. }
  12. { }
  13. { The Original Code is JclSysUtils.pas. }
  14. { }
  15. { The Initial Developer of the Original Code is Marcel van Brakel. }
  16. { Portions created by Marcel van Brakel are Copyright (C) Marcel van Brakel. All rights reserved. }
  17. { }
  18. { Contributors: }
  19. { Alexander Radchenko, }
  20. { Andreas Hausladen (ahuser) }
  21. { Anthony Steele }
  22. { Bernhard Berger }
  23. { Heri Bender }
  24. { Jean-Fabien Connault (cycocrew) }
  25. { Jens Fudickar }
  26. { Jeroen Speldekamp }
  27. { Marcel van Brakel }
  28. { Peter Friese }
  29. { Petr Vones (pvones) }
  30. { Python }
  31. { Robert Marquardt (marquardt) }
  32. { Robert R. Marsh }
  33. { Robert Rossmair (rrossmair) }
  34. { Rudy Velthuis }
  35. { Uwe Schuster (uschuster) }
  36. { Wayne Sherman }
  37. { }
  38. {**************************************************************************************************}
  39. { }
  40. { Description: Various pointer and class related routines. }
  41. { }
  42. {**************************************************************************************************}
  43. { }
  44. { Last modified: $Date:: $ }
  45. { Revision: $Rev:: $ }
  46. { Author: $Author:: $ }
  47. { }
  48. {**************************************************************************************************}
  49. unit JclSysUtils;
  50. {$I jcl.inc}
  51. interface
  52. uses
  53. {$IFDEF UNITVERSIONING}
  54. JclUnitVersioning,
  55. {$ENDIF UNITVERSIONING}
  56. {$IFDEF HAS_UNITSCOPE}
  57. {$IFDEF MSWINDOWS}
  58. Winapi.Windows,
  59. {$ENDIF MSWINDOWS}
  60. System.SysUtils, System.Classes, System.TypInfo, System.SyncObjs,
  61. {$ELSE ~HAS_UNITSCOPE}
  62. {$IFDEF MSWINDOWS}
  63. Windows,
  64. {$ENDIF MSWINDOWS}
  65. SysUtils, Classes, TypInfo, SyncObjs,
  66. {$ENDIF ~HAS_UNITSCOPE}
  67. JclBase, JclSynch;
  68. // memory initialization
  69. // first parameter is "out" to make FPC happy with uninitialized values
  70. procedure ResetMemory(out P; Size: Longint);
  71. // Pointer manipulation
  72. procedure GetAndFillMem(var P: Pointer; const Size: Integer; const Value: Byte);
  73. procedure FreeMemAndNil(var P: Pointer);
  74. function PCharOrNil(const S: string): PChar;
  75. function PAnsiCharOrNil(const S: AnsiString): PAnsiChar;
  76. {$IFDEF SUPPORTS_WIDESTRING}
  77. function PWideCharOrNil(const W: WideString): PWideChar;
  78. {$ENDIF SUPPORTS_WIDESTRING}
  79. function SizeOfMem(const APointer: Pointer): Integer;
  80. function WriteProtectedMemory(BaseAddress, Buffer: Pointer; Size: Cardinal;
  81. out WrittenBytes: Cardinal): Boolean;
  82. // Guards
  83. type
  84. ISafeGuard = interface
  85. function ReleaseItem: Pointer;
  86. function GetItem: Pointer;
  87. procedure FreeItem;
  88. property Item: Pointer read GetItem;
  89. end;
  90. IMultiSafeGuard = interface (IInterface)
  91. function AddItem(Item: Pointer): Pointer;
  92. procedure FreeItem(Index: Integer);
  93. function GetCount: Integer;
  94. function GetItem(Index: Integer): Pointer;
  95. function ReleaseItem(Index: Integer): Pointer;
  96. property Count: Integer read GetCount;
  97. property Items[Index: Integer]: Pointer read GetItem;
  98. end;
  99. TJclSafeGuard = class(TInterfacedObject, ISafeGuard)
  100. private
  101. FItem: Pointer;
  102. public
  103. constructor Create(Mem: Pointer);
  104. destructor Destroy; override;
  105. { ISafeGuard }
  106. function ReleaseItem: Pointer;
  107. function GetItem: Pointer;
  108. procedure FreeItem; virtual;
  109. property Item: Pointer read GetItem;
  110. end;
  111. TJclObjSafeGuard = class(TJclSafeGuard, ISafeGuard)
  112. public
  113. constructor Create(Obj: TObject);
  114. { ISafeGuard }
  115. procedure FreeItem; override;
  116. end;
  117. TJclMultiSafeGuard = class(TInterfacedObject, IMultiSafeGuard)
  118. private
  119. FItems: TList;
  120. public
  121. constructor Create;
  122. destructor Destroy; override;
  123. { IMultiSafeGuard }
  124. function AddItem(Item: Pointer): Pointer;
  125. procedure FreeItem(Index: Integer); virtual;
  126. function GetCount: Integer;
  127. function GetItem(Index: Integer): Pointer;
  128. function ReleaseItem(Index: Integer): Pointer;
  129. property Count: Integer read GetCount;
  130. property Items[Index: Integer]: Pointer read GetItem;
  131. end;
  132. TJclObjMultiSafeGuard = class(TJclMultiSafeGuard, IMultiSafeGuard)
  133. public
  134. { IMultiSafeGuard }
  135. procedure FreeItem(Index: Integer); override;
  136. end;
  137. function Guard(Mem: Pointer; out SafeGuard: ISafeGuard): Pointer; overload;
  138. function Guard(Obj: TObject; out SafeGuard: ISafeGuard): TObject; overload;
  139. function Guard(Mem: Pointer; var SafeGuard: IMultiSafeGuard): Pointer; overload;
  140. function Guard(Obj: TObject; var SafeGuard: IMultiSafeGuard): TObject; overload;
  141. function GuardGetMem(Size: Cardinal; out SafeGuard: ISafeGuard): Pointer;
  142. function GuardAllocMem(Size: Cardinal; out SafeGuard: ISafeGuard): Pointer;
  143. (*
  144. {$IFDEF SUPPORTS_GENERICS}
  145. type
  146. ISafeGuard<T: class> = interface
  147. function ReleaseItem: T;
  148. function GetItem: T;
  149. procedure FreeItem;
  150. property Item: T read GetItem;
  151. end;
  152. TSafeGuard<T: class> = class(TObject, ISafeGuard<T>)
  153. private
  154. FItem: T;
  155. function ReleaseItem: T;
  156. function GetItem: T;
  157. procedure FreeItem;
  158. constructor Create(Instance: T);
  159. destructor Destroy; override;
  160. public
  161. class function New(Instance: T): ISafeGuard<T>; static;
  162. end;
  163. {$ENDIF SUPPORTS_GENERICS}
  164. *)
  165. { Shared memory between processes functions }
  166. // Functions for the shared memory owner
  167. type
  168. ESharedMemError = class(EJclError);
  169. {$IFDEF MSWINDOWS}
  170. { SharedGetMem return ERROR_ALREADY_EXISTS if the shared memory is already
  171. allocated, otherwise it returns 0.
  172. Throws ESharedMemError if the Name is invalid. }
  173. function SharedGetMem(var P{: Pointer}; const Name: string; Size: Cardinal;
  174. DesiredAccess: Cardinal = FILE_MAP_ALL_ACCESS): Integer;
  175. { SharedAllocMem calls SharedGetMem and then fills the memory with zero if
  176. it was not already allocated.
  177. Throws ESharedMemError if the Name is invalid. }
  178. function SharedAllocMem(const Name: string; Size: Cardinal;
  179. DesiredAccess: Cardinal = FILE_MAP_ALL_ACCESS): Pointer;
  180. { SharedFreeMem releases the shared memory if it was the last reference. }
  181. function SharedFreeMem(var P{: Pointer}): Boolean;
  182. // Functions for the shared memory user
  183. { SharedOpenMem returns True if the shared memory was already allocated by
  184. SharedGetMem or SharedAllocMem. Otherwise it returns False.
  185. Throws ESharedMemError if the Name is invalid. }
  186. function SharedOpenMem(var P{: Pointer}; const Name: string;
  187. DesiredAccess: Cardinal = FILE_MAP_ALL_ACCESS): Boolean; overload;
  188. { SharedOpenMem return nil if the shared memory was not already allocated
  189. by SharedGetMem or SharedAllocMem.
  190. Throws ESharedMemError if the Name is invalid. }
  191. function SharedOpenMem(const Name: string;
  192. DesiredAccess: Cardinal = FILE_MAP_ALL_ACCESS): Pointer; overload;
  193. { SharedCloseMem releases the shared memory if it was the last reference. }
  194. function SharedCloseMem(var P{: Pointer}): Boolean;
  195. {$ENDIF MSWINDOWS}
  196. // Binary search
  197. function SearchSortedList(List: TList; SortFunc: TListSortCompare; Item: Pointer;
  198. Nearest: Boolean = False): Integer;
  199. type
  200. TUntypedSearchCompare = function(Param: Pointer; ItemIndex: Integer; const Value): Integer;
  201. function SearchSortedUntyped(Param: Pointer; ItemCount: Integer; SearchFunc: TUntypedSearchCompare;
  202. const Value; Nearest: Boolean = False): Integer;
  203. // Dynamic array sort and search routines
  204. type
  205. TDynArraySortCompare = function (Item1, Item2: Pointer): Integer;
  206. procedure SortDynArray(const ArrayPtr: Pointer; ElementSize: Cardinal; SortFunc: TDynArraySortCompare);
  207. // Usage: SortDynArray(Array, SizeOf(Array[0]), SortFunction);
  208. function SearchDynArray(const ArrayPtr: Pointer; ElementSize: Cardinal; SortFunc: TDynArraySortCompare;
  209. ValuePtr: Pointer; Nearest: Boolean = False): SizeInt;
  210. // Usage: SearchDynArray(Array, SizeOf(Array[0]), SortFunction, @SearchedValue);
  211. { Various compare functions for basic types }
  212. function DynArrayCompareByte(Item1, Item2: Pointer): Integer;
  213. function DynArrayCompareShortInt(Item1, Item2: Pointer): Integer;
  214. function DynArrayCompareWord(Item1, Item2: Pointer): Integer;
  215. function DynArrayCompareSmallInt(Item1, Item2: Pointer): Integer;
  216. function DynArrayCompareInteger(Item1, Item2: Pointer): Integer;
  217. function DynArrayCompareCardinal(Item1, Item2: Pointer): Integer;
  218. function DynArrayCompareInt64(Item1, Item2: Pointer): Integer;
  219. function DynArrayCompareSingle(Item1, Item2: Pointer): Integer;
  220. function DynArrayCompareDouble(Item1, Item2: Pointer): Integer;
  221. function DynArrayCompareExtended(Item1, Item2: Pointer): Integer;
  222. function DynArrayCompareFloat(Item1, Item2: Pointer): Integer;
  223. function DynArrayCompareAnsiString(Item1, Item2: Pointer): Integer;
  224. function DynArrayCompareAnsiText(Item1, Item2: Pointer): Integer;
  225. function DynArrayCompareWideString(Item1, Item2: Pointer): Integer;
  226. function DynArrayCompareWideText(Item1, Item2: Pointer): Integer;
  227. function DynArrayCompareString(Item1, Item2: Pointer): Integer;
  228. function DynArrayCompareText(Item1, Item2: Pointer): Integer;
  229. // Object lists
  230. procedure ClearObjectList(List: TList);
  231. procedure FreeObjectList(var List: TList);
  232. // Reference memory stream
  233. type
  234. TJclReferenceMemoryStream = class(TCustomMemoryStream)
  235. public
  236. constructor Create(const Ptr: Pointer; Size: Longint);
  237. function Write(const Buffer; Count: Longint): Longint; override;
  238. end;
  239. // AutoPtr
  240. type
  241. IAutoPtr = interface
  242. { Returns the object as pointer, so it is easier to assign it to a variable }
  243. function AsPointer: Pointer;
  244. { Returns the AutoPtr handled object }
  245. function AsObject: TObject;
  246. { Releases the object from the AutoPtr. The AutoPtr looses the control over
  247. the object. }
  248. function ReleaseObject: TObject;
  249. end;
  250. TJclAutoPtr = class(TInterfacedObject, IAutoPtr)
  251. private
  252. FValue: TObject;
  253. public
  254. constructor Create(AValue: TObject);
  255. destructor Destroy; override;
  256. { IAutoPtr }
  257. function AsPointer: Pointer;
  258. function AsObject: TObject;
  259. function ReleaseObject: TObject;
  260. end;
  261. function CreateAutoPtr(Value: TObject): IAutoPtr;
  262. // Replacement for the C ternary conditional operator ? :
  263. function Iff(const Condition: Boolean; const TruePart, FalsePart: string): string; overload;
  264. function Iff(const Condition: Boolean; const TruePart, FalsePart: Char): Char; overload;
  265. function Iff(const Condition: Boolean; const TruePart, FalsePart: Byte): Byte; overload;
  266. function Iff(const Condition: Boolean; const TruePart, FalsePart: Integer): Integer; overload;
  267. function Iff(const Condition: Boolean; const TruePart, FalsePart: Cardinal): Cardinal; overload;
  268. function Iff(const Condition: Boolean; const TruePart, FalsePart: Float): Float; overload;
  269. function Iff(const Condition: Boolean; const TruePart, FalsePart: Boolean): Boolean; overload;
  270. function Iff(const Condition: Boolean; const TruePart, FalsePart: Pointer): Pointer; overload;
  271. function Iff(const Condition: Boolean; const TruePart, FalsePart: Int64): Int64; overload;
  272. {$IFDEF SUPPORTS_VARIANT}
  273. function Iff(const Condition: Boolean; const TruePart, FalsePart: Variant): Variant; overload;
  274. {$ENDIF SUPPORTS_VARIANT}
  275. // Classes information and manipulation
  276. type
  277. EJclVMTError = class(EJclError);
  278. // Virtual Methods
  279. {$IFNDEF FPC}
  280. function GetVirtualMethodCount(AClass: TClass): Integer;
  281. {$ENDIF ~FPC}
  282. function GetVirtualMethod(AClass: TClass; const Index: Integer): Pointer;
  283. procedure SetVirtualMethod(AClass: TClass; const Index: Integer; const Method: Pointer);
  284. // Dynamic Methods
  285. type
  286. TDynamicIndexList = array [0..MaxInt div 16] of Word;
  287. PDynamicIndexList = ^TDynamicIndexList;
  288. TDynamicAddressList = array [0..MaxInt div 16] of Pointer;
  289. PDynamicAddressList = ^TDynamicAddressList;
  290. function GetDynamicMethodCount(AClass: TClass): Integer;
  291. function GetDynamicIndexList(AClass: TClass): PDynamicIndexList;
  292. function GetDynamicAddressList(AClass: TClass): PDynamicAddressList;
  293. function HasDynamicMethod(AClass: TClass; Index: Integer): Boolean;
  294. {$IFNDEF FPC}
  295. function GetDynamicMethod(AClass: TClass; Index: Integer): Pointer;
  296. {$ENDIF ~FPC}
  297. { init table methods }
  298. function GetInitTable(AClass: TClass): PTypeInfo;
  299. { field table methods }
  300. type
  301. PFieldEntry = ^TFieldEntry;
  302. TFieldEntry = packed record
  303. OffSet: Integer;
  304. IDX: Word;
  305. Name: ShortString;
  306. end;
  307. PFieldClassTable = ^TFieldClassTable;
  308. TFieldClassTable = packed record
  309. Count: Smallint;
  310. Classes: array [0..8191] of ^TPersistentClass;
  311. end;
  312. PFieldTable = ^TFieldTable;
  313. TFieldTable = packed record
  314. EntryCount: Word;
  315. FieldClassTable: PFieldClassTable;
  316. FirstEntry: TFieldEntry;
  317. {Entries: array [1..65534] of TFieldEntry;}
  318. end;
  319. function GetFieldTable(AClass: TClass): PFieldTable;
  320. { method table }
  321. type
  322. PMethodEntry = ^TMethodEntry;
  323. TMethodEntry = packed record
  324. EntrySize: Word;
  325. Address: Pointer;
  326. Name: ShortString;
  327. end;
  328. PMethodTable = ^TMethodTable;
  329. TMethodTable = packed record
  330. Count: Word;
  331. FirstEntry: TMethodEntry;
  332. {Entries: array [1..65534] of TMethodEntry;}
  333. end;
  334. function GetMethodTable(AClass: TClass): PMethodTable;
  335. function GetMethodEntry(MethodTable: PMethodTable; Index: Integer): PMethodEntry;
  336. // Function to compare if two methods/event handlers are equal
  337. function MethodEquals(aMethod1, aMethod2: TMethod): boolean;
  338. function NotifyEventEquals(aMethod1, aMethod2: TNotifyEvent): boolean;
  339. // Class Parent
  340. procedure SetClassParent(AClass: TClass; NewClassParent: TClass);
  341. function GetClassParent(AClass: TClass): TClass;
  342. {$IFNDEF FPC}
  343. function IsClass(Address: Pointer): Boolean;
  344. function IsObject(Address: Pointer): Boolean;
  345. {$ENDIF ~FPC}
  346. function InheritsFromByName(AClass: TClass; const AClassName: string): Boolean;
  347. // Interface information
  348. function GetImplementorOfInterface(const I: IInterface): TObject;
  349. // interfaced persistent
  350. type
  351. TJclInterfacedPersistent = class(TInterfacedPersistent, IInterface)
  352. protected
  353. FOwnerInterface: IInterface;
  354. FRefCount: Integer;
  355. public
  356. procedure AfterConstruction; override;
  357. { IInterface }
  358. // function QueryInterface(const IID: TGUID; out Obj): HRESULT; virtual; stdcall;
  359. function _AddRef: Integer; stdcall;
  360. function _Release: Integer; stdcall;
  361. end;
  362. // Numeric formatting routines
  363. type
  364. TDigitCount = 0..255;
  365. TDigitValue = -1..35; // invalid, '0'..'9', 'A'..'Z'
  366. TNumericSystemBase = 2..Succ(High(TDigitValue));
  367. TJclNumericFormat = class(TObject)
  368. private
  369. FWantedPrecision: TDigitCount;
  370. FPrecision: TDigitCount;
  371. FNumberOfFractionalDigits: TDigitCount;
  372. FExpDivision: Integer;
  373. FDigitBlockSize: TDigitCount;
  374. FWidth: TDigitCount;
  375. FSignChars: array [Boolean] of Char;
  376. FBase: TNumericSystemBase;
  377. FFractionalPartSeparator: Char;
  378. FDigitBlockSeparator: Char;
  379. FShowPositiveSign: Boolean;
  380. FPaddingChar: Char;
  381. FMultiplier: string;
  382. function GetDigitValue(Digit: Char): Integer;
  383. function GetNegativeSign: Char;
  384. function GetPositiveSign: Char;
  385. procedure InvalidDigit(Digit: Char);
  386. procedure SetPrecision(const Value: TDigitCount);
  387. procedure SetBase(const Value: TNumericSystemBase);
  388. procedure SetNegativeSign(const Value: Char);
  389. procedure SetPositiveSign(const Value: Char);
  390. procedure SetExpDivision(const Value: Integer);
  391. protected
  392. function IntToStr(const Value: Int64; out FirstDigitPos: Integer): string; overload;
  393. function ShowSign(const Value: Float): Boolean; overload;
  394. function ShowSign(const Value: Int64): Boolean; overload;
  395. function SignChar(const Value: Float): Char; overload;
  396. function SignChar(const Value: Int64): Char; overload;
  397. property WantedPrecision: TDigitCount read FWantedPrecision;
  398. public
  399. constructor Create;
  400. function Digit(DigitValue: TDigitValue): Char;
  401. function DigitValue(Digit: Char): TDigitValue;
  402. function IsDigit(Value: Char): Boolean;
  403. function Sign(Value: Char): Integer;
  404. procedure GetMantissaExp(const Value: Float; out Mantissa: string; out Exponent: Integer);
  405. function FloatToHTML(const Value: Float): string;
  406. function IntToStr(const Value: Int64): string; overload;
  407. function FloatToStr(const Value: Float): string; overload;
  408. function StrToInt(const Value: string): Int64;
  409. property Base: TNumericSystemBase read FBase write SetBase;
  410. property Precision: TDigitCount read FPrecision write SetPrecision;
  411. property NumberOfFractionalDigits: TDigitCount read FNumberOfFractionalDigits write FNumberOfFractionalDigits;
  412. property ExponentDivision: Integer read FExpDivision write SetExpDivision;
  413. property DigitBlockSize: TDigitCount read FDigitBlockSize write FDigitBlockSize;
  414. property DigitBlockSeparator: Char read FDigitBlockSeparator write FDigitBlockSeparator;
  415. property FractionalPartSeparator: Char read FFractionalPartSeparator write FFractionalPartSeparator;
  416. property Multiplier: string read FMultiplier write FMultiplier;
  417. property PaddingChar: Char read FPaddingChar write FPaddingChar;
  418. property ShowPositiveSign: Boolean read FShowPositiveSign write FShowPositiveSign;
  419. property Width: TDigitCount read FWidth write FWidth;
  420. property NegativeSign: Char read GetNegativeSign write SetNegativeSign;
  421. property PositiveSign: Char read GetPositiveSign write SetPositiveSign;
  422. end;
  423. function IntToStrZeroPad(Value, Count: Integer): string;
  424. // Child processes
  425. type
  426. // e.g. TStrings.Append
  427. TTextHandler = procedure(const Text: string) of object;
  428. TJclProcessPriority = (ppIdle, ppNormal, ppHigh, ppRealTime, ppBelowNormal, ppAboveNormal);
  429. const
  430. ABORT_EXIT_CODE = {$IFDEF MSWINDOWS} ERROR_CANCELLED {$ELSE} 1223 {$ENDIF};
  431. function Execute(const CommandLine: string; OutputLineCallback: TTextHandler; RawOutput: Boolean = False;
  432. AbortPtr: PBoolean = nil; ProcessPriority: TJclProcessPriority = ppNormal;
  433. AutoConvertOem: Boolean = False): Cardinal; overload;
  434. function Execute(const CommandLine: string; AbortEvent: TJclEvent;
  435. OutputLineCallback: TTextHandler; RawOutput: Boolean = False; ProcessPriority: TJclProcessPriority = ppNormal;
  436. AutoConvertOem: Boolean = False): Cardinal; overload;
  437. function Execute(const CommandLine: string; var Output: string; RawOutput: Boolean = False;
  438. AbortPtr: PBoolean = nil; ProcessPriority: TJclProcessPriority = ppNormal;
  439. AutoConvertOem: Boolean = False): Cardinal; overload;
  440. function Execute(const CommandLine: string; AbortEvent: TJclEvent;
  441. var Output: string; RawOutput: Boolean = False; ProcessPriority: TJclProcessPriority = ppNormal;
  442. AutoConvertOem: Boolean = False): Cardinal; overload;
  443. function Execute(const CommandLine: string; OutputLineCallback, ErrorLineCallback: TTextHandler;
  444. RawOutput: Boolean = False; RawError: Boolean = False; AbortPtr: PBoolean = nil;
  445. ProcessPriority: TJclProcessPriority = ppNormal; AutoConvertOem: Boolean = False): Cardinal; overload;
  446. function Execute(const CommandLine: string; AbortEvent: TJclEvent;
  447. OutputLineCallback, ErrorLineCallback: TTextHandler; RawOutput: Boolean = False; RawError: Boolean = False;
  448. ProcessPriority: TJclProcessPriority = ppNormal; AutoConvertOem: Boolean = False): Cardinal; overload;
  449. function Execute(const CommandLine: string; var Output, Error: string;
  450. RawOutput: Boolean = False; RawError: Boolean = False; AbortPtr: PBoolean = nil;
  451. ProcessPriority: TJclProcessPriority = ppNormal; AutoConvertOem: Boolean = False): Cardinal; overload;
  452. function Execute(const CommandLine: string; AbortEvent: TJclEvent;
  453. var Output, Error: string; RawOutput: Boolean = False; RawError: Boolean = False;
  454. ProcessPriority: TJclProcessPriority = ppNormal; AutoConvertOem: Boolean = False): Cardinal; overload;
  455. type
  456. {$IFDEF MSWINDOWS}
  457. TJclExecuteCmdProcessOptionBeforeResumeEvent = procedure(const ProcessInfo: TProcessInformation) of object;
  458. TStartupVisibility = (svHide, svShow, svNotSet);
  459. {$ENDIF MSWINDOWS}
  460. TJclExecuteCmdProcessOptions = {record} class(TObject)
  461. private
  462. FCommandLine: string;
  463. FAbortPtr: PBoolean;
  464. FAbortEvent: TJclEvent;
  465. FOutputLineCallback: TTextHandler;
  466. FRawOutput: Boolean;
  467. FMergeError: Boolean;
  468. FErrorLineCallback: TTextHandler;
  469. FRawError: Boolean;
  470. FProcessPriority: TJclProcessPriority;
  471. FAutoConvertOem: Boolean;
  472. {$IFDEF MSWINDOWS}
  473. FCreateProcessFlags: DWORD;
  474. FStartupVisibility: TStartupVisibility;
  475. FBeforeResume: TJclExecuteCmdProcessOptionBeforeResumeEvent;
  476. {$ENDIF MSWINDOWS}
  477. FExitCode: Cardinal;
  478. FOutput: string;
  479. FError: string;
  480. public
  481. // in:
  482. property CommandLine: string read FCommandLine write FCommandLine;
  483. property AbortPtr: PBoolean read FAbortPtr write FAbortPtr;
  484. property AbortEvent: TJclEvent read FAbortEvent write FAbortEvent;
  485. property OutputLineCallback: TTextHandler read FOutputLineCallback write FOutputLineCallback;
  486. property RawOutput: Boolean read FRawOutput write FRawOutput default False;
  487. property MergeError: Boolean read FMergeError write FMergeError default False;
  488. property ErrorLineCallback: TTextHandler read FErrorLineCallback write FErrorLineCallback;
  489. property RawError: Boolean read FRawError write FRawError default False;
  490. property ProcessPriority: TJclProcessPriority read FProcessPriority write FProcessPriority default ppNormal;
  491. // AutoConvertOem assumes the process outputs OEM encoded strings and converts them to the
  492. // default string encoding.
  493. property AutoConvertOem: Boolean read FAutoConvertOem write FAutoConvertOem default True;
  494. {$IFDEF MSWINDOWS}
  495. property CreateProcessFlags: DWORD read FCreateProcessFlags write FCreateProcessFlags;
  496. property StartupVisibility: TStartupVisibility read FStartupVisibility write FStartupVisibility;
  497. property BeforeResume: TJclExecuteCmdProcessOptionBeforeResumeEvent read FBeforeResume write FBeforeResume;
  498. {$ENDIF MSWINDOWS}
  499. // out:
  500. property ExitCode: Cardinal read FExitCode;
  501. property Output: string read FOutput;
  502. property Error: string read FError;
  503. public
  504. constructor Create(const ACommandLine: string);
  505. end;
  506. function ExecuteCmdProcess(Options: TJclExecuteCmdProcessOptions): Boolean;
  507. type
  508. {$HPPEMIT 'namespace Jclsysutils'}
  509. {$HPPEMIT '{'}
  510. {$HPPEMIT ' // For some reason, the generator puts this interface after its first'}
  511. {$HPPEMIT ' // usage, resulting in an unusable header file. We fix this by forward'}
  512. {$HPPEMIT ' // declaring the interface.'}
  513. {$HPPEMIT ' __interface IJclCommandLineTool;'}
  514. (*$HPPEMIT '}'*)
  515. IJclCommandLineTool = interface
  516. ['{A0034B09-A074-D811-847D-0030849E4592}']
  517. function GetExeName: string;
  518. function GetOptions: TStrings;
  519. function GetOutput: string;
  520. function GetOutputCallback: TTextHandler;
  521. procedure AddPathOption(const Option, Path: string);
  522. function Execute(const CommandLine: string): Boolean;
  523. procedure SetOutputCallback(const CallbackMethod: TTextHandler);
  524. property ExeName: string read GetExeName;
  525. property Options: TStrings read GetOptions;
  526. property OutputCallback: TTextHandler read GetOutputCallback write SetOutputCallback;
  527. property Output: string read GetOutput;
  528. end;
  529. EJclCommandLineToolError = class(EJclError);
  530. TJclCommandLineTool = class(TInterfacedObject, IJclCommandLineTool)
  531. private
  532. FExeName: string;
  533. FOptions: TStringList;
  534. FOutput: string;
  535. FOutputCallback: TTextHandler;
  536. public
  537. constructor Create(const AExeName: string);
  538. destructor Destroy; override;
  539. { IJclCommandLineTool }
  540. function GetExeName: string;
  541. function GetOptions: TStrings;
  542. function GetOutput: string;
  543. function GetOutputCallback: TTextHandler;
  544. procedure AddPathOption(const Option, Path: string);
  545. function Execute(const CommandLine: string): Boolean;
  546. procedure SetOutputCallback(const CallbackMethod: TTextHandler);
  547. property ExeName: string read GetExeName;
  548. property Options: TStrings read GetOptions;
  549. property OutputCallback: TTextHandler read GetOutputCallback write SetOutputCallback;
  550. property Output: string read GetOutput;
  551. end;
  552. // Console Utilities
  553. function ReadKey: Char;
  554. // Loading of modules (DLLs)
  555. type
  556. {$IFDEF MSWINDOWS}
  557. TModuleHandle = HINST;
  558. {$ENDIF MSWINDOWS}
  559. {$IFDEF LINUX}
  560. TModuleHandle = Pointer;
  561. {$ENDIF LINUX}
  562. const
  563. INVALID_MODULEHANDLE_VALUE = TModuleHandle(0);
  564. function LoadModule(var Module: TModuleHandle; FileName: string): Boolean;
  565. function LoadModuleEx(var Module: TModuleHandle; FileName: string; Flags: Cardinal): Boolean;
  566. procedure UnloadModule(var Module: TModuleHandle);
  567. function GetModuleSymbol(Module: TModuleHandle; SymbolName: string): Pointer;
  568. function GetModuleSymbolEx(Module: TModuleHandle; SymbolName: string; var Accu: Boolean): Pointer;
  569. function ReadModuleData(Module: TModuleHandle; SymbolName: string; var Buffer; Size: Cardinal): Boolean;
  570. function WriteModuleData(Module: TModuleHandle; SymbolName: string; var Buffer; Size: Cardinal): Boolean;
  571. // Conversion Utilities
  572. type
  573. EJclConversionError = class(EJclError);
  574. function StrToBoolean(const S: string): Boolean;
  575. function BooleanToStr(B: Boolean): string;
  576. function IntToBool(I: Integer): Boolean;
  577. function BoolToInt(B: Boolean): Integer;
  578. function TryStrToUInt(const Value: string; out Res: Cardinal): Boolean;
  579. function StrToUIntDef(const Value: string; const Default: Cardinal): Cardinal;
  580. function StrToUInt(const Value: string): Cardinal;
  581. const
  582. {$IFDEF MSWINDOWS}
  583. ListSeparator = ';';
  584. {$ENDIF MSWINDOWS}
  585. {$IFDEF LINUX}
  586. ListSeparator = ':';
  587. {$ENDIF LINUX}
  588. // functions to handle items in a separated list of items
  589. // add items at the end
  590. procedure ListAddItems(var List: string; const Separator, Items: string);
  591. // add items at the end if they are not present
  592. procedure ListIncludeItems(var List: string; const Separator, Items: string);
  593. // delete multiple items
  594. procedure ListRemoveItems(var List: string; const Separator, Items: string);
  595. // delete one item
  596. procedure ListDelItem(var List: string; const Separator: string;
  597. const Index: Integer);
  598. // return the number of item
  599. function ListItemCount(const List, Separator: string): Integer;
  600. // return the Nth item
  601. function ListGetItem(const List, Separator: string;
  602. const Index: Integer): string;
  603. // set the Nth item
  604. procedure ListSetItem(var List: string; const Separator: string;
  605. const Index: Integer; const Value: string);
  606. // return the index of an item
  607. function ListItemIndex(const List, Separator, Item: string): Integer;
  608. // RTL package information
  609. function SystemTObjectInstance: TJclAddr;
  610. function IsCompiledWithPackages: Boolean;
  611. // GUID
  612. function JclGUIDToString(const GUID: TGUID): string;
  613. function JclStringToGUID(const S: string): TGUID;
  614. function GUIDEquals(const GUID1, GUID2: TGUID): Boolean;
  615. // thread safe support
  616. type
  617. TJclIntfCriticalSection = class(TInterfacedObject, IInterface)
  618. private
  619. FCriticalSection: TCriticalSection;
  620. public
  621. constructor Create;
  622. destructor Destroy; override;
  623. { IInterface }
  624. // function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
  625. function _AddRef: Integer; stdcall;
  626. function _Release: Integer; stdcall;
  627. end;
  628. type
  629. {$IFDEF BORLAND}
  630. {$IFDEF COMPILER16_UP}
  631. TFileHandle = THandle;
  632. {$ELSE ~COMPILER16_UP}
  633. TFileHandle = Integer;
  634. {$ENDIF ~COMPILER16_UP}
  635. {$ELSE ~BORLAND}
  636. TFileHandle = THandle;
  637. {$ENDIF ~BORLAND}
  638. TJclSimpleLog = class (TObject)
  639. private
  640. FDateTimeFormatStr: String;
  641. FLogFileHandle: TFileHandle;
  642. FLogFileName: string;
  643. FLoggingActive: Boolean;
  644. FLogWasEmpty: Boolean;
  645. function GetLogOpen: Boolean;
  646. protected
  647. function CreateDefaultFileName: string;
  648. public
  649. constructor Create(const ALogFileName: string = '');
  650. destructor Destroy; override;
  651. procedure ClearLog;
  652. procedure CloseLog;
  653. procedure OpenLog;
  654. procedure Write(const Text: string; Indent: Integer = 0; KeepOpen: Boolean = true); overload;
  655. procedure Write(Strings: TStrings; Indent: Integer = 0; KeepOpen: Boolean = true); overload;
  656. //Writes a line to the log file. The current timestamp is written before the line.
  657. procedure TimeWrite(const Text: string; Indent: Integer = 0; KeepOpen: Boolean = true); overload;
  658. procedure TimeWrite(Strings: TStrings; Indent: Integer = 0; KeepOpen: Boolean = true); overload;
  659. procedure WriteStamp(SeparatorLen: Integer = 0; KeepOpen: Boolean = true);
  660. // DateTimeFormatStr property assumes the values described in "FormatDateTime Function" in Delphi Help
  661. property DateTimeFormatStr: String read FDateTimeFormatStr write FDateTimeFormatStr;
  662. property LogFileName: string read FLogFileName;
  663. //1 Property to activate / deactivate the logging
  664. property LoggingActive: Boolean read FLoggingActive write FLoggingActive default True;
  665. property LogOpen: Boolean read GetLogOpen;
  666. end;
  667. type
  668. TJclFormatSettings = class
  669. private
  670. function GetCurrencyDecimals: Byte; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  671. function GetCurrencyFormat: Byte; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  672. function GetCurrencyString: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  673. function GetDateSeparator: Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  674. function GetDayNamesHighIndex: Integer; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  675. function GetDayNamesLowIndex: Integer; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  676. function GetDecimalSeparator: Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  677. function GetListSeparator: Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  678. function GetLongDateFormat: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  679. function GetLongDayNames(AIndex: Integer): string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  680. function GetLongMonthNames(AIndex: Integer): string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  681. function GetLongTimeFormat: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  682. function GetMonthNamesHighIndex: Integer; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  683. function GetMonthNamesLowIndex: Integer; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  684. function GetNegCurrFormat: Byte; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  685. function GetShortDateFormat: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  686. function GetShortDayNames(AIndex: Integer): string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  687. function GetShortMonthNames(AIndex: Integer): string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  688. function GetShortTimeFormat: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  689. function GetThousandSeparator: Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  690. function GetTimeAMString: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  691. function GetTimePMString: string; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  692. function GetTimeSeparator: Char; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  693. function GetTwoDigitYearCenturyWindow: Word; {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  694. procedure SetCurrencyDecimals(AValue: Byte); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  695. procedure SetCurrencyFormat(const AValue: Byte); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  696. procedure SetCurrencyString(AValue: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  697. procedure SetDateSeparator(const AValue: Char); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  698. procedure SetDecimalSeparator(AValue: Char); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  699. procedure SetListSeparator(const AValue: Char); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  700. procedure SetLongDateFormat(const AValue: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  701. procedure SetLongTimeFormat(const AValue: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  702. procedure SetNegCurrFormat(const AValue: Byte); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  703. procedure SetShortDateFormat(AValue: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  704. procedure SetShortTimeFormat(const AValue: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  705. procedure SetThousandSeparator(AValue: Char); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  706. procedure SetTimeAMString(const AValue: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  707. procedure SetTimePMString(const AValue: string); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  708. procedure SetTimeSeparator(const AValue: Char); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  709. procedure SetTwoDigitYearCenturyWindow(const AValue: Word); {$IFDEF SUPPORTS_INLINE} inline; {$ENDIF}
  710. public
  711. property CurrencyDecimals: Byte read GetCurrencyDecimals write SetCurrencyDecimals;
  712. property CurrencyFormat: Byte read GetCurrencyFormat write SetCurrencyFormat;
  713. property CurrencyString: string read GetCurrencyString write SetCurrencyString;
  714. property DateSeparator: Char read GetDateSeparator write SetDateSeparator;
  715. property DayNamesHighIndex: Integer read GetDayNamesHighIndex;
  716. property DayNamesLowIndex: Integer read GetDayNamesLowIndex;
  717. property DecimalSeparator: Char read GetDecimalSeparator write SetDecimalSeparator;
  718. property ListSeparator: Char read GetListSeparator write SetListSeparator;
  719. property LongDateFormat: string read GetLongDateFormat write SetLongDateFormat;
  720. property LongDayNames[AIndex: Integer]: string read GetLongDayNames;
  721. property LongMonthNames[AIndex: Integer]: string read GetLongMonthNames;
  722. property LongTimeFormat: string read GetLongTimeFormat write SetLongTimeFormat;
  723. property MonthNamesHighIndex: Integer read GetMonthNamesHighIndex;
  724. property MonthNamesLowIndex: Integer read GetMonthNamesLowIndex;
  725. property NegCurrFormat: Byte read GetNegCurrFormat write SetNegCurrFormat;
  726. property ShortDateFormat: string read GetShortDateFormat write SetShortDateFormat;
  727. property ShortDayNames[AIndex: Integer]: string read GetShortDayNames;
  728. property ShortMonthNames[AIndex: Integer]: string read GetShortMonthNames;
  729. property ShortTimeFormat: string read GetShortTimeFormat write SetShortTimeFormat;
  730. property ThousandSeparator: Char read GetThousandSeparator write SetThousandSeparator;
  731. property TimeAMString: string read GetTimeAMString write SetTimeAMString;
  732. property TimePMString: string read GetTimePMString write SetTimePMString;
  733. property TimeSeparator: Char read GetTimeSeparator write SetTimeSeparator;
  734. property TwoDigitYearCenturyWindow: Word read GetTwoDigitYearCenturyWindow write SetTwoDigitYearCenturyWindow;
  735. end;
  736. var
  737. JclFormatSettings: TJclFormatSettings;
  738. // Procedure to initialize the SimpleLog Variable
  739. procedure InitSimpleLog(const ALogFileName: string = ''; AOpenLog: Boolean = true);
  740. // Global Variable to make it easier for an application wide log handling.
  741. // Must be initialized with InitSimpleLog before using
  742. var
  743. SimpleLog : TJclSimpleLog;
  744. // Validates if then variant value is null or is empty
  745. function VarIsNullEmpty(const V: Variant): Boolean;
  746. // Validates if then variant value is null or is empty or VarToStr is a blank string
  747. function VarIsNullEmptyBlank(const V: Variant): Boolean;
  748. {$IFDEF UNITVERSIONING}
  749. const
  750. UnitVersioning: TUnitVersionInfo = (
  751. RCSfile: '$URL$';
  752. Revision: '$Revision$';
  753. Date: '$Date$';
  754. LogPath: 'JCL\source\common';
  755. Extra: '';
  756. Data: nil
  757. );
  758. {$ENDIF UNITVERSIONING}
  759. implementation
  760. uses
  761. {$IFDEF HAS_UNIT_LIBC}
  762. Libc,
  763. {$ENDIF HAS_UNIT_LIBC}
  764. {$IFDEF MSWINDOWS}
  765. JclConsole,
  766. {$ENDIF MSWINDOWS}
  767. {$IFDEF HAS_UNITSCOPE}
  768. System.Variants, System.Types, System.Contnrs,
  769. {$IFDEF HAS_UNIT_ANSISTRINGS}
  770. System.AnsiStrings,
  771. {$ENDIF HAS_UNIT_ANSISTRINGS}
  772. {$ELSE ~HAS_UNITSCOPE}
  773. Variants, Types, Contnrs,
  774. {$IFDEF HAS_UNIT_ANSISTRINGS}
  775. AnsiStrings,
  776. {$ENDIF HAS_UNIT_ANSISTRINGS}
  777. {$ENDIF ~HAS_UNITSCOPE}
  778. JclFileUtils, JclMath, JclResources, JclStrings,
  779. JclStringConversions, JclSysInfo, JclWin32;
  780. // memory initialization
  781. procedure ResetMemory(out P; Size: Longint);
  782. begin
  783. if Size > 0 then
  784. begin
  785. Byte(P) := 0;
  786. FillChar(P, Size, 0);
  787. end;
  788. end;
  789. // Pointer manipulation
  790. procedure GetAndFillMem(var P: Pointer; const Size: Integer; const Value: Byte);
  791. begin
  792. GetMem(P, Size);
  793. FillChar(P^, Size, Value);
  794. end;
  795. procedure FreeMemAndNil(var P: Pointer);
  796. var
  797. Q: Pointer;
  798. begin
  799. Q := P;
  800. P := nil;
  801. FreeMem(Q);
  802. end;
  803. function PCharOrNil(const S: string): PChar;
  804. begin
  805. Result := Pointer(S);
  806. end;
  807. function PAnsiCharOrNil(const S: AnsiString): PAnsiChar;
  808. begin
  809. Result := Pointer(S);
  810. end;
  811. {$IFDEF SUPPORTS_WIDESTRING}
  812. function PWideCharOrNil(const W: WideString): PWideChar;
  813. begin
  814. Result := Pointer(W);
  815. end;
  816. {$ENDIF SUPPORTS_WIDESTRING}
  817. {$IFDEF MSWINDOWS}
  818. type
  819. PUsed = ^TUsed;
  820. TUsed = record
  821. SizeFlags: Integer;
  822. end;
  823. const
  824. cThisUsedFlag = 2;
  825. cPrevFreeFlag = 1;
  826. cFillerFlag = Integer($80000000);
  827. cFlags = cThisUsedFlag or cPrevFreeFlag or cFillerFlag;
  828. function SizeOfMem(const APointer: Pointer): Integer;
  829. var
  830. U: PUsed;
  831. begin
  832. if IsMemoryManagerSet then
  833. Result:= -1
  834. else
  835. begin
  836. Result := 0;
  837. if APointer <> nil then
  838. begin
  839. U := APointer;
  840. U := PUsed(TJclAddr(U) - SizeOf(TUsed));
  841. if (U.SizeFlags and cThisUsedFlag) <> 0 then
  842. Result := (U.SizeFlags) and (not cFlags - SizeOf(TUsed));
  843. end;
  844. end;
  845. end;
  846. {$ENDIF MSWINDOWS}
  847. {$IFDEF LINUX}
  848. function SizeOfMem(const APointer: Pointer): Integer;
  849. begin
  850. if IsMemoryManagerSet then
  851. Result:= -1
  852. else
  853. begin
  854. if APointer <> nil then
  855. Result := malloc_usable_size(APointer)
  856. else
  857. Result := 0;
  858. end;
  859. end;
  860. {$ENDIF LINUX}
  861. function WriteProtectedMemory(BaseAddress, Buffer: Pointer;
  862. Size: Cardinal; out WrittenBytes: Cardinal): Boolean;
  863. {$IFDEF MSWINDOWS}
  864. var
  865. OldProtect, Dummy: Cardinal;
  866. begin
  867. WrittenBytes := 0;
  868. if Size > 0 then
  869. begin
  870. // (outchy) VirtualProtect for DEP issues
  871. OldProtect := 0;
  872. Result := VirtualProtect(BaseAddress, Size, PAGE_EXECUTE_READWRITE, OldProtect);
  873. if Result then
  874. try
  875. Move(Buffer^, BaseAddress^, Size);
  876. WrittenBytes := Size;
  877. if OldProtect in [PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY] then
  878. FlushInstructionCache(GetCurrentProcess, BaseAddress, Size);
  879. finally
  880. Dummy := 0;
  881. VirtualProtect(BaseAddress, Size, OldProtect, Dummy);
  882. end;
  883. end;
  884. Result := WrittenBytes = Size;
  885. end;
  886. {$ENDIF MSWINDOWS}
  887. {$IFDEF LINUX}
  888. { TODO -cHelp : Author: Andreas Hausladen }
  889. { TODO : Works so far, but causes app to hang on termination }
  890. var
  891. AlignedAddress: Cardinal;
  892. PageSize, ProtectSize: Cardinal;
  893. begin
  894. Result := False;
  895. WrittenBytes := 0;
  896. PageSize := Cardinal(getpagesize);
  897. AlignedAddress := Cardinal(BaseAddress) and not (PageSize - 1); // start memory page
  898. // get the number of needed memory pages
  899. ProtectSize := PageSize;
  900. while Cardinal(BaseAddress) + Size > AlignedAddress + ProtectSize do
  901. Inc(ProtectSize, PageSize);
  902. if mprotect(Pointer(AlignedAddress), ProtectSize,
  903. PROT_READ or PROT_WRITE or PROT_EXEC) = 0 then // obtain write access
  904. begin
  905. try
  906. Move(Buffer^, BaseAddress^, Size); // replace code
  907. Result := True;
  908. WrittenBytes := Size;
  909. finally
  910. // Is there any function that returns the current page protection?
  911. // mprotect(p, ProtectSize, PROT_READ or PROT_EXEC); // lock memory page
  912. end;
  913. end;
  914. end;
  915. procedure FlushInstructionCache;
  916. { TODO -cHelp : Author: Andreas Hausladen }
  917. begin
  918. // do nothing
  919. end;
  920. {$ENDIF LINUX}
  921. // Guards
  922. //=== { TJclSafeGuard } ======================================================
  923. constructor TJclSafeGuard.Create(Mem: Pointer);
  924. begin
  925. inherited Create;
  926. FItem := Mem;
  927. end;
  928. destructor TJclSafeGuard.Destroy;
  929. begin
  930. FreeItem;
  931. inherited Destroy;
  932. end;
  933. function TJclSafeGuard.ReleaseItem: Pointer;
  934. begin
  935. Result := FItem;
  936. FItem := nil;
  937. end;
  938. function TJclSafeGuard.GetItem: Pointer;
  939. begin
  940. Result := FItem;
  941. end;
  942. procedure TJclSafeGuard.FreeItem;
  943. begin
  944. if FItem <> nil then
  945. FreeMem(FItem);
  946. FItem := nil;
  947. end;
  948. //=== { TJclObjSafeGuard } ===================================================
  949. constructor TJclObjSafeGuard.Create(Obj: TObject);
  950. begin
  951. inherited Create(Pointer(Obj));
  952. end;
  953. procedure TJclObjSafeGuard.FreeItem;
  954. begin
  955. if FItem <> nil then
  956. begin
  957. TObject(FItem).Free;
  958. FItem := nil;
  959. end;
  960. end;
  961. //=== { TJclMultiSafeGuard } =================================================
  962. constructor TJclMultiSafeGuard.Create;
  963. begin
  964. inherited Create;
  965. FItems := TList.Create;
  966. end;
  967. destructor TJclMultiSafeGuard.Destroy;
  968. var
  969. I: Integer;
  970. begin
  971. for I := FItems.Count - 1 downto 0 do
  972. FreeItem(I);
  973. FItems.Free;
  974. inherited Destroy;
  975. end;
  976. function TJclMultiSafeGuard.AddItem(Item: Pointer): Pointer;
  977. begin
  978. Result := Item;
  979. FItems.Add(Item);
  980. end;
  981. procedure TJclMultiSafeGuard.FreeItem(Index: Integer);
  982. begin
  983. FreeMem(FItems[Index]);
  984. FItems.Delete(Index);
  985. end;
  986. function TJclMultiSafeGuard.GetCount: Integer;
  987. begin
  988. Result := FItems.Count;
  989. end;
  990. function TJclMultiSafeGuard.GetItem(Index: Integer): Pointer;
  991. begin
  992. Result := FItems[Index];
  993. end;
  994. function TJclMultiSafeGuard.ReleaseItem(Index: Integer): Pointer;
  995. begin
  996. Result := FItems[Index];
  997. FItems.Delete(Index);
  998. end;
  999. function Guard(Mem: Pointer; var SafeGuard: IMultiSafeGuard): Pointer; overload;
  1000. begin
  1001. if SafeGuard = nil then
  1002. SafeGuard := TJclMultiSafeGuard.Create;
  1003. Result := SafeGuard.AddItem(Mem);
  1004. end;
  1005. //=== { TJclObjMultiSafeGuard } ==============================================
  1006. procedure TJclObjMultiSafeGuard.FreeItem(Index: Integer);
  1007. begin
  1008. TObject(FItems[Index]).Free;
  1009. FItems.Delete(Index);
  1010. end;
  1011. function Guard(Obj: TObject; var SafeGuard: IMultiSafeGuard): TObject; overload;
  1012. begin
  1013. if SafeGuard = nil then
  1014. SafeGuard := TJclObjMultiSafeGuard.Create;
  1015. Result := SafeGuard.AddItem(Obj);
  1016. end;
  1017. function Guard(Mem: Pointer; out SafeGuard: ISafeGuard): Pointer; overload;
  1018. begin
  1019. Result := Mem;
  1020. SafeGuard := TJclSafeGuard.Create(Mem);
  1021. end;
  1022. function Guard(Obj: TObject; out SafeGuard: ISafeGuard): TObject; overload;
  1023. begin
  1024. Result := Obj;
  1025. SafeGuard := TJclObjSafeGuard.Create(Obj);
  1026. end;
  1027. function GuardGetMem(Size: Cardinal; out SafeGuard: ISafeGuard): Pointer;
  1028. begin
  1029. GetMem(Result, Size);
  1030. Guard(Result, SafeGuard);
  1031. end;
  1032. function GuardAllocMem(Size: Cardinal; out SafeGuard: ISafeGuard): Pointer;
  1033. begin
  1034. Result := AllocMem(Size);
  1035. Guard(Result, SafeGuard);
  1036. end;
  1037. {$IFDEF SUPPORTS_GENERICS_}
  1038. //=== { TSafeGuard<T> } ======================================================
  1039. constructor TSafeGuard<T>.Create(Instance: T);
  1040. begin
  1041. inherited Create;
  1042. FItem := Instance;
  1043. end;
  1044. destructor TSafeGuard<T>.Destroy;
  1045. begin
  1046. FreeItem;
  1047. inherited Destroy;
  1048. end;
  1049. function TSafeGuard<T>.ReleaseItem: T;
  1050. begin
  1051. Result := FItem;
  1052. FItem := nil;
  1053. end;
  1054. function TSafeGuard<T>.GetItem: T;
  1055. begin
  1056. Result := FItem;
  1057. end;
  1058. procedure TSafeGuard<T>.FreeItem;
  1059. begin
  1060. if FItem <> nil then
  1061. FItem.Free;
  1062. FItem := nil;
  1063. end;
  1064. {$ENDIF SUPPORTS_GENERICS_}
  1065. //=== Shared memory functions ================================================
  1066. type
  1067. PMMFHandleListItem = ^TMMFHandleListItem;
  1068. TMMFHandleListItem = record
  1069. Next: PMMFHandleListItem;
  1070. Memory: Pointer;
  1071. Handle: THandle;
  1072. Name: string;
  1073. References: Integer;
  1074. end;
  1075. PMMFHandleList = PMMFHandleListItem;
  1076. var
  1077. MMFHandleList: PMMFHandleList = nil;
  1078. {$IFDEF THREADSAFE}
  1079. MMFFinalized: Boolean = False;
  1080. GlobalMMFHandleListCS: TJclIntfCriticalSection = nil;
  1081. {$ENDIF THREADSAFE}
  1082. {$IFDEF THREADSAFE}
  1083. function GetAccessToHandleList: IInterface;
  1084. var
  1085. OldValue: Pointer;
  1086. CS: TJclIntfCriticalSection;
  1087. begin
  1088. if not Assigned(GlobalMMFHandleListCS) and not MMFFinalized then
  1089. begin
  1090. CS := TJclIntfCriticalSection.Create;
  1091. {$IFDEF RTL200_UP} // Delphi 2009+
  1092. OldValue := InterlockedCompareExchangePointer(Pointer(GlobalMMFHandleListCS), Pointer(CS), nil);
  1093. {$ELSE}
  1094. {$IFDEF RTL160_UP} // Delphi 7-2007
  1095. OldValue := Pointer(InterlockedCompareExchange(Longint(GlobalMMFHandleListCS), Longint(CS), 0));
  1096. {$ELSE} // Delphi 5, 6
  1097. OldValue := InterlockedCompareExchange(Pointer(GlobalMMFHandleListCS), Pointer(CS), nil);
  1098. {$ENDIF RTL180_UP}
  1099. {$ENDIF RTL185_UP}
  1100. if OldValue <> nil then
  1101. CS.Free;
  1102. end;
  1103. Result := GlobalMMFHandleListCS;
  1104. end;
  1105. {$ENDIF THREADSAFE}
  1106. {$IFDEF MSWINDOWS}
  1107. function SharedGetMem(var P{: Pointer}; const Name: string; Size: Cardinal;
  1108. DesiredAccess: Cardinal = FILE_MAP_ALL_ACCESS): Integer;
  1109. var
  1110. FileMappingHandle: THandle;
  1111. Iterate, NewListItem: PMMFHandleListItem;
  1112. Protect: Cardinal;
  1113. {$IFDEF THREADSAFE}
  1114. HandleListAccess: IInterface;
  1115. {$ENDIF THREADSAFE}
  1116. begin
  1117. Result := 0;
  1118. Pointer(P) := nil;
  1119. if not JclCheckWinVersion(5, 0) and ((Name = '') or (Pos('\', Name) > 0)) then
  1120. raise ESharedMemError.CreateResFmt(@RsInvalidMMFName, [Name]);
  1121. {$IFDEF THREADSAFE}
  1122. HandleListAccess := GetAccessToHandleList;
  1123. {$ENDIF THREADSAFE}
  1124. // search for same name
  1125. Iterate := MMFHandleList;
  1126. while Iterate <> nil do
  1127. begin
  1128. if CompareText(Iterate^.Name, Name) = 0 then
  1129. begin
  1130. Inc(Iterate^.References);
  1131. Pointer(P) := Iterate^.Memory;
  1132. Result := ERROR_ALREADY_EXISTS;
  1133. Exit;
  1134. end;
  1135. Iterate := Iterate^.Next;
  1136. end;
  1137. // open file mapping
  1138. FileMappingHandle := OpenFileMapping(DesiredAccess, False, PChar(Name));
  1139. if FileMappingHandle = 0 then
  1140. begin
  1141. if Size = 0 then
  1142. raise ESharedMemError.CreateResFmt(@RsInvalidMMFEmpty, [Name]);
  1143. Protect := PAGE_READWRITE;
  1144. if (Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and (DesiredAccess = FILE_MAP_COPY) then
  1145. Protect := PAGE_WRITECOPY;
  1146. FileMappingHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil, Protect,
  1147. 0, Size, PChar(Name));
  1148. end
  1149. else
  1150. Result := ERROR_ALREADY_EXISTS;
  1151. if GetLastError = ERROR_ALREADY_EXISTS then
  1152. Result := ERROR_ALREADY_EXISTS
  1153. else
  1154. begin
  1155. if FileMappingHandle = 0 then
  1156. RaiseLastOSError;
  1157. end;
  1158. // map view
  1159. Pointer(P) := MapViewOfFile(FileMappingHandle, DesiredAccess, 0, 0, Size);
  1160. if Pointer(P) = nil then
  1161. begin
  1162. try
  1163. RaiseLastOSError;
  1164. except
  1165. CloseHandle(FileMappingHandle);
  1166. raise;
  1167. end;
  1168. end;
  1169. // add list item to MMFHandleList
  1170. New(NewListItem);
  1171. NewListItem^.Name := Name;
  1172. NewListItem^.Handle := FileMappingHandle;
  1173. NewListItem^.Memory := Pointer(P);
  1174. NewListItem^.References := 1;
  1175. NewListItem^.Next := MMFHandleList;
  1176. MMFHandleList := NewListItem;
  1177. end;
  1178. function SharedAllocMem(const Name: string; Size: Cardinal;
  1179. DesiredAccess: Cardinal = FILE_MAP_ALL_ACCESS): Pointer;
  1180. begin
  1181. Result := nil;
  1182. if (SharedGetMem(Result, Name, Size, DesiredAccess) <> ERROR_ALREADY_EXISTS) and
  1183. ((DesiredAccess and (FILE_MAP_WRITE or FILE_MAP_COPY)) <> 0) and
  1184. (Size > 0) and (Result <> nil) then
  1185. ResetMemory(Pointer(Result)^, Size);
  1186. end;
  1187. function SharedFreeMem(var P{: Pointer}): Boolean;
  1188. var
  1189. N, Iterate: PMMFHandleListItem;
  1190. {$IFDEF THREADSAFE}
  1191. HandleListAccess: IInterface;
  1192. {$ENDIF THREADSAFE}
  1193. begin
  1194. if Pointer(P) <> nil then
  1195. begin
  1196. Result := False;
  1197. {$IFDEF THREADSAFE}
  1198. HandleListAccess := GetAccessToHandleList;
  1199. {$ENDIF THREADSAFE}
  1200. Iterate := MMFHandleList;
  1201. N := nil;
  1202. while Iterate <> nil do
  1203. begin
  1204. if Iterate^.Memory = Pointer(P) then
  1205. begin
  1206. if Iterate^.References > 1 then
  1207. begin
  1208. Dec(Iterate^.References);
  1209. Pointer(P) := nil;
  1210. Result := True;
  1211. Exit;
  1212. end;
  1213. UnmapViewOfFile(Iterate^.Memory);
  1214. CloseHandle(Iterate^.Handle);
  1215. if N = nil then
  1216. MMFHandleList := Iterate^.Next
  1217. else
  1218. N^.Next := Iterate^.Next;
  1219. Dispose(Iterate);
  1220. Pointer(P) := nil;
  1221. Result := True;
  1222. Break;
  1223. end;
  1224. N := Iterate;
  1225. Iterate := Iterate^.Next;
  1226. end;
  1227. end
  1228. else
  1229. Result := True;
  1230. end;
  1231. function SharedOpenMem(var P{: Pointer}; const Name: string;
  1232. DesiredAccess: Cardinal = FILE_MAP_ALL_ACCESS): Boolean;
  1233. begin
  1234. Result := SharedGetMem(P, Name, 0, DesiredAccess) = ERROR_ALREADY_EXISTS;
  1235. end;
  1236. function SharedOpenMem(const Name: string;
  1237. DesiredAccess: Cardinal = FILE_MAP_ALL_ACCESS): Pointer;
  1238. begin
  1239. Result := nil;
  1240. SharedGetMem(Result, Name, 0, DesiredAccess);
  1241. end;
  1242. function SharedCloseMem(var P{: Pointer}): Boolean;
  1243. begin
  1244. Result := SharedFreeMem(P);
  1245. end;
  1246. {$ENDIF MSWINDOWS}
  1247. //=== Binary search ==========================================================
  1248. function SearchSortedList(List: TList; SortFunc: TListSortCompare; Item: Pointer; Nearest: Boolean): Integer;
  1249. var
  1250. L, H, I, C: Integer;
  1251. B: Boolean;
  1252. begin
  1253. Result := -1;
  1254. if List <> nil then
  1255. begin
  1256. L := 0;
  1257. H := List.Count - 1;
  1258. B := False;
  1259. while L <= H do
  1260. begin
  1261. I := (L + H) shr 1;
  1262. C := SortFunc(List.List{$IFNDEF RTL230_UP}^{$ENDIF !RTL230_UP}[I], Item);
  1263. if C < 0 then
  1264. L := I + 1
  1265. else
  1266. begin
  1267. H := I - 1;
  1268. if C = 0 then
  1269. begin
  1270. B := True;
  1271. L := I;
  1272. end;
  1273. end;
  1274. end;
  1275. if B then
  1276. Result := L
  1277. else
  1278. if Nearest and (H >= 0) then
  1279. Result := H;
  1280. end;
  1281. end;
  1282. function SearchSortedUntyped(Param: Pointer; ItemCount: Integer; SearchFunc: TUntypedSearchCompare;
  1283. const Value; Nearest: Boolean): Integer;
  1284. var
  1285. L, H, I, C: Integer;
  1286. B: Boolean;
  1287. begin
  1288. Result := -1;
  1289. if ItemCount > 0 then
  1290. begin
  1291. L := 0;
  1292. H := ItemCount - 1;
  1293. B := False;
  1294. while L <= H do
  1295. begin
  1296. I := (L + H) shr 1;
  1297. C := SearchFunc(Param, I, Value);
  1298. if C < 0 then
  1299. L := I + 1
  1300. else
  1301. begin
  1302. H := I - 1;
  1303. if C = 0 then
  1304. begin
  1305. B := True;
  1306. L := I;
  1307. end;
  1308. end;
  1309. end;
  1310. if B then
  1311. Result := L
  1312. else
  1313. if Nearest and (H >= 0) then
  1314. Result := H;
  1315. end;
  1316. end;
  1317. //=== Dynamic array sort and search routines =================================
  1318. procedure SortDynArray(const ArrayPtr: Pointer; ElementSize: Cardinal; SortFunc: TDynArraySortCompare);
  1319. var
  1320. TempBuf: TDynByteArray;
  1321. procedure QuickSort(L, R: SizeInt);
  1322. var
  1323. I, J, T: SizeInt;
  1324. P, IPtr, JPtr: Pointer;
  1325. ElSize: Integer;
  1326. begin
  1327. ElSize := ElementSize;
  1328. repeat
  1329. I := L;
  1330. J := R;
  1331. P := Pointer(TJclAddr(ArrayPtr) + TJclAddr(((L + R) shr 1) * SizeInt(ElementSize)));
  1332. repeat
  1333. IPtr := Pointer(TJclAddr(ArrayPtr) + TJclAddr(I * SizeInt(ElementSize)));
  1334. JPtr := Pointer(TJclAddr(ArrayPtr) + TJclAddr(J * SizeInt(ElementSize)));
  1335. while SortFunc(IPtr, P) < 0 do
  1336. begin
  1337. Inc(I);
  1338. Inc(PByte(IPtr), ElSize);
  1339. end;
  1340. while SortFunc(JPtr, P) > 0 do
  1341. begin
  1342. Dec(J);
  1343. Dec(PByte(JPtr), ElSize);
  1344. end;
  1345. if I <= J then
  1346. begin
  1347. if I <> J then
  1348. begin
  1349. case ElementSize of
  1350. SizeOf(Byte):
  1351. begin
  1352. T := PByte(IPtr)^;
  1353. PByte(IPtr)^ := PByte(JPtr)^;
  1354. PByte(JPtr)^ := T;
  1355. end;
  1356. SizeOf(Word):
  1357. begin
  1358. T := PWord(IPtr)^;
  1359. PWord(IPtr)^ := PWord(JPtr)^;
  1360. PWord(JPtr)^ := T;
  1361. end;
  1362. SizeOf(Integer):
  1363. begin
  1364. T := PInteger(IPtr)^;
  1365. PInteger(IPtr)^ := PInteger(JPtr)^;
  1366. PInteger(JPtr)^ := T;
  1367. end;
  1368. else
  1369. Move(IPtr^, TempBuf[0], ElementSize);
  1370. Move(JPtr^, IPtr^, ElementSize);
  1371. Move(TempBuf[0], JPtr^, ElementSize);
  1372. end;
  1373. end;
  1374. if P = IPtr then
  1375. P := JPtr
  1376. else
  1377. if P = JPtr then
  1378. P := IPtr;
  1379. Inc(I);
  1380. Dec(J);
  1381. end;
  1382. until I > J;
  1383. if L < J then
  1384. QuickSort(L, J);
  1385. L := I;
  1386. until I >= R;
  1387. end;
  1388. begin
  1389. if ArrayPtr <> nil then
  1390. begin
  1391. SetLength(TempBuf, ElementSize);
  1392. QuickSort(0, PSizeInt(TJclAddr(ArrayPtr) - SizeOf(SizeInt))^ - 1);
  1393. end;
  1394. end;
  1395. function SearchDynArray(const ArrayPtr: Pointer; ElementSize: Cardinal; SortFunc: TDynArraySortCompare;
  1396. ValuePtr: Pointer; Nearest: Boolean): SizeInt;
  1397. var
  1398. L, H, I, C: SizeInt;
  1399. B: Boolean;
  1400. begin
  1401. Result := -1;
  1402. if ArrayPtr <> nil then
  1403. begin
  1404. L := 0;
  1405. H := PSizeInt(TJclAddr(ArrayPtr) - SizeOf(SizeInt))^ - 1;
  1406. B := False;
  1407. while L <= H do
  1408. begin
  1409. I := (L + H) shr 1;
  1410. C := SortFunc(Pointer(TJclAddr(ArrayPtr) + TJclAddr(I * SizeInt(ElementSize))), ValuePtr);
  1411. if C < 0 then
  1412. L := I + 1
  1413. else
  1414. begin
  1415. H := I - 1;
  1416. if C = 0 then
  1417. begin
  1418. B := True;
  1419. L := I;
  1420. end;
  1421. end;
  1422. end;
  1423. if B then
  1424. Result := L
  1425. else
  1426. if Nearest and (H >= 0) then
  1427. Result := H;
  1428. end;
  1429. end;
  1430. { Various compare functions for basic types }
  1431. function DynArrayCompareByte(Item1, Item2: Pointer): Integer;
  1432. begin
  1433. Result := PByte(Item1)^ - PByte(Item2)^;
  1434. end;
  1435. function DynArrayCompareShortInt(Item1, Item2: Pointer): Integer;
  1436. begin
  1437. Result := PShortInt(Item1)^ - PShortInt(Item2)^;
  1438. end;
  1439. function DynArrayCompareWord(Item1, Item2: Pointer): Integer;
  1440. begin
  1441. Result := PWord(Item1)^ - PWord(Item2)^;
  1442. end;
  1443. function DynArrayCompareSmallInt(Item1, Item2: Pointer): Integer;
  1444. begin
  1445. Result := PSmallInt(Item1)^ - PSmallInt(Item2)^;
  1446. end;
  1447. function DynArrayCompareInteger(Item1, Item2: Pointer): Integer;
  1448. begin
  1449. if PInteger(Item1)^ < PInteger(Item2)^ then
  1450. Result := -1
  1451. else
  1452. if PInteger(Item1)^ > PInteger(Item2)^ then
  1453. Result := 1
  1454. else
  1455. Result := 0;
  1456. end;
  1457. function DynArrayCompareCardinal(Item1, Item2: Pointer): Integer;
  1458. begin
  1459. if PCardinal(Item1)^ < PCardinal(Item2)^ then
  1460. Result := -1
  1461. else
  1462. if PCardinal(Item1)^ > PCardinal(Item2)^ then
  1463. Result := 1
  1464. else
  1465. Result := 0;
  1466. end;
  1467. function DynArrayCompareInt64(Item1, Item2: Pointer): Integer;
  1468. begin
  1469. if PInt64(Item1)^ < PInt64(Item2)^ then
  1470. Result := -1
  1471. else
  1472. if PInt64(Item1)^ > PInt64(Item2)^ then
  1473. Result := 1
  1474. else
  1475. Result := 0;
  1476. end;
  1477. function DynArrayCompareSingle(Item1, Item2: Pointer): Integer;
  1478. begin
  1479. if PSingle(Item1)^ < PSingle(Item2)^ then
  1480. Result := -1
  1481. else
  1482. if PSingle(Item1)^ > PSingle(Item2)^ then
  1483. Result := 1
  1484. else
  1485. Result := 0;
  1486. end;
  1487. function DynArrayCompareDouble(Item1, Item2: Pointer): Integer;
  1488. begin
  1489. if PDouble(Item1)^ < PDouble(Item2)^ then
  1490. Result := -1
  1491. else
  1492. if PDouble(Item1)^ > PDouble(Item2)^ then
  1493. Result := 1
  1494. else
  1495. Result := 0;
  1496. end;
  1497. function DynArrayCompareExtended(Item1, Item2: Pointer): Integer;
  1498. begin
  1499. if PExtended(Item1)^ < PExtended(Item2)^ then
  1500. Result := -1
  1501. else
  1502. if PExtended(Item1)^ > PExtended(Item2)^ then
  1503. Result := 1
  1504. else
  1505. Result := 0;
  1506. end;
  1507. function DynArrayCompareFloat(Item1, Item2: Pointer): Integer;
  1508. begin
  1509. if PFloat(Item1)^ < PFloat(Item2)^ then
  1510. Result := -1
  1511. else
  1512. if PFloat(Item1)^ > PFloat(Item2)^ then
  1513. Result := 1
  1514. else
  1515. Result := 0;
  1516. end;
  1517. function DynArrayCompareAnsiString(Item1, Item2: Pointer): Integer;
  1518. begin
  1519. Result := AnsiCompareStr(PAnsiString(Item1)^, PAnsiString(Item2)^);
  1520. end;
  1521. function DynArrayCompareAnsiText(Item1, Item2: Pointer): Integer;
  1522. begin
  1523. Result := AnsiCompareText(PAnsiString(Item1)^, PAnsiString(Item2)^);
  1524. end;
  1525. function DynArrayCompareWideString(Item1, Item2: Pointer): Integer;
  1526. begin
  1527. Result := WideCompareStr(PWideString(Item1)^, PWideString(Item2)^);
  1528. end;
  1529. function DynArrayCompareWideText(Item1, Item2: Pointer): Integer;
  1530. begin
  1531. Result := WideCompareText(PWideString(Item1)^, PWideString(Item2)^);
  1532. end;
  1533. function DynArrayCompareString(Item1, Item2: Pointer): Integer;
  1534. begin
  1535. Result := CompareStr(PString(Item1)^, PString(Item2)^);
  1536. end;
  1537. function DynArrayCompareText(Item1, Item2: Pointer): Integer;
  1538. begin
  1539. Result := CompareText(PString(Item1)^, PString(Item2)^);
  1540. end;
  1541. //=== Object lists ===========================================================
  1542. procedure ClearObjectList(List: TList);
  1543. var
  1544. I: Integer;
  1545. begin
  1546. if List <> nil then
  1547. begin
  1548. for I := List.Count - 1 downto 0 do
  1549. begin
  1550. if List[I] <> nil then
  1551. begin
  1552. if TObject(List[I]) is TList then
  1553. begin
  1554. // recursively delete TList sublists
  1555. ClearObjectList(TList(List[I]));
  1556. end;
  1557. TObject(List[I]).Free;
  1558. if (not (List is TComponentList))
  1559. and ((not(List is TObjectList)) or not TObjectList(List).OwnsObjects) then
  1560. List[I] := nil;
  1561. end;
  1562. end;
  1563. List.Clear;
  1564. end;
  1565. end;
  1566. procedure FreeObjectList(var List: TList);
  1567. begin
  1568. if List <> nil then
  1569. begin
  1570. ClearObjectList(List);
  1571. FreeAndNil(List);
  1572. end;
  1573. end;
  1574. //=== { TJclReferenceMemoryStream } ==========================================
  1575. constructor TJclReferenceMemoryStream.Create(const Ptr: Pointer; Size: Longint);
  1576. begin
  1577. {$IFDEF MSWINDOWS}
  1578. Assert(not IsBadReadPtr(Ptr, Size));
  1579. {$ENDIF MSWINDOWS}
  1580. inherited Create;
  1581. SetPointer(Ptr, Size);
  1582. end;
  1583. function TJclReferenceMemoryStream.Write(const Buffer; Count: Longint): Longint;
  1584. begin
  1585. raise EJclError.CreateRes(@RsCannotWriteRefStream);
  1586. end;
  1587. //=== { TJclAutoPtr } ========================================================
  1588. constructor TJclAutoPtr.Create(AValue: TObject);
  1589. begin
  1590. inherited Create;
  1591. FValue := AValue;
  1592. end;
  1593. destructor TJclAutoPtr.Destroy;
  1594. begin
  1595. FValue.Free;
  1596. inherited Destroy;
  1597. end;
  1598. function TJclAutoPtr.AsObject: TObject;
  1599. begin
  1600. Result := FValue;
  1601. end;
  1602. function TJclAutoPtr.AsPointer: Pointer;
  1603. begin
  1604. Result := FValue;
  1605. end;
  1606. function TJclAutoPtr.ReleaseObject: TObject;
  1607. begin
  1608. Result := FValue;
  1609. FValue := nil;
  1610. end;
  1611. function CreateAutoPtr(Value: TObject): IAutoPtr;
  1612. begin
  1613. Result := TJclAutoPtr.Create(Value);
  1614. end;
  1615. //=== replacement for the C distfix operator ? : =============================
  1616. function Iff(const Condition: Boolean; const TruePart, FalsePart: string): string;
  1617. begin
  1618. if Condition then
  1619. Result := TruePart
  1620. else
  1621. Result := FalsePart;
  1622. end;
  1623. function Iff(const Condition: Boolean; const TruePart, FalsePart: Char): Char;
  1624. begin
  1625. if Condition then
  1626. Result := TruePart
  1627. else
  1628. Result := FalsePart;
  1629. end;
  1630. function Iff(const Condition: Boolean; const TruePart, FalsePart: Byte): Byte;
  1631. begin
  1632. if Condition then
  1633. Result := TruePart
  1634. else
  1635. Result := FalsePart;
  1636. end;
  1637. function Iff(const Condition: Boolean; const TruePart, FalsePart: Integer): Integer;
  1638. begin
  1639. if Condition then
  1640. Result := TruePart
  1641. else
  1642. Result := FalsePart;
  1643. end;
  1644. function Iff(const Condition: Boolean; const TruePart, FalsePart: Cardinal): Cardinal;
  1645. begin
  1646. if Condition then
  1647. Result := TruePart
  1648. else
  1649. Result := FalsePart;
  1650. end;
  1651. function Iff(const Condition: Boolean; const TruePart, FalsePart: Float): Float;
  1652. begin
  1653. if Condition then
  1654. Result := TruePart
  1655. else
  1656. Result := FalsePart;
  1657. end;
  1658. function Iff(const Condition: Boolean; const TruePart, FalsePart: Boolean): Boolean;
  1659. begin
  1660. if Condition then
  1661. Result := TruePart
  1662. else
  1663. Result := FalsePart;
  1664. end;
  1665. function Iff(const Condition: Boolean; const TruePart, FalsePart: Pointer): Pointer;
  1666. begin
  1667. if Condition then
  1668. Result := TruePart
  1669. else
  1670. Result := FalsePart;
  1671. end;
  1672. function Iff(const Condition: Boolean; const TruePart, FalsePart: Int64): Int64;
  1673. begin
  1674. if Condition then
  1675. Result := TruePart
  1676. else
  1677. Result := FalsePart;
  1678. end;
  1679. {$IFDEF SUPPORTS_VARIANT}
  1680. function Iff(const Condition: Boolean; const TruePart, FalsePart: Variant): Variant; overload;
  1681. begin
  1682. if Condition then
  1683. Result := TruePart
  1684. else
  1685. Result := FalsePart;
  1686. end;
  1687. {$ENDIF SUPPORTS_VARIANT}
  1688. //=== Classes information and manipulation ===================================
  1689. // Virtual Methods
  1690. // Helper method
  1691. procedure SetVMTPointer(AClass: TClass; Offset: Integer; Value: Pointer);
  1692. var
  1693. WrittenBytes: DWORD;
  1694. PatchAddress: PPointer;
  1695. begin
  1696. {$OVERFLOWCHECKS OFF}
  1697. PatchAddress := Pointer(TJclAddr(AClass) + TJclAddr(Offset));
  1698. {$IFDEF OVERFLOWCHECKS_ON}
  1699. {$OVERFLOWCHECKS ON}
  1700. {$ENDIF OVERFLOWCHECKS_ON}
  1701. if not WriteProtectedMemory(PatchAddress, @Value, SizeOf(Value), WrittenBytes) then
  1702. raise EJclVMTError.CreateResFmt(@RsVMTMemoryWriteError,
  1703. [SysErrorMessage({$IFDEF FPC}GetLastOSError{$ELSE}GetLastError{$ENDIF})]);
  1704. if WrittenBytes <> SizeOf(Pointer) then
  1705. raise EJclVMTError.CreateResFmt(@RsVMTMemoryWriteError, [IntToStr(WrittenBytes)]);
  1706. // make sure that everything keeps working in a dual processor setting
  1707. // (outchy) done by WriteProtectedMemory
  1708. // FlushInstructionCache{$IFDEF MSWINDOWS}(GetCurrentProcess, PatchAddress, SizeOf(Pointer)){$ENDIF};
  1709. end;
  1710. {$IFNDEF FPC}
  1711. function GetVirtualMethodCount(AClass: TClass): Integer;
  1712. type
  1713. PINT_PTR = ^INT_PTR;
  1714. var
  1715. BeginVMT: INT_PTR;
  1716. EndVMT: INT_PTR;
  1717. TablePointer: INT_PTR;
  1718. I: Integer;
  1719. begin
  1720. BeginVMT := INT_PTR(AClass);
  1721. // Scan the offset entries in the class table for the various fields,
  1722. // namely vmtIntfTable, vmtAutoTable, ..., vmtDynamicTable
  1723. // The last entry is always the vmtClassName, so stop once we got there
  1724. // After the last virtual method there is one of these entries.
  1725. EndVMT := PINT_PTR(INT_PTR(AClass) + vmtClassName)^;
  1726. // Set iterator to first item behind VMT table pointer
  1727. I := vmtSelfPtr + SizeOf(Pointer);
  1728. repeat
  1729. TablePointer := PINT_PTR(INT_PTR(AClass) + I)^;
  1730. if (TablePointer <> 0) and (TablePointer >= BeginVMT) and
  1731. (TablePointer < EndVMT) then
  1732. EndVMT := INT_PTR(TablePointer);
  1733. Inc(I, SizeOf(Pointer));
  1734. until I >= vmtClassName;
  1735. Result := (EndVMT - BeginVMT) div SizeOf(Pointer);
  1736. end;
  1737. {$ENDIF ~FPC}
  1738. function GetVirtualMethod(AClass: TClass; const Index: Integer): Pointer;
  1739. begin
  1740. {$OVERFLOWCHECKS OFF}
  1741. Result := PPointer(TJclAddr(AClass) + TJclAddr(Index * SizeOf(Pointer)))^;
  1742. {$IFDEF OVERFLOWCHECKS_ON}
  1743. {$OVERFLOWCHECKS ON}
  1744. {$ENDIF OVERFLOWCHECKS_ON}
  1745. end;
  1746. procedure SetVirtualMethod(AClass: TClass; const Index: Integer; const Method: Pointer);
  1747. begin
  1748. SetVMTPointer(AClass, Index * SizeOf(Pointer), Method);
  1749. end;
  1750. function GetDynamicMethodCount(AClass: TClass): Integer; assembler;
  1751. asm
  1752. {$IFDEF CPU32}
  1753. // --> RAX AClass
  1754. // <-- EAX Result
  1755. MOV EAX, [EAX].vmtDynamicTable
  1756. TEST EAX, EAX
  1757. JE @@Exit
  1758. MOVZX EAX, WORD PTR [EAX]
  1759. {$ENDIF CPU32}
  1760. {$IFDEF CPU64}
  1761. // --> RCX AClass
  1762. // <-- EAX Result
  1763. MOV RAX, [RCX].vmtDynamicTable
  1764. TEST RAX, RAX
  1765. JE @@Exit
  1766. MOVZX RAX, WORD PTR [RAX]
  1767. {$ENDIF CPU64}
  1768. @@Exit:
  1769. end;
  1770. function GetDynamicIndexList(AClass: TClass): PDynamicIndexList; assembler;
  1771. asm
  1772. {$IFDEF CPU32}
  1773. // --> EAX AClass
  1774. // <-- EAX Result
  1775. MOV EAX, [EAX].vmtDynamicTable
  1776. ADD EAX, 2
  1777. {$ENDIF CPU32}
  1778. {$IFDEF CPU64}
  1779. // --> RCX AClass
  1780. // <-- RAX Result
  1781. MOV RAX, [RCX].vmtDynamicTable
  1782. ADD RAX, 2
  1783. {$ENDIF CPU64}
  1784. end;
  1785. function GetDynamicAddressList(AClass: TClass): PDynamicAddressList; assembler;
  1786. asm
  1787. {$IFDEF CPU32}
  1788. // --> EAX AClass
  1789. // <-- EAX Result
  1790. MOV EAX, [EAX].vmtDynamicTable
  1791. MOVZX EDX, Word ptr [EAX]
  1792. ADD EAX, EDX
  1793. ADD EAX, EDX
  1794. ADD EAX, 2
  1795. {$ENDIF CPU32}
  1796. {$IFDEF CPU64}
  1797. // --> RCX AClass
  1798. // <-- RAX Result
  1799. MOV RAX, [RCX].vmtDynamicTable
  1800. MOVZX RDX, Word ptr [RAX]
  1801. ADD RAX, RDX
  1802. ADD RAX, RDX
  1803. ADD RAX, 2
  1804. {$ENDIF CPU64}
  1805. end;
  1806. function HasDynamicMethod(AClass: TClass; Index: Integer): Boolean; assembler;
  1807. // Mainly copied from System.GetDynaMethod
  1808. asm
  1809. {$IFDEF CPU32}
  1810. // --> EAX AClass
  1811. // EDX Index
  1812. // <-- AL Result
  1813. PUSH EDI
  1814. XCHG EAX, EDX
  1815. JMP @@HaveVMT
  1816. @@OuterLoop:
  1817. MOV EDX, [EDX]
  1818. @@HaveVMT:
  1819. MOV EDI, [EDX].vmtDynamicTable
  1820. TEST EDI, EDI
  1821. JE @@Parent
  1822. MOVZX ECX, WORD PTR [EDI]
  1823. PUSH ECX
  1824. ADD EDI,2
  1825. REPNE SCASW
  1826. JE @@Found
  1827. POP ECX
  1828. @@Parent:
  1829. MOV EDX,[EDX].vmtParent
  1830. TEST EDX,EDX
  1831. JNE @@OuterLoop
  1832. MOV EAX, 0
  1833. JMP @@Exit
  1834. @@Found:
  1835. POP EAX
  1836. MOV EAX, 1
  1837. @@Exit:
  1838. POP EDI
  1839. {$ENDIF CPU32}
  1840. {$IFDEF CPU64}
  1841. // --> RCX AClass
  1842. // EDX Index
  1843. // <-- AL Result
  1844. MOV EAX, EDX
  1845. MOV RDX, RCX
  1846. JMP @@HaveVMT
  1847. @@OuterLoop:
  1848. MOV RDX, [RDX]
  1849. @@HaveVMT:
  1850. MOV RDI, [RDX].vmtDynamicTable
  1851. TEST RDI, RDI
  1852. JE @@Parent
  1853. MOVZX RCX, WORD PTR [RDI]
  1854. PUSH RCX
  1855. ADD RDI,2
  1856. REPNE SCASW
  1857. JE @@Found
  1858. POP RCX
  1859. @@Parent:
  1860. MOV RDX,[RDX].vmtParent
  1861. TEST RDX,RDX
  1862. JNE @@OuterLoop
  1863. MOV RAX, 0
  1864. JMP @@Exit
  1865. @@Found:
  1866. POP RAX
  1867. MOV RAX, 1
  1868. @@Exit:
  1869. {$ENDIF CPU64}
  1870. end;
  1871. {$IFNDEF FPC}
  1872. function GetDynamicMethod(AClass: TClass; Index: Integer): Pointer; assembler;
  1873. asm
  1874. CALL System.@FindDynaClass
  1875. end;
  1876. {$ENDIF ~FPC}
  1877. //=== Interface Table ========================================================
  1878. function GetInitTable(AClass: TClass): PTypeInfo; assembler;
  1879. asm
  1880. {$IFDEF CPU32}
  1881. // --> EAX AClass
  1882. // <-- EAX Result
  1883. MOV EAX, [EAX].vmtInitTable
  1884. {$ENDIF CPU32}
  1885. {$IFDEF CPU64}
  1886. // --> RCX AClass
  1887. // <-- RAX Result
  1888. MOV RAX, [RCX].vmtInitTable
  1889. {$ENDIF CPU64}
  1890. end;
  1891. function GetFieldTable(AClass: TClass): PFieldTable; assembler;
  1892. asm
  1893. {$IFDEF CPU32}
  1894. // --> EAX AClass
  1895. // <-- EAX Result
  1896. MOV EAX, [EAX].vmtFieldTable
  1897. {$ENDIF CPU32}
  1898. {$IFDEF CPU64}
  1899. // --> RCX AClass
  1900. // <-- RAX Result
  1901. MOV RAX, [RCX].vmtFieldTable
  1902. {$ENDIF CPU64}
  1903. end;
  1904. function GetMethodTable(AClass: TClass): PMethodTable; assembler;
  1905. asm
  1906. {$IFDEF CPU32}
  1907. // --> EAX AClass
  1908. // <-- EAX Result
  1909. MOV EAX, [EAX].vmtMethodTable
  1910. {$ENDIF CPU32}
  1911. {$IFDEF CPU64}
  1912. // --> RCX AClass
  1913. // <-- RAX Result
  1914. MOV RAX, [RCX].vmtMethodTable
  1915. {$ENDIF CPU64}
  1916. end;
  1917. function GetMethodEntry(MethodTable: PMethodTable; Index: Integer): PMethodEntry;
  1918. begin
  1919. Result := Pointer(TJclAddr(MethodTable) + 2);
  1920. for Index := Index downto 1 do
  1921. Inc(TJclAddr(Result), Result^.EntrySize);
  1922. end;
  1923. function MethodEquals(aMethod1, aMethod2: TMethod): boolean;
  1924. begin
  1925. Result := (aMethod1.Code = aMethod2.Code) and
  1926. (aMethod1.Data = aMethod2.Data);
  1927. end;
  1928. function NotifyEventEquals(aMethod1, aMethod2: TNotifyEvent): boolean;
  1929. begin
  1930. Result := MethodEquals(TMethod(aMethod1),TMethod(aMethod2));
  1931. end;
  1932. //=== Class Parent methods ===================================================
  1933. procedure SetClassParent(AClass: TClass; NewClassParent: TClass);
  1934. var
  1935. WrittenBytes: DWORD;
  1936. PatchAddress: Pointer;
  1937. begin
  1938. {$OVERFLOWCHECKS OFF}
  1939. PatchAddress := PPointer(TJclAddr(AClass) + TJclAddr(vmtParent))^;
  1940. {$IFDEF OVERFLOWCHECKS_ON}
  1941. {$OVERFLOWCHECKS ON}
  1942. {$ENDIF OVERFLOWCHECKS_ON}
  1943. if not WriteProtectedMemory(PatchAddress, @NewClassParent, SizeOf(Pointer), WrittenBytes) then
  1944. raise EJclVMTError.CreateResFmt(@RsVMTMemoryWriteError,
  1945. [SysErrorMessage({$IFDEF FPC}GetLastOSError{$ELSE}GetLastError{$ENDIF})]);
  1946. if WrittenBytes <> SizeOf(Pointer) then
  1947. raise EJclVMTError.CreateResFmt(@RsVMTMemoryWriteError, [IntToStr(WrittenBytes)]);
  1948. // make sure that everything keeps working in a dual processor setting
  1949. // (outchy) done by WriteProtectedMemory
  1950. // FlushInstructionCache{$IFDEF MSWINDOWS}(GetCurrentProcess, PatchAddress, SizeOf(Pointer)){$ENDIF};
  1951. end;
  1952. function GetClassParent(AClass: TClass): TClass; assembler;
  1953. asm
  1954. {$IFDEF CPU32}
  1955. // --> EAX AClass
  1956. // <-- EAX Result
  1957. MOV EAX, [EAX].vmtParent
  1958. TEST EAX, EAX
  1959. JE @@Exit
  1960. MOV EAX, [EAX]
  1961. {$ENDIF CPU32}
  1962. {$IFDEF CPU64}
  1963. // --> RCX AClass
  1964. // <-- RAX Result
  1965. MOV RAX, [RCX].vmtParent
  1966. TEST RAX, RAX
  1967. JE @@Exit
  1968. MOV RAX, [RAX]
  1969. {$ENDIF CPU64}
  1970. @@Exit:
  1971. end;
  1972. {$IFDEF BORLAND}
  1973. function IsClass(Address: Pointer): Boolean; assembler;
  1974. asm
  1975. CMP Address, Address.vmtSelfPtr
  1976. JNZ @False
  1977. MOV Result, True
  1978. JMP @Exit
  1979. @False:
  1980. MOV Result, False
  1981. @Exit:
  1982. end;
  1983. {$ENDIF BORLAND}
  1984. {$IFDEF BORLAND}
  1985. function IsObject(Address: Pointer): Boolean; assembler;
  1986. asm
  1987. // or IsClass(Pointer(Address^));
  1988. MOV EAX, [Address]
  1989. CMP EAX, EAX.vmtSelfPtr
  1990. JNZ @False
  1991. MOV Result, True
  1992. JMP @Exit
  1993. @False:
  1994. MOV Result, False
  1995. @Exit:
  1996. end;
  1997. {$ENDIF BORLAND}
  1998. function InheritsFromByName(AClass: TClass; const AClassName: string): Boolean;
  1999. begin
  2000. while (AClass <> nil) and not AClass.ClassNameIs(AClassName) do
  2001. AClass := AClass.ClassParent;
  2002. Result := AClass <> nil;
  2003. end;
  2004. //=== Interface information ==================================================
  2005. function GetImplementorOfInterface(const I: IInterface): TObject;
  2006. { TODO -cDOC : Original code by Hallvard Vassbotn }
  2007. { TODO -cTesting : Check the implemetation for any further version of compiler }
  2008. const
  2009. AddByte = $04244483; // opcode for ADD DWORD PTR [ESP+4], Shortint
  2010. AddLong = $04244481; // opcode for ADD DWORD PTR [ESP+4], Longint
  2011. type
  2012. PAdjustSelfThunk = ^TAdjustSelfThunk;
  2013. TAdjustSelfThunk = packed record
  2014. case AddInstruction: Longint of
  2015. AddByte: (AdjustmentByte: ShortInt);
  2016. AddLong: (AdjustmentLong: Longint);
  2017. end;
  2018. PInterfaceMT = ^TInterfaceMT;
  2019. TInterfaceMT = packed record
  2020. QueryInterfaceThunk: PAdjustSelfThunk;
  2021. end;
  2022. TInterfaceRef = ^PInterfaceMT;
  2023. var
  2024. QueryInterfaceThunk: PAdjustSelfThunk;
  2025. begin
  2026. try
  2027. Result := Pointer(I);
  2028. if Assigned(Result) then
  2029. begin
  2030. QueryInterfaceThunk := TInterfaceRef(I)^.QueryInterfaceThunk;
  2031. case QueryInterfaceThunk.AddInstruction of
  2032. AddByte:
  2033. Inc(PByte(Result), QueryInterfaceThunk.AdjustmentByte);
  2034. AddLong:
  2035. Inc(PByte(Result), QueryInterfaceThunk.AdjustmentLong);
  2036. else
  2037. Result := nil;
  2038. end;
  2039. end;
  2040. except
  2041. Result := nil;
  2042. end;
  2043. end;
  2044. //=== { TJclInterfacedPersistent } ===========================================
  2045. procedure TJclInterfacedPersistent.AfterConstruction;
  2046. begin
  2047. inherited AfterConstruction;
  2048. if GetOwner <> nil then
  2049. GetOwner.GetInterface(IInterface, FOwnerInterface);
  2050. end;
  2051. function TJclInterfacedPersistent._AddRef: Integer;
  2052. begin
  2053. if FOwnerInterface <> nil then
  2054. Result := FOwnerInterface._AddRef
  2055. else
  2056. Result := InterlockedIncrement(FRefCount);
  2057. end;
  2058. function TJclInterfacedPersistent._Release: Integer;
  2059. begin
  2060. if FOwnerInterface <> nil then
  2061. Result := FOwnerInterface._Release
  2062. else
  2063. begin
  2064. Result := InterlockedDecrement(FRefCount);
  2065. if Result = 0 then
  2066. Destroy;
  2067. end;
  2068. end;
  2069. //=== Numeric formatting routines ============================================
  2070. function IntToStrZeroPad(Value, Count: Integer): string;
  2071. begin
  2072. Result := IntToStr(Value);
  2073. if Length(Result) < Count then
  2074. Result := StrRepeatChar('0', Count - Length(Result)) + Result;
  2075. end;
  2076. //=== { TJclNumericFormat } ==================================================
  2077. { TODO -cHelp : Author: Robert Rossmair }
  2078. { Digit: converts a digit value (number) to a digit (char)
  2079. DigitValue: converts a digit (char) into a number (digit value)
  2080. IntToStr,
  2081. FloatToStr,
  2082. FloatToHTML: converts a numeric value to a base <Base> numeric representation with formating options
  2083. StrToIn: converts a base <Base> numeric representation into an integer, if possible
  2084. GetMantisseExponent: similar to AsString, but returns the Exponent separately as an integer
  2085. }
  2086. const
  2087. {$IFDEF MATH_EXTENDED_PRECISION}
  2088. BinaryPrecision = 64;
  2089. {$ENDIF MATH_EXTENDED_PRECISION}
  2090. {$IFDEF MATH_DOUBLE_PRECISION}
  2091. BinaryPrecision = 53;
  2092. {$ENDIF MATH_DOUBLE_PRECISION}
  2093. {$IFDEF MATH_SINGLE_PRECISION}
  2094. BinaryPrecision = 24;
  2095. {$ENDIF MATH_SINGLE_PRECISION}
  2096. constructor TJclNumericFormat.Create;
  2097. begin
  2098. inherited Create;
  2099. { TODO : Initialize, when possible, from locale info }
  2100. FBase := 10;
  2101. FExpDivision := 1;
  2102. SetPrecision(6);
  2103. FNumberOfFractionalDigits := BinaryPrecision;
  2104. FSignChars[False] := '-';
  2105. FSignChars[True] := '+';
  2106. FPaddingChar := ' ';
  2107. FMultiplier := '×';
  2108. FFractionalPartSeparator := JclFormatSettings.DecimalSeparator;
  2109. FDigitBlockSeparator := JclFormatSettings.ThousandSeparator;
  2110. end;
  2111. procedure TJclNumericFormat.InvalidDigit(Digit: Char);
  2112. begin
  2113. raise EConvertError.CreateResFmt(@RsInvalidDigit, [Base, Digit]);
  2114. end;
  2115. function TJclNumericFormat.Digit(DigitValue: TDigitValue): Char;
  2116. begin
  2117. Assert(DigitValue < Base, Format(LoadResString(@RsInvalidDigitValue), [Base, DigitValue]));
  2118. if DigitValue > 9 then
  2119. Result := Chr(Ord('A') + DigitValue - 10)
  2120. else
  2121. Result := Chr(Ord('0') + DigitValue);
  2122. end;
  2123. function TJclNumericFormat.GetDigitValue(Digit: Char): Integer;
  2124. begin
  2125. Result := CharHex(Digit);
  2126. if (Result = $FF) or (Result >= Base) then
  2127. Result := -1;
  2128. end;
  2129. function TJclNumericFormat.DigitValue(Digit: Char): TDigitValue;
  2130. begin
  2131. Result := GetDigitValue(Digit);
  2132. if Result = -1 then
  2133. InvalidDigit(Digit);
  2134. end;
  2135. function TJclNumericFormat.IsDigit(Value: Char): Boolean;
  2136. begin
  2137. Result := GetDigitValue(Value) <> -1;
  2138. end;
  2139. function TJclNumericFormat.FloatToHTML(const Value: Float): string;
  2140. var
  2141. Mantissa: string;
  2142. Exponent: Integer;
  2143. begin
  2144. GetMantissaExp(Value, Mantissa, Exponent);
  2145. Result := Format('%s %s %d<sup>%d</sup>', [Mantissa, Multiplier, Base, Exponent]);
  2146. end;
  2147. procedure TJclNumericFormat.GetMantissaExp(const Value: Float;
  2148. out Mantissa: string; out Exponent: Integer);
  2149. const
  2150. {$IFDEF FPC}
  2151. InfMantissa: array [Boolean] of string[4] = ('inf', '-inf');
  2152. {$ElSE ~FPC}
  2153. InfMantissa: array [Boolean] of string = ('inf', '-inf');
  2154. {$ENDIF ~FPC}
  2155. var
  2156. BlockDigits: TDigitCount;
  2157. IntDigits, FracDigits: Integer;
  2158. FirstDigitPos, Prec: Integer;
  2159. I, J, N: Integer;
  2160. K: Int64;
  2161. X: Extended;
  2162. HighDigit: Char;
  2163. function GetDigit(X: Extended): Char;
  2164. var
  2165. N: Integer;
  2166. begin
  2167. N := Trunc(X);
  2168. if N > 9 then
  2169. Result := Chr(Ord('A') + N - 10)
  2170. else
  2171. Result := Chr(Ord('0') + N);
  2172. end;
  2173. begin
  2174. X := Abs(Value);
  2175. if X > MaxFloatingPoint then
  2176. begin
  2177. Mantissa := InfMantissa[Value < 0];
  2178. Exponent := 1;
  2179. Exit;
  2180. end
  2181. else
  2182. if X < MinFloatingPoint then
  2183. begin
  2184. Mantissa := Format('%.*f', [Precision, 0.0]);
  2185. Exponent := 1;
  2186. Exit;
  2187. end;
  2188. IntDigits := 1;
  2189. Prec := Precision;
  2190. Exponent := Trunc(LogBaseN(Base, X));
  2191. if FExpDivision > 1 then
  2192. begin
  2193. N := Exponent mod FExpDivision;
  2194. Dec(Exponent, N);
  2195. Inc(IntDigits, N);
  2196. end;
  2197. X := X / Power(Base, Exponent);
  2198. if X < 1.0 then
  2199. begin
  2200. Dec(Exponent, FExpDivision);
  2201. X := X * PowerInt(Base, FExpDivision);
  2202. Inc(IntDigits, FExpDivision - 1);
  2203. end;
  2204. { TODO : Here's a problem if X > High(Int64).
  2205. It *seems* to surface only if ExponentDivision > 12, but it
  2206. has not been investigated if ExponentDivision <= 12 is safe. }
  2207. K := Trunc(X);
  2208. if Value < 0 then
  2209. K := -K;
  2210. Mantissa := IntToStr(K, FirstDigitPos);
  2211. FracDigits := Prec - IntDigits;
  2212. if FracDigits > NumberOfFractionalDigits then
  2213. FracDigits := NumberOfFractionalDigits;
  2214. if FracDigits > 0 then
  2215. begin
  2216. J := Length(Mantissa) + 1;
  2217. // allocate sufficient space for point + digits + digit block separators
  2218. SetLength(Mantissa, FracDigits * 2 + J);
  2219. Mantissa[J] := FractionalPartSeparator;
  2220. I := J + 1;
  2221. BlockDigits := 0;
  2222. while FracDigits > 0 do
  2223. begin
  2224. if (BlockDigits > 0) and (BlockDigits = DigitBlockSize) then
  2225. begin
  2226. Mantissa[I] := DigitBlockSeparator;
  2227. Inc(I);
  2228. BlockDigits := 0;
  2229. end;
  2230. X := Frac(X) * Base;
  2231. Mantissa[I] := GetDigit(X);
  2232. Inc(I);
  2233. Inc(BlockDigits);
  2234. Dec(FracDigits);
  2235. end;
  2236. Mantissa[I] := #0;
  2237. StrResetLength(Mantissa);
  2238. end;
  2239. if Frac(X) >= 0.5 then
  2240. // round up
  2241. begin
  2242. HighDigit := Digit(Base - 1);
  2243. for I := Length(Mantissa) downto 1 do
  2244. begin
  2245. if Mantissa[I] = HighDigit then
  2246. if (I = FirstDigitPos) then
  2247. begin
  2248. Mantissa[I] := '1';
  2249. Inc(Exponent);
  2250. Break;
  2251. end
  2252. else
  2253. Mantissa[I] := '0'
  2254. else
  2255. if (Mantissa[I] = DigitBlockSeparator) or (Mantissa[I] = FractionalPartSeparator) then
  2256. Continue
  2257. else
  2258. begin
  2259. if Mantissa[I] = '9' then
  2260. Mantissa[I] := 'A'
  2261. else
  2262. Mantissa[I] := Succ(Mantissa[I]);
  2263. Break;
  2264. end;
  2265. end;
  2266. end;
  2267. end;
  2268. function TJclNumericFormat.FloatToStr(const Value: Float): string;
  2269. var
  2270. Mantissa: string;
  2271. Exponent: Integer;
  2272. begin
  2273. GetMantissaExp(Value, Mantissa, Exponent);
  2274. Result := Format('%s %s %d^%d', [Mantissa, Multiplier, Base, Exponent]);
  2275. end;
  2276. function TJclNumericFormat.IntToStr(const Value: Int64): string;
  2277. var
  2278. FirstDigitPos: Integer;
  2279. begin
  2280. Result := IntToStr(Value, FirstDigitPos);
  2281. end;
  2282. function TJclNumericFormat.IntToStr(const Value: Int64; out FirstDigitPos: Integer): string;
  2283. const
  2284. MaxResultLen = 64 + 63 + 1; // max. digits + max. group separators + sign
  2285. var
  2286. Remainder: Int64;
  2287. I, N: Integer;
  2288. Chars, Digits: Cardinal;
  2289. LoopFinished, HasSign, SpacePadding: Boolean;
  2290. begin
  2291. SpacePadding := PaddingChar = ' ';
  2292. HasSign := ShowSign(Value);
  2293. Chars := MaxResultLen;
  2294. if Width > Chars then
  2295. Chars := Width;
  2296. Result := StrRepeatChar(' ', Chars);
  2297. Remainder := Abs(Value);
  2298. Digits := 0;
  2299. Chars := 0;
  2300. if HasSign then
  2301. Chars := 1;
  2302. I := MaxResultLen;
  2303. while True do
  2304. begin
  2305. N := Remainder mod Base;
  2306. Remainder := Remainder div Base;
  2307. if N > 9 then
  2308. Result[I] := Chr(Ord('A') + N - 10)
  2309. else
  2310. Result[I] := Chr(Ord('0') + N);
  2311. Dec(I);
  2312. Inc(Digits);
  2313. Inc(Chars);
  2314. if (Remainder = 0) and (SpacePadding or (Chars >= Width)) then
  2315. Break;
  2316. if (Digits = DigitBlockSize) then
  2317. begin
  2318. Inc(Chars);
  2319. LoopFinished := (Remainder = 0) and (Chars = Width);
  2320. if LoopFinished then
  2321. Result[I] := ' '
  2322. else
  2323. Result[I] := DigitBlockSeparator;
  2324. Dec(I);
  2325. if LoopFinished then
  2326. Break;
  2327. Digits := 0;
  2328. end;
  2329. end;
  2330. FirstDigitPos := I + 1;
  2331. if HasSign then
  2332. Result[I] := SignChar(Value)
  2333. else
  2334. Inc(I);
  2335. N := MaxResultLen - Width + 1;
  2336. if N < I then
  2337. I := N;
  2338. Result := Copy(Result, I, MaxResultLen);
  2339. Dec(FirstDigitPos, I - 1);
  2340. end;
  2341. procedure TJclNumericFormat.SetBase(const Value: TNumericSystemBase);
  2342. begin
  2343. FBase := Value;
  2344. SetPrecision(FWantedPrecision);
  2345. end;
  2346. procedure TJclNumericFormat.SetExpDivision(const Value: Integer);
  2347. begin
  2348. if Value <= 1 then
  2349. FExpDivision := 1
  2350. else
  2351. // see TODO in GetMantissaExp
  2352. if Value > 12 then
  2353. FExpDivision := 12
  2354. else
  2355. FExpDivision := Value;
  2356. end;
  2357. procedure TJclNumericFormat.SetPrecision(const Value: TDigitCount);
  2358. begin
  2359. FWantedPrecision := Value;
  2360. // Do not display more digits than Float precision justifies
  2361. if Base = 2 then
  2362. FPrecision := BinaryPrecision
  2363. else
  2364. FPrecision := Trunc(BinaryPrecision / LogBase2(Base));
  2365. if Value < FPrecision then
  2366. FPrecision := Value;
  2367. end;
  2368. function TJclNumericFormat.Sign(Value: Char): Integer;
  2369. begin
  2370. Result := 0;
  2371. if Value = FSignChars[False] then
  2372. Result := -1;
  2373. if Value = FSignChars[True] then
  2374. Result := +1;
  2375. end;
  2376. function TJclNumericFormat.StrToInt(const Value: string): Int64;
  2377. var
  2378. I, N: Integer;
  2379. C: Char;
  2380. begin
  2381. Result := 0;
  2382. I := 1;
  2383. if (Length(Value) >= I)
  2384. and ((Value[I] = '+') or (Value[I] = '-')) then
  2385. Inc(I);
  2386. for I := I to Length(Value) do
  2387. begin
  2388. C := Value[I];
  2389. if C = DigitBlockSeparator then
  2390. Continue
  2391. else
  2392. begin
  2393. N := CharHex(C);
  2394. if (N = $FF) or (N >= Base) then
  2395. InvalidDigit(C);
  2396. Result := Result * Base + N;
  2397. end;
  2398. end;
  2399. if Value[1] = '-' then
  2400. Result := -Result;
  2401. end;
  2402. function TJclNumericFormat.ShowSign(const Value: Float): Boolean;
  2403. begin
  2404. Result := FShowPositiveSign or (Value < 0);
  2405. end;
  2406. function TJclNumericFormat.ShowSign(const Value: Int64): Boolean;
  2407. begin
  2408. Result := FShowPositiveSign or (Value < 0);
  2409. end;
  2410. function TJclNumericFormat.SignChar(const Value: Float): Char;
  2411. begin
  2412. Result := FSignChars[Value >= 0];
  2413. end;
  2414. function TJclNumericFormat.SignChar(const Value: Int64): Char;
  2415. begin
  2416. Result := FSignChars[Value >= 0];
  2417. end;
  2418. function TJclNumericFormat.GetNegativeSign: Char;
  2419. begin
  2420. Result := FSignChars[False];
  2421. end;
  2422. function TJclNumericFormat.GetPositiveSign: Char;
  2423. begin
  2424. Result := FSignChars[True];
  2425. end;
  2426. procedure TJclNumericFormat.SetNegativeSign(const Value: Char);
  2427. begin
  2428. FSignChars[False] := Value;
  2429. end;
  2430. procedure TJclNumericFormat.SetPositiveSign(const Value: Char);
  2431. begin
  2432. FSignChars[True] := Value;
  2433. end;
  2434. //=== Child processes ========================================================
  2435. const
  2436. BufferSize = 255;
  2437. type
  2438. TBuffer = array [0..BufferSize] of AnsiChar;
  2439. TPipeInfo = record
  2440. PipeRead, PipeWrite: THandle;
  2441. Buffer: TBuffer;
  2442. Line: string;
  2443. TextHandler: TTextHandler;
  2444. RawOutput: Boolean;
  2445. AutoConvertOem: Boolean;
  2446. Event: TJclEvent;
  2447. end;
  2448. PPipeInfo = ^TPipeInfo;
  2449. // MuteCRTerminatedLines was "outsourced" from Win32ExecAndRedirectOutput
  2450. function InternalExecuteMuteCRTerminatedLines(const RawOutput: string): string;
  2451. const
  2452. Delta = 1024;
  2453. var
  2454. BufPos, OutPos, LfPos, EndPos: Integer;
  2455. C: Char;
  2456. begin
  2457. SetLength(Result, Length(RawOutput));
  2458. OutPos := 1;
  2459. LfPos := OutPos;
  2460. EndPos := OutPos;
  2461. for BufPos := 1 to Length(RawOutput) do
  2462. begin
  2463. if OutPos >= Length(Result)-2 then
  2464. SetLength(Result, Length(Result) + Delta);
  2465. C := RawOutput[BufPos];
  2466. case C of
  2467. NativeCarriageReturn:
  2468. OutPos := LfPos;
  2469. NativeLineFeed:
  2470. begin
  2471. OutPos := EndPos;
  2472. Result[OutPos] := NativeCarriageReturn;
  2473. Inc(OutPos);
  2474. Result[OutPos] := C;
  2475. Inc(OutPos);
  2476. EndPos := OutPos;
  2477. LfPos := OutPos;
  2478. end;
  2479. else
  2480. Result[OutPos] := C;
  2481. Inc(OutPos);
  2482. EndPos := OutPos;
  2483. end;
  2484. end;
  2485. SetLength(Result, OutPos - 1);
  2486. end;
  2487. procedure InternalExecuteProcessLine(const PipeInfo: TPipeInfo; LineEnd: Integer);
  2488. begin
  2489. if PipeInfo.RawOutput or (PipeInfo.Line[LineEnd] <> NativeCarriageReturn) then
  2490. begin
  2491. while (LineEnd > 0) and CharIsReturn(PipeInfo.Line[LineEnd]) do
  2492. Dec(LineEnd);
  2493. PipeInfo.TextHandler(Copy(PipeInfo.Line, 1, LineEnd));
  2494. end;
  2495. end;
  2496. procedure InternalExecuteProcessBuffer(var PipeInfo: TPipeInfo; PipeBytesRead: Cardinal);
  2497. var
  2498. CR, LF: Integer;
  2499. {$IFDEF MSWINDOWS}
  2500. LineLen, Len: Integer;
  2501. {$ENDIF MSWINDOWS}
  2502. S: AnsiString;
  2503. begin
  2504. {$IFDEF MSWINDOWS}
  2505. if PipeInfo.AutoConvertOem then
  2506. begin
  2507. {$IFDEF UNICODE}
  2508. Len := MultiByteToWideChar(CP_OEMCP, 0, PipeInfo.Buffer, PipeBytesRead, nil, 0);
  2509. LineLen := Length(PipeInfo.Line);
  2510. // Convert directly into the PipeInfo.Line string
  2511. SetLength(PipeInfo.Line, LineLen + Len);
  2512. MultiByteToWideChar(CP_OEMCP, 0, PipeInfo.Buffer, PipeBytesRead, PChar(PipeInfo.Line) + LineLen, Len);
  2513. {$ELSE}
  2514. Len := PipeBytesRead;
  2515. LineLen := Length(PipeInfo.Line);
  2516. // Convert directly into the PipeInfo.Line string
  2517. SetLength(PipeInfo.Line, LineLen + Len);
  2518. OemToAnsiBuff(PipeInfo.Buffer, PAnsiChar(PipeInfo.Line) + LineLen, PipeBytesRead);
  2519. {$ENDIF UNICODE}
  2520. end
  2521. else
  2522. {$ENDIF MSWINDOWS}
  2523. begin
  2524. SetString(S, PipeInfo.Buffer, PipeBytesRead); // interpret as ANSI
  2525. {$IFDEF UNICODE}
  2526. PipeInfo.Line := PipeInfo.Line + string(S); // ANSI => UNICODE
  2527. {$ELSE}
  2528. PipeInfo.Line := PipeInfo.Line + S;
  2529. {$ENDIF UNICODE}
  2530. end;
  2531. if Assigned(PipeInfo.TextHandler) then
  2532. repeat
  2533. CR := Pos(NativeCarriageReturn, PipeInfo.Line);
  2534. if CR = Length(PipeInfo.Line) then
  2535. CR := 0; // line feed at CR + 1 might be missing
  2536. LF := Pos(NativeLineFeed, PipeInfo.Line);
  2537. if (CR > 0) and ((LF > CR + 1) or (LF = 0)) then
  2538. LF := CR; // accept CR as line end
  2539. if LF > 0 then
  2540. begin
  2541. InternalExecuteProcessLine(PipeInfo, LF);
  2542. Delete(PipeInfo.Line, 1, LF);
  2543. end;
  2544. until LF = 0;
  2545. end;
  2546. procedure InternalExecuteReadPipe(var PipeInfo: TPipeInfo; var Overlapped: TOverlapped);
  2547. var
  2548. NullDWORD: ^DWORD; // XE4 broke PDWORD
  2549. Res: DWORD;
  2550. begin
  2551. NullDWORD := nil;
  2552. if not ReadFile(PipeInfo.PipeRead, PipeInfo.Buffer[0], BufferSize, NullDWORD^, @Overlapped) then
  2553. begin
  2554. Res := GetLastError;
  2555. case Res of
  2556. ERROR_BROKEN_PIPE:
  2557. begin
  2558. CloseHandle(PipeInfo.PipeRead);
  2559. PipeInfo.PipeRead := 0;
  2560. end;
  2561. ERROR_IO_PENDING:
  2562. ;
  2563. else
  2564. {$IFDEF DELPHI11_UP}
  2565. RaiseLastOSError(Res);
  2566. {$ELSE}
  2567. RaiseLastOSError;
  2568. {$ENDIF DELPHI11_UP}
  2569. end;
  2570. end;
  2571. end;
  2572. procedure InternalExecuteHandlePipeEvent(var PipeInfo: TPipeInfo; var Overlapped: TOverlapped);
  2573. var
  2574. PipeBytesRead: DWORD;
  2575. begin
  2576. if GetOverlappedResult(PipeInfo.PipeRead, Overlapped, PipeBytesRead, False) then
  2577. begin
  2578. InternalExecuteProcessBuffer(PipeInfo, PipeBytesRead);
  2579. // automatically launch the next read
  2580. InternalExecuteReadPipe(PipeInfo, Overlapped);
  2581. end
  2582. else
  2583. if GetLastError = ERROR_BROKEN_PIPE then
  2584. begin
  2585. CloseHandle(PipeInfo.PipeRead);
  2586. PipeInfo.PipeRead := 0;
  2587. end
  2588. else
  2589. RaiseLastOSError;
  2590. end;
  2591. procedure InternalExecuteFlushPipe(var PipeInfo: TPipeInfo; var Overlapped: TOverlapped);
  2592. var
  2593. PipeBytesRead: DWORD;
  2594. begin
  2595. CancelIo(PipeInfo.PipeRead);
  2596. GetOverlappedResult(PipeInfo.PipeRead, Overlapped, PipeBytesRead, True);
  2597. if PipeBytesRead > 0 then
  2598. InternalExecuteProcessBuffer(PipeInfo, PipeBytesRead);
  2599. while PeekNamedPipe(PipeInfo.PipeRead, nil, 0, nil, @PipeBytesRead, nil) and (PipeBytesRead > 0) do
  2600. begin
  2601. if PipeBytesRead > BufferSize then
  2602. PipeBytesRead := BufferSize;
  2603. if not ReadFile(PipeInfo.PipeRead, PipeInfo.Buffer[0], PipeBytesRead, PipeBytesRead, nil) then
  2604. RaiseLastOSError;
  2605. InternalExecuteProcessBuffer(PipeInfo, PipeBytesRead);
  2606. end;
  2607. end;
  2608. var
  2609. AsyncPipeCounter: Integer;
  2610. // CreateAsyncPipe creates a pipe that uses overlapped reading.
  2611. function CreateAsyncPipe(var hReadPipe, hWritePipe: THandle;
  2612. lpPipeAttributes: PSecurityAttributes; nSize: DWORD): BOOL;
  2613. var
  2614. PipeName: string;
  2615. Error: DWORD;
  2616. PipeReadHandle, PipeWriteHandle: THandle;
  2617. begin
  2618. Result := False;
  2619. if (@hReadPipe = nil) or (@hWritePipe = nil) then
  2620. begin
  2621. SetLastError(ERROR_INVALID_PARAMETER);
  2622. Exit;
  2623. end;
  2624. if nSize = 0 then
  2625. nSize := 4096;
  2626. InterlockedIncrement(AsyncPipeCounter);
  2627. // In some (not so) rare instances there is a race condition
  2628. // where the counter is the same for two threads at the same
  2629. // time. This makes the CreateNamedPipe call below fail
  2630. // because of the limit set to 1 in the call.
  2631. // So, to be sure this call succeeds, we put both the process
  2632. // and thread id in the name of the pipe.
  2633. // This was found to happen while simply starting 7 instances
  2634. // of the same exe file in parallel.
  2635. PipeName := Format('\\.\Pipe\AsyncAnonPipe.%.8x.%.8x.%.8x', [GetCurrentProcessId, GetCurrentThreadId, AsyncPipeCounter]);
  2636. PipeReadHandle := CreateNamedPipe(PChar(PipeName), PIPE_ACCESS_INBOUND or FILE_FLAG_OVERLAPPED,
  2637. PIPE_TYPE_BYTE or PIPE_WAIT, 1, nSize, nSize, 120 * 1000, lpPipeAttributes);
  2638. if PipeReadHandle = INVALID_HANDLE_VALUE then
  2639. Exit;
  2640. PipeWriteHandle := CreateFile(PChar(PipeName), GENERIC_WRITE, 0, lpPipeAttributes, OPEN_EXISTING,
  2641. FILE_ATTRIBUTE_NORMAL {or FILE_FLAG_OVERLAPPED}, 0);
  2642. if PipeWriteHandle = INVALID_HANDLE_VALUE then
  2643. begin
  2644. Error := GetLastError;
  2645. CloseHandle(PipeReadHandle);
  2646. SetLastError(Error);
  2647. Exit;
  2648. end;
  2649. hReadPipe := PipeReadHandle;
  2650. hWritePipe := PipeWriteHandle;
  2651. Result := True;
  2652. end;
  2653. const
  2654. BELOW_NORMAL_PRIORITY_CLASS = $00004000;
  2655. ABOVE_NORMAL_PRIORITY_CLASS = $00008000;
  2656. ProcessPriorities: array [TJclProcessPriority] of DWORD =
  2657. (IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, HIGH_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS,
  2658. BELOW_NORMAL_PRIORITY_CLASS, ABOVE_NORMAL_PRIORITY_CLASS);
  2659. { TJclExecuteCmdProcessOptions }
  2660. constructor TJclExecuteCmdProcessOptions.Create(const ACommandLine: string);
  2661. begin
  2662. inherited Create;
  2663. FCommandLine := ACommandLine;
  2664. FAutoConvertOem := True;
  2665. FProcessPriority := ppNormal;
  2666. end;
  2667. function ExecuteCmdProcess(Options: TJclExecuteCmdProcessOptions): Boolean;
  2668. var
  2669. OutPipeInfo, ErrorPipeInfo: TPipeInfo;
  2670. Index: Cardinal;
  2671. {$IFDEF MSWINDOWS}
  2672. const
  2673. StartupVisibilityFlags: array[TStartupVisibility] of DWORD = (SW_HIDE, SW_SHOW, SW_SHOWDEFAULT);
  2674. var
  2675. StartupInfo: TStartupInfo;
  2676. ProcessInfo: TProcessInformation;
  2677. SecurityAttr: TSecurityAttributes;
  2678. OutOverlapped, ErrorOverlapped: TOverlapped;
  2679. ProcessEvent: TJclDispatcherObject;
  2680. WaitEvents: array of TJclDispatcherObject;
  2681. InternalAbort: Boolean;
  2682. LastError: DWORD;
  2683. CommandLine: string;
  2684. AbortPtr: PBoolean;
  2685. Flags: DWORD;
  2686. begin
  2687. Result := False;
  2688. // hack to pass a null reference to the parameter lpNumberOfBytesRead of ReadFile
  2689. Options.FExitCode := $FFFFFFFF;
  2690. SecurityAttr.nLength := SizeOf(SecurityAttr);
  2691. SecurityAttr.lpSecurityDescriptor := nil;
  2692. SecurityAttr.bInheritHandle := True;
  2693. ResetMemory(OutPipeInfo, SizeOf(OutPipeInfo));
  2694. OutPipeInfo.TextHandler := Options.OutputLineCallback;
  2695. OutPipeInfo.RawOutput := Options.RawOutput;
  2696. OutPipeInfo.AutoConvertOem := Options.AutoConvertOem;
  2697. if not CreateAsyncPipe(OutPipeInfo.PipeRead, OutPipeInfo.PipeWrite, @SecurityAttr, 0) then
  2698. begin
  2699. Options.FExitCode := GetLastError;
  2700. Exit;
  2701. end;
  2702. OutPipeInfo.Event := TJclEvent.Create(@SecurityAttr, False {automatic reset}, False {not flagged}, '' {anonymous});
  2703. ResetMemory(ErrorPipeInfo, SizeOf(ErrorPipeInfo));
  2704. if not Options.MergeError then
  2705. begin
  2706. ErrorPipeInfo.TextHandler := Options.ErrorLineCallback;
  2707. ErrorPipeInfo.RawOutput := Options.RawError;
  2708. ErrorPipeInfo.AutoConvertOem := Options.AutoConvertOem;
  2709. if not CreateAsyncPipe(ErrorPipeInfo.PipeRead, ErrorPipeInfo.PipeWrite, @SecurityAttr, 0) then
  2710. begin
  2711. Options.FExitCode := GetLastError;
  2712. CloseHandle(OutPipeInfo.PipeWrite);
  2713. CloseHandle(OutPipeInfo.PipeRead);
  2714. OutPipeInfo.Event.Free;
  2715. Exit;
  2716. end;
  2717. ErrorPipeInfo.Event := TJclEvent.Create(@SecurityAttr, False {automatic reset}, False {not flagged}, '' {anonymous});
  2718. end;
  2719. ResetMemory(StartupInfo, SizeOf(TStartupInfo));
  2720. StartupInfo.cb := SizeOf(TStartupInfo);
  2721. StartupInfo.dwFlags := STARTF_USESTDHANDLES;
  2722. if Options.StartupVisibility <> svNotSet then
  2723. begin
  2724. StartupInfo.dwFlags := StartupInfo.dwFlags or STARTF_USESHOWWINDOW;
  2725. StartupInfo.wShowWindow := StartupVisibilityFlags[Options.StartupVisibility];
  2726. end;
  2727. StartupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
  2728. StartupInfo.hStdOutput := OutPipeInfo.PipeWrite;
  2729. if Options.MergeError then
  2730. StartupInfo.hStdError := OutPipeInfo.PipeWrite
  2731. else
  2732. StartupInfo.hStdError := ErrorPipeInfo.PipeWrite;
  2733. CommandLine := Options.CommandLine;
  2734. UniqueString(CommandLine); // CommandLine must be in a writable memory block
  2735. ResetMemory(ProcessInfo, SizeOf(ProcessInfo));
  2736. ProcessEvent := nil;
  2737. try
  2738. Flags := Options.CreateProcessFlags and not (NORMAL_PRIORITY_CLASS or IDLE_PRIORITY_CLASS or
  2739. HIGH_PRIORITY_CLASS or REALTIME_PRIORITY_CLASS);
  2740. Flags := Flags or ProcessPriorities[Options.ProcessPriority];
  2741. if Assigned(Options.BeforeResume) then
  2742. Flags := Flags or CREATE_SUSPENDED;
  2743. if CreateProcess(nil, PChar(CommandLine), nil, nil, True, Flags,
  2744. nil, nil, StartupInfo, ProcessInfo) then
  2745. begin
  2746. Result := True;
  2747. try
  2748. try
  2749. if Assigned(Options.BeforeResume) then
  2750. Options.BeforeResume(ProcessInfo);
  2751. finally
  2752. if Flags and CREATE_SUSPENDED <> 0 then // CREATE_SUSPENDED may also have come from CreateProcessFlags
  2753. ResumeThread(ProcessInfo.hThread);
  2754. end;
  2755. // init out and error events
  2756. CloseHandle(OutPipeInfo.PipeWrite);
  2757. OutPipeInfo.PipeWrite := 0;
  2758. if not Options.MergeError then
  2759. begin
  2760. CloseHandle(ErrorPipeInfo.PipeWrite);
  2761. ErrorPipeInfo.PipeWrite := 0;
  2762. end;
  2763. InternalAbort := False;
  2764. AbortPtr := Options.AbortPtr;
  2765. if AbortPtr <> nil then
  2766. AbortPtr^ := {$IFDEF FPC}Byte({$ENDIF}False{$IFDEF FPC}){$ENDIF}
  2767. else
  2768. AbortPtr := @InternalAbort;
  2769. // init the array of events to wait for
  2770. ProcessEvent := TJclDispatcherObject.Attach(ProcessInfo.hProcess);
  2771. SetLength(WaitEvents, 2);
  2772. // add the process first
  2773. WaitEvents[0] := ProcessEvent;
  2774. // add the output event
  2775. WaitEvents[1] := OutPipeInfo.Event;
  2776. // add the error event
  2777. if not Options.MergeError then
  2778. begin
  2779. SetLength(WaitEvents, 3);
  2780. WaitEvents[2] := ErrorPipeInfo.Event;
  2781. end;
  2782. // add the abort event if any
  2783. if Options.AbortEvent <> nil then
  2784. begin
  2785. Options.AbortEvent.ResetEvent;
  2786. Index := Length(WaitEvents);
  2787. SetLength(WaitEvents, Index + 1);
  2788. WaitEvents[Index] := Options.AbortEvent;
  2789. end;
  2790. // init the asynchronous reads
  2791. ResetMemory(OutOverlapped, SizeOf(OutOverlapped));
  2792. OutOverlapped.hEvent := OutPipeInfo.Event.Handle;
  2793. InternalExecuteReadPipe(OutPipeInfo, OutOverlapped);
  2794. if not Options.MergeError then
  2795. begin
  2796. ResetMemory(ErrorOverlapped, SizeOf(ErrorOverlapped));
  2797. ErrorOverlapped.hEvent := ErrorPipeInfo.Event.Handle;
  2798. InternalExecuteReadPipe(ErrorPipeInfo, ErrorOverlapped);
  2799. end;
  2800. // event based loop
  2801. while not {$IFDEF FPC}Boolean({$ENDIF}AbortPtr^{$IFDEF FPC}){$ENDIF} do
  2802. begin
  2803. Index := WaitAlertableForMultipleObjects(WaitEvents, False, INFINITE);
  2804. if Index = WAIT_OBJECT_0 then
  2805. // the subprocess has ended
  2806. Break
  2807. else
  2808. if Index = (WAIT_OBJECT_0 + 1) then
  2809. begin
  2810. // event on output
  2811. InternalExecuteHandlePipeEvent(OutPipeInfo, OutOverlapped);
  2812. end
  2813. else
  2814. if (Index = (WAIT_OBJECT_0 + 2)) and not Options.MergeError then
  2815. begin
  2816. // event on error
  2817. InternalExecuteHandlePipeEvent(ErrorPipeInfo, ErrorOverlapped);
  2818. end
  2819. else
  2820. if ((Index = (WAIT_OBJECT_0 + 2)) and Options.MergeError) or
  2821. ((Index = (WAIT_OBJECT_0 + 3)) and not Options.MergeError) then
  2822. // event on abort
  2823. AbortPtr^ := {$IFDEF FPC}Byte({$ENDIF}True{$IFDEF FPC}){$ENDIF}
  2824. else
  2825. {$IFDEF DELPHI11_UP}
  2826. RaiseLastOSError(Index);
  2827. {$ELSE}
  2828. RaiseLastOSError;
  2829. {$ENDIF DELPHI11_UP}
  2830. end;
  2831. if {$IFDEF FPC}Boolean({$ENDIF}AbortPtr^{$IFDEF FPC}){$ENDIF} then
  2832. TerminateProcess(ProcessEvent.Handle, Cardinal(ABORT_EXIT_CODE));
  2833. if (ProcessEvent.WaitForever = {$IFDEF RTL280_UP}TJclWaitResult.{$ENDIF RTL280_UP}wrSignaled) and not GetExitCodeProcess(ProcessEvent.Handle, Options.FExitCode) then
  2834. Options.FExitCode := $FFFFFFFF;
  2835. CloseHandle(ProcessInfo.hThread);
  2836. ProcessInfo.hThread := 0;
  2837. if OutPipeInfo.PipeRead <> 0 then
  2838. // read data remaining in output pipe
  2839. InternalExecuteFlushPipe(OutPipeinfo, OutOverlapped);
  2840. if not Options.MergeError and (ErrorPipeInfo.PipeRead <> 0) then
  2841. // read data remaining in error pipe
  2842. InternalExecuteFlushPipe(ErrorPipeInfo, ErrorOverlapped);
  2843. except
  2844. // always terminate process in case of an exception.
  2845. // This is especially useful when an exception occurred in one of
  2846. // the texthandler but only do it if the process actually started,
  2847. // this prevents eating up the last error value by calling those
  2848. // three functions with an invalid handle
  2849. // Note that we don't do it in the finally block because these
  2850. // calls would also then eat up the last error value which we tried
  2851. // to avoid in the first place
  2852. if ProcessInfo.hProcess <> 0 then
  2853. begin
  2854. TerminateProcess(ProcessInfo.hProcess, Cardinal(ABORT_EXIT_CODE));
  2855. WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  2856. GetExitCodeProcess(ProcessInfo.hProcess, Options.FExitCode);
  2857. end;
  2858. raise;
  2859. end;
  2860. end;
  2861. finally
  2862. LastError := GetLastError;
  2863. try
  2864. if OutPipeInfo.PipeRead <> 0 then
  2865. CloseHandle(OutPipeInfo.PipeRead);
  2866. if OutPipeInfo.PipeWrite <> 0 then
  2867. CloseHandle(OutPipeInfo.PipeWrite);
  2868. if ErrorPipeInfo.PipeRead <> 0 then
  2869. CloseHandle(ErrorPipeInfo.PipeRead);
  2870. if ErrorPipeInfo.PipeWrite <> 0 then
  2871. CloseHandle(ErrorPipeInfo.PipeWrite);
  2872. if ProcessInfo.hThread <> 0 then
  2873. CloseHandle(ProcessInfo.hThread);
  2874. if Assigned(ProcessEvent) then
  2875. ProcessEvent.Free // this calls CloseHandle(ProcessInfo.hProcess)
  2876. else if ProcessInfo.hProcess <> 0 then
  2877. CloseHandle(ProcessInfo.hProcess);
  2878. OutPipeInfo.Event.Free;
  2879. ErrorPipeInfo.Event.Free;
  2880. finally
  2881. SetLastError(LastError);
  2882. end;
  2883. end;
  2884. {$ENDIF MSWINDOWS}
  2885. {$IFDEF UNIX}
  2886. var
  2887. PipeBytesRead: Cardinal;
  2888. Pipe: PIOFile;
  2889. Cmd: string;
  2890. begin
  2891. Cmd := Format('%s 2>&1', [Options.CommandLine]);
  2892. Pipe := nil;
  2893. try
  2894. Pipe := Libc.popen(PChar(Cmd), 'r');
  2895. { TODO : handle Abort }
  2896. repeat
  2897. PipeBytesRead := fread_unlocked(@OutBuffer, 1, BufferSize, Pipe);
  2898. if PipeBytesRead > 0 then
  2899. ProcessBuffer(OutBuffer, OutLine, PipeBytesRead);
  2900. until PipeBytesRead = 0;
  2901. Result := pclose(Pipe);
  2902. Pipe := nil;
  2903. wait(nil);
  2904. finally
  2905. if Pipe <> nil then
  2906. pclose(Pipe);
  2907. wait(nil);
  2908. end;
  2909. {$ENDIF UNIX}
  2910. if OutPipeInfo.Line <> '' then
  2911. if Assigned(OutPipeInfo.TextHandler) then
  2912. // output wasn't terminated by a line feed...
  2913. // (shouldn't happen, but you never know)
  2914. InternalExecuteProcessLine(OutPipeInfo, Length(OutPipeInfo.Line))
  2915. else
  2916. if Options.RawOutput then
  2917. Options.FOutput := OutPipeInfo.Line
  2918. else
  2919. Options.FOutput := InternalExecuteMuteCRTerminatedLines(OutPipeInfo.Line);
  2920. if ErrorPipeInfo.Line <> '' then
  2921. if Assigned(ErrorPipeInfo.TextHandler) then
  2922. // error wasn't terminated by a line feed...
  2923. // (shouldn't happen, but you never know)
  2924. InternalExecuteProcessLine(ErrorPipeInfo, Length(ErrorPipeInfo.Line))
  2925. else
  2926. if Options.RawError then
  2927. Options.FError := ErrorPipeInfo.Line
  2928. else
  2929. Options.FError := InternalExecuteMuteCRTerminatedLines(ErrorPipeInfo.Line);
  2930. end;
  2931. function InternalExecute(CommandLine: string; AbortPtr: PBoolean; AbortEvent: TJclEvent;
  2932. var Output: string; OutputLineCallback: TTextHandler; RawOutput: Boolean;
  2933. MergeError: Boolean; var Error: string; ErrorLineCallback: TTextHandler; RawError: Boolean;
  2934. ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  2935. var
  2936. Options: TJclExecuteCmdProcessOptions;
  2937. begin
  2938. Options := TJclExecuteCmdProcessOptions.Create(CommandLine);
  2939. try
  2940. Options.AutoConvertOem := AutoConvertOem;
  2941. Options.AbortPtr := AbortPtr;
  2942. Options.AbortEvent := AbortEvent;
  2943. Options.OutputLineCallback := OutputLineCallback;
  2944. Options.RawOutput := RawOutput;
  2945. Options.MergeError := MergeError;
  2946. Options.ErrorLineCallback := ErrorLineCallback;
  2947. Options.RawError := RawError;
  2948. Options.ProcessPriority := ProcessPriority;
  2949. ExecuteCmdProcess(Options);
  2950. Result := Options.ExitCode;
  2951. // Append => backward compatiblity
  2952. Output := Output + Options.Output;
  2953. Error := Error + Options.Error;
  2954. finally
  2955. Options.Free;
  2956. end;
  2957. end;
  2958. { TODO -cHelp :
  2959. RawOutput: Do not process isolated carriage returns (#13).
  2960. That is, for RawOutput = False, lines not terminated by a line feed (#10) are deleted from Output. }
  2961. function Execute(const CommandLine: string; var Output: string; RawOutput: Boolean;
  2962. AbortPtr: PBoolean; ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  2963. var
  2964. Error: string;
  2965. begin
  2966. Error := '';
  2967. Result := InternalExecute(CommandLine, AbortPtr, nil, Output, nil, RawOutput, True, Error,
  2968. nil, False, ProcessPriority, AutoConvertOem);
  2969. end;
  2970. function Execute(const CommandLine: string; AbortEvent: TJclEvent; var Output: string; RawOutput: Boolean;
  2971. ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  2972. var
  2973. Error: string;
  2974. begin
  2975. Error := '';
  2976. Result := InternalExecute(CommandLine, nil, AbortEvent, Output, nil, RawOutput, True, Error,
  2977. nil, False, ProcessPriority, AutoConvertOem);
  2978. end;
  2979. { TODO -cHelp :
  2980. Author: Robert Rossmair
  2981. OutputLineCallback called once per line of output. }
  2982. function Execute(const CommandLine: string; OutputLineCallback: TTextHandler; RawOutput: Boolean;
  2983. AbortPtr: PBoolean; ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  2984. var
  2985. Output, Error: string;
  2986. begin
  2987. Output := '';
  2988. Error := '';
  2989. Result := InternalExecute(CommandLine, AbortPtr, nil, Output, OutputLineCallback, RawOutput, True, Error,
  2990. nil, False, ProcessPriority, AutoConvertOem);
  2991. end;
  2992. function Execute(const CommandLine: string; AbortEvent: TJclEvent; OutputLineCallback: TTextHandler; RawOutput: Boolean;
  2993. ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  2994. var
  2995. Output, Error: string;
  2996. begin
  2997. Output := '';
  2998. Error := '';
  2999. Result := InternalExecute(CommandLine, nil, AbortEvent, Output, OutputLineCallback, RawOutput, True, Error,
  3000. nil, False, ProcessPriority, AutoConvertOem);
  3001. end;
  3002. { TODO -cHelp :
  3003. RawOutput: Do not process isolated carriage returns (#13).
  3004. That is, for RawOutput = False, lines not terminated by a line feed (#10) are deleted from Output. }
  3005. function Execute(const CommandLine: string; var Output, Error: string; RawOutput, RawError: Boolean;
  3006. AbortPtr: PBoolean; ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  3007. begin
  3008. Result := InternalExecute(CommandLine, AbortPtr, nil, Output, nil, RawOutput, False, Error,
  3009. nil, RawError, ProcessPriority, AutoConvertOem);
  3010. end;
  3011. function Execute(const CommandLine: string; AbortEvent: TJclEvent; var Output, Error: string;
  3012. RawOutput, RawError: Boolean; ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  3013. begin
  3014. Result := InternalExecute(CommandLine, nil, AbortEvent, Output, nil, RawOutput, False, Error,
  3015. nil, RawError, ProcessPriority, AutoConvertOem);
  3016. end;
  3017. { TODO -cHelp :
  3018. Author: Robert Rossmair
  3019. OutputLineCallback called once per line of output. }
  3020. function Execute(const CommandLine: string; OutputLineCallback, ErrorLineCallback: TTextHandler;
  3021. RawOutput, RawError: Boolean; AbortPtr: PBoolean; ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  3022. var
  3023. Output, Error: string;
  3024. begin
  3025. Output := '';
  3026. Error := '';
  3027. Result := InternalExecute(CommandLine, AbortPtr, nil, Output, OutputLineCallback, RawOutput, False, Error,
  3028. ErrorLineCallback, RawError, ProcessPriority, AutoConvertOem);
  3029. end;
  3030. function Execute(const CommandLine: string; AbortEvent: TJclEvent; OutputLineCallback, ErrorLineCallback: TTextHandler;
  3031. RawOutput, RawError: Boolean; ProcessPriority: TJclProcessPriority; AutoConvertOem: Boolean): Cardinal;
  3032. var
  3033. Output, Error: string;
  3034. begin
  3035. Output := '';
  3036. Error := '';
  3037. Result := InternalExecute(CommandLine, nil, AbortEvent, Output, OutputLineCallback, RawOutput, False, Error,
  3038. ErrorLineCallback, RawError, ProcessPriority, AutoConvertOem);
  3039. end;
  3040. //=== { TJclCommandLineTool } ================================================
  3041. constructor TJclCommandLineTool.Create(const AExeName: string);
  3042. begin
  3043. inherited Create;
  3044. FOptions := TStringList.Create;
  3045. FExeName := AExeName;
  3046. end;
  3047. destructor TJclCommandLineTool.Destroy;
  3048. begin
  3049. FreeAndNil(FOptions);
  3050. inherited Destroy;
  3051. end;
  3052. procedure TJclCommandLineTool.AddPathOption(const Option, Path: string);
  3053. var
  3054. S: string;
  3055. begin
  3056. S := PathRemoveSeparator(Path);
  3057. {$IFDEF MSWINDOWS}
  3058. S := AnsiLowerCase(S); // file names are case insensitive
  3059. {$ENDIF MSWINDOWS}
  3060. S := Format('-%s%s', [Option, S]);
  3061. // avoid duplicate entries (note that search is case sensitive)
  3062. if GetOptions.IndexOf(S) = -1 then
  3063. GetOptions.Add(S);
  3064. end;
  3065. function TJclCommandLineTool.Execute(const CommandLine: string): Boolean;
  3066. begin
  3067. if Assigned(FOutputCallback) then
  3068. Result := JclSysUtils.Execute(Format('"%s" %s', [ExeName, CommandLine]), FOutputCallback) = 0
  3069. else
  3070. Result := JclSysUtils.Execute(Format('"%s" %s', [ExeName, CommandLine]), FOutput) = 0;
  3071. end;
  3072. function TJclCommandLineTool.GetExeName: string;
  3073. begin
  3074. Result := FExeName;
  3075. end;
  3076. function TJclCommandLineTool.GetOptions: TStrings;
  3077. begin
  3078. Result := FOptions;
  3079. end;
  3080. function TJclCommandLineTool.GetOutput: string;
  3081. begin
  3082. Result := FOutput;
  3083. end;
  3084. function TJclCommandLineTool.GetOutputCallback: TTextHandler;
  3085. begin
  3086. Result := FOutputCallback;
  3087. end;
  3088. procedure TJclCommandLineTool.SetOutputCallback(const CallbackMethod: TTextHandler);
  3089. begin
  3090. FOutputCallback := CallbackMethod;
  3091. end;
  3092. //=== Console Utilities ======================================================
  3093. function ReadKey: Char;
  3094. {$IFDEF MSWINDOWS}
  3095. { TODO -cHelp : Contributor: Robert Rossmair }
  3096. var
  3097. Console: TJclConsole;
  3098. InputMode: TJclConsoleInputModes;
  3099. begin
  3100. Console := TJclConsole.Default;
  3101. InputMode := Console.Input.Mode;
  3102. Console.Input.Mode := [imProcessed];
  3103. Console.Input.Clear;
  3104. Result := Char(Console.Input.GetEvent.Event.KeyEvent.AsciiChar);
  3105. Console.Input.Mode := InputMode;
  3106. end;
  3107. {$ENDIF MSWINDOWS}
  3108. {$IFDEF UNIX}
  3109. { TODO -cHelp : Donator: Wayne Sherman }
  3110. var
  3111. ReadFileDescriptor: TFDSet;
  3112. TimeVal: TTimeVal;
  3113. SaveTerminalSettings: TTermIos;
  3114. RawTerminalSettings: TTermIos;
  3115. begin
  3116. Result := #0;
  3117. //Save Original Terminal Settings
  3118. tcgetattr(stdin, SaveTerminalSettings);
  3119. tcgetattr(stdin, RawTerminalSettings);
  3120. //Put Terminal in RAW mode
  3121. cfmakeraw(RawTerminalSettings);
  3122. tcsetattr(stdin, TCSANOW, RawTerminalSettings);
  3123. try
  3124. //Setup file I/O descriptor for STDIN
  3125. FD_ZERO(ReadFileDescriptor);
  3126. FD_SET(stdin, ReadFileDescriptor);
  3127. TimeVal.tv_sec := High(LongInt); //wait forever
  3128. TimeVal.tv_usec := 0;
  3129. //clear keyboard buffer first
  3130. TCFlush(stdin, TCIFLUSH);
  3131. //wait for a key to be pressed
  3132. if select(1, @ReadFileDescriptor, nil, nil, @TimeVal) > 0 then
  3133. begin
  3134. //Now read the character
  3135. Result := Char(getchar);
  3136. end
  3137. else
  3138. raise EJclError.CreateRes(@RsReadKeyError);
  3139. finally
  3140. //Restore Original Terminal Settings
  3141. tcsetattr(stdin, TCSANOW, SaveTerminalSettings);
  3142. end;
  3143. end;
  3144. {$ENDIF UNIX}
  3145. //=== Loading of modules (DLLs) ==============================================
  3146. function LoadModule(var Module: TModuleHandle; FileName: string): Boolean;
  3147. {$IFDEF MSWINDOWS}
  3148. begin
  3149. if Module = INVALID_MODULEHANDLE_VALUE then
  3150. Module := SafeLoadLibrary(FileName);
  3151. Result := Module <> INVALID_MODULEHANDLE_VALUE;
  3152. end;
  3153. {$ENDIF MSWINDOWS}
  3154. {$IFDEF UNIX}
  3155. begin
  3156. if Module = INVALID_MODULEHANDLE_VALUE then
  3157. Module := dlopen(PChar(FileName), RTLD_NOW);
  3158. Result := Module <> INVALID_MODULEHANDLE_VALUE;
  3159. end;
  3160. {$ENDIF UNIX}
  3161. function LoadModuleEx(var Module: TModuleHandle; FileName: string; Flags: Cardinal): Boolean;
  3162. {$IFDEF MSWINDOWS}
  3163. begin
  3164. if Module = INVALID_MODULEHANDLE_VALUE then
  3165. Module := LoadLibraryEx(PChar(FileName), 0, Flags); // SafeLoadLibrary?
  3166. Result := Module <> INVALID_MODULEHANDLE_VALUE;
  3167. end;
  3168. {$ENDIF MSWINDOWS}
  3169. {$IFDEF UNIX}
  3170. begin
  3171. if Module = INVALID_MODULEHANDLE_VALUE then
  3172. Module := dlopen(PChar(FileName), Flags);
  3173. Result := Module <> INVALID_MODULEHANDLE_VALUE;
  3174. end;
  3175. {$ENDIF UNIX}
  3176. procedure UnloadModule(var Module: TModuleHandle);
  3177. {$IFDEF MSWINDOWS}
  3178. begin
  3179. if Module <> INVALID_MODULEHANDLE_VALUE then
  3180. FreeLibrary(Module);
  3181. Module := INVALID_MODULEHANDLE_VALUE;
  3182. end;
  3183. {$ENDIF MSWINDOWS}
  3184. {$IFDEF UNIX}
  3185. begin
  3186. if Module <> INVALID_MODULEHANDLE_VALUE then
  3187. dlclose(Pointer(Module));
  3188. Module := INVALID_MODULEHANDLE_VALUE;
  3189. end;
  3190. {$ENDIF UNIX}
  3191. function GetModuleSymbol(Module: TModuleHandle; SymbolName: string): Pointer;
  3192. {$IFDEF MSWINDOWS}
  3193. begin
  3194. Result := nil;
  3195. if Module <> INVALID_MODULEHANDLE_VALUE then
  3196. Result := GetProcAddress(Module, PChar(SymbolName));
  3197. end;
  3198. {$ENDIF MSWINDOWS}
  3199. {$IFDEF UNIX}
  3200. begin
  3201. Result := nil;
  3202. if Module <> INVALID_MODULEHANDLE_VALUE then
  3203. Result := dlsym(Module, PChar(SymbolName));
  3204. end;
  3205. {$ENDIF UNIX}
  3206. function GetModuleSymbolEx(Module: TModuleHandle; SymbolName: string; var Accu: Boolean): Pointer;
  3207. {$IFDEF MSWINDOWS}
  3208. begin
  3209. Result := nil;
  3210. if Module <> INVALID_MODULEHANDLE_VALUE then
  3211. Result := GetProcAddress(Module, PChar(SymbolName));
  3212. Accu := Accu and (Result <> nil);
  3213. end;
  3214. {$ENDIF MSWINDOWS}
  3215. {$IFDEF UNIX}
  3216. begin
  3217. Result := nil;
  3218. if Module <> INVALID_MODULEHANDLE_VALUE then
  3219. Result := dlsym(Module, PChar(SymbolName));
  3220. Accu := Accu and (Result <> nil);
  3221. end;
  3222. {$ENDIF UNIX}
  3223. function ReadModuleData(Module: TModuleHandle; SymbolName: string; var Buffer; Size: Cardinal): Boolean;
  3224. var
  3225. Sym: Pointer;
  3226. begin
  3227. Result := True;
  3228. Sym := GetModuleSymbolEx(Module, SymbolName, Result);
  3229. if Result then
  3230. Move(Sym^, Buffer, Size);
  3231. end;
  3232. function WriteModuleData(Module: TModuleHandle; SymbolName: string; var Buffer; Size: Cardinal): Boolean;
  3233. var
  3234. Sym: Pointer;
  3235. begin
  3236. Result := True;
  3237. Sym := GetModuleSymbolEx(Module, SymbolName, Result);
  3238. if Result then
  3239. Move(Buffer, Sym^, Size);
  3240. end;
  3241. //=== Conversion Utilities ===================================================
  3242. const
  3243. DefaultTrueBoolStr = 'True'; // DO NOT LOCALIZE
  3244. DefaultFalseBoolStr = 'False'; // DO NOT LOCALIZE
  3245. DefaultYesBoolStr = 'Yes'; // DO NOT LOCALIZE
  3246. DefaultNoBoolStr = 'No'; // DO NOT LOCALIZE
  3247. function StrToBoolean(const S: string): Boolean;
  3248. var
  3249. LowerCasedText: string;
  3250. begin
  3251. { TODO : Possibility to add localized strings, like in Delphi 7 }
  3252. { TODO : Lower case constants }
  3253. LowerCasedText := LowerCase(S);
  3254. Result := ((S = '1') or
  3255. (LowerCasedText = LowerCase(DefaultTrueBoolStr)) or (LowerCasedText = LowerCase(DefaultYesBoolStr))) or
  3256. (LowerCasedText = LowerCase(DefaultTrueBoolStr[1])) or (LowerCasedText = LowerCase(DefaultYesBoolStr[1]));
  3257. if not Result then
  3258. begin
  3259. Result := not ((S = '0') or
  3260. (LowerCasedText = LowerCase(DefaultFalseBoolStr)) or (LowerCasedText = LowerCase(DefaultNoBoolStr)) or
  3261. (LowerCasedText = LowerCase(DefaultFalseBoolStr[1])) or (LowerCasedText = LowerCase(DefaultNoBoolStr[1])));
  3262. if Result then
  3263. raise EJclConversionError.CreateResFmt(@RsStringToBoolean, [S]);
  3264. end;
  3265. end;
  3266. function BooleanToStr(B: Boolean): string;
  3267. begin
  3268. if B then
  3269. Result := DefaultTrueBoolStr
  3270. else
  3271. Result := DefaultFalseBoolStr;
  3272. end;
  3273. function IntToBool(I: Integer): Boolean;
  3274. begin
  3275. Result := I <> 0;
  3276. end;
  3277. function BoolToInt(B: Boolean): Integer;
  3278. begin
  3279. Result := Ord(B);
  3280. end;
  3281. function TryStrToUInt(const Value: string; out Res: Cardinal): Boolean;
  3282. var i6: Int64;
  3283. begin
  3284. Result := false;
  3285. if not TryStrToInt64(Value, i6) then exit;
  3286. if ( i6 < Low(Res)) or ( i6 > High(Res)) then exit;
  3287. Result := true;
  3288. Res := i6;
  3289. end;
  3290. function StrToUIntDef(const Value: string; const Default: Cardinal): Cardinal;
  3291. begin
  3292. if not TryStrToUInt(Value, Result)
  3293. then Result := Default;
  3294. end;
  3295. function StrToUInt(const Value: string): Cardinal;
  3296. begin
  3297. if not TryStrToUInt(Value, Result)
  3298. then raise EConvertError.Create('"'+Value+'" is not within range of Cardinal data type');
  3299. end;
  3300. //=== RTL package information ================================================
  3301. function SystemTObjectInstance: TJclAddr;
  3302. begin
  3303. Result := ModuleFromAddr(Pointer(System.TObject));
  3304. end;
  3305. function IsCompiledWithPackages: Boolean;
  3306. begin
  3307. Result := SystemTObjectInstance <> HInstance;
  3308. end;
  3309. //=== GUID ===================================================================
  3310. function JclGUIDToString(const GUID: TGUID): string;
  3311. begin
  3312. Result := Format('{%.8x-%.4x-%.4x-%.2x%.2x-%.2x%.2x%.2x%.2x%.2x%.2x}',
  3313. [GUID.D1, GUID.D2, GUID.D3, GUID.D4[0], GUID.D4[1], GUID.D4[2],
  3314. GUID.D4[3], GUID.D4[4], GUID.D4[5], GUID.D4[6], GUID.D4[7]]);
  3315. end;
  3316. function JclStringToGUID(const S: string): TGUID;
  3317. begin
  3318. if (Length(S) <> 38) or (S[1] <> '{') or (S[10] <> '-') or (S[15] <> '-') or
  3319. (S[20] <> '-') or (S[25] <> '-') or (S[38] <> '}') then
  3320. raise EJclConversionError.CreateResFmt(@RsInvalidGUIDString, [S]);
  3321. Result.D1 := StrToInt('$' + Copy(S, 2, 8));
  3322. Result.D2 := StrToInt('$' + Copy(S, 11, 4));
  3323. Result.D3 := StrToInt('$' + Copy(S, 16, 4));
  3324. Result.D4[0] := StrToInt('$' + Copy(S, 21, 2));
  3325. Result.D4[1] := StrToInt('$' + Copy(S, 23, 2));
  3326. Result.D4[2] := StrToInt('$' + Copy(S, 26, 2));
  3327. Result.D4[3] := StrToInt('$' + Copy(S, 28, 2));
  3328. Result.D4[4] := StrToInt('$' + Copy(S, 30, 2));
  3329. Result.D4[5] := StrToInt('$' + Copy(S, 32, 2));
  3330. Result.D4[6] := StrToInt('$' + Copy(S, 34, 2));
  3331. Result.D4[7] := StrToInt('$' + Copy(S, 36, 2));
  3332. end;
  3333. function GUIDEquals(const GUID1, GUID2: TGUID): Boolean;
  3334. begin
  3335. Result := (GUID1.D1 = GUID2.D1) and (GUID1.D2 = GUID2.D2) and (GUID1.D3 = GUID2.D3) and
  3336. (GUID1.D4[0] = GUID2.D4[0]) and (GUID1.D4[1] = GUID2.D4[1]) and
  3337. (GUID1.D4[2] = GUID2.D4[2]) and (GUID1.D4[3] = GUID2.D4[3]) and
  3338. (GUID1.D4[4] = GUID2.D4[4]) and (GUID1.D4[5] = GUID2.D4[5]) and
  3339. (GUID1.D4[6] = GUID2.D4[6]) and (GUID1.D4[7] = GUID2.D4[7]);
  3340. end;
  3341. // add items at the end
  3342. procedure ListAddItems(var List: string; const Separator, Items: string);
  3343. var
  3344. StrList, NewItems: TStringList;
  3345. Index: Integer;
  3346. begin
  3347. StrList := TStringList.Create;
  3348. try
  3349. StrToStrings(List, Separator, StrList, False);
  3350. NewItems := TStringList.Create;
  3351. try
  3352. StrToStrings(Items, Separator, NewItems);
  3353. for Index := 0 to NewItems.Count - 1 do
  3354. StrList.Add(NewItems.Strings[Index]);
  3355. List := StringsToStr(StrList, Separator);
  3356. finally
  3357. NewItems.Free;
  3358. end;
  3359. finally
  3360. StrList.Free;
  3361. end;
  3362. end;
  3363. // add items at the end if they are not present
  3364. procedure ListIncludeItems(var List: string; const Separator, Items: string);
  3365. var
  3366. StrList, NewItems: TStringList;
  3367. Index: Integer;
  3368. Item: string;
  3369. begin
  3370. StrList := TStringList.Create;
  3371. try
  3372. StrToStrings(List, Separator, StrList, False);
  3373. NewItems := TStringList.Create;
  3374. try
  3375. StrToStrings(Items, Separator, NewItems);
  3376. for Index := 0 to NewItems.Count - 1 do
  3377. begin
  3378. Item := NewItems.Strings[Index];
  3379. if StrList.IndexOf(Item) = -1 then
  3380. StrList.Add(Item);
  3381. end;
  3382. List := StringsToStr(StrList, Separator);
  3383. finally
  3384. NewItems.Free;
  3385. end;
  3386. finally
  3387. StrList.Free;
  3388. end;
  3389. end;
  3390. // delete multiple items
  3391. procedure ListRemoveItems(var List: string; const Separator, Items: string);
  3392. var
  3393. StrList, RemItems: TStringList;
  3394. Index, Position: Integer;
  3395. Item: string;
  3396. begin
  3397. StrList := TStringList.Create;
  3398. try
  3399. StrToStrings(List, Separator, StrList, False);
  3400. RemItems := TStringList.Create;
  3401. try
  3402. StrToStrings(Items, Separator, RemItems, False);
  3403. for Index := 0 to RemItems.Count - 1 do
  3404. begin
  3405. Item := RemItems.Strings[Index];
  3406. repeat
  3407. Position := StrList.IndexOf(Item);
  3408. if Position >= 0 then
  3409. StrList.Delete(Position);
  3410. until Position < 0;
  3411. end;
  3412. List := StringsToStr(StrList, Separator);
  3413. finally
  3414. RemItems.Free;
  3415. end;
  3416. finally
  3417. StrList.Free;
  3418. end;
  3419. end;
  3420. // delete one item
  3421. procedure ListDelItem(var List: string; const Separator: string; const Index: Integer);
  3422. var
  3423. StrList: TStringList;
  3424. begin
  3425. StrList := TStringList.Create;
  3426. try
  3427. StrToStrings(List, Separator, StrList, False);
  3428. StrList.Delete(Index);
  3429. List := StringsToStr(StrList, Separator);
  3430. finally
  3431. StrList.Free;
  3432. end;
  3433. end;
  3434. // return the number of item
  3435. function ListItemCount(const List, Separator: string): Integer;
  3436. var
  3437. StrList: TStringList;
  3438. begin
  3439. StrList := TStringList.Create;
  3440. try
  3441. StrToStrings(List, Separator, StrList, False);
  3442. Result := StrList.Count;
  3443. finally
  3444. StrList.Free;
  3445. end;
  3446. end;
  3447. // return the Nth item
  3448. function ListGetItem(const List, Separator: string; const Index: Integer): string;
  3449. var
  3450. StrList: TStringList;
  3451. begin
  3452. StrList := TStringList.Create;
  3453. try
  3454. StrToStrings(List, Separator, StrList, False);
  3455. Result := StrList.Strings[Index];
  3456. finally
  3457. StrList.Free;
  3458. end;
  3459. end;
  3460. // set the Nth item
  3461. procedure ListSetItem(var List: string; const Separator: string;
  3462. const Index: Integer; const Value: string);
  3463. var
  3464. StrList: TStringList;
  3465. begin
  3466. StrList := TStringList.Create;
  3467. try
  3468. StrToStrings(List, Separator, StrList, False);
  3469. StrList.Strings[Index] := Value;
  3470. List := StringsToStr(StrList, Separator);
  3471. finally
  3472. StrList.Free;
  3473. end;
  3474. end;
  3475. // return the index of an item
  3476. function ListItemIndex(const List, Separator, Item: string): Integer;
  3477. var
  3478. StrList: TStringList;
  3479. begin
  3480. StrList := TStringList.Create;
  3481. try
  3482. StrToStrings(List, Separator, StrList, False);
  3483. Result := StrList.IndexOf(Item);
  3484. finally
  3485. StrList.Free;
  3486. end;
  3487. end;
  3488. //=== { TJclIntfCriticalSection } ============================================
  3489. constructor TJclIntfCriticalSection.Create;
  3490. begin
  3491. inherited Create;
  3492. FCriticalSection := TCriticalSection.Create;
  3493. end;
  3494. destructor TJclIntfCriticalSection.Destroy;
  3495. begin
  3496. FCriticalSection.Free;
  3497. inherited Destroy;
  3498. end;
  3499. function TJclIntfCriticalSection._AddRef: Integer;
  3500. begin
  3501. FCriticalSection.Acquire;
  3502. Result := -1;
  3503. end;
  3504. function TJclIntfCriticalSection._Release: Integer;
  3505. begin
  3506. FCriticalSection.Release;
  3507. Result := -1;
  3508. end;
  3509. //=== { TJclSimpleLog } ======================================================
  3510. {$IFDEF LINUX}
  3511. const
  3512. INVALID_HANDLE_VALUE = 0;
  3513. {$ENDIF LINUX}
  3514. constructor TJclSimpleLog.Create(const ALogFileName: string = '');
  3515. begin
  3516. if ALogFileName = '' then
  3517. FLogFileName := CreateDefaultFileName
  3518. else
  3519. FLogFileName := ALogFileName;
  3520. FLogFileHandle := TFileHandle(INVALID_HANDLE_VALUE);
  3521. FLoggingActive := True;
  3522. end;
  3523. function TJclSimpleLog.CreateDefaultFileName: string;
  3524. begin
  3525. Result := PathExtractFileDirFixed(ParamStr(0)) +
  3526. PathExtractFileNameNoExt(ParamStr(0)) + '_Err.log';
  3527. end;
  3528. destructor TJclSimpleLog.Destroy;
  3529. begin
  3530. CloseLog;
  3531. inherited Destroy;
  3532. end;
  3533. procedure TJclSimpleLog.ClearLog;
  3534. var
  3535. WasOpen: Boolean;
  3536. begin
  3537. WasOpen := LogOpen;
  3538. if WasOpen then
  3539. CloseLog;
  3540. if not FileExists(FlogFileName) then
  3541. Exit;
  3542. FLogFileHandle := FileCreate(FLogFileName);
  3543. FLogWasEmpty := True;
  3544. if Not WasOpen then
  3545. CloseLog;
  3546. end;
  3547. procedure TJclSimpleLog.CloseLog;
  3548. begin
  3549. if LogOpen then
  3550. begin
  3551. FileClose(FLogFileHandle);
  3552. FLogFileHandle := TFileHandle(INVALID_HANDLE_VALUE);
  3553. FLogWasEmpty := False;
  3554. end;
  3555. end;
  3556. function TJclSimpleLog.GetLogOpen: Boolean;
  3557. begin
  3558. Result := DWORD_PTR(FLogFileHandle) <> INVALID_HANDLE_VALUE;
  3559. end;
  3560. procedure TJclSimpleLog.OpenLog;
  3561. begin
  3562. if not LogOpen then
  3563. begin
  3564. FLogFileHandle := FileOpen(FLogFileName, fmOpenWrite or fmShareDenyWrite);
  3565. if LogOpen then
  3566. FLogWasEmpty := FileSeek(FLogFileHandle, 0, soFromEnd) = 0
  3567. else
  3568. begin
  3569. FLogFileHandle := FileCreate(FLogFileName);
  3570. FLogWasEmpty := True;
  3571. if LogOpen then
  3572. FileWrite(FLogFileHandle, BOM_UTF8[0], Length(BOM_UTF8));
  3573. end;
  3574. end
  3575. else
  3576. FLogWasEmpty := False;
  3577. end;
  3578. procedure TJclSimpleLog.Write(const Text: string; Indent: Integer = 0; KeepOpen: Boolean = true);
  3579. var
  3580. S: string;
  3581. UTF8S: TUTF8String;
  3582. SL: TStringList;
  3583. I: Integer;
  3584. WasOpen: Boolean;
  3585. begin
  3586. if LoggingActive then
  3587. begin
  3588. WasOpen := LogOpen;
  3589. if not WasOpen then
  3590. OpenLog;
  3591. if LogOpen then
  3592. begin
  3593. SL := TStringList.Create;
  3594. try
  3595. SL.Text := Text;
  3596. for I := 0 to SL.Count - 1 do
  3597. begin
  3598. S := StringOfChar(' ', Indent) + StrEnsureSuffix(NativeLineBreak, TrimRight(SL[I]));
  3599. UTF8S := StringToUTF8(S);
  3600. FileWrite(FLogFileHandle, UTF8S[1], Length(UTF8S));
  3601. end;
  3602. finally
  3603. SL.Free;
  3604. end;
  3605. // Keep the logfile Open when it was opened before and the KeepOpen is active
  3606. if not (WasOpen and KeepOpen) then
  3607. CloseLog;
  3608. end;
  3609. end;
  3610. end;
  3611. procedure TJclSimpleLog.Write(Strings: TStrings; Indent: Integer = 0; KeepOpen: Boolean = true);
  3612. begin
  3613. if Assigned(Strings) then
  3614. Write(Strings.Text, Indent, KeepOpen);
  3615. end;
  3616. procedure TJclSimpleLog.TimeWrite(const Text: string; Indent: Integer = 0; KeepOpen: Boolean = true);
  3617. var
  3618. S: string;
  3619. UTF8S: TUTF8String;
  3620. SL: TStringList;
  3621. I: Integer;
  3622. WasOpen: Boolean;
  3623. begin
  3624. if LoggingActive then
  3625. begin
  3626. WasOpen := LogOpen;
  3627. if not LogOpen then
  3628. OpenLog;
  3629. if LogOpen then
  3630. begin
  3631. SL := TStringList.Create;
  3632. try
  3633. SL.Text := Text;
  3634. for I := 0 to SL.Count - 1 do
  3635. begin
  3636. if DateTimeFormatStr = '' then
  3637. S := DateTimeToStr(Now)+' : '+StringOfChar(' ', Indent) + StrEnsureSuffix(NativeLineBreak, TrimRight(SL[I]))
  3638. else
  3639. S := FormatDateTime( DateTimeFormatStr, Now)+' : '+StringOfChar(' ', Indent) + StrEnsureSuffix(NativeLineBreak, TrimRight(SL[I]));
  3640. UTF8S := StringToUTF8(S);
  3641. FileWrite(FLogFileHandle, UTF8S[1], Length(UTF8S));
  3642. end;
  3643. finally
  3644. SL.Free;
  3645. end;
  3646. if Not WasOpen and Not KeepOpen then
  3647. CloseLog;
  3648. end;
  3649. end;
  3650. end;
  3651. procedure TJclSimpleLog.TimeWrite(Strings: TStrings; Indent: Integer = 0; KeepOpen: Boolean = true);
  3652. begin
  3653. if Assigned(Strings) then
  3654. TimeWrite(Strings.Text, Indent, KeepOpen);
  3655. end;
  3656. procedure TJclSimpleLog.WriteStamp(SeparatorLen: Integer = 0; KeepOpen: Boolean = true);
  3657. var
  3658. WasOpen: Boolean;
  3659. begin
  3660. if SeparatorLen <= 0 then
  3661. SeparatorLen := 40;
  3662. if LoggingActive then
  3663. begin
  3664. WasOpen := LogOpen;
  3665. if not LogOpen then
  3666. begin
  3667. OpenLog;
  3668. if LogOpen and not FLogWasEmpty then
  3669. Write(NativeLineBreak);
  3670. end;
  3671. if LogOpen then
  3672. begin
  3673. Write(StrRepeat('=', SeparatorLen), 0, True);
  3674. if DateTimeFormatStr = '' then
  3675. Write(Format('= %-*s =', [SeparatorLen - 4, DateTimeToStr(Now)]), 0, True)
  3676. else
  3677. Write(Format('= %-*s =', [SeparatorLen - 4, FormatDateTime( DateTimeFormatStr, Now)]), 0, True);
  3678. Write(StrRepeat('=', SeparatorLen), 0, True);
  3679. if Not WasOpen and Not KeepOpen then
  3680. CloseLog;
  3681. end;
  3682. end;
  3683. end;
  3684. procedure InitSimpleLog(const ALogFileName: string = ''; AOpenLog: Boolean = true);
  3685. begin
  3686. if Assigned(SimpleLog) then
  3687. FreeAndNil(SimpleLog);
  3688. SimpleLog := TJclSimpleLog.Create(ALogFileName);
  3689. if AOpenLog then
  3690. SimpleLog.OpenLog;
  3691. end;
  3692. function TJclFormatSettings.GetCurrencyDecimals: Byte;
  3693. begin
  3694. {$IFDEF RTL220_UP}
  3695. Result := FormatSettings.CurrencyDecimals;
  3696. {$ELSE}
  3697. Result := SysUtils.CurrencyDecimals;
  3698. {$ENDIF}
  3699. end;
  3700. function TJclFormatSettings.GetCurrencyFormat: Byte;
  3701. begin
  3702. {$IFDEF RTL220_UP}
  3703. Result := FormatSettings.CurrencyFormat;
  3704. {$ELSE}
  3705. Result := SysUtils.CurrencyFormat;
  3706. {$ENDIF}
  3707. end;
  3708. function TJclFormatSettings.GetCurrencyString: string;
  3709. begin
  3710. {$IFDEF RTL220_UP}
  3711. Result := FormatSettings.CurrencyString;
  3712. {$ELSE}
  3713. Result := SysUtils.CurrencyString;
  3714. {$ENDIF}
  3715. end;
  3716. function TJclFormatSettings.GetDateSeparator: Char;
  3717. begin
  3718. {$IFDEF RTL220_UP}
  3719. Result := FormatSettings.DateSeparator;
  3720. {$ELSE}
  3721. Result := SysUtils.DateSeparator;
  3722. {$ENDIF}
  3723. end;
  3724. function TJclFormatSettings.GetDayNamesHighIndex: Integer;
  3725. begin
  3726. {$IFDEF RTL220_UP}
  3727. Result := High(FormatSettings.LongDayNames);
  3728. {$ELSE}
  3729. Result := High(SysUtils.LongDayNames);
  3730. {$ENDIF}
  3731. end;
  3732. function TJclFormatSettings.GetDayNamesLowIndex: Integer;
  3733. begin
  3734. {$IFDEF RTL220_UP}
  3735. Result := Low(FormatSettings.LongDayNames);
  3736. {$ELSE}
  3737. Result := Low(SysUtils.LongDayNames);
  3738. {$ENDIF}
  3739. end;
  3740. function TJclFormatSettings.GetDecimalSeparator: Char;
  3741. begin
  3742. {$IFDEF RTL220_UP}
  3743. Result := FormatSettings.DecimalSeparator;
  3744. {$ELSE}
  3745. Result := SysUtils.DecimalSeparator;
  3746. {$ENDIF}
  3747. end;
  3748. function TJclFormatSettings.GetListSeparator: Char;
  3749. begin
  3750. {$IFDEF RTL220_UP}
  3751. Result := FormatSettings.ListSeparator;
  3752. {$ELSE}
  3753. Result := SysUtils.ListSeparator;
  3754. {$ENDIF}
  3755. end;
  3756. function TJclFormatSettings.GetLongDateFormat: string;
  3757. begin
  3758. {$IFDEF RTL220_UP}
  3759. Result := FormatSettings.LongDateFormat;
  3760. {$ELSE}
  3761. Result := SysUtils.LongDateFormat;
  3762. {$ENDIF}
  3763. end;
  3764. { TJclFormatSettings }
  3765. function TJclFormatSettings.GetLongDayNames(AIndex: Integer): string;
  3766. begin
  3767. {$IFDEF RTL220_UP}
  3768. Result := FormatSettings.LongDayNames[AIndex];
  3769. {$ELSE}
  3770. Result := SysUtils.LongDayNames[AIndex];
  3771. {$ENDIF}
  3772. end;
  3773. function TJclFormatSettings.GetLongMonthNames(AIndex: Integer): string;
  3774. begin
  3775. {$IFDEF RTL220_UP}
  3776. Result := FormatSettings.LongMonthNames[AIndex];
  3777. {$ELSE}
  3778. Result := SysUtils.LongMonthNames[AIndex];
  3779. {$ENDIF}
  3780. end;
  3781. function TJclFormatSettings.GetLongTimeFormat: string;
  3782. begin
  3783. {$IFDEF RTL220_UP}
  3784. Result := FormatSettings.LongTimeFormat;
  3785. {$ELSE}
  3786. Result := SysUtils.LongTimeFormat;
  3787. {$ENDIF}
  3788. end;
  3789. function TJclFormatSettings.GetMonthNamesHighIndex: Integer;
  3790. begin
  3791. {$IFDEF RTL220_UP}
  3792. Result := High(FormatSettings.LongMonthNames);
  3793. {$ELSE}
  3794. Result := High(SysUtils.LongMonthNames);
  3795. {$ENDIF}
  3796. end;
  3797. function TJclFormatSettings.GetMonthNamesLowIndex: Integer;
  3798. begin
  3799. {$IFDEF RTL220_UP}
  3800. Result := Low(FormatSettings.LongMonthNames);
  3801. {$ELSE}
  3802. Result := Low(SysUtils.LongMonthNames);
  3803. {$ENDIF}
  3804. end;
  3805. function TJclFormatSettings.GetNegCurrFormat: Byte;
  3806. begin
  3807. {$IFDEF RTL220_UP}
  3808. Result := FormatSettings.NegCurrFormat;
  3809. {$ELSE}
  3810. Result := SysUtils.NegCurrFormat;
  3811. {$ENDIF}
  3812. end;
  3813. function TJclFormatSettings.GetShortDateFormat: string;
  3814. begin
  3815. {$IFDEF RTL220_UP}
  3816. Result := FormatSettings.ShortDateFormat;
  3817. {$ELSE}
  3818. Result := SysUtils.ShortDateFormat;
  3819. {$ENDIF}
  3820. end;
  3821. function TJclFormatSettings.GetShortDayNames(AIndex: Integer): string;
  3822. begin
  3823. {$IFDEF RTL220_UP}
  3824. Result := FormatSettings.ShortDayNames[AIndex];
  3825. {$ELSE}
  3826. Result := SysUtils.ShortDayNames[AIndex];
  3827. {$ENDIF}
  3828. end;
  3829. function TJclFormatSettings.GetShortMonthNames(AIndex: Integer): string;
  3830. begin
  3831. {$IFDEF RTL220_UP}
  3832. Result := FormatSettings.ShortMonthNames[AIndex];
  3833. {$ELSE}
  3834. Result := SysUtils.ShortMonthNames[AIndex];
  3835. {$ENDIF}
  3836. end;
  3837. function TJclFormatSettings.GetShortTimeFormat: string;
  3838. begin
  3839. {$IFDEF RTL220_UP}
  3840. Result := FormatSettings.ShortTimeFormat;
  3841. {$ELSE}
  3842. Result := SysUtils.ShortTimeFormat;
  3843. {$ENDIF}
  3844. end;
  3845. function TJclFormatSettings.GetThousandSeparator: Char;
  3846. begin
  3847. {$IFDEF RTL220_UP}
  3848. Result := FormatSettings.ThousandSeparator;
  3849. {$ELSE}
  3850. Result := SysUtils.ThousandSeparator;
  3851. {$ENDIF}
  3852. end;
  3853. function TJclFormatSettings.GetTimeAMString: string;
  3854. begin
  3855. {$IFDEF RTL220_UP}
  3856. Result := FormatSettings.TimeAMString;
  3857. {$ELSE}
  3858. Result := SysUtils.TimeAMString;
  3859. {$ENDIF}
  3860. end;
  3861. function TJclFormatSettings.GetTimePMString: string;
  3862. begin
  3863. {$IFDEF RTL220_UP}
  3864. Result := FormatSettings.TimePMString;
  3865. {$ELSE}
  3866. Result := SysUtils.TimePMString;
  3867. {$ENDIF}
  3868. end;
  3869. function TJclFormatSettings.GetTimeSeparator: Char;
  3870. begin
  3871. {$IFDEF RTL220_UP}
  3872. Result := FormatSettings.TimeSeparator;
  3873. {$ELSE}
  3874. Result := SysUtils.TimeSeparator;
  3875. {$ENDIF}
  3876. end;
  3877. function TJclFormatSettings.GetTwoDigitYearCenturyWindow: Word;
  3878. begin
  3879. {$IFDEF RTL220_UP}
  3880. Result := FormatSettings.TwoDigitYearCenturyWindow;
  3881. {$ELSE}
  3882. Result := SysUtils.TwoDigitYearCenturyWindow;
  3883. {$ENDIF}
  3884. end;
  3885. procedure TJclFormatSettings.SetCurrencyDecimals(AValue: Byte);
  3886. begin
  3887. {$IFDEF RTL220_UP}
  3888. FormatSettings.CurrencyDecimals := AValue;
  3889. {$ELSE}
  3890. SysUtils.CurrencyDecimals := AValue;
  3891. {$ENDIF}
  3892. end;
  3893. procedure TJclFormatSettings.SetCurrencyFormat(const AValue: Byte);
  3894. begin
  3895. {$IFDEF RTL220_UP}
  3896. FormatSettings.CurrencyFormat := AValue;
  3897. {$ELSE}
  3898. SysUtils.CurrencyFormat := AValue;
  3899. {$ENDIF}
  3900. end;
  3901. procedure TJclFormatSettings.SetCurrencyString(AValue: string);
  3902. begin
  3903. {$IFDEF RTL220_UP}
  3904. FormatSettings.CurrencyString := AValue;
  3905. {$ELSE}
  3906. SysUtils.CurrencyString := AValue;
  3907. {$ENDIF}
  3908. end;
  3909. procedure TJclFormatSettings.SetDateSeparator(const AValue: Char);
  3910. begin
  3911. {$IFDEF RTL220_UP}
  3912. FormatSettings.DateSeparator := AValue;
  3913. {$ELSE}
  3914. SysUtils.DateSeparator := AValue;
  3915. {$ENDIF}
  3916. end;
  3917. procedure TJclFormatSettings.SetDecimalSeparator(AValue: Char);
  3918. begin
  3919. {$IFDEF RTL220_UP}
  3920. FormatSettings.DecimalSeparator := AValue;
  3921. {$ELSE}
  3922. SysUtils.DecimalSeparator := AValue;
  3923. {$ENDIF}
  3924. end;
  3925. procedure TJclFormatSettings.SetListSeparator(const AValue: Char);
  3926. begin
  3927. {$IFDEF RTL220_UP}
  3928. FormatSettings.ListSeparator := AValue;
  3929. {$ELSE}
  3930. SysUtils.ListSeparator := AValue;
  3931. {$ENDIF}
  3932. end;
  3933. procedure TJclFormatSettings.SetLongDateFormat(const AValue: string);
  3934. begin
  3935. {$IFDEF RTL220_UP}
  3936. FormatSettings.LongDateFormat := AValue;
  3937. {$ELSE}
  3938. SysUtils.LongDateFormat := AValue;
  3939. {$ENDIF}
  3940. end;
  3941. procedure TJclFormatSettings.SetLongTimeFormat(const AValue: string);
  3942. begin
  3943. {$IFDEF RTL220_UP}
  3944. FormatSettings.LongTimeFormat := AValue;
  3945. {$ELSE}
  3946. SysUtils.LongTimeFormat := AValue;
  3947. {$ENDIF}
  3948. end;
  3949. procedure TJclFormatSettings.SetNegCurrFormat(const AValue: Byte);
  3950. begin
  3951. {$IFDEF RTL220_UP}
  3952. FormatSettings.NegCurrFormat := AValue;
  3953. {$ELSE}
  3954. SysUtils.NegCurrFormat := AValue;
  3955. {$ENDIF}
  3956. end;
  3957. procedure TJclFormatSettings.SetShortDateFormat(AValue: string);
  3958. begin
  3959. {$IFDEF RTL220_UP}
  3960. FormatSettings.ShortDateFormat := AValue;
  3961. {$ELSE}
  3962. SysUtils.ShortDateFormat := AValue;
  3963. {$ENDIF}
  3964. end;
  3965. procedure TJclFormatSettings.SetShortTimeFormat(const AValue: string);
  3966. begin
  3967. {$IFDEF RTL220_UP}
  3968. FormatSettings.ShortTimeFormat := AValue;
  3969. {$ELSE}
  3970. SysUtils.ShortTimeFormat := AValue;
  3971. {$ENDIF}
  3972. end;
  3973. procedure TJclFormatSettings.SetThousandSeparator(AValue: Char);
  3974. begin
  3975. {$IFDEF RTL220_UP}
  3976. FormatSettings.TimeSeparator := AValue;
  3977. {$ELSE}
  3978. SysUtils.TimeSeparator := AValue;
  3979. {$ENDIF}
  3980. end;
  3981. procedure TJclFormatSettings.SetTimeAMString(const AValue: string);
  3982. begin
  3983. {$IFDEF RTL220_UP}
  3984. FormatSettings.TimeAMString := AValue;
  3985. {$ELSE}
  3986. SysUtils.TimeAMString := AValue;
  3987. {$ENDIF}
  3988. end;
  3989. procedure TJclFormatSettings.SetTimePMString(const AValue: string);
  3990. begin
  3991. {$IFDEF RTL220_UP}
  3992. FormatSettings.TimePMString := AValue;
  3993. {$ELSE}
  3994. SysUtils.TimePMString := AValue;
  3995. {$ENDIF}
  3996. end;
  3997. procedure TJclFormatSettings.SetTimeSeparator(const AValue: Char);
  3998. begin
  3999. {$IFDEF RTL220_UP}
  4000. FormatSettings.TimeSeparator := AValue;
  4001. {$ELSE}
  4002. SysUtils.TimeSeparator := AValue;
  4003. {$ENDIF}
  4004. end;
  4005. procedure TJclFormatSettings.SetTwoDigitYearCenturyWindow(const AValue: Word);
  4006. begin
  4007. {$IFDEF RTL220_UP}
  4008. FormatSettings.TwoDigitYearCenturyWindow:= AValue;
  4009. {$ELSE}
  4010. SysUtils.TwoDigitYearCenturyWindow:= AValue;
  4011. {$ENDIF}
  4012. end;
  4013. function VarIsNullEmpty(const V: Variant): Boolean;
  4014. begin
  4015. Result := VarIsNull(V) or VarIsEmpty(V);
  4016. end;
  4017. function VarIsNullEmptyBlank(const V: Variant): Boolean;
  4018. begin
  4019. Result := VarIsNull(V) or VarIsEmpty(V) or (VarToStr(V) = '');
  4020. end;
  4021. initialization
  4022. SimpleLog := nil;
  4023. {$IFDEF UNITVERSIONING}
  4024. RegisterUnitVersion(HInstance, UnitVersioning);
  4025. {$ENDIF UNITVERSIONING}
  4026. finalization
  4027. {$IFDEF UNITVERSIONING}
  4028. UnregisterUnitVersion(HInstance);
  4029. {$ENDIF UNITVERSIONING}
  4030. {$IFDEF MSWINDOWS}
  4031. {$IFDEF THREADSAFE}
  4032. // The user must release shared memory blocks himself. We don't clean up his
  4033. // memory leaks and make it impossible to release the shared memory in other
  4034. // unit's finalization blocks.
  4035. MMFFinalized := True;
  4036. FreeAndNil(GlobalMMFHandleListCS);
  4037. {$ENDIF THREADSAFE}
  4038. {$ENDIF MSWINDOWS}
  4039. if Assigned(SimpleLog) then
  4040. FreeAndNil(SimpleLog);
  4041. end.