JclSysUtils.pas 129 KB

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