Plantage inspecteur d'objet

Signaler
Messages postés
991
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
-
Messages postés
1154
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
-
Salut tout le monde.

J'ai un bug dans l'IDE de delphi, et je sais pas si ca peut se resoudre.
Je m'explique:
J'ai un composant, qui a comme propriete published un objet Obj.
l'affectation d'une autre propriété de ce composant va detruire ou remplacer cet objet Obj.
donc, dans l'inspecteur d'objets, quand je selectionne mon composant, j'ai :
 Comp.prop1
+Comp.Obj
 Comp.prop2

si je change Comp.prop2, ca va detruire Comp.Obj et le remplacer par une autre instance (pas forcement la meme classe)
Je n'ai aucun probleme si Comp.Obj n'est pas deroulé dans l'inspecteur. Par contre, si je l'expand, mon inspecteur d'objets ressemble à :
 Comp.prop1

-Comp.Obj
   Obj.propX
   Obj.propY
Comp.prop2
et des que je modifie prop2 pour remplacer l'instance de mon Obj, j'ai un plantage (AV de Delphi).

Est-ce que je m'y prend mal?
Y a t'il moyen de collapser l'editeur par le code avant de remplacer l'instance?

Merci de votre aide

9 réponses


Espèce de petit coquin ... toi, tu nous dis pas tout

1)C'est un composant que tu fais  ? (je suis sur que oui )
2)Il y a t'il un lien entre Obj et propriété 2 ?
3)Est ce un composant visuel ?
4)Pourrait tu nous passer le coder stp ?
Messages postés
991
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
7
1) "J'ai un composant, qui ..." -> oui c'est un composant
2)oui, SetProp2 fait un free de obj puis un create.
3)le compo oui, mais peu importe, l'important est qu'il soit modifiable dans l'inspecteur d'objet
4)rhooo franky t'essaye de voler mon code de ma proprieté intelectuelle a
moi. Et ca je dis non, c'est top secret, c'est pour vendre a l'armee
americaine et je crois pas qu'ils apprecieront que tu entre en
possession du code (a moins que tu ne dispose d'un vaccin anti-napalm  )
Voici le bébé. le code du compo n'a pas grand interet, c'est le code de l'objet (precedament obj) qui est interessant. Je pense que ca pourra servir a d'autres, c'est un compo d'affichage d'infos. Un truc qui doit etre un design pattern decoration je crois. Il publie une fonction d'affichage, implementée differament selon le compo afficheur. Bref, voila le code:

CODE DE L'OBJET (Obj)

interface

type
  TIGHintLink = class(TControl)
  private
    FControl: TControl;
  protected
    function GetHintValue: string; virtual;
    procedure SetHintValue(const Value: string); virtual;
  public
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    constructor Create(AOwner: TComponent; AControl: TControl); reintroduce; virtual;
    destructor Destroy; override;
  published
    property Control: TControl read FControl;
    property HintValue: string read GetHintValue write SetHintValue;
  end;

  TIGHLStatusBar = class(TIGHintLink)
  private
    FPanelIndex: integer;
    function StatusBar: TStatusBar;
  protected
    function GetHintValue: string; override;
    procedure SetHintValue(const Value: string); override;
  public
    constructor Create(AOwner: TComponent; AControl: TControl); override;
  published
    property PanelIndex: integer read FPanelIndex write FPanelIndex;
  end;

implementation

type
  TFakeControl = class(TControl);  //pour acceder au "Caption"

{ TIGHintLink }

constructor TIGHintLink.Create(AOwner: TComponent; AControl: TControl);
begin
  inherited Create(AOwner);

  FControl := AControl;
  FControl.FreeNotification(self);
end;

destructor TIGHintLink.Destroy;
begin

  inherited;
end;

function TIGHintLink.GetHintValue: string;
begin
  if not Assigned(Control) then
    Exit; //<<<<<<<<<< Exit >>>>>>>>>>

  Result := TFakeControl(Control).Caption;
end;

procedure TIGHintLink.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if (AComponent FControl) and (Operation opRemove) then
  begin
    FControl := nil;
  end;

  //TODO : Self.Free;
end;

procedure TIGHintLink.SetHintValue(const Value: string);
begin
  if not Assigned(Control) then
    Exit; //<<<<<<<<<< Exit >>>>>>>>>>

  TFakeControl(Control).Caption := Value;
end;

{ TIGHLStatusBar }

constructor TIGHLStatusBar.Create(AOwner: TComponent; AControl: TControl);
begin
  if not (AControl is TStatusBar) then
    AControl := nil;

  inherited Create(AOwner, AControl);

  FPanelIndex := 0;
end;

function TIGHLStatusBar.GetHintValue: string;
begin
  if not Assigned(Control) then
    Exit; //<<<<<<<<<< Exit >>>>>>>>>>

  if StatusBar.SimplePanel then
    Result := StatusBar.SimpleText
  else if (FPanelIndex >= 0) and (FPanelIndex < StatusBar.Panels.Count) then
    Result := StatusBar.Panels[FPanelIndex].Text
  else
    Result := '';
end;

procedure TIGHLStatusBar.SetHintValue(const Value: string);
begin
  if not Assigned(Control) then
    Exit; //<<<<<<<<<< Exit >>>>>>>>>>

  if StatusBar.SimplePanel then
    StatusBar.SimpleText := Value
  else if (FPanelIndex >= 0) and (FPanelIndex < StatusBar.Panels.Count) then
    StatusBar.Panels[FPanelIndex].Text := Value;
end;

function TIGHLStatusBar.StatusBar: TStatusBar;
begin
  Result := TStatusBar(Control);
end;

UTILISATION DANS LE COMPO:

TMonCompo = class(Tcomponent)
private
      procedure SetHintView(const Value: TControl);
      function GetHintView: TControl;
published
      property HintLink: TIGHintLink read FHintLink;
      property HintView: TControl read GetHintView write SetHintView;
end;

procedure TMonCompo .SetHintView(const Value: TControl);
begin
  if Assigned(FHintLink) then
    FreeAndNil(FHintLink);

  if not Assigned(Value) then
    Exit; //<<<<<<<<<< Exit >>>>>>>>>>

  if Value is TStatusBar then
    FHintLink := TIGHLStatusBar.Create(self, Value)
  else
    FHintLink := TIGHintLink.Create(self, Value);
end;

function TMonCompo .GetHintView: TControl;
begin
  if Assigned(FHintLink) then
  begin
    Result := FHintLink.Control;
  end
  else
  begin
    Result := nil;
  end;
end;

Salut,

J'ai pas regardé dans le détail mais des choses me genent : voila une correction

interface

type
  TIGHintLink = class(TControl)
  private
    FControl: TControl;
  protected
    function GetHintValue: string; virtual;
    procedure SetHintValue(const Value: string); virtual;
  public
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    constructor Create(AOwner: TComponent; AControl: TControl); reintroduce; virtual;
    destructor Destroy; override;
  published
    property Control: TControl read FControl;
    property HintValue: string read GetHintValue write SetHintValue;
  end;

  TIGHLStatusBar = class(TIGHintLink)
  private
    FPanelIndex: integer;
    function StatusBar: TStatusBar;
  protected
    function GetHintValue: string; override;
    procedure SetHintValue(const Value: string); override;
  public
    constructor Create(AOwner: TComponent; AControl: TControl); override;
  published
    property PanelIndex: integer read FPanelIndex write FPanelIndex;
  end;

implementation

type
  TFakeControl = class(TControl);  //pour acceder au "Caption"

{ TIGHintLink }

constructor TIGHintLink.Create(AOwner: TComponent; AControl: TControl);
begin
  inherited Create(AOwner);
  FControl := AControl;
  FControl.FreeNotification(self);
end;

destructor TIGHintLink.Destroy;
begin

  inherited;
end;

function TIGHintLink.GetHintValue: string;
begin
  Result:='';
  if Assigned(Control) then
  Result := TFakeControl(Control).Caption;
end;

procedure TIGHintLink.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;  if (AComponent FControl) and (Operation opRemove) then
  FControl := nil;

  //TODO : Self.Free;
end;

procedure TIGHintLink.SetHintValue(const Value: string);
begin
  if Assigned(Control) then
  TFakeControl(Control).Caption := Value;
end;

{ TIGHLStatusBar }

constructor TIGHLStatusBar.Create(AOwner: TComponent; AControl: TControl);
begin
  if not (AControl is TStatusBar) then
    AControl := nil;
  inherited Create(AOwner, AControl);
  FPanelIndex := 0;
end;

function TIGHLStatusBar.GetHintValue: string;
begin
  Result:='';
  if Assigned(Control) then
    Begin
      If StatusBar.SimplePanel then Result := StatusBar.SimpleText;
      if (FPanelIndex >= 0) and (FPanelIndex < StatusBar.Panels.Count) then
      Result := StatusBar.Panels[FPanelIndex].Text;
    End; 
end;

procedure TIGHLStatusBar.SetHintValue(const Value: string);
begin
  if Assigned(Control) then
    Begin
      if StatusBar.SimplePanel then StatusBar.SimpleText := Value;
      if (FPanelIndex >= 0) and (FPanelIndex < StatusBar.Panels.Count) then
      StatusBar.Panels[FPanelIndex].Text := Value;
    End; 
end;

function TIGHLStatusBar.StatusBar: TStatusBar;
begin
  Result := TStatusBar(Control);
end;

UTILISATION DANS LE COMPO:

TMonCompo = class(Tcomponent)
private
      procedure SetHintView(const Value: TControl);
      function GetHintView: TControl;
published
      property HintLink: TIGHintLink read FHintLink;
      property HintView: TControl read GetHintView write SetHintView;
end;

procedure TMonCompo .SetHintView(const Value: TControl);
begin
  if Assigned(FHintLink) then
    FreeAndNil(FHintLink);
  if Assigned(Value) then
    Begin
      if Value is TStatusBar then FHintLink := TIGHLStatusBar.Create(self, Value)
      else FHintLink := TIGHintLink.Create(self, Value);
  End;     
end;

function TMonCompo .GetHintView: TControl;
begin
  Result:=Nil;
  if Assigned(FHintLink) then Result := FHintLink.Control;
end;

Question: Dans le OnCreate de ton composant a tu crée FHintLink ?
Messages postés
991
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
7
Question: Dans le OnCreate de ton composant a tu crée FHintLink ?
Réponse : Non, il est créé quand on affecte une valeur a la property HintView

le principe est que tu choisi un composant X sur la même fiche que le compo, et c'est X qui affichera les informations de mon compo.

un exemple serait de faire un composant hérite de TImage, surcharger la méthode MouseMove dans le genre :

procedure TMonImage.MouseMove(..., X, Y);
begin
  if Assigned(Self.HintLink) then
    Self.HintLink.HintValue := IntToStr(X) + ',' + IntToStr(Y);
end;

Tu pose la TMonImage, tu affecte sa propriété HintView à la StatusBar1 de la même form, et les infos s'affichent sur la statusbar. L'intérêt est que quel que soit le composant que tu choisis, il peut afficher le texte.

Le plantage a lieu uniquement quand la propriété HintLink est expanded (petit "-" a cote du nom de propriété).
NB : je suis sous D7

En ce qui concerne le code que tu as mis, la différence est que mes getters peuvent ne pas renvoyer de valeur. Effectivement, j'ai rajouté les Exit un peu machinalement après avoir codé tout ça, sans trop vérifier. Je vais tester ça mercredi.
Messages postés
991
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
7
bon j'ai fait le test avec le result toujours affecté: aucun changement.

Par contre, j'ai reussi a preciser le plantage :
j'affecte StatusBar1 à HintView -> Creation d'une instance de TIGHLStatusBar
j'expand HintLink -> je vois bien la propriete PanelIndex (propre a cette heritier de TIGHintLink)
j'affecte Panel1 à HintView -> Creation d'une instance de TIGHintLink
-> AV $0000000 parce que l'inspecteur d'objet contient une ligne 'PanelIndex' qui n'est pas disponible pour l'instance en cours de HintLink.

Ce qui ne plante pas :
 - si le HintLink est collapsé
 - si je passe d'un hintlink A à un hintlink B, si B a au moins les meme proprietes que A

je ne pense pas que ca puisse se resoudre. Ca doit etre un "bug" de l'IDE (mais bon c'est peut etre bizare de changer d'instance comme ca a la volée).
Messages postés
1154
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
2
Bonsoir,

Je ne pense pas que cela soit un bug de l'IDE; c'est certainement un problème de "récursivité" dans la destruction des composants. Quelquechose me chagrine dans ton code, c'est l'absence d'appel de RemoveFreeNotification.
Je te donne une piste à creuser (non testée, car entièrement faite en live en fonction du code ci-dessus ;) :

constructor TIGHintLink.Create(AOwner: TComponent; AControl: TControl);
begin
  inherited Create(AOwner);

  FControl := AControl;

  // FreeNotification seulement si AControl<>NIL
  if Assigned( FControl ) then
    FControl.FreeNotification(self);
end;

procedure TMonCompo .SetHintView(const Value: TControl);
begin
  // Si même composant, rien à faire
  if ( Value = FHintLink ) then Exit ;

  // Pas de suppression si c'est déjà le cas
  if Assigned(FHintLink) and ( not (csDestroying in FHintLink.ComponentState ) ) then
  begin
    FHintLink.RemoveFreeNotification(self);
    FreeAndNil(FHintLink);
  end ;

  if Assigned(Value) then 
  Begin
    if Value is TStatusBar
      then FHintLink := TIGHLStatusBar.Create(self, Value)
      else FHintLink := TIGHintLink.Create(self, Value);
  End;     
end;

Cordialement. <hr />"L'imagination est plus importante que le savoir." Albert Einstein
Messages postés
991
Date d'inscription
samedi 25 octobre 2003
Statut
Membre
Dernière intervention
29 août 2013
7
constructor TIGHintLink.Create(AOwner: TComponent; AControl: TControl);
begin
  inherited Create(AOwner);

  FControl := AControl;

  // FreeNotification seulement si AControl<>NIL : effectivement, il le faut. Ceci dit, je ne l'appelle pas avec
AControl=

nil. L'ide le fait peut-être !

  if Assigned( FControl ) then
    FControl.FreeNotification(self);
end;




procedure TMonCompo .SetHintView(const Value: TControl);
begin
  // Si même composant, rien à faire
  if ( Value = FHintLink.Control ) then Exit ; // je suppose la faute d'attention ;)

  // Pas de suppression si c'est déjà le cas
pour le csDestroying, pourquoi pas. Par contre, pourquoi le RemoveFreeNotification ?
j'ai regardé le fonctionnement de FreeNotification un jour. Ce que je me souviens c'est:
 procedure A.FreeNotification(B)
 begin
   if listeANotifier.indexOf(B) = -1 then
   begin
     listeANotifier.Add(B)
     B.freeNotification(A)
   end;
 end;

 procedure A.Destroy
 begin
   for B in listeANotifier do
     B.Notification(A, opRemove)
  end;
le fait de pas faire RemoveFreeNotification va faire :
 freeAndNil(FhintLink)
  -> FHintLink.notifierLesBonsCompos
     ->
self(
TMonCompo
).Notification
        -> Self.removeFreeNotification(FHintLink) //grace au inherited de Notification

  if Assigned(FHintLink) and ( not (csDestroying in FHintLink.ComponentState ) ) then
  begin
    FHintLink.RemoveFreeNotification(self);
    FreeAndNil(FHintLink);
  end ;

  if Assigned(Value) then 
  Begin
    if Value is TStatusBar
      then FHintLink := TIGHLStatusBar.Create(self, Value)
      else FHintLink := TIGHintLink.Create(self, Value);
  End;     
end;

je crois que c'est un bug, car comme je l'ai dit :
Ce qui plante, et ce qui ne plante pas:
 - si le HintLink est collapsé (dans tous les cas)
 - si je passe d'un hintlink A à un hintlink B, si B a au moins les meme proprietes que A
 
- si je passe d'un hintlink A à un hintlink B, si B n'a pas toutes les proprietes de A

Vous pensez pas?
Je crois que l'ide, quand on expand, lit les property published, et ajout une ligne a la list view. Des qu'il y a un changement, elle reafraichi la valeur de chaque ligne (et donc plante sur celles qui ne sont plus presentes). Il faudra que je teste avec un hitlink expanded, et de mettre le hintview à nil. je pense qu'il devrait planter.

Vous avez deja vu un tel type de comportement qui fonctionnait?
Pensez vous que ma methode de conception soit bizare ?
Messages postés
1154
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
2
Si le HintLink est collapsé, l'inspecteur d'objet ne parcourt pas tous les objets contenus et donc il est normal de ne pas planter.
Si il ne l'est pas (collapsé), est que l'inspecteur plante, c'est qu'il scrute un objet (et donc un pointeur) incohérent. J'ai eu un phénomène similaire il y quelques années lors du développement d'un composant qui créait automatiquement un autre composant. Celui-ci était lié à des composants dynamiques et était détruit dès que ceux-ci n'existaient plus.

Je jette un oeil pour retrouver le code du composant en question. Mais si je me souviens bien, le moyen employé pour débugguer ce composant a été de créer un fichier log afin de suivre l'ensemble des méthodes (leur début, leur fin) et la création et destruction des composants.

P.S. Je ne pourrais pas le donner ici (code propriétaire pour un client)

Cordialement. <hr />"L'imagination est plus importante que le savoir." Albert Einstein