Runtime Error 216 [Résolu]

jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention - 17 févr. 2007 à 12:04 - Dernière réponse : jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention
- 18 févr. 2007 à 13:22
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
Afficher la suite 

14 réponses

Répondre au sujet
f0xi 4304 Messages postés samedi 16 octobre 2004Date d'inscription 9 mars 2018 Dernière intervention - 18 févr. 2007 à 03:03
+3
Utile
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)
Cette réponse vous a-t-elle aidé ?  
Commenter la réponse de f0xi
jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention - 18 févr. 2007 à 13:07
+3
Utile
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
Cette réponse vous a-t-elle aidé ?  
Commenter la réponse de jnmchl
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 17 févr. 2007 à 13:28
0
Utile
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
Commenter la réponse de WhiteHippo
jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention - 17 févr. 2007 à 14:07
0
Utile
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
Commenter la réponse de jnmchl
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 17 févr. 2007 à 15:03
0
Utile
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
Commenter la réponse de WhiteHippo
jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention - 17 févr. 2007 à 16:12
0
Utile
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
Commenter la réponse de jnmchl
jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention - 17 févr. 2007 à 16:33
0
Utile
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
Commenter la réponse de jnmchl
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 17 févr. 2007 à 17:55
0
Utile
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
Commenter la réponse de WhiteHippo
f0xi 4304 Messages postés samedi 16 octobre 2004Date d'inscription 9 mars 2018 Dernière intervention - 17 févr. 2007 à 19:35
0
Utile
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)
Commenter la réponse de f0xi
jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention - 17 févr. 2007 à 22:12
0
Utile
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
Commenter la réponse de jnmchl
jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention - 17 févr. 2007 à 22:15
0
Utile
"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
Commenter la réponse de jnmchl
florenth 1105 Messages postés dimanche 1 août 2004Date d'inscription 17 août 2008 Dernière intervention - 18 févr. 2007 à 13:01
0
Utile
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 !
Commenter la réponse de florenth
florenth 1105 Messages postés dimanche 1 août 2004Date d'inscription 17 août 2008 Dernière intervention - 18 févr. 2007 à 13:02
0
Utile
Mince ! j'avais pas tourné la page !
Commenter la réponse de florenth
jnmchl 67 Messages postés dimanche 16 octobre 2005Date d'inscription 13 novembre 2009 Dernière intervention - 18 févr. 2007 à 13:22
0
Utile
MonGRP.Tbz[i]:=StGRP;





Jean-Michel
Commenter la réponse de jnmchl

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.