Runtime Error 216 [Résolu]

Signaler
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
-
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
-
Bonjour,
J'ai lu tous (sauf oubli) les topics sur ce sujet...
 Malheureusement, je ne trouve pas la solution à mon problème.
Voilà j'ai donc une appli qui me fait le "RunTime Error 216" à un moment précis :
lorsque je quitte l'application. C'est frustrant car tout fonctionne bien, et dès qu'on quitte :
hop ce message d'erreur, laissant penser que le programmeur a laissé un bug, c'est pas propre !
Mes observations :
 - une fois j'ai retiré le fichier d'aide et ça ne le faisait plus
 - une fois j'ai rendu visible un Memo caché et ça ne le faisait plus
 - il me semble qu'une fois ça marchait puis plus alors que je n'étais pas intervenu sur le code !!!
Maintenant, je n'ai plus ce mémo car inutile et je ai enlevé le fichier d'aide ... et j'ai l'erreur !
Note : mon application use de "tout ce qui est à risque" dans la génération des RunTime Error 216 :
- utilisation d'objets perso crées en dynamique 
- utilisation d'un PChar (pour des messageBox) ...
mais bon ... à l'exécution ça marche très bien !
Faut-il libérer des trucs à la fermeture ? Est-ce un pb de mémoire ?
Merci de m'aider. Je commence à tourner en bourrique !
Jean-Michel

14 réponses

Messages postés
4202
Date d'inscription
samedi 16 octobre 2004
Statut
Modérateur
Dernière intervention
13 juin 2020
32
essaye plutot comme ça :

procedure TFPrinc.PnlTblClick(Sender: TObject);
begin
  if not BtAjoute.Enabled then
     Application.MessageBox(PChar(MessageValid),'Saisie incomplète',0);
end;

<hr size="2" width="100%" />Croc (click me)
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
1
Bonjour,

J'ai découvert d'où venait mon bug ...
Je défini un type :
  TGRP = record         // caractéristiques des groupements
    ID,Lib : string;
    Nz : integer;
    TbZ : array [0..12] of String;
  end;
et puis après ...
var MonGRP : TGRP; 
...
MonGRP[i]:=StGRP;
  avec i = 20 !!!

Curieusement lors de la compilation j'aurais dû avoir l'erreur "indice hors limite" ?
A ceux qui tiennent une base de données sur le RunTime Error 216 !

J'ai rectifié avec un tableau dynamique, et là impecable !
Je prends en compte toutefois toutes les autres recomandations, MERCI à tous.

Jean-Michel
Messages postés
1154
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
2
Bonjour,
Le plus simple pour découvrir d'où vient le problème c'est de créer un fichier log pour avoir un suivi de tout ce que fait ton application. Des logiciels comme Memory Sleuth, MemCheck(http://v.mahon.free.fr/pro/freeware/memcheck/) permmettent également le débuggage des problèmes de mémoire.

Cordialement.
<hr />"L'imagination est plus importante que le savoir." Albert Einstein
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
1
je l'ai fait : tout fonctionne nickel, jusqu'au bout ...
dès que je quitte l'application ... j'ai le message d'erreur : l'application est bien fermée !
J'ai l'impression qu'il fait peut-être appel à quelque chose qui est déjà détruit.
Pour info : je ne libère rien : on m'a dit que de toutes façons le CLOSE libère tout !
... je commence à douter.

Jean-Michel
Messages postés
1154
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
2
Pour ma part, si je crée un objet, un pointeur, etc... alors je me charge de le détruire... Ainsi je maitrise entièrement ce qui se passe dans l'application, et je n'ai pas de mauvaises surprises. Delphi ne dispose pas d'un véritable ramasse miettes, alors il me semble plus que risqué de lui laisser gérer TOUTES les libérations...

P.S. Je ne parlais pas de la gestion avec des interfaces

Cordialement.
<hr />"L'imagination est plus importante que le savoir." Albert Einstein
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
1
Oui, mais de toutes façons :
1) j'ai besoin des objets créés jusqu'à la fin ... à quel moment les détruire ?
 sur un évènement OnForm ? Close, CloseQuery, Destroy, Desactivate ?
2) j'ai fais un test j'ai mis entre {} toutes les parties de code des objets créés, donc avec zéro objet créé : même résultat ! 
Merci
Jean-Michel
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
1
Pour t'aider je joins une partie du code sur l'objet que je crée :

dans l'unité réservée à la définition des objets :

type
  TSegment = class(TCustomControl)
  private
    { Déclarations privées }
    FFX1,FFX2,FFY1,FFY2 : integer;
  protected
    { Déclarations protégées }
    procedure Paint; override;
  public
    { Déclarations publiques }
    constructor Create ( AOwner : TComponent); override;
    destructor Destroy; override;
  published
    { Déclarations publiées }
    Property X1 : integer read FFX1 write FFX1;
    property X2 : integer read FFX2 write FFX2;
    property Y1 : integer read FFY1 write FFY1;
    property Y2 : integer read FFY2 write FFY2;
  end;
...
constructor TSegment.Create ( AOwner : TComponent);
begin
  inherited Create ( AOwner);
  ...
end;

dans l'unité de la form principale ...

var TSeg:TSegment;
...
// Affichage des segments
    for i0:=0 to CompteurSeg-1 do begin
      TSeg := TSegment.Create(FPrinc);
      TSeg.X1:= ListSeg[i0].x1;
      TSeg.Y1:= ListSeg[i0].y1;
      TSeg.X2:= ListSeg[i0].x2;
      TSeg.Y2:= ListSeg[i0].y2;
    end;

Comment détruire ?
 TSeg.free;
il y en a une centaine ...
for i := Componentcount-1 downto 0 do 
  if (Components[i] is TSegment) then Components[i].free; 

Merci,

Jean-Michel
Messages postés
1154
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
2
Alors voyons, comment j'envisagerais l'affaire :
  1 - Création d'une TList pour maintenir la liste de chacun des segments.
  2 - La destruction de ceux ci se résumerait alors à parcourir les éléments de la liste et les détruire 1 à 1, avant de détruire la liste elle-même dans le destroy de la fiche.
  3 - Si le problème continue sans les objets crées, il faut vérifier si l'erreur ne vient pas d'un composant perso ou non posé sur la fiche.

N.B. Moi je préfère un bon vieux FreeAndNil à la méthode Free

Cordialement.
<hr />"L'imagination est plus importante que le savoir." Albert Einstein
Messages postés
4202
Date d'inscription
samedi 16 octobre 2004
Statut
Modérateur
Dernière intervention
13 juin 2020
32
Runtime Error 216 : general protection fault

ce qu'en dis microsoft :
erreur qui se produit quand l'ordinateur est infecté par le cheval de troie SubSeven

bon je pense qu'en tant qu'utilisateur avertis, tu as un anti-virus a jours et que tu scan regulierement ton pc avec un anti-malware.

liberer les objets ne suffit pas!
il y a une convention de liberation a respecter

exemple :

objet1 := {classe}.create({parent});

plus tard :

objet2 := {classe}.create({parent});

a la fermeture :

Incorrect :
objet1.Free;
objet2.Free;

Correct :


objet2.Free;
objet1.Free;

ceci :

var TSeg:TSegment;
...
    for i0:=0 to CompteurSeg-1 do begin
      TSeg := TSegment.Create(FPrinc);
      TSeg.X1:= ListSeg[i0].x1;
      TSeg.Y1:= ListSeg[i0].y1;
      TSeg.X2:= ListSeg[i0].x2;
      TSeg.Y2:= ListSeg[i0].y2;
    end;

n'est pas bien ! car a chaque Create seul FPrinc garde les references de TSeg et encore ce n'est pas sur.

il faut utiliser soit : un tableau dynamique de TSeg, soit mieux, un TObjectList qu'on derive pour utiliser avec TSeg.
on peu egalement utiliser les TCollectionItem et TCollection qui ont un fonctionnement a peu prés similaire au TObjectList.

exemple avec TObjectList :

type
 { TSegment }
 TSegment = class(TCustomControl)
  private
    FFX1,FFX2,FFY1,FFY2 : integer;
  protected
    procedure Paint; override;
  public
    constructor Create ( AOwner : TComponent); override;
    destructor Destroy; override;
  published
    Property X1 : integer read FFX1 write FFX1;
    property X2 : integer read FFX2 write FFX2;
    property Y1 : integer read FFY1 write FFY1;
    property Y2 : integer read FFY2 write FFY2;
  end;

  { TSegmentList }
  TSegmentList = class(TList)
  private
    FOwnsObjects: Boolean;
  protected
    procedure Notify(Ptr: Pointer; Action: TListNotification); override;
    function GetItem(Index: Integer): TSegment;
    procedure SetItem(Index: Integer; Segment : TSegment);
  public
    constructor Create; overload;
    constructor Create(AOwnsObjects: Boolean); overload;

    property Items[Index: Integer]: TSegment read GetItem write SetItem; default;
    function Add(Segment: TSegment): Integer;
    procedure Insert(Index: Integer; Segment: TSegment);
    function Extract(Item: TSegment): TSegment;
    function Remove(Segment: TSegment): Integer;
    function IndexOf(Segment: TSegment): Integer;
    function First: TSegment;
    function Last: TSegment;    function FindInstanceOf(AClass: TClass; AExact: Boolean True; AStartAt: Integer 0): Integer;
    property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
  end;

<hr size="2" width="100%" />
{ TSegmentList implementation }
constructor TSegmentList.Create;
begin
  inherited Create;
  FOwnsObjects := True;
end;

constructor TSegmentList.Create(AOwnsObjects: Boolean);
begin
  inherited Create;
  FOwnsObjects := AOwnsObjects;
end;

function TSegmentList.Add(Segment: TSegment): Integer;
begin
  Result := inherited Add(Segment);
end;

function TSegmentList.Extract(Item: TSegment): TSegment;
begin
  Result := TSegment(inherited Extract(Item));
end;

function TSegmentList.First: TSegment;
begin
  Result := TSegment(inherited First);
end;

function TSegmentList.Last : TSegment;
begin
  Result := TSegment(inherited Last);
end;

function TSegmentList.GetItem(Index: Integer): TSegment;
begin
  Result := inherited Items[Index];
end;

function TSegmentList.IndexOf(Segment: TSegment): Integer;
begin
  Result := inherited IndexOf(Segment);
end;

procedure TSegmentList.Insert(Index: Integer; Segment: TSegment);
begin
  inherited Insert(Index, Segment);
end;

function TSegmentList.FindInstanceOf(AClass: TClass; AExact: Boolean;
  AStartAt: Integer): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := AStartAt to Count - 1 do
    if (AExact and (Items[I].ClassType = AClass)) or
       (not AExact and Items[I].InheritsFrom(AClass)) then
    begin
      Result := I;
      break;
    end;
end;

procedure TSegmentList.Notify(Ptr: Pointer; Action: TListNotification);
begin
  if OwnsObjects then
    if Action = lnDeleted then
       TSegment(Ptr).Free;
  inherited Notify(Ptr, Action);
end;

function TSegmentList.Remove(Segment: TSegment): Integer;
begin
  Result := inherited Remove(Segment);
end;

procedure TSegmentList.SetItem(Index: Integer; Segment: TSegment);
begin
  inherited Items[Index] := Segment;
end;

<hr size="2" width="100%" />
{ dans form1 }

var
  TSL : TSegmentList;

procedure TForm1.FormCreate(Sender: TObject);
var N : integer;
begin
  TSL := TSegmentList.Create(true);
  for N := 0 to 9 do
      TSL.Add(TSegment.Create(Self));
end;

procedure TForm1.FormDestroy(Sender: TObject);
var N : integer;
begin
  for N := TSL.Count-1 to 0 do
      TSL.Remove(TSL.Items[N]);
  TSL.Free;
end;

<hr size="2" width="100%" />Croc (click me)
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
1
Bonsoir,
Oui, j'ai bien lu l'histoire du cheval de Troie : je ne suis pas infecté.

Je pensais bien que le code suivant n'est pas pur ...

var TSeg:TSegment;
...
    for i0:=0 to CompteurSeg-1 do begin
      TSeg := TSegment.Create(FPrinc);
      TSeg.X1:= ListSeg[i0].x1;
      TSeg.Y1:= ListSeg[i0].y1;
      TSeg.X2:= ListSeg[i0].x2;
      TSeg.Y2:= ListSeg[i0].y2;
    end;

pourtant l'erreur a subsisté même après avoir mis la création des objets hors service.
Je vais essayer tes préconisations, merci.
Sinon l'autre partie sensible du code pourrait être

var MessageValid : string;
...
// Aide à la validation
procedure TFPrinc.PnlTblClick(Sender: TObject);
var p:PansiChar;
begin
  if not BtAjoute.Enabled then begin
    GetMem(P,Length(MessageValid)+1);
    StrCopy(P,PChar(MessageValid));
    Application.MessageBox(P,'Saisie incomplète',0);
    FreeMem(P);
  end;
end;

MessageValid étant un string donc le contenu dépend deu traitement.

Merci
Jean-Michel
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
1
"Mon extrait de code" ... n'est pas bien ! car a chaque Create seul FPrinc garde les references de TSeg et encore ce n'est pas sur.

ceci signifie-t-il aussi qu'il se pourrait que FPrinc soit "saturé" au niveau mémoire ?

Jean-Michel
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
1
Pourquoi tu t'embettes tant avec ton Messagebox ?

Application.MessageBox(PChar(MessageValid), 'Saisie incomplète', 0);

Delphi te permet de transtyper directement un string en PChar sans avoir à en faire une copie !
Messages postés
1023
Date d'inscription
dimanche 1 août 2004
Statut
Membre
Dernière intervention
17 août 2008
1
Mince ! j'avais pas tourné la page !
Messages postés
63
Date d'inscription
dimanche 16 octobre 2005
Statut
Membre
Dernière intervention
13 novembre 2009
1
MonGRP.Tbz[i]:=StGRP;





Jean-Michel