0

i would like to send a message with postmessage() to a TObject instance, but after the thread terminates, the program does not step into method HandleThreadCompletion.

const
  WM_THREAD_COMPLETE = WM_USER + 5437;

The TObject instance (TMaster) contains the identifier for the thread object

type
  TMaster = class(TObject)
  private
    ...
    Fslave_search_thread : Tsrch_slave_thread;
    fMsgHandlerHWND : HWND;
    function start_slvsearch_th: integer; 
    procedure HandleThreadCompletion(var Message: TMessage);
    ...
  public
    ...
  end;

constructor TMaster.Create (aNode: TTreeNode; aName, anIP, aMAC: string);
begin
  ...
  fMsgHandlerHWND := AllocateHWnd(HandleThreadCompletion);
  ...
end;

I free and nil the thread in TMaster.HandleThreadCompletion()

procedure TMaster.HandleThreadCompletion(var Message: TMessage);
begin
  if message.msg = WM_THREAD_COMPLETE then begin
    if Assigned(Fslave_search_thread) then
    begin
      Fslave_search_thread.WaitFor;
      Fslave_search_thread.Free;
      Fslave_search_thread := nil;
      ...
    end
  end else
    message.result := DefWindowProc(fMsgHandlerHWND, Message.Msg, Message.wParam, Message.lParam) ; 
end;

Creating the thread here (suspended), filling some fields than starting it (resume).

function TMaster.start_slvsearch_th: integer;
var
  i: integer;
begin
  if not Assigned(Fslave_search_thread) then begin
    ...
    Fslave_search_thread := Tsrch_slave_thread.Create(true);
    ... 
  with Fslave_search_thread do
  begin
    master := self;
    master_HWND := self.fMsgHandlerHWND;
    FreeOnTerminate := False;
    Resume;   
    ...
  end;
end;

When terminated (with setting the terminate flag from outside or the cycle finishes), sending the message:

procedure Tsrch_slave_thread.Execute;
begin
  while master.CMD_LISTCNT = 1 do 
  begin 
    ...
    if terminated then break;
  end;
  PostMessage(master_HWND, WM_THREAD_COMPLETE, 0, 0);   
  ...
end;

http://members.upclive.hu/ikt/thread_terminate_test_postmessage.zip

john_who_is_doe
  • 389
  • 3
  • 18
  • Lol, string 'Hello' been cut from the beginning of my message. No need for any flourish? (: – john_who_is_doe Nov 14 '13 at 12:52
  • `begin`s and `end`s do not match in the second code sample. Could you fix this? – jpfollenius Nov 14 '13 at 13:21
  • 1
    What is the purpose of freeing the thread through a PostMessage call? Why not set FreeOnTerminate = true. If you want to be notified, just assign a method in `TMaster` to the TThread.OnTerminate. – LU RD Nov 14 '13 at 14:49
  • An aside. There's little point calling `Fslave_search_thread.WaitFor` since the call to `Free` will do that. Which makes the `if` immediately before pointless. You can do all of that with a single call to `FreeAndNil`. – David Heffernan Nov 14 '13 at 14:54
  • I wonder why the VCL never calls the default window procedure for allocated windows. – Sertac Akyuz Nov 15 '13 at 00:25
  • @SertacAkyuz I don't think I would call `DefWindowProc` here. That window is only meant to receive one message. – David Heffernan Nov 15 '13 at 06:57
  • @David - However it receives more than one. At the very least WM_DESTROY, WM_NCDESTROY when you *deallocate* it (CREATE counterparts are received before the window is subclassed). – Sertac Akyuz Nov 15 '13 at 10:22
  • @Sertac Hmm, good point. I guess I'm doing it all wrong. Thanks. – David Heffernan Nov 15 '13 at 10:31
  • @David - Maybe it's not wrong after all. I think I trust VCL that much.. – Sertac Akyuz Nov 15 '13 at 10:36
  • 1
    Re (test project): In tmaster.create, you're destroying the 'utility' window immediately after it has been created. Possibly you meant to have try/except instead of try/finally. – Sertac Akyuz Nov 15 '13 at 13:52
  • If you tested the return of the `PostMessage`, it would take just a few seconds for you to resolve this. – Sertac Akyuz Nov 15 '13 at 14:01
  • @SertacAkyuz - Everybody, please just let me silently leave the room. – john_who_is_doe Nov 15 '13 at 14:38
  • @diop - I didn't mean to offend you. I just wanted to stress the importance of error checking while using api. – Sertac Akyuz Nov 15 '13 at 14:40
  • No i did not take it as an offend. I just feel ashamed because my own stupidity. Sorry all for taking your time. – john_who_is_doe Nov 15 '13 at 14:44
  • @diop - Ah, ok. No need to feel that way. Mistakes.. we all do.. At least I know I do.. – Sertac Akyuz Nov 15 '13 at 14:46
  • @Sertac Regarding DefWindowProc, it seems that I'm not a very good learner: http://stackoverflow.com/questions/9097782/trap-wm-copydata-from-delphi-component – David Heffernan Nov 16 '13 at 05:36
  • @David - Thanks for that, I was still a bit uneasy about not calling it. – Sertac Akyuz Nov 16 '13 at 09:44
  • @DavidHeffernan - (: Its a bit hard whose solution to accept, because: -> You were the first with: 3. The master destroys the window before it processes the message. But LU RD was giving a working alternative and finally, it was Sertac who gave me 'direct' solution (but ok he was not answering, so i cannot accept his answer). (: Ok i give it to David Heffernan, because his answer was the first good solution related to the topic. But thank you all (especially Sertac Akyuz). – john_who_is_doe Nov 17 '13 at 10:28

2 Answers2

1

The most obvious explanations are:

  1. The master thread has terminated before it can process the message.
  2. The master thread is not pumping its message queue.
  3. The master thread destroys the window before it processes the message.

If you had shown an SSCCE I could have narrowed it down further. Since we cannot see an SSCCE, that means you'll need to narrow it down.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • OK, 2. looks like the most likely candidate. The thread which executes `AllocateHWnd` needs to have a message pump. Can you show it. – David Heffernan Nov 14 '13 at 14:50
  • 1. Master is just an object representing a hw device (not thread), i start and stop (terminate, then free) slave_search_thread from here. Master object is present all the time, never free or recreate it (only manually sometimes) 2. Would you please explain? :) 3. Why? When? How could that happen? (: – diop078 5 mins ago – john_who_is_doe Nov 14 '13 at 14:50
  • The thread that calls `AllocateHWnd` needs to pump its message queue. Otherwise the message will not be dispatched. Can you show how the message queue on that thread is pumped. – David Heffernan Nov 14 '13 at 14:52
  • Well then, that seems to be the problem because i dont do anything like that. (: – john_who_is_doe Nov 14 '13 at 15:11
  • I can't advise how to best deal with that since I don't know enough about the design of your app. But if you don't pump the queue, the message won't be dispatched. – David Heffernan Nov 14 '13 at 15:38
  • @David, "The thread that calls AllocateHWnd needs to pump its message queue" - I'm far from being a Threading master, but isn't `AllocateHWnd` itself sufficient enough to pump the message queue in the `WndProc` dispatching messages via `DefWindowProc`? – kobik Nov 14 '13 at 15:38
  • 1
    @kobik Nope. The thread has to run a message loop to pull the messages off the queue. It won't happen unless you do it. VCL does it for you in Application.Run. Somebody's got to do it. – David Heffernan Nov 14 '13 at 16:05
  • Maybe this article: [Sending messages to non-windowed applications](http://delphi.about.com/od/windowsshellapi/l/aa093003b.htm) miss-lead me. Last paragraph states "If we write a non-windowed application, for instance a console application or a non-interactive service...", Then on the next page a TTread example with AllocateHWnd with no message pump. – kobik Nov 14 '13 at 16:17
  • "VCL does it for you in Application.Run" TMaster is in the main VCL thread – john_who_is_doe Nov 14 '13 at 19:28
  • It only pumps the queue if you let it. You did not show SSCCE. – David Heffernan Nov 14 '13 at 19:49
  • Ok, tomorrow. Thank You. – john_who_is_doe Nov 14 '13 at 21:44
  • i commented out the 'if message.msg = WM_THREAD_COMPLETE' condition, so i can see that the program jumping in the TMaster.HandleThreadCompletion method all the time, so there should be some message processing(?) – john_who_is_doe Nov 15 '13 at 07:27
  • Make an SSCCE. Otherwise we are all guessing. You've got the code. We don't. – David Heffernan Nov 15 '13 at 07:29
  • Thats a bit of work to clean up things for sending something usable, but im on it. Info: If i use Fslave_search_thread's OnTerminate event handler (TMaster.xyz) and write 'Fslave_search_thread.WaitFor' in it (just for testing), the program freezes at this line, so another problem arised. Appreciate your helping. – john_who_is_doe Nov 15 '13 at 08:09
  • @diop078, in the `OnTerminate` event you must not call `thread.WaitFor`. Just set `FreeOnTerminate := true;` when you start the thread. In the `OnTerminate` event, just collect the thread information and set `Fslave_search_thread` to nil. – LU RD Nov 15 '13 at 11:08
  • 'Recreated' the problem in a small app, link provided at the end of the message. Thank You. – john_who_is_doe Nov 15 '13 at 13:31
  • Please post the code in the question. Ideally should just be a single file. No need for GUI. Ideally a console app is best. But you may need a Forms app for message loop. Don't really want to look at offsite files. – David Heffernan Nov 15 '13 at 14:02
1

Update:

Sertac found the reason to your problems. The fMsgHandlerHWND was destroyed immediately after it was created in a try/finally construct which should have been a try/except (or something else).

Anyway, the rest of my answer would have been less error prone to implement.


It is difficult to say what is wrong with your code without a test case that produces your observation.

However, I would suggest to use a simpler approach to handle the thread shut down.

Before starting the thread, assign an event handler to the OnTerminate property. By setting TThread.FreeOnTerminate you don't have to explicitly free the thread object.

In the OnTerminate event handler, just collect data from the thread (if any) and set the thread reference to nil.

type
  TMaster = class
    private
      FThread: TMyThread;
      procedure OnThreadTerminate(Sender: TObject);
    public
      constructor Create;
      procedure StopThread;
      function StartThread: Boolean;
  end;

constructor TMaster.Create;
begin
  Inherited;
  FThread := Nil;
end;

procedure TMaster.StopThread;
begin
  if Assigned(FThread) then
    FThread.Terminate;
end;

procedure TMaster.OnThreadTerminate(Sender: TObject);
begin
  // Collect data from the thread
  ...
  // Just clear the thread reference
  FThread := Nil;
end;

function TMaster.StartThread: Boolean;
begin
  Result := (FThread = Nil);
  if Result then
  begin
    FThread := TMyThread.Create(True);
    FThread.FreeOnTerminate := True;  // Thread will self destruct
    FThread.OnTerminate := Self.OnThreadTerminate;  // Called when thread is terminating
    FThread.Start; // Older Delphi versions uses Resume
  end;
end;
LU RD
  • 34,438
  • 5
  • 88
  • 296
  • Well thank you, yes maybe i will do this way, but im just trying the possibilities im reading from Martin Harvey's 'Multithreading - The Delphi Way' (around chapter 4). Anyway, later i would like to use the postmessage approach for different multithreading tasks, so i would like to see how it should work. – john_who_is_doe Nov 15 '13 at 13:08
  • Also i dont get why is the program hanging when i try to free Fslave_search_thread from the thread's OnTerminate event handler (a TMaster method), without automatic thread free (FreeOnTerminate := False). I know its not the point of your answer, im just telling. – john_who_is_doe Nov 15 '13 at 13:38
  • Calling `WaitFor` or free in the `OnTerminate` event will most likely cause a deadlock. Don't do that. – LU RD Nov 15 '13 at 14:03
  • @MartinJames posted an example of using the `PostMessage` from threads in an elegant way. [`PostToMainThread`](http://borland.newsgroups.archived.at/public.delphi.language.delphi.win32/200603/060330160.html). See also [`How to do asynchronuous programming in Delphi?`](http://stackoverflow.com/a/12425900/576719). Btw, Martin Harvey is currently writing a new book about multithreading. – LU RD Nov 15 '13 at 14:24