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.

3 comments:

  1. I spent some time tracking this one down a few months ago. The problem is the parameter type - if you're implementing a message handler you have to make sure the message record's type matches the one used by your ancestor. In this case, the problem is that TBaseClass.WMLButtonDown expects a TWMLButtonDown record, while TNewClass.WMLButtonDown expects a TMessage record. The problem is mentioned in my "Tips for Converting VCL Components to VCL.NET" article (http://www.shorterpath.com/develop/zonearticles/vcltonet.asp).

    ReplyDelete
  2. whadaya mean "we", white-man? ;P

    ReplyDelete
  3. I'm here for moral support... besides I find the deadly problems and you fix them ;-)...

    ReplyDelete

Please keep your comments related to the post on which you are commenting. No spam, personal attacks, or general nastiness. I will be watching and will delete comments I find irrelevant, offensive and unnecessary.