Search code examples
c++c++builderc++builder-6

Deleting dynamically created control borland 6 c++ builder


I have a problem deleting a dynamically created button in Borland C++Builder 6.

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner) {
  TButton *but = new TButton(this);
  but->Left = 100;
  but->Top = 100;
  but->OnClick = click;
  Form1->InsertControl(but);
}

void __fastcall TForm1::click(TObject *Sender) {
    delete Sender;
}

When I click on a created button the error "Access violation at address 40005905 in module 'rtl60.bpl', read of address 00000018" appears

I know its wrong to assign a method to button that delets it, but I really need deleting button by pressing on it


Solution

  • There are two bugs in your code.

    1. the global Form1 variable has not been assigned yet when the TForm1 constructor is called, so your Form1->InsertControl(but) statement is invalid and likely to crash. Use this-> instead of Form1->. However, you are not suppossed to call InsertControl() directly, set the button's Parent property instead:

      but->Parent = this;
      
    2. It is not safe to delete the Sender of an event while the event handler is running. The RTL still needs access to the object after the handler has exited (as evident by your AccessViolation error). You will have to delay the delete, such as with a short timer:

      void __fastcall TForm1::DeleteButtonTimerElapsed(TObject *Sender)
      {
          TObject *obj = reinterpret_cast<TObject*>(DeleteButtonTimer->Tag);
          DeleteButtonTimer->Tag = 0;
          DeleteButtonTimer->Enabled = false;
          delete obj;
      }
      
      void __fastcall TForm1::click(TObject *Sender)
      {
          DeleteButtonTimer->Tag = reinterpret_cast<int>(Sender);
          DeleteButtonTimer->Enabled = true;
      }
      

      Or post a custom message to yourself using PostMessage() (I prefer this approach):

      #define WM_DELETE_OBJECT (WM_USER + 1)
      
      void __fastcall TForm1::WndProc(TMessage &Message)
      {
          if (Message.Msg == WM_DELETE_OBJECT)
              delete reinterpret_cast<TObject*>(Message.LParam);
          else
              TForm::WndProc(Message);
      }
      
      void __fastcall TForm1::click(TObject *Sender)
      {
          TButton *btn = static_cast<TButton*>(Sender);
          btn->OnClick = NULL;
          PostMessage(Handle, WM_DELETE_OBJECT, 0, reinterpret_cast<LPARAM>(Sender));
      }