Newsgroups : Borland : borland.public.delphi.internet.winsock : 2006 Mar : Re: TIdTCPServer - threads, global data

www.cryer.info
Managed Newsgroup Archive

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.

Replies:

In response to:

www.cryer.info
Managed Newsgroup Archive