2009-09-17 8 views
3

J'ai une classe qui implémente une interface, qui est rendue disponible pour les plugins. La déclaration de classe est assez simple. Il n'y a qu'une seule instance de cette classe pour une application entière. Lorsque la fonction qui renvoie l'interface est appelée, elle appelle _AddRef sur l'interface récupérée avant de la renvoyer comme résultat. Malheureusement, cela fonctionne jusqu'à ce que j'essaie de libérer l'objet (voir la section «finalisation») - il signale une opération de pointeur invalide. Si je le commente, cela fonctionne très bien (cependant FastMM signale des fuites de mémoire, donc l'objet n'est pas libéré).Delphi7, passant l'interface de l'objet - provoque une opération de pointeur invalide lors de la libération de l'objet

Voici la partie du code dans la fonction qui retourne l'interface (en fait c'est une QueryInterface surchargée de ma classe "ServicesManager").

if ConfigManager.GetInterface(IID, obj) then 
begin 
    ISDK_ConfigManager(obj)._AddRef; 
    result:= 0; 
end 

et le code de la classe ConfigManager ...

type 
    TConfigManager = class(TInterfacedObject, ISDK_ConfigManager) 
    private 
    ... 
    end; 

var 
    ConfigManager: TConfigManager; 
implementation 

... 

initialization 
    ConfigManager:= TConfigManager.Create(); 
finalization 
    if ConfigManager <> nil then 
    FreeAndNil(ConfigManager); //if I comment it out, it leaks the memory but no Invalid Ptr. Op. raises 

Qu'est-ce que je fais mal? Je dois transmettre une référence à exactement cette instance de ConfigManager.

+0

Que fait TConfigManager.Destroy? – Ozan

+0

rien qui causerait une erreur. pour être sûr que je l'ai commenté aussi bien avant de poster ici, ne m'a pas aidé ... – migajek

+0

ok j'ai trouvé une * solution de contournement *, pas un * correctif *. Au lieu d'appeler FreeAndNil, j'appelle ._release ... Mais toujours à la recherche d'une réponse !! – migajek

Répondre

10

Le conseil numéro un que vous entendrez lorsque vous traitez avec des interfaces est de jamais mélanger des références d'interface avec des références d'objet. Cela signifie qu'une fois que vous commencez à faire référence à un objet via une référence d'interface, vous cessez de vous y référer via une référence d'objet. Déjà. La raison en est que la première fois que vous affectez une variable d'interface, le nombre de références de l'objet devient 1. Lorsque cette variable est hors de portée ou reçoit une nouvelle valeur, le nombre de références devient zéro et l'objet se libère. Tout cela sans aucune modification de la variable de référence d'objet d'origine, donc lorsque vous essayez d'utiliser cette variable, ce n'est pas un pointeur nul, mais l'objet auquel il fait référence est parti - c'est une référence dangling. Lorsque vous essayez de libérer quelque chose qui n'existe pas, vous obtenez une exception d'opération-pointeur-invalide. Déclarer votre variable ConfigManager comme une interface.

Ne le libérez pas vous-même. Une fois que vous faites cela, vous pouvez déplacer la déclaration entière de TConfigManager dans la section d'implémentation car aucun code en dehors de cette unité ne s'y référera.

De même, il existe rarement une raison de fournir votre propre implémentation de QueryInterface. (Vous avez dit que vous l'avez dépassé, mais c'est impossible car ce n'est pas virtuel.) Celui fourni par TInterfacedObject devrait suffire. Celui que vous fournissez est causant une fuite de mémoire parce que vous incrémentez le nombre de référence quand vous ne devriez pas être. GetInterface appelle déjà _AddRef (en effectuant une affectation d'interface), de sorte que vous renvoyez des objets dont le nombre de références est gonflé.

+0

ad 1. ok, et que faire si je voudrais que mon formulaire principal implémente l'une des interfaces, et que ce soit passé comme le plugin le demande? BTW Je l'ai implémenté de la même façon (Form.GetInterface ... + _AddRef) et ça marche là-bas! ad 2.right, ne pas vraiment le surcharger, juste en utilisant "propre" version;) Pourquoi? parce que ce que je passe au plugin est l'interface à "services" qui est la seule chose que le plugin peut accéder et est très limité, n'expose aucune fonction. Cependant plus tard, il peut être utilisé pour accéder à différentes interfaces, avec le code likie ' (FApplication comme ISDK_ConfigManager) .DoSomething ... ' – migajek

+0

Dans la forme, '_AddRef' ne fait pas vraiment quoi que ce soit. Et je ne comprends pas pourquoi vous avez votre propre implémentation de 'QueryInterface'. * Toutes les interfaces * fournissent 'QueryInterface', même votre interface" service "qui, selon vous, n'expose aucune fonction. (Et s'il n'a aucune fonction, alors pourquoi existe-t-il?) –

+0

il existe pour fournir des interfaces pour tous les services disponibles, comme dans l'exemple donné;) – migajek

2

Vous avez dit que c'est un système de plugin? Est-ce que vous chargez vos plugins en tant que BPL? J'ai rencontré ce problème la semaine dernière, en fait. Vous ne pouvez pas compter sur la finalisation pour effacer vos références d'interface. Vous devez vous assurer de les effacer avant de décharger le plugin, ou son espace mémoire devient invalide. Edit: En "effaçant les références d'interface", j'entends appeler _Release, soit en le mettant manuellement à zéro, soit en laissant les références hors de portée. Si votre gestionnaire d'interface contient des références d'interface aux plugins, ils seront effacés lorsque le gestionnaire d'interface sera détruit.

+0

Non, les plugins sont des dll mais ils utilisent tous deux (app & plugins) des BPL partagées. Huh pourriez-vous s'il vous plaît dites-moi ce que vous voulez dire en effaçant les références ifaces? Comment avez-vous édité ma requête pour mettre en évidence la syntaxe? : P Je ne reçois pas cet éditeur sur StackOverflow, il ne gère pas les tags < code > pour moi ... – migajek

+0

StackOverflow ne fait pas de code-vue avec < code > tags. Il le fait par indentation spéciale, ou en arrière si vous incorporez du code dans un paragraphe. Je l'ai réparé en supprimant les étiquettes, en mettant en évidence le code, et en appuyant sur le bouton "010 101" au-dessus de l'éditeur, qui configure automatiquement le formatage. –

1

Je suis totalement d'accord avec Rob.

Ce qui aide probablement à réécrire votre code d'initialisation comme ci-dessous.

Maintenant, ConfigManager est de type ISDK_ConfigManager, et en lui affectant zéro, le compte de référence sera décrémenté. Lorsque le compte de référence devient zéro, il sera automatiquement libéré.

type 
    TConfigManager = class(TInterfacedObject, ISDK_ConfigManager) 
    private 
    ... 
    end; 

var 
    ConfigManager: ISDK_ConfigManager; 
implementation 

... 

initialization 
    ConfigManager:= TConfigManager.Create(); 
finalization 
    ConfigManager := nil; 
end; 

--jeroen

0

ne classe TConfigManager a une méthode déclarée comme "publié"?

Questions connexes