J'ai écrit un ensemble de composants qui se connectent les uns aux autres via des propriétés d'interface publiées. Ils sont enregistrés et installés dans un package de conception.
L'utilisation de propriétés d'interface publiées n'est pas si courante en Delphi, et donc, sans grande surprise, cela ne semble pas fonctionner si bien.
Cela fonctionne bien lorsque les composants résident sur le même formulaire, cependant les liens de propriété d'interface entre les composants sur des formulaires différents posent problème.
Contrairement aux liens d'objets avec des composants sur un autre formulaire, les liens d'interface ne semblent pas être reconnus par l'IDE. Ce que je veux dire est mieux décrit par un exemple, lorsque vous avez 2 formulaires ouverts dans l'IDE, et que vous avez des liens entre les composants sur eux, alors essayer de passer à la vue formulaire en tant que texte (Alt+F12) ferait correctement remarquer à l'IDE que :
Module 'UnitXXX.pas' has open descendents or linked modules. Cannot close.
Mais si la propriété est une interface alors cela ne se produit pas, à la place le lien est rompu (et c'est le meilleur scénario lorsque vous utilisez le mécanisme de Notification pour effacer les références, sinon vous vous retrouvez avec un pointeur invalide)
Un autre problème, probablement en conséquence du même bogue, est que lorsque vous ouvrez un projet dans l'IDE, l'ordre dans lequel les formulaires seront réouverts est indéfini, donc l'IDE peut essayer d'ouvrir un formulaire qui contient des composants ayant des liens d'interface vers des composants sur un autre formulaire, mais cet autre formulaire n'est pas recréé encore. Cela aboutit donc soit à une AV soit à des liens rompus.
Retour dans les années 90 lorsque j'utilisais des Ensembles de données
et des Sources de données
je me souviens de problèmes similaires avec les liens entre les formulaires qui disparaissaient, donc c'est un peu similaire.
Comme solution temporaire j'ai ajouté des propriétés publiées en double, pour chaque propriété Interface j'ai ajouté une autre déclarée en tant que TComponent
. Cela permet à Delphi de reconnaître qu'il y a un lien entre les formulaires, mais c'est une solution de contournement assez laide à dire le moins.
Donc je me demande s'il y a quelque chose que je peux faire pour résoudre ce problème ? C'est un bogue de l'IDE et probablement pas directement corrigible, mais peut-être puis-je remplacer quelque chose ou autrement m'insérer dans le mécanisme de streaming pour contourner plus efficacement ce bogue.
Je ne suis jamais allé aussi loin dans le mécanisme de streaming, mais je soupçonne que le mécanisme de Fixup est censé gérer cela. Il y a un csFixups
TComponentState
donc j'espère qu'une solution de contournement est possible.
Édit : Utilisation de D2007.
Mise à jour :
Nouvel exemple reproductible mis à jour téléchargé sur http://www.filedropper.com/fixupbugproject2
Ajouté property RéférenceComposant: TComponent
pour qu'il soit facile de comparer et de tracer streaming d'interface vs composant.
J'ai limité le problème au niveau de l'assembleur qui dépasse mes compétences.
Dans la procédure GlobalFixupReferences
située dans l'unité classes
elle appelle :
(GetOrdProp(FInstance, FPropInfo) <> 0)
qui exécute finalement :
function TInterfacedComponent.GetInterfaceReference: IInterface;
begin
// décommentez le code ci-dessous pour éviter l'exception
{ if (csLoading in ComponentState) and (FInterfaceReference = nil) then
// ne pas assigner de valeur à result pour éviter l'exception
else
}
result := FInterfaceReference; // <----- L'exception se produit ici
end;
Comme vous pouvez le voir à partir du commentaire, la seule façon que j'ai trouvée pour éviter l'exception est de ne pas attribuer de valeur au résultat, mais cela casse la fonctionnalité puisque la comparaison ci-dessus dans GlobalFixupReferences
échoue en raison de GetOrdProp <> 0
, ce qui rompt le lien.
En traçant plus en profondeur, l'emplacement exact de l'exception se trouve dans
procedure _IntfCopy(var Dest: IInterface; const Source: IInterface);
dans l'unité system
Cette ligne en particulier déclenche une lecture de l'adresse 0x80000000
{ Maintenant, nous sommes dans les cas moins courants. }
@@NilSource:
MOV ECX, [EAX] // obtenir la valeur actuelle
Alors, pourquoi le MOV
échoue et ce qu'il ne va pas avec ECX
ou EAX
je n'en ai aucune idée.
5 votes
C'est une question intéressante. Cela semble un peu au-delà de mon expérience personnelle. Je soupçonne que si vous aviez un projet de démonstration, cela aiderait tout enquêteur en herbe.
1 votes
@DavidHeffernan Il est assez facile de reproduire, il vous suffit d'avoir une descendance de TComponent avec une propriété publiée de type IInterface. Enregistrez-le, installez le package et déposez-en un sur chacun des deux formulaires vierges. Cela étant dit, je pourrais le faire moi-même et vous laisser jouer avec...
1 votes
Je pense que cela aiderait si vous le faisiez. Baissez les barrières pour nous.
0 votes
Quelle(s) version(s) de Delphi utilisez-vous? Peut-être important pour toute solution / contournement?
1 votes
Apparemment
GetOrdProp
n'aime pas les propriétés avec une fonction getter. Si vous changezproperty InterfaceReference ... read GetInterfaceReference
àproperty InterfaceReference ... read FInterfaceReference
, l'AV disparaît.0 votes
@JensMühlenhoff Oui, le problème semble être dans GetOrdProp, pour les propriétés d'interface avec un getter, le code ASM est incorrect, peut-être qu'il ne configure pas correctement les registres avant d'appeler le getter, donc l'accès/l'attribution de résultats dans le getter provoque un AV.
0 votes
@DanielMauric Peut-être que le système de streaming ne devrait pas appeler GetOrdProp en premier lieu, mais mes connaissances dans ce domaine sont trop limitées.
0 votes
@JensMühlenhoff GetOrdProp fonctionne avec un champ donc je suppose que c'est correct. L'intuition me dit que le problème est que quelque soit l'endroit en mémoire/enregistrement utilisé pour le résultat de l'obtenteur n'est pas effacé avant l'appel, donc définir le résultat tente de diminuer le nombre de références sur un emplacement mémoire aléatoire. Pas de preuve, juste des suppositions.
0 votes
Voir stackoverflow.com/questions/6278381/… -- cela pourrait offrir des informations connexes.