Other people's windows

By: CodeGear

Abstract: This article demonstrates how to subclass non-Delphi windows from within a Delphi application.

Every window has a procedure associated with it that receives all the messages that are sent to it. To subclass a window means to replace the procedure associated with the window by another user-defined procedure. The main use of subclassing windows is to customize how a window appears or behaves.

The handle of the procedure associated with the window can be obtained by calling the GetWindowLong function:

hproc:TFarproc;
hproc:=TFarProc(GetWindowLong(hwnd,GWL_WNDPROC));

If hwnd is the handle of the function then hproc is the handle of the procedure associated with the window.

Now define a procedure that will replace the original procedure associated with the window. For example:

type
 TForm1=class(TForm)
 private
  hproc:TFarproc;
 protected
  procedure WndProc(var msg:TMessage);
 end;

Here, the procedure WndProc will replace the original window procedure.

To replace the original procedure with WndProc, you must first call MakeObjectInstance, which converts a member procedure (here WndProc is member of TForm1 class) to a standard procedure. This is because WndProc is a member function and Windows does not understand class member functions. Class member functions have a "self" pointer as a hidden first parameter which uniquely identifies a class object. (Remember that an object is a specific instantiation of a class.) The API callback does not know how to pass "self."

To convert the member procedure to a standard procedure, call MakeObjectInstance as shown here:

fproc:TFarProc;
fproc:=MakeObjectInstance(WndProc);

Then call SetWindowlong to replace the procedure of the window:

SetWindowlong(hwnd,GWL_WNDPROC,longword(fcurProc));

Now all messages that are sent to the window will be intercepted by your WndProc procedure.

The messages that are not handled by WndProc can be sent to the original procedure by using CallWindowProc. Here is how to call the function:

procedure TForm1.DlgProc(var msg:Tmessage);
begin
 case msg.msg of
  WM_SIZE:
   begin
    //user defined code
   end;
  WM_PAINT:
   begin
    //user defined code
   end;
 end;
 //all unhandled messages are sent to original procedure. Here,
 //hproc is the  handle to the original window procedure.
 msg.result := CallWindowProc(hproc, hwnd, msg.msg, msg.wparam, msg.lparam);
end;

Once you have finished you have to destroy the handle created by the MakeObjectInstance(WndProc) function by calling FreeObjectInstance, which is also defined in the forms unit. You usually call this function when the form is closed. Here is how it's done:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 FreeObjectInstance(fproc);
end;

Server Response from: ETNASC03