Saturday, July 17, 2004

Message methods in Delphi for .NET

Just got bit by this little oddity in how Delphi for .NET handles message methods. What is wrong with the following because it will compile?

TBaseClass = class(TWinControl)
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
end;

TNewClass = class(TBaseClass)
procedure WMLButtonDown(var Message: TMessage); message WM_LBUTTONDOWN;
end;

...

procedure TBaseClass.WMLButtonDown(var Message: TWMLButtonDown);
begin
inherited;
{ special processing here }
end;

procedure TNewClass.WMLButtonDown(var Message: TWMLButtonDown);
begin
inherited;
{ more special procesing here }
end;

It seems that the compiler will generate a raw call to the inherited method when it sees the "inherited" keyword without doing anything with the paramter. The ancestor's message handler will now treat the TMessage parameter as if it were a TWMLButtonDown message cracker. This wreaks havoc on the .NET runtime, but not in the way one would expect. One would possibly expect exceptions like invalid casts. However there is actually no telling what might happen. It actually react just as randomly as if you'd taken a random Integer value and cast it to an object reference then proceeded to call methods or dereference it. Now, of course the above code doesn't pass PEVerify, and had I actually thought to do that I would have seen the problem. So for all you component developers out there moving your Delphi components to Delphi 8 for .NET, just keep this in mind. You should declare your descendant message methods to match the ancestor's declaration (even though it is not required in Delphi for Win32). Yes, Danny and I are going to be working on some solutions to this delima for a future Delphi for .NET release.