JclSysUtils.pas 129 KB

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