11

Why this code does not crash? T is nil. How it is possible to access Caption if T is nil?

procedure Crash;                                                                          
VAR T: TButton;
begin
 T:= NIL;
 T.Caption:= ''; <---------- this works
end;
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
Gabriel
  • 20,797
  • 27
  • 159
  • 293
  • 1
    @KenWhite That's what I thought too, but I just tested, and no matter what value I pass, it still doesn't crash. Internally, it successfully sends Windows messages for `WM_SETTEXT` and `CM_TEXTCHANGED`. – Jerry Dodge Jul 03 '18 at 19:01
  • 5
    Because it is wrapper around Windows control that uses messages to set actual values. And it has safe guard that prevents sending messages on nil controls. Of course, it also works because property setter is not virtual. – Dalija Prasnikar Jul 03 '18 at 19:08
  • 1
    @Ken Setting caption to the empty string certainly does do something. – David Heffernan Jul 03 '18 at 20:33

1 Answers1

20

The TButton control is a wrapper around the Win32 Button control. It uses the Windows messaging system to operate upon it. And the core method for doing so, TControl.Perform(), has a built in safeguard against sending messages if Self is nil:

function TControl.Perform(Msg: Cardinal; WParam: WPARAM; LParam: LPARAM): LRESULT;
var
  Message: TMessage;
begin
  Message.Msg := Msg;
  Message.WParam := WParam;
  Message.LParam := LParam;
  Message.Result := 0;

  if Self <> nil then // <-- here
    WindowProc(Message); 

  Result := Message.Result;
end;

Caption is a property whose setter uses the non-virtual TControl.GetText() and TControl.SetText() methods, which can be safely called upon nil objects, as their functionality rely on sending various messages (WM_GETTEXTLEN and WM_SETTEXT) to the control, and only involve local variables or passed parameters. So the actual object is not accessed when nil, thus no crash.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159