http://www.techques.com/question/1-4073197/How-do-I-send-and-handle-message-between-TService-parent-thread-and-child-thread?

I took a look at OmniThreadLibrary and it looked like overkill for my purposes.

I wrote a simple library I call TCommThread.

It allows you to pass data back to the main thread without worrying about

any of the complexities of threads or Windows messages.

Here's the code if you'd like to try it.

CommThread Library:

  1. unit Threading.CommThread;
  2.  
  3. interface
  4.  
  5. uses
  6. Classes, SysUtils, ExtCtrls, SyncObjs, Generics.Collections, DateUtils;
  7.  
  8. const
  9. CTID_USER = ;
  10. PRM_USER = ;
  11.  
  12. CTID_STATUS = ;
  13. CTID_PROGRESS = ;
  14.  
  15. type
  16. TThreadParams = class(TDictionary<String, Variant>);
  17. TThreadObjects = class(TDictionary<String, TObject>);
  18.  
  19. TCommThreadParams = class(TObject)
  20. private
  21. FThreadParams: TThreadParams;
  22. FThreadObjects: TThreadObjects;
  23. public
  24. constructor Create;
  25. destructor Destroy; override;
  26.  
  27. procedure Clear;
  28.  
  29. function GetParam(const ParamName: String): Variant;
  30. function SetParam(const ParamName: String; ParamValue: Variant): TCommThreadParams;
  31. function GetObject(const ObjectName: String): TObject;
  32. function SetObject(const ObjectName: String; Obj: TObject): TCommThreadParams;
  33. end;
  34.  
  35. TCommQueueItem = class(TObject)
  36. private
  37. FSender: TObject;
  38. FMessageId: Integer;
  39. FCommThreadParams: TCommThreadParams;
  40. public
  41. destructor Destroy; override;
  42.  
  43. property Sender: TObject read FSender write FSender;
  44. property MessageId: Integer read FMessageId write FMessageId;
  45. property CommThreadParams: TCommThreadParams read FCommThreadParams write FCommThreadParams;
  46. end;
  47.  
  48. TCommQueue = class(TQueue<TCommQueueItem>);
  49.  
  50. ICommDispatchReceiver = interface
  51. ['{A4E2C9D1-E4E8-497D-A9BF-FAFE2D3A7C49}']
  52. procedure QueueMessage(Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams);
  53. procedure CommThreadTerminated(Sender: TObject);
  54. function Cancelled: Boolean;
  55. end;
  56.  
  57. TCommThread = class(TThread)
  58. protected
  59. FCommThreadParams: TCommThreadParams;
  60. FCommDispatchReceiver: ICommDispatchReceiver;
  61. FName: String;
  62. FProgressFrequency: Integer;
  63. FNextSendTime: TDateTime;
  64.  
  65. procedure SendStatusMessage(const StatusText: String; StatusType: Integer = ); virtual;
  66. procedure SendProgressMessage(ProgressID: Int64; Progress, ProgressMax: Integer; AlwaysSend: Boolean = TRUE); virtual;
  67. public
  68. constructor Create(CommDispatchReceiver: TObject); reintroduce; virtual;
  69. destructor Destroy; override;
  70.  
  71. function SetParam(const ParamName: String; ParamValue: Variant): TCommThread;
  72. function GetParam(const ParamName: String): Variant;
  73. function SetObject(const ObjectName: String; Obj: TObject): TCommThread;
  74. function GetObject(const ObjectName: String): TObject;
  75. procedure SendCommMessage(MessageId: Integer; CommThreadParams: TCommThreadParams); virtual;
  76.  
  77. property Name: String read FName;
  78. end;
  79.  
  80. TCommThreadClass = Class of TCommThread;
  81.  
  82. TCommThreadQueue = class(TObjectList<TCommThread>);
  83.  
  84. TCommThreadDispatchState = (
  85. ctsIdle,
  86. ctsActive,
  87. ctsTerminating
  88. );
  89.  
  90. TOnReceiveThreadMessage = procedure(Source, Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams) of object;
  91. TOnStateChange = procedure(Sender: TObject; State: TCommThreadDispatchState) of object;
  92. TOnStatus = procedure(Source, Sender: TObject; const ID: String; StatusText: String; StatusType: Integer) of object;
  93. TOnProgress = procedure(Source, Sender: TObject; const ID: String; Progress, ProgressMax: Integer) of object;
  94.  
  95. TBaseCommThreadDispatch = class(TComponent, ICommDispatchReceiver)
  96. private
  97. FProcessQueueTimer: TTimer;
  98. FCSReceiveMessage: TCriticalSection;
  99. FCSCommThreads: TCriticalSection;
  100. FCommQueue: TCommQueue;
  101. FActiveThreads: TList;
  102. FCommThreadClass: TCommThreadClass;
  103. FCommThreadDispatchState: TCommThreadDispatchState;
  104.  
  105. function CreateThread(const ThreadName: String = ''): TCommThread;
  106. function GetActiveThreadCount: Integer;
  107. function GetStateText: String;
  108. protected
  109. FOnReceiveThreadMessage: TOnReceiveThreadMessage;
  110. FOnStateChange: TOnStateChange;
  111. FOnStatus: TOnStatus;
  112. FOnProgress: TOnProgress;
  113. FManualMessageQueue: Boolean;
  114. FProgressFrequency: Integer;
  115.  
  116. procedure SetManualMessageQueue(const Value: Boolean);
  117. procedure SetProcessQueueTimerInterval(const Value: Integer);
  118. procedure SetCommThreadDispatchState(const Value: TCommThreadDispatchState);
  119. procedure QueueMessage(Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams);
  120. procedure OnProcessQueueTimer(Sender: TObject);
  121. function GetProcessQueueTimerInterval: Integer;
  122.  
  123. procedure CommThreadTerminated(Sender: TObject); virtual;
  124. function Finished: Boolean; virtual;
  125.  
  126. procedure DoOnReceiveThreadMessage(Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams); virtual;
  127. procedure DoOnStateChange; virtual;
  128.  
  129. procedure TerminateActiveThreads;
  130.  
  131. property OnReceiveThreadMessage: TOnReceiveThreadMessage read FOnReceiveThreadMessage write FOnReceiveThreadMessage;
  132. property OnStateChange: TOnStateChange read FOnStateChange write FOnStateChange;
  133. property OnStatus: TOnStatus read FOnStatus write FOnStatus;
  134. property OnProgress: TOnProgress read FOnProgress write FOnProgress;
  135.  
  136. property ProgressFrequency: Integer read FProgressFrequency write FProgressFrequency;
  137. property ProcessQueueTimerInterval: Integer read GetProcessQueueTimerInterval write SetProcessQueueTimerInterval;
  138. property ManualMessageQueue: Boolean read FManualMessageQueue write SetManualMessageQueue;
  139. property CommThreadDispatchState: TCommThreadDispatchState read FCommThreadDispatchState write SetCommThreadDispatchState;
  140. public
  141. constructor Create(AOwner: TComponent); override;
  142. destructor Destroy; override;
  143.  
  144. function NewThread(const ThreadName: String = ''): TCommThread; virtual;
  145. procedure ProcessMessageQueue; virtual;
  146. procedure Stop; virtual;
  147. function State: TCommThreadDispatchState;
  148. function Cancelled: Boolean;
  149.  
  150. property ActiveThreadCount: Integer read GetActiveThreadCount;
  151. property StateText: String read GetStateText;
  152.  
  153. property CommThreadClass: TCommThreadClass read FCommThreadClass write FCommThreadClass;
  154. end;
  155.  
  156. TCommThreadDispatch = class(TBaseCommThreadDispatch)
  157. published
  158. property OnReceiveThreadMessage: TOnReceiveThreadMessage read FOnReceiveThreadMessage write FOnReceiveThreadMessage;
  159. property OnStateChange: TOnStateChange read FOnStateChange write FOnStateChange;
  160.  
  161. property ProgressFrequency: Integer read FProgressFrequency write FProgressFrequency;
  162. property ProcessQueueTimerInterval: Integer read GetProcessQueueTimerInterval write SetProcessQueueTimerInterval;
  163. property ManualMessageQueue: Boolean read FManualMessageQueue write SetManualMessageQueue;
  164. end;
  165.  
  166. TBaseStatusCommThreadDispatch = class(TBaseCommThreadDispatch)
  167. protected
  168. FOnStatus: TOnStatus;
  169. FOnProgress: TOnProgress;
  170.  
  171. procedure DoOnReceiveThreadMessage(Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams); override;
  172.  
  173. procedure DoOnStatus(Sender: TObject;const ID: String; const StatusText: String; StatusType: Integer); virtual;
  174. procedure DoOnProgress(Sender: TObject; const ID: String; Progress, ProgressMax: Integer); virtual;
  175.  
  176. property OnStatus: TOnStatus read FOnStatus write FOnStatus;
  177. property OnProgress: TOnProgress read FOnProgress write FOnProgress;
  178. end;
  179.  
  180. TStatusCommThreadDispatch = class(TBaseStatusCommThreadDispatch)
  181. published
  182. property OnReceiveThreadMessage: TOnReceiveThreadMessage read FOnReceiveThreadMessage write FOnReceiveThreadMessage;
  183. property OnStateChange: TOnStateChange read FOnStateChange write FOnStateChange;
  184. property OnStatus: TOnStatus read FOnStatus write FOnStatus;
  185. property OnProgress: TOnProgress read FOnProgress write FOnProgress;
  186.  
  187. property ProgressFrequency: Integer read FProgressFrequency write FProgressFrequency;
  188. property ProcessQueueTimerInterval: Integer read GetProcessQueueTimerInterval write SetProcessQueueTimerInterval;
  189. property ManualMessageQueue: Boolean read FManualMessageQueue write SetManualMessageQueue;
  190. end;
  191.  
  192. implementation
  193.  
  194. const
  195. PRM_STATUS_TEXT = 'Status';
  196. PRM_STATUS_TYPE = 'Type';
  197. PRM_PROGRESS_ID = 'ProgressID';
  198. PRM_PROGRESS = 'Progess';
  199. PRM_PROGRESS_MAX = 'ProgressMax';
  200.  
  201. resourcestring
  202. StrCommReceiverMustSupportInterface = 'CommDispatchReceiver must support ICommDispatchReceiver interface';
  203. StrSenderMustBeATCommThread = 'Sender must be a TCommThread';
  204. StrUnableToFindTerminatedThread = 'Unable to find the terminated thread';
  205. StrIdle = 'Idle';
  206. StrTerminating = 'Terminating';
  207. StrActive = 'Active';
  208.  
  209. { TCommThread }
  210.  
  211. constructor TCommThread.Create(CommDispatchReceiver: TObject);
  212. begin
  213. Assert(Supports(CommDispatchReceiver, ICommDispatchReceiver, FCommDispatchReceiver), StrCommReceiverMustSupportInterface);
  214.  
  215. inherited Create(TRUE);
  216.  
  217. FCommThreadParams := TCommThreadParams.Create;
  218. end;
  219.  
  220. destructor TCommThread.Destroy;
  221. begin
  222. FCommDispatchReceiver.CommThreadTerminated(Self);
  223.  
  224. FreeAndNil(FCommThreadParams);
  225.  
  226. inherited;
  227. end;
  228.  
  229. function TCommThread.GetObject(const ObjectName: String): TObject;
  230. begin
  231. Result := FCommThreadParams.GetObject(ObjectName);
  232. end;
  233.  
  234. function TCommThread.GetParam(const ParamName: String): Variant;
  235. begin
  236. Result := FCommThreadParams.GetParam(ParamName);
  237. end;
  238.  
  239. procedure TCommThread.SendCommMessage(MessageId: Integer;
  240. CommThreadParams: TCommThreadParams);
  241. begin
  242. FCommDispatchReceiver.QueueMessage(Self, MessageId, CommThreadParams);
  243. end;
  244.  
  245. procedure TCommThread.SendProgressMessage(ProgressID: Int64; Progress,
  246. ProgressMax: Integer; AlwaysSend: Boolean);
  247. begin
  248. if (AlwaysSend) or (now > FNextSendTime) then
  249. begin
  250. // Send a status message to the comm receiver
  251. SendCommMessage(CTID_PROGRESS, TCommThreadParams.Create
  252. .SetParam(PRM_PROGRESS_ID, ProgressID)
  253. .SetParam(PRM_PROGRESS, Progress)
  254. .SetParam(PRM_PROGRESS_MAX, ProgressMax));
  255.  
  256. if not AlwaysSend then
  257. FNextSendTime := now + (FProgressFrequency * OneMillisecond);
  258. end;
  259. end;
  260.  
  261. procedure TCommThread.SendStatusMessage(const StatusText: String;
  262. StatusType: Integer);
  263. begin
  264. // Send a status message to the comm receiver
  265. SendCommMessage(CTID_STATUS, TCommThreadParams.Create
  266. .SetParam(PRM_STATUS_TEXT, StatusText)
  267. .SetParam(PRM_STATUS_TYPE, StatusType));
  268. end;
  269.  
  270. function TCommThread.SetObject(const ObjectName: String;
  271. Obj: TObject): TCommThread;
  272. begin
  273. Result := Self;
  274.  
  275. FCommThreadParams.SetObject(ObjectName, Obj);
  276. end;
  277.  
  278. function TCommThread.SetParam(const ParamName: String;
  279. ParamValue: Variant): TCommThread;
  280. begin
  281. Result := Self;
  282.  
  283. FCommThreadParams.SetParam(ParamName, ParamValue);
  284. end;
  285.  
  286. { TCommThreadDispatch }
  287.  
  288. function TBaseCommThreadDispatch.Cancelled: Boolean;
  289. begin
  290. Result := State = ctsTerminating;
  291. end;
  292.  
  293. procedure TBaseCommThreadDispatch.CommThreadTerminated(Sender: TObject);
  294. var
  295. idx: Integer;
  296. begin
  297. FCSCommThreads.Enter;
  298. try
  299. Assert(Sender is TCommThread, StrSenderMustBeATCommThread);
  300.  
  301. // Find the thread in the active thread list
  302. idx := FActiveThreads.IndexOf(Sender);
  303.  
  304. Assert(idx <> -, StrUnableToFindTerminatedThread);
  305.  
  306. // if we find it, remove it (we should always find it)
  307. FActiveThreads.Delete(idx);
  308. finally
  309. FCSCommThreads.Leave;
  310. end;
  311. end;
  312.  
  313. constructor TBaseCommThreadDispatch.Create(AOwner: TComponent);
  314. begin
  315. inherited;
  316.  
  317. FCommThreadClass := TCommThread;
  318.  
  319. FProcessQueueTimer := TTimer.Create(nil);
  320. FProcessQueueTimer.Enabled := FALSE;
  321. FProcessQueueTimer.Interval := ;
  322. FProcessQueueTimer.OnTimer := OnProcessQueueTimer;
  323. FProgressFrequency := ;
  324.  
  325. FCommQueue := TCommQueue.Create;
  326.  
  327. FActiveThreads := TList.Create;
  328.  
  329. FCSReceiveMessage := TCriticalSection.Create;
  330. FCSCommThreads := TCriticalSection.Create;
  331. end;
  332.  
  333. destructor TBaseCommThreadDispatch.Destroy;
  334. begin
  335. // Stop the queue timer
  336. FProcessQueueTimer.Enabled := FALSE;
  337.  
  338. TerminateActiveThreads;
  339.  
  340. // Pump the queue while there are active threads
  341. while CommThreadDispatchState <> ctsIdle do
  342. begin
  343. ProcessMessageQueue;
  344.  
  345. sleep();
  346. end;
  347.  
  348. // Free everything
  349. FreeAndNil(FProcessQueueTimer);
  350. FreeAndNil(FCommQueue);
  351. FreeAndNil(FCSReceiveMessage);
  352. FreeAndNil(FCSCommThreads);
  353. FreeAndNil(FActiveThreads);
  354.  
  355. inherited;
  356. end;
  357.  
  358. procedure TBaseCommThreadDispatch.DoOnReceiveThreadMessage(Sender: TObject;
  359. MessageId: Integer; CommThreadParams: TCommThreadParams);
  360. begin
  361. // Don't send the messages if we're being destroyed
  362. if not (csDestroying in ComponentState) then
  363. begin
  364. if Assigned(FOnReceiveThreadMessage) then
  365. FOnReceiveThreadMessage(Self, Sender, MessageId, CommThreadParams);
  366. end;
  367. end;
  368.  
  369. procedure TBaseCommThreadDispatch.DoOnStateChange;
  370. begin
  371. if (Assigned(FOnStateChange)) and (not (csDestroying in ComponentState)) then
  372. FOnStateChange(Self, FCommThreadDispatchState);
  373. end;
  374.  
  375. function TBaseCommThreadDispatch.GetActiveThreadCount: Integer;
  376. begin
  377. Result := FActiveThreads.Count;
  378. end;
  379.  
  380. function TBaseCommThreadDispatch.GetProcessQueueTimerInterval: Integer;
  381. begin
  382. Result := FProcessQueueTimer.Interval;
  383. end;
  384.  
  385. function TBaseCommThreadDispatch.GetStateText: String;
  386. begin
  387. case State of
  388. ctsIdle: Result := StrIdle;
  389. ctsTerminating: Result := StrTerminating;
  390. ctsActive: Result := StrActive;
  391. end;
  392. end;
  393.  
  394. function TBaseCommThreadDispatch.NewThread(const ThreadName: String): TCommThread;
  395. begin
  396. if FCommThreadDispatchState = ctsTerminating then
  397. Result := nil
  398. else
  399. begin
  400. // Make sure we're active
  401. if CommThreadDispatchState = ctsIdle then
  402. CommThreadDispatchState := ctsActive;
  403.  
  404. Result := CreateThread(ThreadName);
  405.  
  406. FActiveThreads.Add(Result);
  407.  
  408. if ThreadName = '' then
  409. Result.FName := IntToStr(Integer(Result))
  410. else
  411. Result.FName := ThreadName;
  412.  
  413. Result.FProgressFrequency := FProgressFrequency;
  414. end;
  415. end;
  416.  
  417. function TBaseCommThreadDispatch.CreateThread(
  418. const ThreadName: String): TCommThread;
  419. begin
  420. Result := FCommThreadClass.Create(Self);
  421.  
  422. Result.FreeOnTerminate := TRUE;
  423. end;
  424.  
  425. procedure TBaseCommThreadDispatch.OnProcessQueueTimer(Sender: TObject);
  426. begin
  427. ProcessMessageQueue;
  428. end;
  429.  
  430. procedure TBaseCommThreadDispatch.ProcessMessageQueue;
  431. var
  432. CommQueueItem: TCommQueueItem;
  433. begin
  434. if FCommThreadDispatchState in [ctsActive, ctsTerminating] then
  435. begin
  436. if FCommQueue.Count > then
  437. begin
  438. FCSReceiveMessage.Enter;
  439. try
  440. CommQueueItem := FCommQueue.Dequeue;
  441.  
  442. while Assigned(CommQueueItem) do
  443. begin
  444. try
  445. DoOnReceiveThreadMessage(CommQueueItem.Sender, CommQueueItem.MessageId, CommQueueItem.CommThreadParams);
  446. finally
  447. FreeAndNil(CommQueueItem);
  448. end;
  449.  
  450. if FCommQueue.Count > then
  451. CommQueueItem := FCommQueue.Dequeue;
  452. end;
  453. finally
  454. FCSReceiveMessage.Leave
  455. end;
  456. end;
  457.  
  458. if Finished then
  459. begin
  460. FCommThreadDispatchState := ctsIdle;
  461.  
  462. DoOnStateChange;
  463. end;
  464. end;
  465. end;
  466.  
  467. function TBaseCommThreadDispatch.Finished: Boolean;
  468. begin
  469. Result := FActiveThreads.Count = ;
  470. end;
  471.  
  472. procedure TBaseCommThreadDispatch.QueueMessage(Sender: TObject; MessageId: Integer;
  473. CommThreadParams: TCommThreadParams);
  474. var
  475. CommQueueItem: TCommQueueItem;
  476. begin
  477. FCSReceiveMessage.Enter;
  478. try
  479. CommQueueItem := TCommQueueItem.Create;
  480. CommQueueItem.Sender := Sender;
  481. CommQueueItem.MessageId := MessageId;
  482. CommQueueItem.CommThreadParams := CommThreadParams;
  483.  
  484. FCommQueue.Enqueue(CommQueueItem);
  485. finally
  486. FCSReceiveMessage.Leave
  487. end;
  488. end;
  489.  
  490. procedure TBaseCommThreadDispatch.SetCommThreadDispatchState(
  491. const Value: TCommThreadDispatchState);
  492. begin
  493. if FCommThreadDispatchState <> ctsTerminating then
  494. begin
  495. if Value = ctsActive then
  496. begin
  497. if not FManualMessageQueue then
  498. FProcessQueueTimer.Enabled := TRUE;
  499. end
  500. else
  501. TerminateActiveThreads;
  502. end;
  503.  
  504. FCommThreadDispatchState := Value;
  505.  
  506. DoOnStateChange;
  507. end;
  508.  
  509. procedure TBaseCommThreadDispatch.SetManualMessageQueue(const Value: Boolean);
  510. begin
  511. FManualMessageQueue := Value;
  512. end;
  513.  
  514. procedure TBaseCommThreadDispatch.SetProcessQueueTimerInterval(const Value: Integer);
  515. begin
  516. FProcessQueueTimer.Interval := Value;
  517. end;
  518.  
  519. function TBaseCommThreadDispatch.State: TCommThreadDispatchState;
  520. begin
  521. Result := FCommThreadDispatchState;
  522. end;
  523.  
  524. procedure TBaseCommThreadDispatch.Stop;
  525. begin
  526. if CommThreadDispatchState = ctsActive then
  527. TerminateActiveThreads;
  528. end;
  529.  
  530. procedure TBaseCommThreadDispatch.TerminateActiveThreads;
  531. var
  532. i: Integer;
  533. begin
  534. if FCommThreadDispatchState = ctsActive then
  535. begin
  536. // Lock threads
  537. FCSCommThreads.Acquire;
  538. try
  539. FCommThreadDispatchState := ctsTerminating;
  540.  
  541. DoOnStateChange;
  542.  
  543. // Terminate each thread in turn
  544. for i := to pred(FActiveThreads.Count) do
  545. TCommThread(FActiveThreads[i]).Terminate;
  546. finally
  547. FCSCommThreads.Release;
  548. end;
  549. end;
  550. end;
  551.  
  552. { TCommThreadParams }
  553.  
  554. procedure TCommThreadParams.Clear;
  555. begin
  556. FThreadParams.Clear;
  557. FThreadObjects.Clear;
  558. end;
  559.  
  560. constructor TCommThreadParams.Create;
  561. begin
  562. FThreadParams := TThreadParams.Create;
  563. FThreadObjects := TThreadObjects.Create;
  564. end;
  565.  
  566. destructor TCommThreadParams.Destroy;
  567. begin
  568. FreeAndNil(FThreadParams);
  569. FreeAndNil(FThreadObjects);
  570.  
  571. inherited;
  572. end;
  573.  
  574. function TCommThreadParams.GetObject(const ObjectName: String): TObject;
  575. begin
  576. Result := FThreadObjects.Items[ObjectName];
  577. end;
  578.  
  579. function TCommThreadParams.GetParam(const ParamName: String): Variant;
  580. begin
  581. Result := FThreadParams.Items[ParamName];
  582. end;
  583.  
  584. function TCommThreadParams.SetObject(const ObjectName: String;
  585. Obj: TObject): TCommThreadParams;
  586. begin
  587. FThreadObjects.AddOrSetValue(ObjectName, Obj);
  588.  
  589. Result := Self;
  590. end;
  591.  
  592. function TCommThreadParams.SetParam(const ParamName: String;
  593. ParamValue: Variant): TCommThreadParams;
  594. begin
  595. FThreadParams.AddOrSetValue(ParamName, ParamValue);
  596.  
  597. Result := Self;
  598. end;
  599.  
  600. { TCommQueueItem }
  601.  
  602. destructor TCommQueueItem.Destroy;
  603. begin
  604. if Assigned(FCommThreadParams) then
  605. FreeAndNil(FCommThreadParams);
  606.  
  607. inherited;
  608. end;
  609.  
  610. { TBaseStatusCommThreadDispatch }
  611.  
  612. procedure TBaseStatusCommThreadDispatch.DoOnReceiveThreadMessage(
  613. Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams);
  614. begin
  615. inherited;
  616.  
  617. case MessageId of
  618. // Status Message
  619. CTID_STATUS: DoOnStatus(Sender,
  620. Name,
  621. CommThreadParams.GetParam(PRM_STATUS_TEXT),
  622. CommThreadParams.GetParam(PRM_STATUS_TYPE));
  623. // Progress Message
  624. CTID_PROGRESS: DoOnProgress(Sender,
  625. CommThreadParams.GetParam(PRM_PROGRESS_ID),
  626. CommThreadParams.GetParam(PRM_PROGRESS),
  627. CommThreadParams.GetParam(PRM_PROGRESS_MAX));
  628. end;
  629. end;
  630.  
  631. procedure TBaseStatusCommThreadDispatch.DoOnStatus(Sender: TObject; const ID,
  632. StatusText: String; StatusType: Integer);
  633. begin
  634. if (not (csDestroying in ComponentState)) and (Assigned(FOnStatus)) then
  635. FOnStatus(Self, Sender, ID, StatusText, StatusType);
  636. end;
  637.  
  638. procedure TBaseStatusCommThreadDispatch.DoOnProgress(Sender: TObject;
  639. const ID: String; Progress, ProgressMax: Integer);
  640. begin
  641. if not (csDestroying in ComponentState) and (Assigned(FOnProgress)) then
  642. FOnProgress(Self, Sender, ID, Progress, ProgressMax);
  643. end;
  644.  
  645. end.

To use the library, simply descend your thread from the TCommThread thread and override the Execute procedure:

  1. MyCommThreadObject = class(TCommThread)
  2. public
  3. procedure Execute; override;
  4. end;

Next, create a descendant of the TStatusCommThreadDispatch component and set it's events.

  1. MyCommThreadComponent := TStatusCommThreadDispatch.Create(Self);
  2.  
  3. // Add the event handlers
  4. MyCommThreadComponent.OnStateChange := OnStateChange;
  5. MyCommThreadComponent.OnReceiveThreadMessage := OnReceiveThreadMessage;
  6. MyCommThreadComponent.OnStatus := OnStatus;
  7. MyCommThreadComponent.OnProgress := OnProgress;
  8.  
  9. // Set the thread class
  10. MyCommThreadComponent.CommThreadClass := TMyCommThread;

Make sure you set the CommThreadClass to your TCommThread descendant.

Now all you need to do is create the threads via MyCommThreadComponent:

  1. FCommThreadComponent.NewThread
  2. .SetParam('MyThreadInputParameter', '')
  3. .SetObject('MyThreadInputObject', MyObject)
  4. .Start;

Add as many parameters and objects as you like. In your threads Execute method you can retrieve the parameters and objects.

  1. MyThreadParameter := GetParam('MyThreadInputParameter'); //
  2. MyThreadObject := GetObject('MyThreadInputObject'); // MyObject

Parameters will be automatically freed. You need to manage objects yourself.

To send a message back to the main thread from the threads execute method:

  1. FCommDispatchReceiver.QueueMessage(Self, CTID_MY_MESSAGE_ID, TCommThreadParams.Create
  2. .SetObject('MyThreadObject', MyThreadObject)
  3. .SetParam('MyThreadOutputParameter', MyThreadParameter));

Again, parameters will be destroyed automatically, objects you have to manage yourself.

To receive messages in the main thread either attach the OnReceiveThreadMessage event

or override the DoOnReceiveThreadMessage procedure:

  1. procedure DoOnReceiveThreadMessage(Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams); override;

Use the overridden procedure to process the messages sent back to your main thread:

  1. procedure THostDiscovery.DoOnReceiveThreadMessage(Sender: TObject;
  2. MessageId: Integer; CommThreadParams: TCommThreadParams);
  3. begin
  4. inherited;
  5.  
  6. case MessageId of
  7.  
  8. CTID_MY_MESSAGE_ID:
  9. begin
  10. // Process the CTID_MY_MESSAGE_ID message
  11. DoSomethingWithTheMessage(CommThreadParams.GetParam('MyThreadOutputParameter'),
  12. CommThreadParams.GeObject('MyThreadObject'));
  13. end;
  14. end;
  15. end;

The messages are pumped in the ProcessMessageQueue procedure.

This procedure is called via a TTimer.

If you use the component in a console app you will need to call ProcessMessageQueue manually.

The timer will start when the first thread is created.

It will stop when the last thread has finished.

If you need to control when the timer stops you can override the Finished procedure.

You can also perform actions depending on the state of the threads by overriding the DoOnStateChange procedure.

Take a look at the TCommThread descendant TStatusCommThreadDispatch.

It implements the sending of simple Status and Progress messages back to the main thread.

I hope this helps and that I've explained it OK.

This is related to my previous answer, but I was limited to 30000 characters.

Here's the code for a test app that uses TCommThread:

Test App (.pas)

  1. unit frmMainU;
  2.  
  3. interface
  4.  
  5. uses
  6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7. Dialogs, ComCtrls, ExtCtrls, StdCtrls,
  8.  
  9. Threading.CommThread;
  10.  
  11. type
  12. TMyCommThread = class(TCommThread)
  13. public
  14. procedure Execute; override;
  15. end;
  16.  
  17. TfrmMain = class(TForm)
  18. Panel1: TPanel;
  19. lvLog: TListView;
  20. btnStop: TButton;
  21. btnNewThread: TButton;
  22. StatusBar1: TStatusBar;
  23. btn30NewThreads: TButton;
  24. tmrUpdateStatusBar: TTimer;
  25. procedure FormCreate(Sender: TObject);
  26. procedure btnStopClick(Sender: TObject);
  27. procedure Button3Click(Sender: TObject);
  28. procedure Button4Click(Sender: TObject);
  29. procedure tmrUpdateStatusBarTimer(Sender: TObject);
  30. private
  31. FCommThreadComponent: TStatusCommThreadDispatch;
  32.  
  33. procedure OnReceiveThreadMessage(Source, Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams);
  34. procedure OnStateChange(Sender: TObject; State: TCommThreadDispatchState);
  35. procedure UpdateStatusBar;
  36. procedure OnStatus(Source, Sender: TObject; const ID: String; StatusText: String; StatusType: Integer);
  37. procedure OnProgress(Source, Sender: TObject; const ID: String; Progress, ProgressMax: Integer);
  38. public
  39.  
  40. end;
  41.  
  42. var
  43. frmMain: TfrmMain;
  44.  
  45. implementation
  46.  
  47. resourcestring
  48. StrStatusIDDProgre = 'StatusID: %s, Progress: %d, ProgressMax: %d';
  49. StrActiveThreadsD = 'Active Threads: %d, State: %s';
  50. StrIdle = 'Idle';
  51. StrActive = 'Active';
  52. StrTerminating = 'Terminating';
  53.  
  54. {$R *.dfm}
  55.  
  56. { TMyCommThread }
  57.  
  58. procedure TMyCommThread.Execute;
  59. var
  60. i: Integer;
  61. begin
  62. SendCommMessage(, TCommThreadParams.Create.SetParam('status', 'started'));
  63.  
  64. for i := to do
  65. begin
  66. sleep();
  67.  
  68. SendStatusMessage(format('Thread: %s, i = %d', [Name, i]), );
  69.  
  70. if Terminated then
  71. Break;
  72.  
  73. sleep();
  74.  
  75. SendProgressMessage(Integer(Self), i, , FALSE);
  76. end;
  77.  
  78. if Terminated then
  79. SendCommMessage(, TCommThreadParams.Create.SetParam('status', 'terminated'))
  80. else
  81. SendCommMessage(, TCommThreadParams.Create.SetParam('status', 'finished'));
  82. end;
  83.  
  84. { TfrmMain }
  85.  
  86. procedure TfrmMain.btnStopClick(Sender: TObject);
  87. begin
  88. FCommThreadComponent.Stop;
  89. end;
  90.  
  91. procedure TfrmMain.Button3Click(Sender: TObject);
  92. var
  93. i: Integer;
  94. begin
  95. for i := to do
  96. FCommThreadComponent.NewThread
  97. .SetParam('input_param1', 'test_value')
  98. .Start;
  99. end;
  100.  
  101. procedure TfrmMain.Button4Click(Sender: TObject);
  102. begin
  103. FCommThreadComponent.NewThread
  104. .SetParam('input_param1', 'test_value')
  105. .Start;
  106. end;
  107.  
  108. procedure TfrmMain.FormCreate(Sender: TObject);
  109. begin
  110. FCommThreadComponent := TStatusCommThreadDispatch.Create(Self);
  111.  
  112. // Add the event handlers
  113. FCommThreadComponent.OnStateChange := OnStateChange;
  114. FCommThreadComponent.OnReceiveThreadMessage := OnReceiveThreadMessage;
  115. FCommThreadComponent.OnStatus := OnStatus;
  116. FCommThreadComponent.OnProgress := OnProgress;
  117.  
  118. // Set the thread class
  119. FCommThreadComponent.CommThreadClass := TMyCommThread;
  120. end;
  121.  
  122. procedure TfrmMain.OnProgress(Source, Sender: TObject; const ID: String; Progress, ProgressMax: Integer);
  123. begin
  124. With lvLog.Items.Add do
  125. begin
  126. Caption := '-';
  127.  
  128. SubItems.Add(format(StrStatusIDDProgre, [Id, Progress, ProgressMax]));
  129. end;
  130. end;
  131.  
  132. procedure TfrmMain.OnReceiveThreadMessage(Source, Sender: TObject; MessageId: Integer; CommThreadParams: TCommThreadParams);
  133. begin
  134. if MessageID = then
  135. With lvLog.Items.Add do
  136. begin
  137. Caption := IntToStr(MessageId);
  138.  
  139. SubItems.Add(CommThreadParams.GetParam('status'));
  140. end;
  141. end;
  142.  
  143. procedure TfrmMain.UpdateStatusBar;
  144. begin
  145. StatusBar1.SimpleText := format(StrActiveThreadsD, [FCommThreadComponent.ActiveThreadCount, FCommThreadComponent.StateText]);
  146. end;
  147.  
  148. procedure TfrmMain.OnStateChange(Sender: TObject; State: TCommThreadDispatchState);
  149. begin
  150. With lvLog.Items.Add do
  151. begin
  152. case State of
  153. ctsIdle: Caption := StrIdle;
  154. ctsActive: Caption := StrActive;
  155. ctsTerminating: Caption := StrTerminating;
  156. end;
  157. end;
  158. end;
  159.  
  160. procedure TfrmMain.OnStatus(Source, Sender: TObject; const ID: String; StatusText: String; StatusType: Integer);
  161. begin
  162. With lvLog.Items.Add do
  163. begin
  164. Caption := IntToStr(StatusType);
  165.  
  166. SubItems.Add(StatusText);
  167. end;
  168. end;
  169.  
  170. procedure TfrmMain.tmrUpdateStatusBarTimer(Sender: TObject);
  171. begin
  172. UpdateStatusBar;
  173. end;
  174.  
  175. end.

Test app (.dfm)

  1. object frmMain: TfrmMain
  2. Left =
  3. Top =
  4. Caption = 'CommThread Test'
  5. ClientHeight =
  6. ClientWidth =
  7. Color = clBtnFace
  8. Font.Charset = DEFAULT_CHARSET
  9. Font.Color = clWindowText
  10. Font.Height = -
  11. Font.Name = 'Tahoma'
  12. Font.Style = []
  13. OldCreateOrder = False
  14. OnCreate = FormCreate
  15. PixelsPerInch =
  16. TextHeight =
  17. object Panel1: TPanel
  18. AlignWithMargins = True
  19. Left =
  20. Top =
  21. Width =
  22. Height =
  23. Margins.Right =
  24. Align = alLeft
  25. BevelOuter = bvNone
  26. TabOrder =
  27. object btnStop: TButton
  28. AlignWithMargins = True
  29. Left =
  30. Top =
  31. Width =
  32. Height =
  33. Margins.Left =
  34. Margins.Top =
  35. Margins.Right =
  36. Margins.Bottom =
  37. Align = alTop
  38. Caption = 'Stop'
  39. TabOrder =
  40. OnClick = btnStopClick
  41. end
  42. object btnNewThread: TButton
  43. Left =
  44. Top =
  45. Width =
  46. Height =
  47. Align = alTop
  48. Caption = 'New Thread'
  49. TabOrder =
  50. OnClick = Button4Click
  51. end
  52. object btn30NewThreads: TButton
  53. Left =
  54. Top =
  55. Width =
  56. Height =
  57. Align = alTop
  58. Caption = '30 New Threads'
  59. TabOrder =
  60. OnClick = Button3Click
  61. end
  62. end
  63. object lvLog: TListView
  64. AlignWithMargins = True
  65. Left =
  66. Top =
  67. Width =
  68. Height =
  69. Align = alClient
  70. Columns = <
  71. item
  72. Caption = 'Message ID'
  73. Width =
  74. end
  75. item
  76. AutoSize = True
  77. Caption = 'Info'
  78. end>
  79. ReadOnly = True
  80. RowSelect = True
  81. TabOrder =
  82. ViewStyle = vsReport
  83. end
  84. object StatusBar1: TStatusBar
  85. Left =
  86. Top =
  87. Width =
  88. Height =
  89. Panels = <>
  90. SimplePanel = True
  91. end
  92. object tmrUpdateStatusBar: TTimer
  93. Interval =
  94. OnTimer = tmrUpdateStatusBarTimer
  95. Left =
  96. Top =
  97. end
  98. end

TCommThread -- 在delphi线程中实现消息循环的更多相关文章

  1. TMsgThread, TCommThread -- 在delphi线程中实现消息循环

    http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...

  2. TMsgThread, TCommThread -- 在delphi线程中实现消息循环(105篇博客,好多研究消息的文章)

    在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使用很方便,但是有时候我们需要在线程类中使用消息循环,delphi没有提供.   花了两天的事件研究了 ...

  3. Looper.loop() android线程中的消息循环

    Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Loope ...

  4. 安卓中的消息循环机制Handler及Looper详解

    我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...

  5. delphi XE7 中的消息

    在delphi XE7的程序开发中,消息机制保证进程间的通信. 在程序中,消息来自: 1)系统: 通知你的程序用户输入,涂画以及其他的系统范围的事件: 2)你的程序:不同的程序部分之间的通信信息.   ...

  6. Chrome中的消息循环

    主要是自己做个学习笔记吧,我经验也不是很丰富,以前学习多线程的时候就感觉写多线程程序很麻烦.主要是线程之间要通信,要切线程,要同步,各种麻烦.我本身的工作经历决定了也没有太多的工作经验,所以chrom ...

  7. Windows 消息循环(2) - WPF中的消息循环

    接上文: Windows 消息循环(1) - 概览 win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来. 本文介绍 WPF 中是如何使用消息循环来驱动程序的. 4 消息循环在 ...

  8. 【转载】Delphi7从子线程中发送消息到主线程触发事件执行

    在对数据库的操作时,有时要用一个子线程来进行后台的数据操作.比如说数据备份,转档什么的.在主窗口还能同是进行其它操作.而有时后台每处理一个数据文件,要向主窗口发送消息,让主窗口实时显示处理进度在窗口上 ...

  9. 事件循环和线程没有必然关系(就像Windows子线程默认没有消息循环一样),模态对话框和事件循环也没有必然关系(QWidget直接就可以)

    周末天冷,索性把电脑抱到床上上网,这几天看了 dbzhang800 博客关于 Qt 事件循环的几篇 Blog,发现自己对 Qt 的事件循环有不少误解.从来只看到现象,这次借 dbzhang800 的博 ...

随机推荐

  1. MySQL与Oracle 差异比较之七用户权限

    用户权限 编号 类别 ORACLE MYSQL 注释 1 创建用户 Create user user_name identified by user_password default tablespa ...

  2. Linux makefile教程之更新函数库文件十[转]

    使用make更新函数库文件 ——————————— 函数库文件也就是对Object文件(程序编译的中间文件)的打包文件.在Unix下,一般是由命令"ar"来完成打包工作. 一.函数 ...

  3. UML系列图------用例图介绍

    UML-Unified Model Language 统一建模语言,又称标准建模语言.是用来对软件密集系统进行可视化建模的一种语言. 在UML系统开发中有三个主要的模型: 功能模型: 从用户的角度展示 ...

  4. Oracle数据泵

    要使用数据泵必须先创建数据库目录        数据库目录只允许sys创建        普通用户使用 必须授权 假设scott 用户是导出导入用户 SQL> ! mkdir dp_dir SQ ...

  5. python GUI模块的转变

    Tkinter → tkintertkMessageBox → tkinter.messageboxtkColorChooser → tkinter.colorchoosertkFileDialog ...

  6. IOS播放音乐和音效

    1.播放音效 1.1 首先获取到音效文件路径 NSString *path = [[NSBundle mainBundle] pathForResource:soundFileName ofType: ...

  7. 【转】Bellman_ford算法

    原文链接:http://www.cnblogs.com/Jason-Damon/archive/2012/04/21/2460850.html 摘自百度百科 Bellman-ford算法是求含负权图的 ...

  8. MySQL安装(图文详解)

    下面的是MySQL安装的图解,用的可执行文件安装的,详细说明了一下!打开下载的mysql安装文件mysql-5.0.27-win32.zip,双击解压缩,运行“setup.exe”,出现如下界面 my ...

  9. string中的substr() 和 find() 函数

    string问题中经常遇到在stringA中查找stringB,主要通过substr()跟find()来完成 substr().find().replace() 都可以用一个位置加上一个长读去描述子串 ...

  10. PHP强大的内置filter (一)

    <?php #PHP内置的validate filter $input_data = True; $result = filter_var($input_data,FILTER_VALIDATE ...