classes.inc 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. {**********************************************************************
  11. * Class implementations are in separate files. *
  12. **********************************************************************}
  13. type
  14. {$ifdef CPU16}
  15. TFilerFlagsInt = Byte;
  16. {$else CPU16}
  17. TFilerFlagsInt = LongInt;
  18. {$endif CPU16}
  19. var
  20. ClassList : TThreadlist;
  21. ClassAliasList : TStringList;
  22. {
  23. Include all message strings
  24. Add a language with IFDEF LANG_NAME
  25. just befor the final ELSE. This way English will always be the default.
  26. }
  27. {$IFDEF LANG_GERMAN}
  28. {$i constsg.inc}
  29. {$ELSE}
  30. {$IFDEF LANG_SPANISH}
  31. {$i constss.inc}
  32. {$ENDIF}
  33. {$ENDIF}
  34. { Utility routines }
  35. {$i util.inc}
  36. { TBits implementation }
  37. {$i bits.inc}
  38. { All streams implementations: }
  39. { Tstreams THandleStream TFileStream TResourcseStreams TStringStream }
  40. { TCustomMemoryStream TMemoryStream }
  41. {$i streams.inc}
  42. { TParser implementation}
  43. {$i parser.inc}
  44. { TCollection and TCollectionItem implementations }
  45. {$i collect.inc}
  46. { TList and TThreadList implementations }
  47. {$i lists.inc}
  48. { TStrings and TStringList implementations }
  49. {$i stringl.inc}
  50. { TThread implementation }
  51. { system independend threading code }
  52. var
  53. { event executed by SychronizeInternal to wake main thread if it sleeps in
  54. CheckSynchronize }
  55. SynchronizeTimeoutEvent: PRtlEvent;
  56. { the head of the queue containing the entries to be Synchronized - Nil if the
  57. queue is empty }
  58. ThreadQueueHead: TThread.PThreadQueueEntry;
  59. { the tail of the queue containing the entries to be Synchronized - Nil if the
  60. queue is empty }
  61. ThreadQueueTail: TThread.PThreadQueueEntry;
  62. { used for serialized access to the queue }
  63. ThreadQueueLock: TRtlCriticalSection;
  64. { this list holds all instances of external threads that need to be freed at
  65. the end of the program }
  66. ExternalThreads: TThreadList;
  67. { this list signals that the ExternalThreads list is cleared and thus the
  68. thread instances don't need to remove themselves }
  69. ExternalThreadsCleanup: Boolean = False;
  70. { this must be a global var, otherwise unwanted optimizations might happen in
  71. TThread.SpinWait() }
  72. SpinWaitDummy: LongWord;
  73. threadvar
  74. { the instance of the current thread; in case of an external thread this is
  75. Nil until TThread.GetCurrentThread was called once (the RTLs need to ensure
  76. that threadvars are initialized with 0!) }
  77. CurrentThreadVar: TThread;
  78. type
  79. { this type is used if a thread is created using
  80. TThread.CreateAnonymousThread }
  81. TAnonymousThread = class(TThread)
  82. private
  83. fProc: TProcedure;
  84. protected
  85. procedure Execute; override;
  86. public
  87. { as in TThread aProc needs to be changed to TProc once closures are
  88. supported }
  89. constructor Create(aProc: TProcedure);
  90. end;
  91. procedure TAnonymousThread.Execute;
  92. begin
  93. fProc();
  94. end;
  95. constructor TAnonymousThread.Create(aProc: TProcedure);
  96. begin
  97. { an anonymous thread is created suspended and with FreeOnTerminate set }
  98. inherited Create(True);
  99. FreeOnTerminate := True;
  100. fProc := aProc;
  101. end;
  102. type
  103. { this type is used by TThread.GetCurrentThread if the thread does not yet
  104. have a value in CurrentThreadVar (Note: the main thread is also created as
  105. a TExternalThread) }
  106. TExternalThread = class(TThread)
  107. protected
  108. { dummy method to remove the warning }
  109. procedure Execute; override;
  110. public
  111. constructor Create;
  112. destructor Destroy; override;
  113. end;
  114. procedure TExternalThread.Execute;
  115. begin
  116. { empty }
  117. end;
  118. constructor TExternalThread.Create;
  119. begin
  120. FExternalThread := True;
  121. { the parameter is unimportant if FExternalThread is True }
  122. inherited Create(False);
  123. with ExternalThreads.LockList do
  124. try
  125. Add(Self);
  126. finally
  127. ExternalThreads.UnlockList;
  128. end;
  129. end;
  130. destructor TExternalThread.Destroy;
  131. begin
  132. inherited;
  133. if not ExternalThreadsCleanup then
  134. with ExternalThreads.LockList do
  135. try
  136. Extract(Self);
  137. finally
  138. ExternalThreads.UnlockList;
  139. end;
  140. end;
  141. function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
  142. var
  143. FreeThread: Boolean;
  144. Thread: TThread absolute ThreadObjPtr;
  145. begin
  146. { if Suspend checks FSuspended before doing anything, make sure it }
  147. { knows we're currently not suspended (this flag may have been set }
  148. { to true if CreateSuspended was true) }
  149. // Thread.FSuspended:=false;
  150. // wait until AfterConstruction has been called, so we cannot
  151. // free ourselves before TThread.Create has finished
  152. // (since that one may check our VTM in case of $R+, and
  153. // will call the AfterConstruction method in all cases)
  154. // Thread.Suspend;
  155. try
  156. { The thread may be already terminated at this point, e.g. if it was intially
  157. suspended, or if it wasn't ever scheduled for execution for whatever reason.
  158. So bypass user code if terminated. }
  159. if not Thread.Terminated then begin
  160. CurrentThreadVar := Thread;
  161. Thread.Execute;
  162. end;
  163. except
  164. Thread.FFatalException := TObject(AcquireExceptionObject);
  165. end;
  166. FreeThread := Thread.FFreeOnTerminate;
  167. Result := Thread.FReturnValue;
  168. Thread.FFinished := True;
  169. Thread.DoTerminate;
  170. if FreeThread then
  171. Thread.Free;
  172. EndThread(Result);
  173. end;
  174. { system-dependent code }
  175. {$i tthread.inc}
  176. constructor TThread.Create(CreateSuspended: Boolean;
  177. const StackSize: SizeUInt);
  178. begin
  179. inherited Create;
  180. if FExternalThread then
  181. FThreadID := GetCurrentThreadID
  182. else
  183. SysCreate(CreateSuspended, StackSize);
  184. end;
  185. destructor TThread.Destroy;
  186. begin
  187. if not FExternalThread then begin
  188. SysDestroy;
  189. if FHandle <> TThreadID(0) then
  190. CloseThread(FHandle);
  191. end;
  192. RemoveQueuedEvents(Self);
  193. DoneSynchronizeEvent;
  194. { set CurrentThreadVar to Nil? }
  195. inherited Destroy;
  196. end;
  197. procedure TThread.Start;
  198. begin
  199. { suspend/resume are now deprecated in Delphi (they also don't work
  200. on most platforms in FPC), so a different method was required
  201. to start a thread if it's create with fSuspended=true -> that's
  202. what this method is for. }
  203. Resume;
  204. end;
  205. function TThread.GetSuspended: Boolean;
  206. begin
  207. GetSuspended:=FSuspended;
  208. end;
  209. procedure TThread.AfterConstruction;
  210. begin
  211. inherited AfterConstruction;
  212. // enable for all platforms once http://bugs.freepascal.org/view.php?id=16884
  213. // is fixed for all platforms (in case the fix for non-unix platforms also
  214. // requires this field at least)
  215. {$if defined(unix) or defined(windows) or defined(os2) or defined(hasamiga)}
  216. if not FExternalThread and not FInitialSuspended then
  217. Resume;
  218. {$endif}
  219. end;
  220. procedure ExecuteThreadQueueEntry(aEntry: TThread.PThreadQueueEntry);
  221. begin
  222. if Assigned(aEntry^.Method) then
  223. aEntry^.Method()
  224. // enable once closures are supported
  225. {else
  226. aEntry^.ThreadProc();}
  227. end;
  228. procedure ThreadQueueAppend(aEntry: TThread.PThreadQueueEntry);
  229. begin
  230. { do we really need a synchronized call? }
  231. if GetCurrentThreadID = MainThreadID then begin
  232. ExecuteThreadQueueEntry(aEntry);
  233. if not Assigned(aEntry^.SyncEvent) then
  234. Dispose(aEntry);
  235. end else begin
  236. System.EnterCriticalSection(ThreadQueueLock);
  237. try
  238. { add the entry to the thread queue }
  239. if Assigned(ThreadQueueTail) then begin
  240. ThreadQueueTail^.Next := aEntry;
  241. end else
  242. ThreadQueueHead := aEntry;
  243. ThreadQueueTail := aEntry;
  244. finally
  245. System.LeaveCriticalSection(ThreadQueueLock);
  246. end;
  247. { ensure that the main thread knows that something awaits }
  248. RtlEventSetEvent(SynchronizeTimeoutEvent);
  249. if assigned(WakeMainThread) then
  250. WakeMainThread(aEntry^.Thread);
  251. { is this a Synchronize or Queue entry? }
  252. if Assigned(aEntry^.SyncEvent) then begin
  253. RtlEventWaitFor(aEntry^.SyncEvent);
  254. if Assigned(aEntry^.Exception) then
  255. raise aEntry^.Exception;
  256. end;
  257. end;
  258. end;
  259. procedure TThread.InitSynchronizeEvent;
  260. begin
  261. if Assigned(FSynchronizeEntry) then
  262. Exit;
  263. New(FSynchronizeEntry);
  264. FillChar(FSynchronizeEntry^, SizeOf(TThreadQueueEntry), 0);
  265. FSynchronizeEntry^.Thread := Self;
  266. FSynchronizeEntry^.ThreadID := ThreadID;
  267. FSynchronizeEntry^.SyncEvent := RtlEventCreate;
  268. end;
  269. procedure TThread.DoneSynchronizeEvent;
  270. begin
  271. if not Assigned(FSynchronizeEntry) then
  272. Exit;
  273. RtlEventDestroy(FSynchronizeEntry^.SyncEvent);
  274. Dispose(FSynchronizeEntry);
  275. FSynchronizeEntry := Nil;
  276. end;
  277. class procedure TThread.Synchronize(AThread: TThread; AMethod: TThreadMethod);
  278. var
  279. syncentry: PThreadQueueEntry;
  280. begin
  281. if not Assigned(AThread) then begin
  282. { use a local synchronize event }
  283. New(syncentry);
  284. FillChar(syncentry^, SizeOf(TThreadQueueEntry), 0);
  285. syncentry^.ThreadID := GetCurrentThreadID;
  286. syncentry^.SyncEvent := RtlEventCreate;
  287. end else begin
  288. { the Synchronize event is instantiated on demand }
  289. AThread.InitSynchronizeEvent;
  290. syncentry := AThread.FSynchronizeEntry;
  291. end;
  292. syncentry^.Exception := Nil;
  293. syncentry^.Method := AMethod;
  294. ThreadQueueAppend(syncentry);
  295. syncentry^.Method := Nil;
  296. syncentry^.Next := Nil;
  297. if not Assigned(AThread) then begin
  298. { clean up again }
  299. RtlEventDestroy(syncentry^.SyncEvent);
  300. Dispose(syncentry);
  301. end;
  302. end;
  303. procedure TThread.Synchronize(AMethod: TThreadMethod);
  304. begin
  305. TThread.Synchronize(self,AMethod);
  306. end;
  307. Function PopThreadQueueHead : TThread.PThreadQueueEntry;
  308. begin
  309. Result:=ThreadQueueHead;
  310. if (Result<>Nil) then
  311. begin
  312. System.EnterCriticalSection(ThreadQueueLock);
  313. try
  314. Result:=ThreadQueueHead;
  315. if Result<>Nil then
  316. ThreadQueueHead:=ThreadQueueHead^.Next;
  317. if Not Assigned(ThreadQueueHead) then
  318. ThreadQueueTail := Nil;
  319. finally
  320. System.LeaveCriticalSection(ThreadQueueLock);
  321. end;
  322. end;
  323. end;
  324. function CheckSynchronize(timeout : longint=0) : boolean;
  325. { assumes being called from GUI thread }
  326. var
  327. ExceptObj: Exception;
  328. tmpentry: TThread.PThreadQueueEntry;
  329. begin
  330. result:=false;
  331. { first sanity check }
  332. if Not IsMultiThread then
  333. Exit
  334. { second sanity check }
  335. else if GetCurrentThreadID<>MainThreadID then
  336. raise EThread.CreateFmt(SCheckSynchronizeError,[GetCurrentThreadID]);
  337. if timeout>0 then
  338. RtlEventWaitFor(SynchronizeTimeoutEvent,timeout)
  339. else
  340. RtlEventResetEvent(SynchronizeTimeoutEvent);
  341. tmpentry := PopThreadQueueHead;
  342. while Assigned(tmpentry) do
  343. begin
  344. { step 2: execute the method }
  345. exceptobj := Nil;
  346. try
  347. ExecuteThreadQueueEntry(tmpentry);
  348. except
  349. exceptobj := Exception(AcquireExceptionObject);
  350. end;
  351. { step 3: error handling and cleanup }
  352. if Assigned(tmpentry^.SyncEvent) then
  353. begin
  354. { for Synchronize entries we pass back the Exception and trigger
  355. the event that Synchronize waits in }
  356. tmpentry^.Exception := exceptobj;
  357. RtlEventSetEvent(tmpentry^.SyncEvent)
  358. end
  359. else
  360. begin
  361. { for Queue entries we dispose the entry and raise the exception }
  362. Dispose(tmpentry);
  363. if Assigned(exceptobj) then
  364. raise exceptobj;
  365. end;
  366. tmpentry := PopThreadQueueHead;
  367. end;
  368. end;
  369. class function TThread.GetCurrentThread: TThread;
  370. begin
  371. { if this is the first time GetCurrentThread is called for an external thread
  372. we need to create a corresponding TExternalThread instance }
  373. Result := CurrentThreadVar;
  374. if not Assigned(Result) then begin
  375. Result := TExternalThread.Create;
  376. CurrentThreadVar := Result;
  377. end;
  378. end;
  379. class function TThread.GetIsSingleProcessor: Boolean;
  380. begin
  381. Result := FProcessorCount <= 1;
  382. end;
  383. procedure TThread.Queue(aMethod: TThreadMethod);
  384. begin
  385. Queue(Self, aMethod);
  386. end;
  387. class procedure TThread.Queue(aThread: TThread; aMethod: TThreadMethod); static;
  388. var
  389. queueentry: PThreadQueueEntry;
  390. begin
  391. New(queueentry);
  392. FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
  393. queueentry^.Thread := aThread;
  394. queueentry^.ThreadID := GetCurrentThreadID;
  395. queueentry^.Method := aMethod;
  396. { the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
  397. ThreadQueueAppend(queueentry);
  398. end;
  399. class procedure TThread.RemoveQueuedEvents(aThread: TThread; aMethod: TThreadMethod);
  400. var
  401. entry, tmpentry, lastentry: PThreadQueueEntry;
  402. begin
  403. { anything to do at all? }
  404. if not Assigned(aThread) and not Assigned(aMethod) then
  405. Exit;
  406. System.EnterCriticalSection(ThreadQueueLock);
  407. try
  408. lastentry := Nil;
  409. entry := ThreadQueueHead;
  410. while Assigned(entry) do begin
  411. { first check for the thread }
  412. if Assigned(aThread) and (entry^.Thread <> aThread) and (entry^.ThreadID <> aThread.ThreadID) then begin
  413. lastentry := entry;
  414. entry := entry^.Next;
  415. Continue;
  416. end;
  417. { then check for the method }
  418. if entry^.Method <> aMethod then begin
  419. lastentry := entry;
  420. entry := entry^.Next;
  421. Continue;
  422. end;
  423. { skip entries added by Synchronize }
  424. if Assigned(entry^.SyncEvent) then begin
  425. lastentry := entry;
  426. entry := entry^.Next;
  427. Continue;
  428. end;
  429. { ok, we need to remove this entry }
  430. tmpentry := entry;
  431. if Assigned(lastentry) then
  432. lastentry^.Next := entry^.Next;
  433. entry := entry^.Next;
  434. if ThreadQueueHead = tmpentry then
  435. ThreadQueueHead := entry;
  436. if ThreadQueueTail = tmpentry then
  437. ThreadQueueTail := lastentry;
  438. { only dispose events added by Queue }
  439. if not Assigned(tmpentry^.SyncEvent) then
  440. Dispose(tmpentry);
  441. end;
  442. finally
  443. System.LeaveCriticalSection(ThreadQueueLock);
  444. end;
  445. end;
  446. class procedure TThread.RemoveQueuedEvents(aMethod: TThreadMethod);
  447. begin
  448. RemoveQueuedEvents(Nil, aMethod);
  449. end;
  450. class procedure TThread.RemoveQueuedEvents(aThread: TThread);
  451. begin
  452. RemoveQueuedEvents(aThread, Nil);
  453. end;
  454. class function TThread.CheckTerminated: Boolean;
  455. begin
  456. { this method only works with threads created by TThread, so we can make a
  457. shortcut here }
  458. if not Assigned(CurrentThreadVar) then
  459. raise EThreadExternalException.Create(SThreadExternal);
  460. Result := CurrentThreadVar.FTerminated;
  461. end;
  462. class procedure TThread.SetReturnValue(aValue: Integer);
  463. begin
  464. { this method only works with threads created by TThread, so we can make a
  465. shortcut here }
  466. if not Assigned(CurrentThreadVar) then
  467. raise EThreadExternalException.Create(SThreadExternal);
  468. CurrentThreadVar.FReturnValue := aValue;
  469. end;
  470. class function TThread.CreateAnonymousThread(aProc: TProcedure): TThread;
  471. begin
  472. if not Assigned(aProc) then
  473. raise Exception.Create(SNoProcGiven);
  474. Result := TAnonymousThread.Create(aProc);
  475. end;
  476. {$ifdef THREADNAME_IS_ANSISTRING}
  477. { the platform implements the AnsiString variant and the UnicodeString variant
  478. simply calls the AnsiString variant }
  479. class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
  480. begin
  481. NameThreadForDebugging(AnsiString(aThreadName), aThreadID);
  482. end;
  483. {$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}
  484. class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
  485. begin
  486. { empty }
  487. end;
  488. {$endif}
  489. {$else}
  490. {$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}
  491. { the platform implements the UnicodeString variant and the AnsiString variant
  492. simply calls the UnicodeString variant }
  493. class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
  494. begin
  495. { empty }
  496. end;
  497. {$endif}
  498. class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
  499. begin
  500. NameThreadForDebugging(UnicodeString(aThreadName), aThreadID);
  501. end;
  502. {$endif}
  503. class procedure TThread.Yield;
  504. begin
  505. ThreadSwitch;
  506. end;
  507. class procedure TThread.Sleep(aMilliseconds: Cardinal);
  508. begin
  509. SysUtils.Sleep(aMilliseconds);
  510. end;
  511. class procedure TThread.SpinWait(aIterations: LongWord);
  512. var
  513. i: LongWord;
  514. begin
  515. { yes, it's just a simple busy wait to burn some cpu cycles... and as the job
  516. of this loop is to burn CPU cycles we switch off any optimizations that
  517. could interfere with this (e.g. loop unrolling) }
  518. { Do *NOT* do $PUSH, $OPTIMIZATIONS OFF, <code>, $POP because optimization is
  519. not a local switch, which means $PUSH/POP doesn't affect it, so that turns
  520. off *ALL* optimizations for code below this point. Thanks to this we shipped
  521. large parts of the classes unit with optimizations off between 2012-12-27
  522. and 2014-06-06.
  523. Instead, use a global var for the spinlock, because that is always handled
  524. as volatile, so the access won't be optimized away by the compiler. (KB) }
  525. for i:=1 to aIterations do
  526. begin
  527. Inc(SpinWaitDummy); // SpinWaitDummy *MUST* be global
  528. end;
  529. end;
  530. {$ifndef HAS_TTHREAD_GETSYSTEMTIMES}
  531. class procedure TThread.GetSystemTimes(out aSystemTimes: TSystemTimes);
  532. begin
  533. { by default we just return a zeroed out record }
  534. FillChar(aSystemTimes, SizeOf(aSystemTimes), 0);
  535. end;
  536. {$endif}
  537. class function TThread.GetTickCount: LongWord;
  538. begin
  539. Result := SysUtils.GetTickCount;
  540. end;
  541. class function TThread.GetTickCount64: QWord;
  542. begin
  543. Result := SysUtils.GetTickCount64;
  544. end;
  545. { TSimpleThread allows objects to create a threading method without defining
  546. a new thread class }
  547. Type
  548. TSimpleThread = class(TThread)
  549. private
  550. FExecuteMethod: TThreadExecuteHandler;
  551. protected
  552. procedure Execute; override;
  553. public
  554. constructor Create(ExecuteMethod: TThreadExecuteHandler; AOnterminate : TNotifyEvent);
  555. end;
  556. TSimpleStatusThread = class(TThread)
  557. private
  558. FExecuteMethod: TThreadExecuteStatusHandler;
  559. FStatus : String;
  560. FOnStatus : TThreadStatusNotifyEvent;
  561. protected
  562. procedure Execute; override;
  563. Procedure DoStatus;
  564. Procedure SetStatus(Const AStatus : String);
  565. public
  566. constructor Create(ExecuteMethod: TThreadExecuteStatusHandler; AOnStatus : TThreadStatusNotifyEvent; AOnterminate : TNotifyEvent);
  567. end;
  568. TSimpleProcThread = class(TThread)
  569. private
  570. FExecuteMethod: TThreadExecuteCallBack;
  571. FCallOnTerminate : TNotifyCallBack;
  572. FData : Pointer;
  573. protected
  574. Procedure TerminateCallBack(Sender : TObject);
  575. procedure Execute; override;
  576. public
  577. constructor Create(ExecuteMethod: TThreadExecuteCallBack; AData : Pointer; AOnterminate : TNotifyCallBack);
  578. end;
  579. TSimpleStatusProcThread = class(TThread)
  580. private
  581. FExecuteMethod: TThreadExecuteStatusCallBack;
  582. FCallOnTerminate : TNotifyCallBack;
  583. FStatus : String;
  584. FOnStatus : TThreadStatusNotifyCallBack;
  585. FData : Pointer;
  586. protected
  587. procedure Execute; override;
  588. Procedure DoStatus;
  589. Procedure SetStatus(Const AStatus : String);
  590. Procedure TerminateCallBack(Sender : TObject);
  591. public
  592. constructor Create(ExecuteMethod: TThreadExecuteStatusCallBack; AData : Pointer; AOnStatus : TThreadStatusNotifyCallBack; AOnterminate : TNotifyCallBack);
  593. end;
  594. { TSimpleThread }
  595. constructor TSimpleThread.Create(ExecuteMethod: TThreadExecuteHandler; AOnTerminate: TNotifyEvent);
  596. begin
  597. FExecuteMethod := ExecuteMethod;
  598. OnTerminate := AOnTerminate;
  599. inherited Create(False);
  600. end;
  601. procedure TSimpleThread.Execute;
  602. begin
  603. FreeOnTerminate := True;
  604. FExecuteMethod;
  605. end;
  606. { TSimpleStatusThread }
  607. constructor TSimpleStatusThread.Create(ExecuteMethod: TThreadExecuteStatusHandler;AOnStatus : TThreadStatusNotifyEvent; AOnTerminate: TNotifyEvent);
  608. begin
  609. FExecuteMethod := ExecuteMethod;
  610. OnTerminate := AOnTerminate;
  611. FOnStatus:=AOnStatus;
  612. FStatus:='';
  613. inherited Create(False);
  614. end;
  615. procedure TSimpleStatusThread.Execute;
  616. begin
  617. FreeOnTerminate := True;
  618. FExecuteMethod(@SetStatus);
  619. end;
  620. procedure TSimpleStatusThread.SetStatus(Const AStatus : String);
  621. begin
  622. If (AStatus=FStatus) then
  623. exit;
  624. FStatus:=AStatus;
  625. If Assigned(FOnStatus) then
  626. Synchronize(@DoStatus);
  627. end;
  628. procedure TSimpleStatusThread.DoStatus;
  629. begin
  630. FOnStatus(Self,FStatus);
  631. end;
  632. { TSimpleProcThread }
  633. constructor TSimpleProcThread.Create(ExecuteMethod: TThreadExecuteCallBack; AData : Pointer; AOnTerminate: TNotifyCallBack);
  634. begin
  635. FExecuteMethod := ExecuteMethod;
  636. FCallOnTerminate := AOnTerminate;
  637. FData:=AData;
  638. If Assigned(FCallOnTerminate) then
  639. OnTerminate:=@TerminateCallBack;
  640. inherited Create(False);
  641. end;
  642. procedure TSimpleProcThread.Execute;
  643. begin
  644. FreeOnTerminate := True;
  645. FExecuteMethod(FData);
  646. end;
  647. procedure TSimpleProcThread.TerminateCallBack(Sender : TObject);
  648. begin
  649. if Assigned(FCallOnTerminate) then
  650. FCallOnTerminate(Sender,FData);
  651. end;
  652. { TSimpleStatusProcThread }
  653. constructor TSimpleStatusProcThread.Create(ExecuteMethod: TThreadExecuteStatusCallback; AData : Pointer; AOnStatus : TThreadStatusNotifyCallBack; AOnTerminate: TNotifyCallBack);
  654. begin
  655. FExecuteMethod := ExecuteMethod;
  656. FCallOnTerminate := AOnTerminate;
  657. FData:=AData;
  658. If Assigned(FCallOnTerminate) then
  659. OnTerminate:=@TerminateCallBack;
  660. FOnStatus:=AOnStatus;
  661. FStatus:='';
  662. inherited Create(False);
  663. end;
  664. procedure TSimpleStatusProcThread.Execute;
  665. begin
  666. FreeOnTerminate := True;
  667. FExecuteMethod(FData,@SetStatus);
  668. end;
  669. procedure TSimpleStatusProcThread.SetStatus(Const AStatus : String);
  670. begin
  671. If (AStatus=FStatus) then
  672. exit;
  673. FStatus:=AStatus;
  674. If Assigned(FOnStatus) then
  675. Synchronize(@DoStatus);
  676. end;
  677. procedure TSimpleStatusProcThread.DoStatus;
  678. begin
  679. FOnStatus(Self,FData,FStatus);
  680. end;
  681. procedure TSimpleStatusProcThread.TerminateCallBack(Sender : TObject);
  682. begin
  683. if Assigned(FCallOnTerminate) then
  684. FCallOnTerminate(Sender,FData);
  685. end;
  686. Class Function TThread.ExecuteInThread(AMethod : TThreadExecuteHandler; AOnTerminate : TNotifyEvent = Nil) : TThread;
  687. begin
  688. Result:=TSimpleThread.Create(AMethod,AOnTerminate);
  689. end;
  690. Class Function TThread.ExecuteInThread(AMethod : TThreadExecuteCallback; AData : Pointer; AOnTerminate : TNotifyCallback = Nil) : TThread;
  691. begin
  692. Result:=TSimpleProcThread.Create(AMethod,AData,AOnTerminate);
  693. end;
  694. Class Function TThread.ExecuteInThread(AMethod : TThreadExecuteStatusHandler; AOnStatus : TThreadStatusNotifyEvent; AOnTerminate : TNotifyEvent = Nil) : TThread;
  695. begin
  696. If Not Assigned(AOnStatus) then
  697. Raise EThread.Create(SErrStatusCallBackRequired);
  698. Result:=TSimpleStatusThread.Create(AMethod,AOnStatus,AOnTerminate);
  699. end;
  700. Class Function TThread.ExecuteInThread(AMethod : TThreadExecuteStatusCallback; AOnStatus : TThreadStatusNotifyCallback;AData : Pointer = Nil; AOnTerminate : TNotifyCallBack = Nil) : TThread;
  701. begin
  702. If Not Assigned(AOnStatus) then
  703. Raise EThread.Create(SErrStatusCallBackRequired);
  704. Result:=TSimpleStatusProcThread.Create(AMethod,AData,AOnStatus,AOnTerminate);
  705. end;
  706. { TPersistent implementation }
  707. {$i persist.inc }
  708. {$i sllist.inc}
  709. {$i resref.inc}
  710. { TComponent implementation }
  711. {$i compon.inc}
  712. { TBasicAction implementation }
  713. {$i action.inc}
  714. { TDataModule implementation }
  715. {$i dm.inc}
  716. { Class and component registration routines }
  717. {$I cregist.inc}
  718. { Interface related stuff }
  719. {$I intf.inc}
  720. {**********************************************************************
  721. * Miscellaneous procedures and functions *
  722. **********************************************************************}
  723. function ExtractStrings(Separators, WhiteSpace: TSysCharSet; Content: PChar; Strings: TStrings; AddEmptyStrings : Boolean = False): Integer;
  724. var
  725. b, c : pchar;
  726. procedure SkipWhitespace;
  727. begin
  728. while (c^ in Whitespace) do
  729. inc (c);
  730. end;
  731. procedure AddString;
  732. var
  733. l : integer;
  734. s : string;
  735. begin
  736. l := c-b;
  737. if (l > 0) or AddEmptyStrings then
  738. begin
  739. if assigned(Strings) then
  740. begin
  741. setlength(s, l);
  742. if l>0 then
  743. move (b^, s[1],l*SizeOf(char));
  744. Strings.Add (s);
  745. end;
  746. inc (result);
  747. end;
  748. end;
  749. var
  750. quoted : char;
  751. begin
  752. result := 0;
  753. c := Content;
  754. Quoted := #0;
  755. Separators := Separators + [#13, #10] - ['''','"'];
  756. SkipWhitespace;
  757. b := c;
  758. while (c^ <> #0) do
  759. begin
  760. if (c^ = Quoted) then
  761. begin
  762. if ((c+1)^ = Quoted) then
  763. inc (c)
  764. else
  765. Quoted := #0
  766. end
  767. else if (Quoted = #0) and (c^ in ['''','"']) then
  768. Quoted := c^;
  769. if (Quoted = #0) and (c^ in Separators) then
  770. begin
  771. AddString;
  772. inc (c);
  773. SkipWhitespace;
  774. b := c;
  775. end
  776. else
  777. inc (c);
  778. end;
  779. if (c <> b) then
  780. AddString;
  781. end;
  782. { Point and rectangle constructors }
  783. function Point(AX, AY: Integer): TPoint;
  784. begin
  785. with Result do
  786. begin
  787. X := AX;
  788. Y := AY;
  789. end;
  790. end;
  791. function SmallPoint(AX, AY: SmallInt): TSmallPoint;
  792. begin
  793. with Result do
  794. begin
  795. X := AX;
  796. Y := AY;
  797. end;
  798. end;
  799. function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect;
  800. begin
  801. with Result do
  802. begin
  803. Left := ALeft;
  804. Top := ATop;
  805. Right := ARight;
  806. Bottom := ABottom;
  807. end;
  808. end;
  809. function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect;
  810. begin
  811. with Result do
  812. begin
  813. Left := ALeft;
  814. Top := ATop;
  815. Right := ALeft + AWidth;
  816. Bottom := ATop + AHeight;
  817. end;
  818. end;
  819. function PointsEqual(const P1, P2: TPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  820. begin
  821. { lazy, but should work }
  822. result:=QWord(P1)=QWord(P2);
  823. end;
  824. function PointsEqual(const P1, P2: TSmallPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  825. begin
  826. { lazy, but should work }
  827. result:=DWord(P1)=DWord(P2);
  828. end;
  829. function InvalidPoint(X, Y: Integer): Boolean;
  830. begin
  831. result:=(X=-1) and (Y=-1);
  832. end;
  833. function InvalidPoint(const At: TPoint): Boolean;
  834. begin
  835. result:=(At.x=-1) and (At.y=-1);
  836. end;
  837. function InvalidPoint(const At: TSmallPoint): Boolean;
  838. begin
  839. result:=(At.x=-1) and (At.y=-1);
  840. end;
  841. { Object filing routines }
  842. var
  843. IntConstList: TThreadList;
  844. type
  845. TIntConst = class
  846. IntegerType: PTypeInfo; // The integer type RTTI pointer
  847. IdentToIntFn: TIdentToInt; // Identifier to Integer conversion
  848. IntToIdentFn: TIntToIdent; // Integer to Identifier conversion
  849. constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
  850. AIntToIdent: TIntToIdent);
  851. end;
  852. constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
  853. AIntToIdent: TIntToIdent);
  854. begin
  855. IntegerType := AIntegerType;
  856. IdentToIntFn := AIdentToInt;
  857. IntToIdentFn := AIntToIdent;
  858. end;
  859. procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToIntFn: TIdentToInt;
  860. IntToIdentFn: TIntToIdent);
  861. begin
  862. IntConstList.Add(TIntConst.Create(IntegerType, IdentToIntFn, IntToIdentFn));
  863. end;
  864. function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
  865. var
  866. i: Integer;
  867. begin
  868. with IntConstList.LockList do
  869. try
  870. for i := 0 to Count - 1 do
  871. if TIntConst(Items[i]).IntegerType = AIntegerType then
  872. exit(TIntConst(Items[i]).IntToIdentFn);
  873. Result := nil;
  874. finally
  875. IntConstList.UnlockList;
  876. end;
  877. end;
  878. function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
  879. var
  880. i: Integer;
  881. begin
  882. with IntConstList.LockList do
  883. try
  884. for i := 0 to Count - 1 do
  885. with TIntConst(Items[I]) do
  886. if TIntConst(Items[I]).IntegerType = AIntegerType then
  887. exit(IdentToIntFn);
  888. Result := nil;
  889. finally
  890. IntConstList.UnlockList;
  891. end;
  892. end;
  893. function IdentToInt(const Ident: String; out Int: LongInt;
  894. const Map: array of TIdentMapEntry): Boolean;
  895. var
  896. i: Integer;
  897. begin
  898. for i := Low(Map) to High(Map) do
  899. if CompareText(Map[i].Name, Ident) = 0 then
  900. begin
  901. Int := Map[i].Value;
  902. exit(True);
  903. end;
  904. Result := False;
  905. end;
  906. function IntToIdent(Int: LongInt; var Ident: String;
  907. const Map: array of TIdentMapEntry): Boolean;
  908. var
  909. i: Integer;
  910. begin
  911. for i := Low(Map) to High(Map) do
  912. if Map[i].Value = Int then
  913. begin
  914. Ident := Map[i].Name;
  915. exit(True);
  916. end;
  917. Result := False;
  918. end;
  919. function GlobalIdentToInt(const Ident: String; var Int: LongInt):boolean;
  920. var
  921. i : Integer;
  922. begin
  923. with IntConstList.LockList do
  924. try
  925. for i := 0 to Count - 1 do
  926. if TIntConst(Items[I]).IdentToIntFn(Ident, Int) then
  927. Exit(True);
  928. Result := false;
  929. finally
  930. IntConstList.UnlockList;
  931. end;
  932. end;
  933. { TPropFixup }
  934. // Tainted. TPropFixup is being removed.
  935. Type
  936. TInitHandler = Class(TObject)
  937. AHandler : TInitComponentHandler;
  938. AClass : TComponentClass;
  939. end;
  940. {$ifndef i8086}
  941. type
  942. TCodePtrList = TList;
  943. {$endif i8086}
  944. Var
  945. InitHandlerList : TList;
  946. FindGlobalComponentList : TCodePtrList;
  947. procedure RegisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
  948. begin
  949. if not(assigned(FindGlobalComponentList)) then
  950. FindGlobalComponentList:=TCodePtrList.Create;
  951. if FindGlobalComponentList.IndexOf(CodePointer(AFindGlobalComponent))<0 then
  952. FindGlobalComponentList.Add(CodePointer(AFindGlobalComponent));
  953. end;
  954. procedure UnregisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
  955. begin
  956. if assigned(FindGlobalComponentList) then
  957. FindGlobalComponentList.Remove(CodePointer(AFindGlobalComponent));
  958. end;
  959. function FindGlobalComponent(const Name: string): TComponent;
  960. var
  961. i : sizeint;
  962. begin
  963. FindGlobalComponent:=nil;
  964. if assigned(FindGlobalComponentList) then
  965. begin
  966. for i:=FindGlobalComponentList.Count-1 downto 0 do
  967. begin
  968. FindGlobalComponent:=TFindGlobalComponent(FindGlobalComponentList[i])(name);
  969. if assigned(FindGlobalComponent) then
  970. break;
  971. end;
  972. end;
  973. end;
  974. procedure RegisterInitComponentHandler(ComponentClass: TComponentClass; Handler: TInitComponentHandler);
  975. Var
  976. I : Integer;
  977. H: TInitHandler;
  978. begin
  979. If (InitHandlerList=Nil) then
  980. InitHandlerList:=TList.Create;
  981. H:=TInitHandler.Create;
  982. H.Aclass:=ComponentClass;
  983. H.AHandler:=Handler;
  984. try
  985. With InitHandlerList do
  986. begin
  987. I:=0;
  988. While (I<Count) and not H.AClass.InheritsFrom(TInitHandler(Items[I]).AClass) do
  989. Inc(I);
  990. { override? }
  991. if (I<Count) and (TInitHandler(Items[I]).AClass=H.AClass) then
  992. begin
  993. TInitHandler(Items[I]).AHandler:=Handler;
  994. H.Free;
  995. end
  996. else
  997. InitHandlerList.Insert(I,H);
  998. end;
  999. except
  1000. H.Free;
  1001. raise;
  1002. end;
  1003. end;
  1004. { all targets should at least include the sysres.inc dummy in the system unit to compile this }
  1005. function CreateComponentfromRes(const res : string;Inst : THandle;var Component : TComponent) : Boolean;
  1006. var
  1007. ResStream : TResourceStream;
  1008. begin
  1009. result:=true;
  1010. if Inst=0 then
  1011. Inst:=HInstance;
  1012. try
  1013. ResStream:=TResourceStream.Create(Inst,res,RT_RCDATA);
  1014. try
  1015. Component:=ResStream.ReadComponent(Component);
  1016. finally
  1017. ResStream.Free;
  1018. end;
  1019. except
  1020. on EResNotFound do
  1021. result:=false;
  1022. end;
  1023. end;
  1024. function DefaultInitHandler(Instance: TComponent; RootAncestor: TClass): Boolean;
  1025. function doinit(_class : TClass) : boolean;
  1026. begin
  1027. result:=false;
  1028. if (_class.ClassType=TComponent) or (_class.ClassType=RootAncestor) then
  1029. exit;
  1030. result:=doinit(_class.ClassParent);
  1031. result:=CreateComponentfromRes(_class.ClassName,0,Instance) or result;
  1032. end;
  1033. begin
  1034. GlobalNameSpace.BeginWrite;
  1035. try
  1036. result:=doinit(Instance.ClassType);
  1037. finally
  1038. GlobalNameSpace.EndWrite;
  1039. end;
  1040. end;
  1041. function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
  1042. Var
  1043. I : Integer;
  1044. begin
  1045. I:=0;
  1046. if not Assigned(InitHandlerList) then begin
  1047. Result := True;
  1048. Exit;
  1049. end;
  1050. Result:=False;
  1051. With InitHandlerList do
  1052. begin
  1053. I:=0;
  1054. // Instance is the normally the lowest one, so that one should be used when searching.
  1055. While Not result and (I<Count) do
  1056. begin
  1057. If (Instance.InheritsFrom(TInitHandler(Items[i]).AClass)) then
  1058. Result:=TInitHandler(Items[i]).AHandler(Instance,RootAncestor);
  1059. Inc(I);
  1060. end;
  1061. end;
  1062. end;
  1063. function InitComponentRes(const ResName: String; Instance: TComponent): Boolean;
  1064. begin
  1065. Result:=ReadComponentRes(ResName,Instance)=Instance;
  1066. end;
  1067. function SysReadComponentRes(HInstance : THandle; const ResName: String; Instance: TComponent): TComponent;
  1068. Var
  1069. H : TFPResourceHandle;
  1070. begin
  1071. { Windows unit also has a FindResource function, use the one from
  1072. system unit here. }
  1073. H:=system.FindResource(HInstance,ResName,RT_RCDATA);
  1074. if (PtrInt(H)=0) then
  1075. Result:=Nil
  1076. else
  1077. With TResourceStream.Create(HInstance,ResName,RT_RCDATA) do
  1078. try
  1079. Result:=ReadComponent(Instance);
  1080. Finally
  1081. Free;
  1082. end;
  1083. end;
  1084. function ReadComponentRes(const ResName: String; Instance: TComponent): TComponent;
  1085. begin
  1086. Result:=SysReadComponentRes(Hinstance,Resname,Instance);
  1087. end;
  1088. function ReadComponentResEx(HInstance: THandle; const ResName: String): TComponent;
  1089. begin
  1090. Result:=SysReadComponentRes(Hinstance,ResName,Nil);
  1091. end;
  1092. function ReadComponentResFile(const FileName: String; Instance: TComponent): TComponent;
  1093. var
  1094. FileStream: TStream;
  1095. begin
  1096. FileStream := TFileStream.Create(FileName, fmOpenRead {!!!:or fmShareDenyWrite});
  1097. try
  1098. Result := FileStream.ReadComponentRes(Instance);
  1099. finally
  1100. FileStream.Free;
  1101. end;
  1102. end;
  1103. procedure WriteComponentResFile(const FileName: String; Instance: TComponent);
  1104. var
  1105. FileStream: TStream;
  1106. begin
  1107. FileStream := TFileStream.Create(FileName, fmCreate);
  1108. try
  1109. FileStream.WriteComponentRes(Instance.ClassName, Instance);
  1110. finally
  1111. FileStream.Free;
  1112. end;
  1113. end;
  1114. Function FindNestedComponent(Root : TComponent; APath : String; CStyle : Boolean = True) : TComponent;
  1115. Function GetNextName : String; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
  1116. Var
  1117. P : Integer;
  1118. CM : Boolean;
  1119. begin
  1120. P:=Pos('.',APath);
  1121. CM:=False;
  1122. If (P=0) then
  1123. begin
  1124. If CStyle then
  1125. begin
  1126. P:=Pos('->',APath);
  1127. CM:=P<>0;
  1128. end;
  1129. If (P=0) Then
  1130. P:=Length(APath)+1;
  1131. end;
  1132. Result:=Copy(APath,1,P-1);
  1133. Delete(APath,1,P+Ord(CM));
  1134. end;
  1135. Var
  1136. C : TComponent;
  1137. S : String;
  1138. begin
  1139. If (APath='') then
  1140. Result:=Nil
  1141. else
  1142. begin
  1143. Result:=Root;
  1144. While (APath<>'') And (Result<>Nil) do
  1145. begin
  1146. C:=Result;
  1147. S:=Uppercase(GetNextName);
  1148. Result:=C.FindComponent(S);
  1149. If (Result=Nil) And (S='OWNER') then
  1150. Result:=C;
  1151. end;
  1152. end;
  1153. end;
  1154. threadvar
  1155. GlobalLoaded, GlobalLists: TFpList;
  1156. procedure BeginGlobalLoading;
  1157. begin
  1158. if not Assigned(GlobalLists) then
  1159. GlobalLists := TFpList.Create;
  1160. GlobalLists.Add(GlobalLoaded);
  1161. GlobalLoaded := TFpList.Create;
  1162. end;
  1163. { Notify all global components that they have been loaded completely }
  1164. procedure NotifyGlobalLoading;
  1165. var
  1166. i: Integer;
  1167. begin
  1168. for i := 0 to GlobalLoaded.Count - 1 do
  1169. TComponent(GlobalLoaded[i]).Loaded;
  1170. end;
  1171. procedure EndGlobalLoading;
  1172. begin
  1173. { Free the memory occupied by BeginGlobalLoading }
  1174. GlobalLoaded.Free;
  1175. GlobalLoaded := TFpList(GlobalLists.Last);
  1176. GlobalLists.Delete(GlobalLists.Count - 1);
  1177. if GlobalLists.Count = 0 then
  1178. begin
  1179. GlobalLists.Free;
  1180. GlobalLists := nil;
  1181. end;
  1182. end;
  1183. function CollectionsEqual(C1, C2: TCollection): Boolean;
  1184. begin
  1185. // !!!: Implement this
  1186. CollectionsEqual:=false;
  1187. end;
  1188. function CollectionsEqual(C1, C2: TCollection; Owner1, Owner2: TComponent): Boolean;
  1189. procedure stream_collection(s : tstream;c : tcollection;o : tcomponent);
  1190. var
  1191. w : twriter;
  1192. begin
  1193. w:=twriter.create(s,4096);
  1194. try
  1195. w.root:=o;
  1196. w.flookuproot:=o;
  1197. w.writecollection(c);
  1198. finally
  1199. w.free;
  1200. end;
  1201. end;
  1202. var
  1203. s1,s2 : tmemorystream;
  1204. begin
  1205. result:=false;
  1206. if (c1.classtype<>c2.classtype) or
  1207. (c1.count<>c2.count) then
  1208. exit;
  1209. if c1.count = 0 then
  1210. begin
  1211. result:= true;
  1212. exit;
  1213. end;
  1214. s1:=tmemorystream.create;
  1215. try
  1216. s2:=tmemorystream.create;
  1217. try
  1218. stream_collection(s1,c1,owner1);
  1219. stream_collection(s2,c2,owner2);
  1220. result:=(s1.size=s2.size) and (CompareChar(s1.memory^,s2.memory^,s1.size)=0);
  1221. finally
  1222. s2.free;
  1223. end;
  1224. finally
  1225. s1.free;
  1226. end;
  1227. end;
  1228. { Object conversion routines }
  1229. type
  1230. CharToOrdFuncty = Function(var charpo: Pointer): Cardinal;
  1231. function CharToOrd(var P: Pointer): Cardinal;
  1232. begin
  1233. result:= ord(pchar(P)^);
  1234. inc(pchar(P));
  1235. end;
  1236. function WideCharToOrd(var P: Pointer): Cardinal;
  1237. begin
  1238. result:= ord(pwidechar(P)^);
  1239. inc(pwidechar(P));
  1240. end;
  1241. function Utf8ToOrd(var P:Pointer): Cardinal;
  1242. begin
  1243. // Should also check for illegal utf8 combinations
  1244. Result := Ord(PChar(P)^);
  1245. Inc(P);
  1246. if (Result and $80) <> 0 then
  1247. if (Ord(Result) and %11100000) = %11000000 then begin
  1248. Result := ((Result and %00011111) shl 6)
  1249. or (ord(PChar(P)^) and %00111111);
  1250. Inc(P);
  1251. end else if (Ord(Result) and %11110000) = %11100000 then begin
  1252. Result := ((Result and %00011111) shl 12)
  1253. or ((ord(PChar(P)^) and %00111111) shl 6)
  1254. or (ord((PChar(P)+1)^) and %00111111);
  1255. Inc(P,2);
  1256. end else begin
  1257. Result := ((ord(Result) and %00011111) shl 18)
  1258. or ((ord(PChar(P)^) and %00111111) shl 12)
  1259. or ((ord((PChar(P)+1)^) and %00111111) shl 6)
  1260. or (ord((PChar(P)+2)^) and %00111111);
  1261. Inc(P,3);
  1262. end;
  1263. end;
  1264. procedure ObjectBinaryToText(Input, Output: TStream; Encoding: TObjectTextEncoding);
  1265. procedure OutStr(s: String);
  1266. begin
  1267. if Length(s) > 0 then
  1268. Output.Write(s[1], Length(s));
  1269. end;
  1270. procedure OutLn(s: String);
  1271. begin
  1272. OutStr(s + LineEnding);
  1273. end;
  1274. procedure Outchars(P, LastP : Pointer; CharToOrdFunc: CharToOrdFuncty;
  1275. UseBytes: boolean = false);
  1276. var
  1277. res, NewStr: String;
  1278. w: Cardinal;
  1279. InString, NewInString: Boolean;
  1280. begin
  1281. if p = nil then begin
  1282. res:= '''''';
  1283. end
  1284. else
  1285. begin
  1286. res := '';
  1287. InString := False;
  1288. while P < LastP do
  1289. begin
  1290. NewInString := InString;
  1291. w := CharToOrdfunc(P);
  1292. if w = ord('''') then
  1293. begin //quote char
  1294. if not InString then
  1295. NewInString := True;
  1296. NewStr := '''''';
  1297. end
  1298. else if (Ord(w) >= 32) and ((Ord(w) < 127) or (UseBytes and (Ord(w)<256))) then
  1299. begin //printable ascii or bytes
  1300. if not InString then
  1301. NewInString := True;
  1302. NewStr := char(w);
  1303. end
  1304. else
  1305. begin //ascii control chars, non ascii
  1306. if InString then
  1307. NewInString := False;
  1308. NewStr := '#' + IntToStr(w);
  1309. end;
  1310. if NewInString <> InString then
  1311. begin
  1312. NewStr := '''' + NewStr;
  1313. InString := NewInString;
  1314. end;
  1315. res := res + NewStr;
  1316. end;
  1317. if InString then
  1318. res := res + '''';
  1319. end;
  1320. OutStr(res);
  1321. end;
  1322. procedure OutString(s: String);
  1323. begin
  1324. OutChars(Pointer(S),PChar(S)+Length(S),@CharToOrd,Encoding=oteLFM);
  1325. end;
  1326. procedure OutWString(W: WideString);
  1327. begin
  1328. OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
  1329. end;
  1330. procedure OutUString(W: UnicodeString);
  1331. begin
  1332. OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
  1333. end;
  1334. procedure OutUtf8Str(s: String);
  1335. begin
  1336. if Encoding=oteLFM then
  1337. OutChars(Pointer(S),PChar(S)+Length(S),@CharToOrd)
  1338. else
  1339. OutChars(Pointer(S),PChar(S)+Length(S),@Utf8ToOrd);
  1340. end;
  1341. function ReadWord : word; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1342. begin
  1343. Result:=Input.ReadWord;
  1344. Result:=LEtoN(Result);
  1345. end;
  1346. function ReadDWord : longword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1347. begin
  1348. Result:=Input.ReadDWord;
  1349. Result:=LEtoN(Result);
  1350. end;
  1351. function ReadQWord : qword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1352. begin
  1353. Input.ReadBuffer(Result,sizeof(Result));
  1354. Result:=LEtoN(Result);
  1355. end;
  1356. {$ifndef FPUNONE}
  1357. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1358. function ExtendedToDouble(e : pointer) : double;
  1359. var mant : qword;
  1360. exp : smallint;
  1361. sign : boolean;
  1362. d : qword;
  1363. begin
  1364. move(pbyte(e)[0],mant,8); //mantissa : bytes 0..7
  1365. move(pbyte(e)[8],exp,2); //exponent and sign: bytes 8..9
  1366. mant:=LEtoN(mant);
  1367. exp:=LetoN(word(exp));
  1368. sign:=(exp and $8000)<>0;
  1369. if sign then exp:=exp and $7FFF;
  1370. case exp of
  1371. 0 : mant:=0; //if denormalized, value is too small for double,
  1372. //so it's always zero
  1373. $7FFF : exp:=2047 //either infinity or NaN
  1374. else
  1375. begin
  1376. dec(exp,16383-1023);
  1377. if (exp>=-51) and (exp<=0) then //can be denormalized
  1378. begin
  1379. mant:=mant shr (-exp);
  1380. exp:=0;
  1381. end
  1382. else
  1383. if (exp<-51) or (exp>2046) then //exponent too large.
  1384. begin
  1385. Result:=0;
  1386. exit;
  1387. end
  1388. else //normalized value
  1389. mant:=mant shl 1; //hide most significant bit
  1390. end;
  1391. end;
  1392. d:=word(exp);
  1393. d:=d shl 52;
  1394. mant:=mant shr 12;
  1395. d:=d or mant;
  1396. if sign then d:=d or $8000000000000000;
  1397. Result:=pdouble(@d)^;
  1398. end;
  1399. {$ENDIF}
  1400. {$endif}
  1401. function ReadInt(ValueType: TValueType): Int64;
  1402. begin
  1403. case ValueType of
  1404. vaInt8: Result := ShortInt(Input.ReadByte);
  1405. vaInt16: Result := SmallInt(ReadWord);
  1406. vaInt32: Result := LongInt(ReadDWord);
  1407. vaInt64: Result := Int64(ReadQWord);
  1408. end;
  1409. end;
  1410. function ReadInt: Int64;
  1411. begin
  1412. Result := ReadInt(TValueType(Input.ReadByte));
  1413. end;
  1414. {$ifndef FPUNONE}
  1415. function ReadExtended : extended;
  1416. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1417. var ext : array[0..9] of byte;
  1418. {$ENDIF}
  1419. begin
  1420. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1421. Input.ReadBuffer(ext[0],10);
  1422. Result:=ExtendedToDouble(@(ext[0]));
  1423. {$ELSE}
  1424. Input.ReadBuffer(Result,sizeof(Result));
  1425. {$ENDIF}
  1426. end;
  1427. {$endif}
  1428. function ReadSStr: String;
  1429. var
  1430. len: Byte;
  1431. begin
  1432. len := Input.ReadByte;
  1433. SetLength(Result, len);
  1434. if (len > 0) then
  1435. Input.ReadBuffer(Result[1], len);
  1436. end;
  1437. function ReadLStr: String;
  1438. var
  1439. len: DWord;
  1440. begin
  1441. len := ReadDWord;
  1442. SetLength(Result, len);
  1443. if (len > 0) then
  1444. Input.ReadBuffer(Result[1], len);
  1445. end;
  1446. function ReadWStr: WideString;
  1447. var
  1448. len: DWord;
  1449. {$IFDEF ENDIAN_BIG}
  1450. i : integer;
  1451. {$ENDIF}
  1452. begin
  1453. len := ReadDWord;
  1454. SetLength(Result, len);
  1455. if (len > 0) then
  1456. begin
  1457. Input.ReadBuffer(Pointer(@Result[1])^, len*2);
  1458. {$IFDEF ENDIAN_BIG}
  1459. for i:=1 to len do
  1460. Result[i]:=widechar(SwapEndian(word(Result[i])));
  1461. {$ENDIF}
  1462. end;
  1463. end;
  1464. function ReadUStr: UnicodeString;
  1465. var
  1466. len: DWord;
  1467. {$IFDEF ENDIAN_BIG}
  1468. i : integer;
  1469. {$ENDIF}
  1470. begin
  1471. len := ReadDWord;
  1472. SetLength(Result, len);
  1473. if (len > 0) then
  1474. begin
  1475. Input.ReadBuffer(Pointer(@Result[1])^, len*2);
  1476. {$IFDEF ENDIAN_BIG}
  1477. for i:=1 to len do
  1478. Result[i]:=widechar(SwapEndian(word(Result[i])));
  1479. {$ENDIF}
  1480. end;
  1481. end;
  1482. procedure ReadPropList(indent: String);
  1483. procedure ProcessValue(ValueType: TValueType; Indent: String);
  1484. procedure ProcessBinary;
  1485. var
  1486. ToDo, DoNow, i: LongInt;
  1487. lbuf: array[0..31] of Byte;
  1488. s: String;
  1489. begin
  1490. ToDo := ReadDWord;
  1491. OutLn('{');
  1492. while ToDo > 0 do begin
  1493. DoNow := ToDo;
  1494. if DoNow > 32 then DoNow := 32;
  1495. Dec(ToDo, DoNow);
  1496. s := Indent + ' ';
  1497. Input.ReadBuffer(lbuf, DoNow);
  1498. for i := 0 to DoNow - 1 do
  1499. s := s + IntToHex(lbuf[i], 2);
  1500. OutLn(s);
  1501. end;
  1502. OutLn(indent + '}');
  1503. end;
  1504. var
  1505. s: String;
  1506. { len: LongInt; }
  1507. IsFirst: Boolean;
  1508. {$ifndef FPUNONE}
  1509. ext: Extended;
  1510. {$endif}
  1511. begin
  1512. case ValueType of
  1513. vaList: begin
  1514. OutStr('(');
  1515. IsFirst := True;
  1516. while True do begin
  1517. ValueType := TValueType(Input.ReadByte);
  1518. if ValueType = vaNull then break;
  1519. if IsFirst then begin
  1520. OutLn('');
  1521. IsFirst := False;
  1522. end;
  1523. OutStr(Indent + ' ');
  1524. ProcessValue(ValueType, Indent + ' ');
  1525. end;
  1526. OutLn(Indent + ')');
  1527. end;
  1528. vaInt8: OutLn(IntToStr(ShortInt(Input.ReadByte)));
  1529. vaInt16: OutLn( IntToStr(SmallInt(ReadWord)));
  1530. vaInt32: OutLn(IntToStr(LongInt(ReadDWord)));
  1531. vaInt64: OutLn(IntToStr(Int64(ReadQWord)));
  1532. {$ifndef FPUNONE}
  1533. vaExtended: begin
  1534. ext:=ReadExtended;
  1535. Str(ext,S);// Do not use localized strings.
  1536. OutLn(S);
  1537. end;
  1538. {$endif}
  1539. vaString: begin
  1540. OutString(ReadSStr);
  1541. OutLn('');
  1542. end;
  1543. vaIdent: OutLn(ReadSStr);
  1544. vaFalse: OutLn('False');
  1545. vaTrue: OutLn('True');
  1546. vaBinary: ProcessBinary;
  1547. vaSet: begin
  1548. OutStr('[');
  1549. IsFirst := True;
  1550. while True do begin
  1551. s := ReadSStr;
  1552. if Length(s) = 0 then break;
  1553. if not IsFirst then OutStr(', ');
  1554. IsFirst := False;
  1555. OutStr(s);
  1556. end;
  1557. OutLn(']');
  1558. end;
  1559. vaLString:
  1560. begin
  1561. OutString(ReadLStr);
  1562. OutLn('');
  1563. end;
  1564. vaWString:
  1565. begin
  1566. OutWString(ReadWStr);
  1567. OutLn('');
  1568. end;
  1569. vaUString:
  1570. begin
  1571. OutWString(ReadWStr);
  1572. OutLn('');
  1573. end;
  1574. vaNil:
  1575. OutLn('nil');
  1576. vaCollection: begin
  1577. OutStr('<');
  1578. while Input.ReadByte <> 0 do begin
  1579. OutLn(Indent);
  1580. Input.Seek(-1, soFromCurrent);
  1581. OutStr(indent + ' item');
  1582. ValueType := TValueType(Input.ReadByte);
  1583. if ValueType <> vaList then
  1584. OutStr('[' + IntToStr(ReadInt(ValueType)) + ']');
  1585. OutLn('');
  1586. ReadPropList(indent + ' ');
  1587. OutStr(indent + ' end');
  1588. end;
  1589. OutLn('>');
  1590. end;
  1591. {vaSingle: begin OutLn('!!Single!!'); exit end;
  1592. vaCurrency: begin OutLn('!!Currency!!'); exit end;
  1593. vaDate: begin OutLn('!!Date!!'); exit end;}
  1594. vaUTF8String: begin
  1595. OutUtf8Str(ReadLStr);
  1596. OutLn('');
  1597. end;
  1598. else
  1599. Raise EReadError.CreateFmt(SErrInvalidPropertyType,[Ord(ValueType)]);
  1600. end;
  1601. end;
  1602. begin
  1603. while Input.ReadByte <> 0 do begin
  1604. Input.Seek(-1, soFromCurrent);
  1605. OutStr(indent + ReadSStr + ' = ');
  1606. ProcessValue(TValueType(Input.ReadByte), Indent);
  1607. end;
  1608. end;
  1609. procedure ReadObject(indent: String);
  1610. var
  1611. b: Byte;
  1612. ObjClassName, ObjName: String;
  1613. ChildPos: LongInt;
  1614. begin
  1615. // Check for FilerFlags
  1616. b := Input.ReadByte;
  1617. if (b and $f0) = $f0 then begin
  1618. if (b and 2) <> 0 then ChildPos := ReadInt;
  1619. end else begin
  1620. b := 0;
  1621. Input.Seek(-1, soFromCurrent);
  1622. end;
  1623. ObjClassName := ReadSStr;
  1624. ObjName := ReadSStr;
  1625. OutStr(Indent);
  1626. if (b and 1) <> 0 then OutStr('inherited')
  1627. else
  1628. if (b and 4) <> 0 then OutStr('inline')
  1629. else OutStr('object');
  1630. OutStr(' ');
  1631. if ObjName <> '' then
  1632. OutStr(ObjName + ': ');
  1633. OutStr(ObjClassName);
  1634. if (b and 2) <> 0 then OutStr('[' + IntToStr(ChildPos) + ']');
  1635. OutLn('');
  1636. ReadPropList(indent + ' ');
  1637. while Input.ReadByte <> 0 do begin
  1638. Input.Seek(-1, soFromCurrent);
  1639. ReadObject(indent + ' ');
  1640. end;
  1641. OutLn(indent + 'end');
  1642. end;
  1643. type
  1644. PLongWord = ^LongWord;
  1645. const
  1646. signature: PChar = 'TPF0';
  1647. begin
  1648. if Input.ReadDWord <> PLongWord(Pointer(signature))^ then
  1649. raise EReadError.Create('Illegal stream image' {###SInvalidImage});
  1650. ReadObject('');
  1651. end;
  1652. procedure ObjectBinaryToText(Input, Output: TStream);
  1653. begin
  1654. ObjectBinaryToText(Input,Output,oteDFM);
  1655. end;
  1656. procedure ObjectTextToBinary(Input, Output: TStream);
  1657. var
  1658. parser: TParser;
  1659. procedure WriteWord(w : word); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1660. begin
  1661. w:=NtoLE(w);
  1662. Output.WriteWord(w);
  1663. end;
  1664. procedure WriteDWord(lw : longword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1665. begin
  1666. lw:=NtoLE(lw);
  1667. Output.WriteDWord(lw);
  1668. end;
  1669. procedure WriteQWord(qw : qword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1670. begin
  1671. qw:=NtoLE(qw);
  1672. Output.WriteBuffer(qw,sizeof(qword));
  1673. end;
  1674. {$ifndef FPUNONE}
  1675. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1676. procedure DoubleToExtended(d : double; e : pointer);
  1677. var mant : qword;
  1678. exp : smallint;
  1679. sign : boolean;
  1680. begin
  1681. mant:=(qword(d) and $000FFFFFFFFFFFFF) shl 12;
  1682. exp :=(qword(d) shr 52) and $7FF;
  1683. sign:=(qword(d) and $8000000000000000)<>0;
  1684. case exp of
  1685. 0 : begin
  1686. if mant<>0 then //denormalized value: hidden bit is 0. normalize it
  1687. begin
  1688. exp:=16383-1022;
  1689. while (mant and $8000000000000000)=0 do
  1690. begin
  1691. dec(exp);
  1692. mant:=mant shl 1;
  1693. end;
  1694. dec(exp); //don't shift, most significant bit is not hidden in extended
  1695. end;
  1696. end;
  1697. 2047 : exp:=$7FFF //either infinity or NaN
  1698. else
  1699. begin
  1700. inc(exp,16383-1023);
  1701. mant:=(mant shr 1) or $8000000000000000; //unhide hidden bit
  1702. end;
  1703. end;
  1704. if sign then exp:=exp or $8000;
  1705. mant:=NtoLE(mant);
  1706. exp:=NtoLE(word(exp));
  1707. move(mant,pbyte(e)[0],8); //mantissa : bytes 0..7
  1708. move(exp,pbyte(e)[8],2); //exponent and sign: bytes 8..9
  1709. end;
  1710. {$ENDIF}
  1711. procedure WriteExtended(e : extended);
  1712. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1713. var ext : array[0..9] of byte;
  1714. {$ENDIF}
  1715. begin
  1716. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1717. DoubleToExtended(e,@(ext[0]));
  1718. Output.WriteBuffer(ext[0],10);
  1719. {$ELSE}
  1720. Output.WriteBuffer(e,sizeof(e));
  1721. {$ENDIF}
  1722. end;
  1723. {$endif}
  1724. procedure WriteString(s: String);
  1725. var size : byte;
  1726. begin
  1727. if length(s)>255 then size:=255
  1728. else size:=length(s);
  1729. Output.WriteByte(size);
  1730. if Length(s) > 0 then
  1731. Output.WriteBuffer(s[1], size);
  1732. end;
  1733. procedure WriteLString(Const s: String);
  1734. begin
  1735. WriteDWord(Length(s));
  1736. if Length(s) > 0 then
  1737. Output.WriteBuffer(s[1], Length(s));
  1738. end;
  1739. procedure WriteWString(Const s: WideString);
  1740. var len : longword;
  1741. {$IFDEF ENDIAN_BIG}
  1742. i : integer;
  1743. ws : widestring;
  1744. {$ENDIF}
  1745. begin
  1746. len:=Length(s);
  1747. WriteDWord(len);
  1748. if len > 0 then
  1749. begin
  1750. {$IFDEF ENDIAN_BIG}
  1751. setlength(ws,len);
  1752. for i:=1 to len do
  1753. ws[i]:=widechar(SwapEndian(word(s[i])));
  1754. Output.WriteBuffer(ws[1], len*sizeof(widechar));
  1755. {$ELSE}
  1756. Output.WriteBuffer(s[1], len*sizeof(widechar));
  1757. {$ENDIF}
  1758. end;
  1759. end;
  1760. procedure WriteInteger(value: Int64);
  1761. begin
  1762. if (value >= -128) and (value <= 127) then begin
  1763. Output.WriteByte(Ord(vaInt8));
  1764. Output.WriteByte(byte(value));
  1765. end else if (value >= -32768) and (value <= 32767) then begin
  1766. Output.WriteByte(Ord(vaInt16));
  1767. WriteWord(word(value));
  1768. end else if (value >= -2147483648) and (value <= 2147483647) then begin
  1769. Output.WriteByte(Ord(vaInt32));
  1770. WriteDWord(longword(value));
  1771. end else begin
  1772. Output.WriteByte(ord(vaInt64));
  1773. WriteQWord(qword(value));
  1774. end;
  1775. end;
  1776. procedure ProcessWideString(const left : widestring);
  1777. var ws : widestring;
  1778. begin
  1779. ws:=left+parser.TokenWideString;
  1780. while parser.NextToken = '+' do
  1781. begin
  1782. parser.NextToken; // Get next string fragment
  1783. if not (parser.Token in [toString,toWString]) then
  1784. parser.CheckToken(toWString);
  1785. ws:=ws+parser.TokenWideString;
  1786. end;
  1787. Output.WriteByte(Ord(vaWstring));
  1788. WriteWString(ws);
  1789. end;
  1790. procedure ProcessProperty; forward;
  1791. procedure ProcessValue;
  1792. var
  1793. {$ifndef FPUNONE}
  1794. flt: Extended;
  1795. {$endif}
  1796. s: String;
  1797. stream: TMemoryStream;
  1798. begin
  1799. case parser.Token of
  1800. toInteger:
  1801. begin
  1802. WriteInteger(parser.TokenInt);
  1803. parser.NextToken;
  1804. end;
  1805. {$ifndef FPUNONE}
  1806. toFloat:
  1807. begin
  1808. Output.WriteByte(Ord(vaExtended));
  1809. flt := Parser.TokenFloat;
  1810. WriteExtended(flt);
  1811. parser.NextToken;
  1812. end;
  1813. {$endif}
  1814. toString:
  1815. begin
  1816. s := parser.TokenString;
  1817. while parser.NextToken = '+' do
  1818. begin
  1819. parser.NextToken; // Get next string fragment
  1820. case parser.Token of
  1821. toString : s:=s+parser.TokenString;
  1822. toWString : begin
  1823. ProcessWideString(WideString(s));
  1824. exit;
  1825. end
  1826. else parser.CheckToken(toString);
  1827. end;
  1828. end;
  1829. if (length(S)>255) then
  1830. begin
  1831. Output.WriteByte(Ord(vaLString));
  1832. WriteLString(S);
  1833. end
  1834. else
  1835. begin
  1836. Output.WriteByte(Ord(vaString));
  1837. WriteString(s);
  1838. end;
  1839. end;
  1840. toWString:
  1841. ProcessWideString('');
  1842. toSymbol:
  1843. begin
  1844. if CompareText(parser.TokenString, 'True') = 0 then
  1845. Output.WriteByte(Ord(vaTrue))
  1846. else if CompareText(parser.TokenString, 'False') = 0 then
  1847. Output.WriteByte(Ord(vaFalse))
  1848. else if CompareText(parser.TokenString, 'nil') = 0 then
  1849. Output.WriteByte(Ord(vaNil))
  1850. else
  1851. begin
  1852. Output.WriteByte(Ord(vaIdent));
  1853. WriteString(parser.TokenComponentIdent);
  1854. end;
  1855. Parser.NextToken;
  1856. end;
  1857. // Set
  1858. '[':
  1859. begin
  1860. parser.NextToken;
  1861. Output.WriteByte(Ord(vaSet));
  1862. if parser.Token <> ']' then
  1863. while True do
  1864. begin
  1865. parser.CheckToken(toSymbol);
  1866. WriteString(parser.TokenString);
  1867. parser.NextToken;
  1868. if parser.Token = ']' then
  1869. break;
  1870. parser.CheckToken(',');
  1871. parser.NextToken;
  1872. end;
  1873. Output.WriteByte(0);
  1874. parser.NextToken;
  1875. end;
  1876. // List
  1877. '(':
  1878. begin
  1879. parser.NextToken;
  1880. Output.WriteByte(Ord(vaList));
  1881. while parser.Token <> ')' do
  1882. ProcessValue;
  1883. Output.WriteByte(0);
  1884. parser.NextToken;
  1885. end;
  1886. // Collection
  1887. '<':
  1888. begin
  1889. parser.NextToken;
  1890. Output.WriteByte(Ord(vaCollection));
  1891. while parser.Token <> '>' do
  1892. begin
  1893. parser.CheckTokenSymbol('item');
  1894. parser.NextToken;
  1895. // ConvertOrder
  1896. Output.WriteByte(Ord(vaList));
  1897. while not parser.TokenSymbolIs('end') do
  1898. ProcessProperty;
  1899. parser.NextToken; // Skip 'end'
  1900. Output.WriteByte(0);
  1901. end;
  1902. Output.WriteByte(0);
  1903. parser.NextToken;
  1904. end;
  1905. // Binary data
  1906. '{':
  1907. begin
  1908. Output.WriteByte(Ord(vaBinary));
  1909. stream := TMemoryStream.Create;
  1910. try
  1911. parser.HexToBinary(stream);
  1912. WriteDWord(stream.Size);
  1913. Output.WriteBuffer(Stream.Memory^, stream.Size);
  1914. finally
  1915. stream.Free;
  1916. end;
  1917. parser.NextToken;
  1918. end;
  1919. else
  1920. parser.Error(SInvalidProperty);
  1921. end;
  1922. end;
  1923. procedure ProcessProperty;
  1924. var
  1925. name: String;
  1926. begin
  1927. // Get name of property
  1928. parser.CheckToken(toSymbol);
  1929. name := parser.TokenString;
  1930. while True do begin
  1931. parser.NextToken;
  1932. if parser.Token <> '.' then break;
  1933. parser.NextToken;
  1934. parser.CheckToken(toSymbol);
  1935. name := name + '.' + parser.TokenString;
  1936. end;
  1937. WriteString(name);
  1938. parser.CheckToken('=');
  1939. parser.NextToken;
  1940. ProcessValue;
  1941. end;
  1942. procedure ProcessObject;
  1943. var
  1944. Flags: Byte;
  1945. ObjectName, ObjectType: String;
  1946. ChildPos: Integer;
  1947. begin
  1948. if parser.TokenSymbolIs('OBJECT') then
  1949. Flags :=0 { IsInherited := False }
  1950. else begin
  1951. if parser.TokenSymbolIs('INHERITED') then
  1952. Flags := 1 { IsInherited := True; }
  1953. else begin
  1954. parser.CheckTokenSymbol('INLINE');
  1955. Flags := 4;
  1956. end;
  1957. end;
  1958. parser.NextToken;
  1959. parser.CheckToken(toSymbol);
  1960. ObjectName := '';
  1961. ObjectType := parser.TokenString;
  1962. parser.NextToken;
  1963. if parser.Token = ':' then begin
  1964. parser.NextToken;
  1965. parser.CheckToken(toSymbol);
  1966. ObjectName := ObjectType;
  1967. ObjectType := parser.TokenString;
  1968. parser.NextToken;
  1969. if parser.Token = '[' then begin
  1970. parser.NextToken;
  1971. ChildPos := parser.TokenInt;
  1972. parser.NextToken;
  1973. parser.CheckToken(']');
  1974. parser.NextToken;
  1975. Flags := Flags or 2;
  1976. end;
  1977. end;
  1978. if Flags <> 0 then begin
  1979. Output.WriteByte($f0 or Flags);
  1980. if (Flags and 2) <> 0 then
  1981. WriteInteger(ChildPos);
  1982. end;
  1983. WriteString(ObjectType);
  1984. WriteString(ObjectName);
  1985. // Convert property list
  1986. while not (parser.TokenSymbolIs('END') or
  1987. parser.TokenSymbolIs('OBJECT') or
  1988. parser.TokenSymbolIs('INHERITED') or
  1989. parser.TokenSymbolIs('INLINE')) do
  1990. ProcessProperty;
  1991. Output.WriteByte(0); // Terminate property list
  1992. // Convert child objects
  1993. while not parser.TokenSymbolIs('END') do ProcessObject;
  1994. parser.NextToken; // Skip end token
  1995. Output.WriteByte(0); // Terminate property list
  1996. end;
  1997. const
  1998. signature: PChar = 'TPF0';
  1999. begin
  2000. parser := TParser.Create(Input);
  2001. try
  2002. Output.WriteBuffer(signature[0], 4);
  2003. ProcessObject;
  2004. finally
  2005. parser.Free;
  2006. end;
  2007. end;
  2008. procedure ObjectResourceToText(Input, Output: TStream);
  2009. begin
  2010. Input.ReadResHeader;
  2011. ObjectBinaryToText(Input, Output);
  2012. end;
  2013. procedure ObjectTextToResource(Input, Output: TStream);
  2014. var
  2015. StartPos, FixupInfo: LongInt;
  2016. parser: TParser;
  2017. name: String;
  2018. begin
  2019. // Get form type name
  2020. StartPos := Input.Position;
  2021. parser := TParser.Create(Input);
  2022. try
  2023. if not parser.TokenSymbolIs('OBJECT') then parser.CheckTokenSymbol('INHERITED');
  2024. parser.NextToken;
  2025. parser.CheckToken(toSymbol);
  2026. parser.NextToken;
  2027. parser.CheckToken(':');
  2028. parser.NextToken;
  2029. parser.CheckToken(toSymbol);
  2030. name := parser.TokenString;
  2031. finally
  2032. parser.Free;
  2033. Input.Position := StartPos;
  2034. end;
  2035. name := UpperCase(name);
  2036. Output.WriteResourceHeader(name,FixupInfo); // Write resource header
  2037. ObjectTextToBinary(Input, Output); // Convert the stuff!
  2038. Output.FixupResourceHeader(FixupInfo); // Insert real resource data size
  2039. end;
  2040. { Utility routines }
  2041. function LineStart(Buffer, BufPos: PChar): PChar;
  2042. begin
  2043. Result := BufPos;
  2044. while Result > Buffer do begin
  2045. Dec(Result);
  2046. if Result[0] = #10 then break;
  2047. end;
  2048. end;
  2049. procedure CommonInit;
  2050. begin
  2051. SynchronizeTimeoutEvent:=RtlEventCreate;
  2052. InitCriticalSection(ThreadQueueLock);
  2053. MainThreadID:=GetCurrentThreadID;
  2054. ExternalThreads := TThreadList.Create;
  2055. TThread.FProcessorCount := CPUCount;
  2056. InitCriticalsection(ResolveSection);
  2057. InitHandlerList:=Nil;
  2058. FindGlobalComponentList:=nil;
  2059. IntConstList := TThreadList.Create;
  2060. ClassList := TThreadList.Create;
  2061. ClassAliasList := TStringList.Create;
  2062. { on unix this maps to a simple rw synchornizer }
  2063. GlobalNameSpace := TMultiReadExclusiveWriteSynchronizer.Create;
  2064. RegisterInitComponentHandler(TComponent,@DefaultInitHandler);
  2065. end;
  2066. procedure CommonCleanup;
  2067. var
  2068. i: Integer;
  2069. tmpentry: TThread.PThreadQueueEntry;
  2070. begin
  2071. GlobalNameSpace.BeginWrite;
  2072. with IntConstList.LockList do
  2073. try
  2074. for i := 0 to Count - 1 do
  2075. TIntConst(Items[I]).Free;
  2076. finally
  2077. IntConstList.UnlockList;
  2078. end;
  2079. IntConstList.Free;
  2080. ClassList.Free;
  2081. ClassAliasList.Free;
  2082. RemoveFixupReferences(nil, '');
  2083. DoneCriticalsection(ResolveSection);
  2084. GlobalLists.Free;
  2085. ComponentPages.Free;
  2086. FreeAndNil(NeedResolving);
  2087. { GlobalNameSpace is an interface so this is enough }
  2088. GlobalNameSpace:=nil;
  2089. if (InitHandlerList<>Nil) then
  2090. for i := 0 to InitHandlerList.Count - 1 do
  2091. TInitHandler(InitHandlerList.Items[I]).Free;
  2092. InitHandlerList.Free;
  2093. InitHandlerList:=Nil;
  2094. FindGlobalComponentList.Free;
  2095. FindGlobalComponentList:=nil;
  2096. ExternalThreadsCleanup:=True;
  2097. with ExternalThreads.LockList do
  2098. try
  2099. for i := 0 to Count - 1 do
  2100. TThread(Items[i]).Free;
  2101. finally
  2102. ExternalThreads.UnlockList;
  2103. end;
  2104. FreeAndNil(ExternalThreads);
  2105. RtlEventDestroy(SynchronizeTimeoutEvent);
  2106. { clean up the queue, but keep in mind that the entries used for Synchronize
  2107. are owned by the corresponding TThread }
  2108. while Assigned(ThreadQueueHead) do begin
  2109. tmpentry := ThreadQueueHead;
  2110. ThreadQueueHead := tmpentry^.Next;
  2111. if not Assigned(tmpentry^.SyncEvent) then
  2112. Dispose(tmpentry);
  2113. end;
  2114. DoneCriticalSection(ThreadQueueLock);
  2115. end;
  2116. { TFiler implementation }
  2117. {$i filer.inc}
  2118. { TReader implementation }
  2119. {$i reader.inc}
  2120. { TWriter implementations }
  2121. {$i writer.inc}
  2122. {$i twriter.inc}