KCranky
KCranky

Reputation: 15

Delphi 7 Invalid Pointer Operation with Custom Component

I have a custom component class that I use as a framework to load comments and user details, etc, onto a form. The Class is a sub-class of a panel and contains 3 labels and a memo.

Upon Closing my form, or trying to free the object, i get a "Invalid Pointer Operation" error. I know this is from trying to free an object twice, or accessing RAM that is not available. However, I don't know how to fix it. It's holding me back quite a bit, as I have to load different comments on different topics, and I cannot until I clear the form of the current comments.

Here is the class-related code:

    type
      TSkeleton = class(TPanel)
        private
        fName : TLabel;
        fStudNo : TLabel;
        fTimeAndDate : TLabel;
        fComment : TMemo;
     public
       Constructor Create (AOwner: TComponent); overload; override;
       constructor Create(AOwner:TForm; sName, sStudNo, sTime, sDate, sComment: string; ComCount: integer); overload;
 end;
{ TSkeleton }

constructor TSkeleton.Create(AOwner: TComponent);
begin
//
end;

constructor TSkeleton.Create(AOwner: TForm; sName, sStudNo, sTime, sDate,
  sComment: string; ComCount: integer);
begin
  inherited Create(AOwner);
  Parent := AOwner;
  Width := 800;
  Height := 250;
  Top := 448+((ComCount-1)*250);
  Left := 16;
  BevelInner := bvSpace;
  BevelOuter := bvLowered;

  fName := TLabel.Create(fName);
  self.InsertControl(fName);
  with fName do
  begin
   Caption := sName;
   Font.Name := 'Garamond';
   Font.Size  := 30;
   Left := 7;
   Top := 4;
  end;

  fStudNo := TLabel.Create(fStudNo);
  self.InsertControl(fStudNo);
    with fStudNo do
      begin
       Caption := sStudNo;
       Font.Name := 'Garamond';
       Font.Size  := 15;
       Left := 15;
       Top := 52;
      end;

  fTimeAndDate := TLabel.Create(fTimeAndDate);
  self.InsertControl(fTimeAndDate);
    with fTimeAndDate do
      begin
       Caption := sTime + ' ' + sDate;
       Font.Name := 'Garamond';
       Font.Size  := 20;
       Left := 583;
       Top := 4;
      end;

  fComment := TMemo.Create(fComment);
  self.InsertControl(fComment);
    with fComment do
      begin
       Lines.Add(sComment);
       Font.Name := 'Garamond';
       Font.Size  := 12;
       Left := 152;
       Top := 56;
       Height := 161;
       Width := 633;
       ReadOnly := True;
       ScrollBars := ssVertical;
      end;

end;

If you would like to see the other code used (reading the textfile, creating the array of objects, etc) please say so. It's not directly related to the class so I did not think it would be necessary.

Thank you in advance.

Edit: Based on @Remy Lebeau's code, and @NGLN's comments, I have decided to post everything necessary.

After fixing the class based on @Remy's code, I was still receiving the error. This led me to believe the error was where I was using the class, particularly in the array of objects I was creating.

Previously, my code was

  for i := 0 to ComCount-1 do
  begin
    fArrObjects[i+1] :=  TSkeleton.Create(TargetForm);
    with fArrObjects[i+1] do
    begin
      Parent := TargetForm;
      TheName := fArrComments[i][0];
      StudNo := fArrComments[i][1];
      Time := fArrComments[i][2];
      Date := fArrComments[i][3];
      Comment := fArrComments[i][4];
      ComCount := i+1;
    end;

Changing

fArrObjects[i+1]

to

 fArrObjects[i]

solved the issue.

Thank you to @Remy for correcting the errors in the class.

Upvotes: 0

Views: 931

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596176

There are some problems with your code.

Parent := AOwner;

Do not set the Parent from inside of the constructor. It is the responsibility of the caller to set the Parent after the object is fully constructed first.

You do not have a destructor defined, and you are creating your child objects with nil Owners. TSkeleton should be the Owner instead, eg:

//fName := TLabel.Create(fName);
fName := TLabel.Create(Self);
...

You should not be calling InsertControl() directly. Use the Parent property instead, eg:

//self.InsertControl(fName);
fName.Parent := Self;
...

Why do you have two constructors? Your overridden constructor does not do anything, not even call the base constructor, and your custom constructor is not called at design-time (unless you have other code that is creating instances of TSkeleton programmably). I suggest you get rid of the custom constructor and expose published properties to manipulate the child controls as needed.

Lastly, since you are creating sub-components, you should mark them as such via TComponent.SetSubComponen().

With that said, try something more like this:

type
  TSkeleton = class(TPanel)
  private
    fName : TLabel;
    fStudNo : TLabel;
    fTimeAndDate : TLabel;
    fComment : TMemo;

    fTime: string;
    fDate: string;
    fComCount: Integer;

    function GetTheName: string;
    procedure SetTheName(const AValue: string);
    function GetStudNo: string;
    procedure SetStudNo(const AValue: string);
    procedure SetTime(const AValue: string);
    procedure SetDate(const AValue: string);
    function GetComment: string;
    procedure SetComment(const AValue: string);
    procedure SetComCount(AValue: integer);

  public
    constructor Create (AOwner: TComponent); override;

  published
    property TheName: string read GetTheName write SetTheName;
    property StudNo: string read GetStudNo write SetStudNo;
    property Time: string read fTime write SetTime;
    property Date: string read fDate write SetDate;
    property Comment: string read GetComment write SetComment;
    property ComCount: integer read fComCount write SetComCount;
  end;

constructor TSkeleton.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  Width := 800;
  Height := 250;
  Left := 16;
  BevelInner := bvSpace;
  BevelOuter := bvLowered;

  fName := TLabel.Create(Self);
  fName.SetSubComponent(True);
  fName.Parent := Self;
  fName.Font.Name := 'Garamond';
  fName.Font.Size  := 30;
  fName.Left := 7;
  fName.Top := 4;

  fStudNo := TLabel.Create(Self);
  fStudNo.SetSubComponent(True);
  fStudNo.Parent := Self;
  fStudNo.Font.Name := 'Garamond';
  fStudNo.Font.Size  := 15;
  fStudNo.Left := 15;
  fStudNo.Top := 52;

  fTimeAndDate := TLabel.Create(Self);
  fTimeAndDate.SetSubComponent(True);
  fTimeAndDate.Parent := Self;
  fTimeAndDate.Font.Name := 'Garamond';
  fTimeAndDate.Font.Size  := 20;
  fTimeAndDate.Left := 583;
  fTimeAndDate.Top := 4;

  fComment := TMemo.Create(Self);
  fComment.SetSubComponent(True);
  fComment.Parent := Self;
  fComment.Font.Name := 'Garamond';
  fComment.Font.Size  := 12;
  fComment.Left := 152;
  fComment.Top := 56;
  fComment.Height := 161;
  fComment.Width := 633;
  fComment.ReadOnly := True;
  fComment.ScrollBars := ssVertical;
end;

function TSkeleton.GetTheName: string;
begin
  Result := fName.Caption;
end;

procedure TSkeleton.SetTheName(const AValue: string);
begin
  fName.Caption := AValue;
end;

function TSkeleton.GetStudNo: string;
begin
  Result := fStudNo.Caption;
end;

procedure TSkeleton.SetStudNo(const AValue: string);
begin
  fStudNo.Caption := AValue;
end;

procedure TSkeleton.SetTime(const AValue: string);
begin
  if fTime <> AValue then
  begin
    fTime := AValue;
    fTimeAndDate.Caption := fTime + ' ' + fDate;
  end;
end;

procedure TSkeleton.SetDate(const AValue: string);
begin
  if fDate <> AValue then
  begin
    fDate := AValue;
    fTimeAndDate.Caption := fTime + ' ' + fDate;
  end;
end;

function TSkeleton.GetComment: string;
begin
  Result := fComment.Text;
end;

procedure TSkeleton.SetComment(const AValue: string);
begin
  fComment.Text := AValue;
end;

procedure TSkeleton.SetComCount(AValue: integer);
begin
  if fComCount <> AValue then
  begin
    fComCount := AValue;
    Top := 448+((FComCount-1)*250);
  end;
end;

Upvotes: 4

Related Questions