2010. február 5., péntek

Subclassing Non Delphi Windows


Problem/Question/Abstract:

How to subclass non Delphi windows

Answer:

Every window has a procedure associated with it that recieves 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 works.

The handle of the procedure associated with the window can be got 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 see the code below.

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 the procedure WndProc you have to first call the function 'MakeObjectInstance'  defined in forms unit which converts a member procedure(Here WndProc is member of TForm1 class) to a standard procedure.

This is because the WndProc procedure 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 (keep in mind 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 the MakeObjectInstance function as shown below.

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

after you have done this

call the SetWindowlong function to replace the procedure of the window as shown below

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

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

The messages that are not handled by the WndProc can be sent to the original procedure by using the CallWindowProc function.

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 finshed you have to destroy the handle created by the MakeObjectInstance(WndProc) function by calling the 'FreeObjectInstance' function which is also defined in the forms unit. You usually call this function when the form is closed. Example of how to call the function is shown below.

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

Nincsenek megjegyzések:

Megjegyzés küldése