Author: Peter Johnson
Sometimes we need a non-windowed component (i.e. one that isn't derived from
TWinControl) to receive Windows messages - but non-windowed component don't have
window handles. For example suppose we are developing a non-visual component that
registers our application as a clipboard viewer so the application can respond to
changes in the clipboard. To get information about clipboard changes our component
needs to receive messages from Windows.
Answer:
The Delphi library function AllocateHWnd is used to create a hidden window for us
and the related DeallocateHWnd disposes of the window when we've finished with it.
The hidden window needs a window procedure. We can use a method of our component
class to provide the window procedure. AllocateHWnd takes a reference to the method
its parameter - it takes care of the problem of registering the method as a window
procedure for us. In the method we handle the messages we are interested in and
hand the rest off to Windows using the DefWindowProc API call.
The following code gives the skeleton of how to use AllocateHWnd. First, here's the
class declaration from the interface section of code:
1 type2 // Our class derived from TComponent3 // (or another ancestor class)4 TMyClass = class(TComponent)
5 private6 FHWnd: HWND;
7 // field to store the window handle8 {...}9 protected10 procedure WndMethod(var Msg: TMessage); virtual;
11 // the window proc - called by Windows to handle12 // the given message13 {...}14 public15 constructor Create(AOwner: TComponent); override;
16 // create window proc here17 destructor Destroy; override;
18 // free window proc here19 {...}20 end;
21 22 //And here's the implementation details: 23 24 TMyClass.Create(AOwner: TComponent);
25 begin26 inherited Create(AOwner);
27 {... }28 // Create the window29 FHWnd := AllocateHWnd(WndMethod);
30 { ...}31 end;
32 33 TMyClass.Destroy;
34 begin35 {...}36 // Destroy the window37 DeallocateHWnd(FHWnd);
38 {...}39 inherited Destroy;
40 end;
41 42 TMyClass.WndMethod(var Msg: TMessage);
43 var44 Handled: Boolean;
45 begin46 // Assume we handle message47 Handled := True;
48 case Msg.Msg of49 WM_SOMETHING: DoSomething;
50 // Code to handle a message51 WM_SOMETHINGELSE: DoSomethingElse;
52 // Code to handle another message53 {...}54 else55 // We didn't handle message56 Handled := False;
57 end;
58 if Handled then59 // We handled message - record in message result60 Msg.Result := 0
61 else62 // We didn't handle message63 // pass to DefWindowProc and record result64 Msg.Result := DefWindowProc(FHWnd, Msg.Msg,
65 Msg.WParam, Msg.LParam);
66 end;
Of course, we could just use the Windows API to create a window the hard way and
provide a windows procedure. But it is more difficult to use a method (rather than
a simple procedure) as a window procedure if we do it this way. The clever features
about AllocateHWnd are that (a) it creates the hidden window for us and (b) it
allows us to use a method, rather than a simple procedure as the window procedure
-- and a method is more useful since it has access to the class's private data.
Component Download: http://www.delphidabbler.com/download.php?file=pjcbview.zip