Quote
Mike Bruce wrote:
> Joris,
> Could I just ask you ( since I am not very clear on threads or thread
> programming ) what do you mean by create my form in the message handler?
> Which message handler do you mean ? Is it the application.messagehandler ?
You wanted to use VCL stuff as a concequence of a callback getting
called inside the context of a secondary thread, wright?
VCL stuff is off limits in secondary threads. It is completely not
threadsafe. So what you need here is to trigger some code in the context
of the 'main' thread, from within the context of a secondary thread.
Now, remember the 'main' thread is essentially a message loop. Only
thing it does is pop a message off out of the message stack and handle
that message. Posting or sending messages is done with a threadsafe
win32 api function.
So, there's you solution. The callback function that executes in the
context of the secondary thread can use postmessage to put a message on
the message stack. Some time later, the main thread will pop this
message out of the stack and respond to it.
Quote
> I did try postmessage and just posted the data to the edit on a form - then
> in the editonchange event I copied it to a grid which is what I wanted - but
> it didn't work - nothing appeared. However - it did work when I used
> sendmessage which I have a feeling I shouldn't be using ??
There are two good ways of handling messages. You can do it by
'subclassing', which, essentially, means that you replace a pointer to a
window specific general message handler function with a pointer to your
own code that can in turn call the old code. This is probably not what
you want here.
A 'cleaner' way to do it is to inherit from a TWinControl and add your
particular message handler in the declaration (and of course
implementation) part. Since you already have at least one 'form' window
around, usually, a 'form' window is used for this. Here's an example
const
{this is the message code, any value from WM_USER and upwards will do}
WM_MYMESSAGE = WM_USER + 0;
type
TForm1 = class(TForm)
private
procedure MyMessageHandler(var a: TMessage); message WM_MYMESSAGE;
end;
implementation
procedure TForm1.MyMessageHandler(var a: TMessage);
begin
{do the VCL stuff here, in the context of the main application thread}
Vcl.Whatever;
end;
procedure Callback;
begin
{this gets called in the context of a secondary thread, but needs to
cause the Vcl.Whatever action somehow}
PostMessage(Form1.Handle,WM_MYMESSAGE,0,0);
end;
Note that this requires that Callback somehow needs to know the handle
of the window the message needs to be posted to. Sometimes this is dead
easy (eg a single instance window, present throughout the course of the
application). Sometimes this is a synchronization issue by itself (eg
threads that have to lazily die in idle time after their 'containing'
window is vanished).
The difference between PostMessage and SendMessage is simple.
PostMessage guarantees that you do not have to wait for the message to
be handled and cannot deadlock. Another advantage therefore is that no
processing power is wasted on true multi processor machines. A
disadvantage is that you cannot get any return value or any feedback as
to when or how the message got handled from this function. SendMessage
is exactly the opposite: it returns after the message got handled.
BTW, the use of PostMessage and SendMessage this way is quite standard
to the rest of the world. Synchronize is more popular to Delphi users
though. The VCL creates a dedicated window when it creates its first
thread. Synchronize, underneath, boils down to a sendmessage to this
window. It's the same principle, only, you can't 'see' what you are
really doing (which is crucially important in all multithreaded design),
and consequently you can't tailor it to any specific needs afterwards.
Hope this helps,
Joris