Last edit: 05-03-17 Graham Wideman


Delphi VCL Message Handling
Article created: 98-09-30

This page is simply a collection of notes on the flow of windows messages through the Delphi VCL "windows procs". This is a topic significant to designing a component which can "hook" another component's message handler, a technique needed for possible Drag or Drop components.

Overview: Delphi Help page "Dispatching messages".
Reference: Delphi Developers Handbook (Cantu and others).

The Players:

TControl = class(TComponent)
  procedure WndProc(var Message: TMessage); virtual;
  property WindowProc: TWndMethod read FWindowProc write FWindowProc;
  procedure DefaultHandler(var Message); override;

TWinControl = class(TControl)
  property DefWndProc: Pointer read FDefWndProc write FDefWndProc;
  procedure MainWndProc(var Message: TMessage);
  procedure DefaultHandler(var Message); override;

Non-Object function:
function InitWndProc(HWindow: HWnd; Message, WParam: Longint; LParam: Longint): Longint; stdcall;

Message-Handling Call Sequence

Deduced from VCL source code:

  Call sequence Comment How initialized
  System has WM_xxx for TWinControlDescendant (TWCD)   see table below
proc TWCD.MainWinProc probably not overriden, thus: TWinControl.MainWinProc  
prop TWCD.WindowProc can be hooked TControl.Create
proc TWCD.WndProc default (or end of hook chain)  
proc TControl.WinProc descendants call inherited  
proc TObject.Dispatch specific message handler if any (ie: procedure with "message" directive)... otherwise...  
proc TWCD.DefaultHandler    
proc TWinControl.DefaultHandler descendants call inherited  
prop FDefWndProc   TWinControl.Create calls  TWinControl.CreateParams sets FDefWndProc := DefWindowProc()
system Default system window proc for this control class ... or...
proc TControl.DefaultHandler (only if no handle)  

TWinControl.CreateWnd registers TWinControl.MainWndProc as the proc the the system should call with messages. Details:

Separate steps Action
TWinControl.Create FObjectInstance := MakeObjectInstance(MainWndProc);
TWinControl.CreateWnd WindowClass.lpfnWndProc := @InitWndProc;
[...] Windows.RegisterClass(WindowClass) [...]
InitWndProc SetWindowLong(HWindow, GWL_WNDPROC, Longint(CreationControl.FObjectInstance))

Hooking Example: TDockTree

TDockTree is an object that is associated with a TWinControl to allow it to provide docking to other controls (at least, I think that's what it does!)  At any rate, it provides an example of an "approved" way to hook another control's messages. Here are the relevant pieces:

constructor TDockTree.Create(DockSite: TWinControl);
  if not (csDesigning in DockSite.ComponentState) then
    FOldWndProc := FDockSite.WindowProc;
    FDockSite.WindowProc := WindowProc;

destructor TDockTree.Destroy;
  if @FOldWndProc <> nil then
    FDockSite.WindowProc := FOldWndProc;
  inherited Destroy;

procedure TDockTree.WindowProc(var Message: TMessage);
  [... bunch of docking-related processing... then...]

Go to:  Drag and Drop Intro Page, or  wideman-one