PDA

View Full Version : Passing method pointers to a function



Useless Hacker
01-12-2003, 10:40 PM
Good evening :)

I have a small problem. Basically, I want to pass the method pointer for the function PaintProc into the Register... function. But Delphi won't let me. :( Here is my code:
type
TMessageHandler = function(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT of object;

TThing = class(TObject)
protected
function PaintProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;

function RegisterMessageHandler(Msg: UINT; Handler: TMessageHandler): TMessageHandler;
public
constructor Create;
end;

...

constructor TThing.Create;
begin
RegisterMessageHandler(WM_PAINT, PaintProc); // this doesn't work
RegisterMessageHandler(WM_PAINT, @PaintProc); // or this...
end;

Alimonster
02-12-2003, 12:33 AM
The following works fine for me in Delphi 7:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TMessageHandler = function(hWnd: HWND; Msg: UINT; wParam:
WPARAM; lParam: LPARAM): LRESULT of object;

TInfo = record
Msg : UINT;
CallMe: TMessageHandler;
end;
PInfo = ^TInfo;

//------------------------------------------------------------------------------

TThing = class(TObject)
private
FInfo: TList;
protected
function PaintProc(hWnd: HWND; Msg: UINT;
wParam: WPARAM; lParam: LPARAM): LRESULT;
function RegisterMessageHandler(Msg: UINT;
Handler: TMessageHandler): TMessageHandler;

procedure ClearInfo;

procedure HandleSomeMessage(Msg: UINT);
public
constructor Create;
destructor Destroy; override;
end;

//----------------------------------------------------------------------------

TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
FObject: TThing;
public
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

//------------------------------------------------------------------------------

function TThing.PaintProc(hWnd: HWND; Msg: UINT;
wParam: WPARAM; lParam: LPARAM): LRESULT;
begin
ShowMessage('In here');
Result := 0;
end;

//------------------------------------------------------------------------------

function TThing.RegisterMessageHandler(Msg: UINT; Handler: TMessageHandler):
TMessageHandler;
var
NewInfo: PInfo;
begin
New(NewInfo);
NewInfo.Msg := Msg;
NewInfo.CallMe := Handler;
FInfo.Add(NewInfo);

Result := Handler;
end;

//------------------------------------------------------------------------------

constructor TThing.Create;
begin
FInfo := TList.Create;
RegisterMessageHandler(WM_PAINT, PaintProc);
end;

//------------------------------------------------------------------------------

destructor TThing.Destroy;
begin
ClearInfo;
FInfo.Free;
end;

//------------------------------------------------------------------------------

procedure TThing.ClearInfo;
var
i: Integer;
begin
for i := 0 to FInfo.Count - 1 do
Dispose(FInfo[i]);

FInfo.Clear;
end;

//------------------------------------------------------------------------------

procedure TThing.HandleSomeMessage(Msg: UINT);
var
i: Integer;
ThisOne: PInfo;
begin
for i := 0 to FInfo.Count - 1 do
begin
ThisOne := FInfo[i];

if ThisOne.Msg = Msg then
begin
if Assigned(ThisOne.CallMe) then
ThisOne.CallMe(Application.Handle, ThisOne.Msg, 0, 0);
end;
end;
end;

//------------------------------------------------------------------------------

procedure TForm1.FormCreate(Sender: TObject);
begin
FObject := TThing.Create;
end;

//------------------------------------------------------------------------------

procedure TForm1.FormDestroy(Sender: TObject);
begin
FObject.Free;
end;

//------------------------------------------------------------------------------

procedure TForm1.Button1Click(Sender: TObject);
begin
FObject.HandleSomeMessage(WM_PAINT);
end;

//------------------------------------------------------------------------------

end.

Alimonster
02-12-2003, 12:50 AM
Oh, and something for you to be careful of: pointers/functions "of object" are 8 bytes, not 4, so they can't be stored in a Pointer (not enough room!). Officially, they're of type "TMethod", having a Code and a Data pointer. The Code pointer points to the function and the Data pointer to the actual object instance IIRC. This is the reason I used the TInfo record rather than storing pointers directly -- the pointer to TInfo is 4 bytes as usual.

Useless Hacker
02-12-2003, 02:25 PM
EDIT: BTW Alimonster, where did you get the rest of my code? :shock:

Well, I've got it working now. :)

In my actual code I was doing:

constructor TThing.Create;
begin
Self.RegisterMessageHandler(WM_PAINT, PaintProc); // this doesn't work
end;
Which gives a "Not enough actual parameters" error.

But doing this works:

constructor TThing.Create;
begin
RegisterMessageHandler(WM_PAINT, PaintProc); // this does work
end;

I would have thought these would be exactly the same. :?

Alimonster
03-12-2003, 09:18 AM
EDIT: BTW Alimonster, where did you get the rest of my code? :shock:
I have sinister, psychic powers... or I just wrote the most obvious test cradle for the given task, and it happens to look like yours. :P But I like the sinister psychic powers explanation.


I would have thought these would be exactly the same. :?
So would I, to be honest. Perhaps there's a subtle distinction that I can't think of yet. I've reproduced this behaviour ("self." being different) for this example with Delphi 5 Enterprise at work. I'll try Delphi 7 when I get home, if I remember.