Newsgroups : Borland : borland.public.delphi.internet.winsock : 2006 Mar : Re: TIdTCPServer - threads, global data
| Subject: | Re: TIdTCPServer - threads, global data |
| Posted by: | "Martin James" (mjames_falc..@dial.pipex.com) |
| Date: | Wed, 15 Mar 2006 19:26:32 |
Remy has raised the annoying point that posting/sending directly to form
handles is unsafe. I have never seen any problem with it, but since Billy's
best does happily recreate windows whenever it realises that it's internal
structure does not allow some window operation, the possibility does exist.
Fortunately, the problem, such as it is, is easily worked around by posting
messages to a hidden window and then, from the WndProc of the hidden window,
calling 'perform' on the form *instance*, so avoiding any use of the form
*handle*. Since the hidden window does not do anything much, its handle
will never change.
If 'recreateWindow' worries you, try the unit below. It only transfers one
32-bit reference since the other PostMessage parameter is needed to transfer
the form reference. This is not any great problem since one object can
transfer anything.
Rgds,
Martin
unit postToMainThread;
{ Code to send an object to the main thread from a secondary thread.
Creates a main-thread window in it's initialization section.
'queueObjectToMainThread' procedure uses the postMessage API to queue a
TObject and a TControl instance to the window in wParam and lParam, using
a
global message number, 'CM_OBJECTRX'.
The 'postThreadWndProc' procedure that gets the message extracts the
TControl
and calls 'perform' with the object as lParam.
Receiving TControls, (normally forms), should have a message-handler for
'CM_OBJECTRX' to process the received objects.
This postMessage system, with a seperate main-thread window, is safer than
posting directly to forms. Windows crappy code has to recreate Windows
for
some operations, (eg. change BidMode) because..well.. it's crappy. This
means that the form handle can change & a message posted to the 'old' form
handle will be lost. Using a seperately-created non-visible main-thread
window to post to is safer.
PostMessage also avoids the twin horrors of:
'TThread.synchronize' (sucks)
'TThread.waitFor' (sucks bad)
}
interface
uses
Windows, Messages, Classes, Forms, sysUtils, controls;
procedure queueObjectToMainThread(aControl:TControl;anObject:TObject);
const
CM_OBJECTRX=$8FF0;
implementation
var
ThreadPostWindow:Thandle;
procedure queueObjectToMainThread(aControl:TControl;anObject:TObject);
// posts the object to the ThreadPostWindow so it can call
'aControl.perform'
// in the main thread with the anObject as the lParam.
begin
if not postMessage(ThreadPostWindow,CM_OBJECTRX,
integer(aControl),integer(anObject)) then
raise exception.create('PostMessage to main thread failed');
end;
function postThreadWndProc(Window: HWND; Mess, wParam, lParam: Longint):
Longint; stdcall;
// The WndProc to receive the 'queueToMainThread' postings
function winControlPerform:integer;
// assumes that wParam is a TControl reference, eg. a form instance.
var thisWinControl:TControl;
begin
try
thisWinControl:=TControl(wparam);
thisWinControl.Perform(CM_OBJECTRX,0,lParam);
result:=-1;
except
raise exception.create('Bad TControl received in postToMainThread');
end;
end;
begin
case Mess of
CM_OBJECTRX:result:=winControlPerform;
else
Result := DefWindowProc(Window, Mess, wParam, lParam);
end;
end;
var
ThreadPostWindowClass: TWndClass = (
style: 0;
lpfnWndProc: @postThreadWndProc;
cbClsExtra: 0;
cbWndExtra: 0;
hInstance: 0;
hIcon: 0;
hCursor: 0;
hbrBackground: 0;
lpszMenuName: nil;
lpszClassName: 'TpostThreadWindow');
initialization
// create window for all postings to main thread
Windows.RegisterClass(ThreadPostWindowClass);
ThreadPostWindow:=CreateWindow(ThreadPostWindowClass.lpszClassName, '', 0,
0, 0, 0, 0, 0, 0, HInstance, nil);
finalization
DestroyWindow(ThreadPostWindow);
end.