Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Destruktor löst Exception aus (https://www.delphipraxis.net/68174-destruktor-loest-exception-aus.html)

3_of_8 25. Apr 2006 18:13


Destruktor löst Exception aus
 
Morgen.

Ich habe folgendes Problem: Jedesmal, wenn ich den Destruktor einer von mir erstellten Klasse aufrufe, bekomme ich eine EInvalidPointer. Ich könnte es ja noch verstehen, wenn der Destruktor von mir überschrieben wäre, aber ich benutze den vererbten Destruktor (meine Klasse ist von TGraphicControl abgeleitet.)

Quelltext habe ich mal gepostet:

(Der gleiche Fehler tritt manchmal auch im Konstruktor auf. Aber dort scheint er zufällig aufzutreten, im Destruktor tritt er immer auf.)

Delphi-Quellcode:
unit Module;

interface

{$R 'res\icons\icons.res'}
{$R 'res\images\images.res'}

uses Windows, Messages, Types, Classes, Controls, Graphics, Basic,
  Buttons, Forms, Dialogs, SysUtils, Port, IniFiles;

const
   WM_DESTROY_MODULE=Messages.WM_USER+42;

type
   TMessage=record
      Msg: Word;
  end;

  TModule=class;

    TModuleDestructionEvent=procedure(Sender: TObject;
     var DoDestroy: Boolean; Module: TModule) of object;

  TRectArray=array of TRect;

  TPortArray=array of TPort;

  TSpeedButtonArray=array of TSpeedButton;

  TTool=(tlMove=0, tlDelete=1, tlWire=2, tlModule=3);

  TModuleInfo=record
    Group, ID: Byte;
    Title: string;
  end;

  TWirePoint=record
    X, Y: Integer;
  end;

  TWire=record
    Nodes: array of TWirePoint;
    BitWidth: Cardinal;
    Sender, Recipient: TModule;
  end;

  TModule=class(TGraphicControl)
  protected
    FMovable: Boolean;
    FInitialX, FInitialY: Integer;
    FImage: TBitmap;
    FInputPorts, FOutputPorts: TPortArray;
      FActiveTool: TTool;
    FTitle: String;
    FGroup, FID: Cardinal;
    FInputPositions, FOutputPositions: TRectArray;
    procedure HandleMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure HandleMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure HandleMouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
  public
    constructor Create(AOwner: TComponent); override;
    class function GetTitle: String;
    procedure Compute; virtual; abstract;
    property Group: Cardinal read FGroup;
    property ID: Cardinal read FID;
  published
    property Movable: Boolean read FMovable write FMovable default True;
    property Image: TBitmap read FImage write FImage;
    property InputPorts: TPortArray read FInputPorts write FInputPorts;
    property OutputPorts: TPortArray read FOutputPorts write FOutputPorts;
    property ActiveTool: TTool read FActiveTool write FActiveTool;
  end;

  TAutoLoadingModule=class(TModule)
  public
    constructor Create(AOwner: TComponent); override;
    procedure Paint; override;
  end;

  TModuleClass=class of TModule;

procedure AddModuleButton(Module: TModuleClass; var ModuleButtons:
  TSpeedButtonArray; Parent: TWinControl; OnClick: TNotifyEvent;
  Indent: Integer=4; GroupIndex: Integer=1);

implementation

procedure AddModuleButton(Module: TModuleClass; var ModuleButtons:
  TSpeedButtonArray; Parent: TWinControl; OnClick: TNotifyEvent;
  Indent: Integer=4; GroupIndex: Integer=1);
var Button: TSpeedButton;
begin
  Button:=TSpeedButton.Create(Parent);
  Button.Parent:=Parent;
  Button.Glyph.LoadFromResourceName(HINSTANCE, Module.ClassName);
  Button.Width:=28;
  Button.Height:=28;
  Button.Left:=Indent+length(ModuleButtons)*32;
  Button.Top:=4;
  Button.Tag:=Integer(Module);
  Button.OnClick:=OnClick;
  Button.GroupIndex:=GroupIndex;
  Button.Hint:=Module.GetTitle;
  Button.ShowHint:=True;
  Button.Flat:=True;
  setlength(ModuleButtons, length(ModuleButtons)+1);
  ModuleButtons[high(ModuleButtons)]:=Button;
end;

constructor TModule.Create(AOwner: TComponent);
var ini: TIniFile;
      CN: String;
    I, n: Integer;
begin
  inherited Create(AOwner);
  CN:=ClassName;
  if csOpaque in ControlStyle then ControlStyle:=ControlStyle- [csOpaque];
  OnMouseDown:=HandleMouseDown;
  OnMouseUp:=HandleMouseUp;
  OnMouseMove:=HandleMouseMove;
  FMovable:=True;
   ini:=TIniFile.Create(ModuleConfigFile);
  try
   if ini.SectionExists(CN) then
  begin
  FGroup:=ini.ReadInteger(CN,'Group',0);
  FID:=ini.ReadInteger(CN,'ID',0);
   FTitle:=ini.ReadString(CN,'Title','');
  n:=ini.ReadInteger(CN,'InputPorts',0);
  setlength(FInputPorts,n);
  for I:=1 to n do
     FInputPorts[I]:=TPort.Create(Compute,1,Rect(
       ini.ReadInteger(CN,'Input'+inttostr(I)+'Left',0),
      ini.ReadInteger(CN,'Input'+inttostr(I)+'Top',0),
      ini.ReadInteger(CN,'Input'+inttostr(I)+'Right',0),
      ini.ReadInteger(CN,'Input'+inttostr(I)+'Bottom',0)));
  n:=ini.ReadInteger(CN,'OutputPorts',0);
  setlength(FOutputPorts,n);
  for I:=1 to n do
     FOutputPorts[I]:=TPort.Create(Compute,1,Rect(
       ini.ReadInteger(CN,'Output'+inttostr(I)+'Left',0),
      ini.ReadInteger(CN,'Output'+inttostr(I)+'Top',0),
      ini.ReadInteger(CN,'Output'+inttostr(I)+'Right',0),
      ini.ReadInteger(CN,'Output'+inttostr(I)+'Bottom',0)));
  end;
  finally
     ini.Free;
  end;
end;

class function TModule.GetTitle: String;
var ini: TIniFile;
begin
ini:=TIniFile.Create(ModuleConfigFile);
try
Result:=ini.ReadString(ClassName,'Title','');
finally
ini.free;
end;
end;

procedure TModule.HandleMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if FMovable and (ActiveTool=tlMove) then
  begin
    Screen.Cursor:=crSizeAll;
    FInitialX:=X;
    FInitialY:=Y;
  end;
end;

procedure TModule.HandleMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if FMovable then
  if (ActiveTool=tlMove) then
  begin
    Screen.Cursor:=crDefault;
  end;
  if (ActiveTool=tlDelete) then
  begin
      PostMessage(parent.Handle,WM_DESTROY_MODULE,Integer(Self),0);
  end;
 end;

procedure TModule.HandleMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin
  if (Shift=[ssLeft])and FMovable and (ActiveTool=tlMove) then
  begin
    Left:=(Left+X-FInitialX)div 8*8;
    Top:=(Top+Y-FInitialY)div 8*8;
  end;
end;

constructor TAutoLoadingModule.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FImage:=TBitmap.Create;
  FImage.LoadFromResourceName(HInstance, '_'+ClassName);
  Width:=FImage.Width;
  Height:=FImage.Height;
  Constraints.MinWidth:=FImage.Width;
  Constraints.MaxWidth:=FImage.Width;
  Constraints.MinHeight:=FImage.Height;
  Constraints.MaxHeight:=FImage.Height;
end;

procedure TAutoLoadingModule.Paint;
begin
  inherited Paint;
  Canvas.Brush.Style:=bsClear;
  Canvas.Pen.Style:=psClear;
  Canvas.BrushCopy(FImage.Canvas.ClipRect, FImage,
    FImage.Canvas.ClipRect, clFuchsia);
end;

end.

Hawkeye219 25. Apr 2006 18:37

Re: Destruktor löst Exception aus
 
Hallo,

die Variablen FInputPorts und FOutputPorts sind dynamische Arrays, deren Elements die Nummern 0..n-1 tragen.
Du benutzt aber die Elemente 1..n und überschreibst damit den Speicher hinter den Arrays:

Delphi-Quellcode:
for I:=1 to n do
   FInputPorts[I]:=TPort.Create(Compute,1,Rect(
     ini.ReadInteger(CN,'Input'+inttostr(I)+'Left',0),
     ini.ReadInteger(CN,'Input'+inttostr(I)+'Top',0),
     ini.ReadInteger(CN,'Input'+inttostr(I)+'Right',0),
     ini.ReadInteger(CN,'Input'+inttostr(I)+'Bottom',0)));
n:=ini.ReadInteger(CN,'OutputPorts',0);
setlength(FOutputPorts,n);
for I:=1 to n do
   FOutputPorts[I]:=TPort.Create(Compute,1,Rect(
     ini.ReadInteger(CN,'Output'+inttostr(I)+'Left',0),
    ini.ReadInteger(CN,'Output'+inttostr(I)+'Top',0),
    ini.ReadInteger(CN,'Output'+inttostr(I)+'Right',0),
    ini.ReadInteger(CN,'Output'+inttostr(I)+'Bottom',0)));
end;
Gruß Hawkeye

Thorben77 25. Apr 2006 18:45

Re: Destruktor löst Exception aus
 
Außerdem musste Du alle Klasseninstanzen, die Du im Konstruktor und anderswo erstellst, im Destruktor wieder freigeben; konkret meine ich die TPort-Instanzen in Deinem Port-Array, die Du hier erstellst :wink: :
Delphi-Quellcode:
  for I:=1 to n do
     FInputPorts[I]:=TPort.Create(Compute,1,Rect(
       ini.ReadInteger(CN,'Input'+inttostr(I)+'Left',0),
      ini.ReadInteger(CN,'Input'+inttostr(I)+'Top',0),
      ini.ReadInteger(CN,'Input'+inttostr(I)+'Right',0),
      ini.ReadInteger(CN,'Input'+inttostr(I)+'Bottom',0)));
  n:=ini.ReadInteger(CN,'OutputPorts',0);
  setlength(FOutputPorts,n);
  for I:=1 to n do
     FOutputPorts[I]:=TPort.Create(Compute,1,Rect(
       ini.ReadInteger(CN,'Output'+inttostr(I)+'Left',0),
      ini.ReadInteger(CN,'Output'+inttostr(I)+'Top',0),
      ini.ReadInteger(CN,'Output'+inttostr(I)+'Right',0),
      ini.ReadInteger(CN,'Output'+inttostr(I)+'Bottom',0)));
  end;
Edit: Und Du solltest, wie Hawkeye219 schon schreibt, die for-Schleife so schreiben:
Delphi-Quellcode:
for I := 0 to n - 1 do

3_of_8 25. Apr 2006 18:54

Re: Destruktor löst Exception aus
 
:wall:

Komischerweise funktioniert es manchmal und manchmal nicht.

Und was den Destruktor betrifft, so ist der atm mein Hauptproblem. Und die TPort Instanzen sind dann zwar unschön, weil "Speichermüll", aber trotzdem nicht für die InvalidPointer Exception verantwortlich, oder?

EDIT: :wall: Wie war das mit dem Wald und den Bäumen? Naja egal, auf jeden Fall funktioniert es jetzt. Fragt mich nicht warum, aber es geht. Komischerweise konnte ich die Exceptions nicht mit Haltepunkten lokalisieren. Die Exception ist immer nach der letzten Anweisung im Konstruktor aufgetreten, bzw. an einer unbekannten Stelle im Destruktor.

Angel4585 25. Apr 2006 19:03

Re: Destruktor löst Exception aus
 
Zitat:

Zitat von 3_of_8
Komischerweise konnte ich die Exceptions nicht mit Haltepunkten lokalisieren. Die
Exception ist immer nach der letzten Anweisung im Konstruktor aufgetreten, bzw. an einer unbekannten Stelle im Destruktor.

Das hatte ich auch mal.. dann hab ich mir angewöhnt eine Application.OnException zuzuweisen und dort ALLE Exceptions abzufangen die ich nicht mit nem try except Block bekomme.

3_of_8 25. Apr 2006 19:04

Re: Destruktor löst Exception aus
 
Das ist aber nicht gerade guter Stil...

Thorben77 25. Apr 2006 19:12

Re: Destruktor löst Exception aus
 
Zitat:

Zitat von 3_of_8
Und was den Destruktor betrifft, so ist der atm mein Hauptproblem. Und die TPort-Instanzen sind dann zwar unschön, weil "Speichermüll", aber trotzdem nicht für die InvalidPointer-Exception verantwortlich, oder?

Selbst dann soltest Du sie freigeben:
Delphi-Quellcode:
TModule = class(TGraphicControl)
  public
    destructor Destroy; override;
  end;

...

destructor TModule.Destroy;
var
  I: Integer;
begin
  for I := Length(FInputPorts) - 1 downto 0 do
    FInputPorts[I].Free;
  for I := Length(FOutputPorts) - 1 downto 0 do
    FOutputPorts[I].Free;
  inherited Destroy;
end;
Das sollte Dich wohl nicht überfordern :wink: .


Zitat:

Zitat von 3_of_8
Das ist aber nicht gerade guter Stil...

*zustimm*

3_of_8 25. Apr 2006 21:09

Re: Destruktor löst Exception aus
 
Jaja, habe ich schon gemacht. Allerdings kommt "Speicherlecks entfernen" für mich erst nach "Das ganze zum Laufen bringen." ;)

Ich hab das schon gemacht, direkt nachdem das Programm funktioniert hat. Jetzt gehts auf zur Verdrahtung. Das wird kompliziert.


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:02 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz