Question

Historique

L'application Je travaille avec a plusieurs DLL COM.

L'une des DLL COM a un objet global singleton qui stocke des pointeurs vers des interfaces COM dans d'autres DLL. Parce qu'il est un objet global singleton, j'ai employé idiome initialisation paresseuse, car il est possible que l'interface Je tente d'obtenir un pointeur vers existe dans une DLL qui n'a pas encore été chargé.

( Side note: Ceci est particulièrement important lors de l'enregistrement d'une DLL unique, comme les objets globaux seront construits dans le processus de regsvr32, et je ne veux pas la DLL pour tenter d'obtenir une une interface à une autre DLL au cours de ce processus).

Par exemple, ma méthode paresseuse d'initialisation ferait quelque chose comme ceci:

CComPtr<IMyOtherObject>&
CGlobalSingleton::
GetMyOtherObject()
{
    // SNIP: Other code removed for clarity...

    if (! m_pMyOtherObject)
    {
        hr = pUnknown->QueryInterface(IID_IMyOtherObject,
            (void**) &m_pMyOtherObject);
    }

    return m_pMyOtherObject;
}

NOTE:. m_pMyOtherObject est une variable membre du type CComPtr

L'initialisation paresseuse ne peut pas être utile à mon problème ici, mais je suis, y compris pour l'exhaustivité.

Problème

Ce que je constate est que, dans certaines circonstances, je me assertions quand ma demande a échoué arrête. Cependant, si je change mon code pour appeler QueryInterface() tous temps je besoin d'accéder IID_IMyOtherOBject (plutôt que de les stocker en tant que variable membre) ce qui empêche les assertions.

Cela me semble être un objet COM question de vie. Mon hypothèse est que parce que je suis storing un pointeur COM, il doit y avoir une sorte de synchronisation entre la destruction de l'interface COM que je pointe, et mon pointeur vers elle.

Ma compréhension de la classe CComPtr (dont je me sers) est qu'il enlève beaucoup des maux de tête de traiter des questions de vie (à savoir appeler AddRef() et Release()). Mais il ne semble pas fonctionner dans mon cas.

Quelqu'un peut-il choisir ce que je peux faire mal?

Était-ce utile?

La solution

Au lieu de mettre en œuvre votre propre singleton mondiale, regardez en utilisant IGlobalInterfaceTable l'interface à la place. Il est un singleton qui est fourni par le système d'exploitation au niveau du processus. L'un de vos DLL peuvent mettre leurs objets COM dans la table, et les autres DLL peut les retrouver en cas de besoin. Tout ce que vous devez mettre en œuvre de votre part est une façon pour les DLL pour échanger les cookies DWORD de la table entre eux.

Autres conseils

Vous retourner une référence au pointeur intelligent qui pourrait ne pas augmenter le nombre de références. Désolé, je vérifierais mais il est tard ici. C'est mon intuition et il correspond à ce que vous décrivez -. Examiner les constructeurs de copie pour CComPtr

Hope qui aide,

K

Un coup de poignard sauvage dans l'obscurité: Est-il possible que CGlobalSingleton pourrait être détruit après CoUninitialize() est appelé, en aucun cas? Si elle était, et m_pMyOtherObject a donc été également détruite après COM désinitialisation, ce serait une autre façon de provoquer la violation d'accès Igor mentionné.

Je soupçonne que le problème est dans votre compréhension de la sémantique copie / affectation de la classe CComPtr; Je ne suis pas particulièrement familier avec CComPtr, mais dans mon expérience des pointeurs intelligents ont tendance à ne pas fonctionner comme vous pourriez les attendre à. D'abord, vous devriez lire la documentation CComPtr et assurez-vous de comprendre comment cela fonctionne (il ne serait pas mal à regarder le code source, que ce soit). Vous pouvez également essayer de mettre des points d'arrêt dans le AddRef () et Release () membres de CComPtr pour voir ce qui se passe pendant et après l'appel à GetMyOtherObject (), en particulier si vous stocker temporairement la valeur de retour et il est hors de portée.

Sons comme m_pMyOtherObject est encore en vie lorsque vous arrêtez votre application. En plus de copier soit les problèmes du constructeur m_pMyOtherObject devrait être un CComPtr ou CGlobalSingleton doit appeler la méthode de m_pMyOtherObject de Release sur la destruction.

Edité par souci de clarté.

Modifier a juste fait un test rapide et n'a pas rencontré de problèmes en utilisant la fonction renvoyant une référence à CComPtr. il ne Bien que cela soit un peu inhabituel ne provoque aucun problème de comptage de référence.

Je voulais étendre bien sur ce qui se passe si m_pMyOtherObject n'est pas un pointeur intelligent. Dans ce scénario, il ne sera jamais publié. Permettez-moi de vous montrer pourquoi:

  1. Vous appelez QueryInterface sur un certain pointeur. Il appellera AddRef sur cet objet.
  2. Vous revenez soit CComPtr & CComPtr & ou pointeur d'interface nue. C'est largement hors de propos. Aucune opération de comptage ref lieu (sauf si vous attribuez la valeur de retour à une autre CComPtr, qui Addref il. Mais depuis que CComPtr va équilibrer avec un appel pour le libérer n'a pas d'importance).
  3. Qu'est-ce que vous vous retrouvez avec est 1 appel à AddRef et 0 à libérer ou 2 appels à AddRef et 1 à la libération. En d'autres termes, ils sont déséquilibrés et vous avez une fuite de référence.

Pour éviter cela, vous devez structurer votre programme comme celui-ci:

class CGlobalSingleton{

CComPtr<IMyOtherObject> m_spMyOtherObject;

IMyOtherObject* GetMyOtherObject()
{
    // SNIP: Other code removed for clarity...

    if (! m_spMyOtherObject)
    {
        //pUnknown gets AddRef'ed, but that's OK, m_spMyOtherObject will call release when CGlobalSingleton goes out of scope
        hr = pUnknown->QueryInterface(IID_IMyOtherObject,
            (void**) &m_spMyOtherObject);
    }

    return m_pMyOtherObject;
}
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top