Pergunta

Fundo

A aplicação que estou trabalhando tem várias DLLs.

Uma das DLLs tem um objeto singleton global, que armazena ponteiros para COM interfaces em outras DLLs. Porque é um objeto singleton global, que têm empregado a inicialização lenta idioma porque é possível que a interface Eu estou tentando obter um ponteiro para existe em uma DLL que ainda não foi carregado.

( Side-note: Isto é especialmente importante quando registrar um único DLL, como os objetos globais será construído dentro do processo regsvr32, e eu não quero que a DLL para tentar adquirir uma interface para outra DLL durante este processo.)

Por exemplo, o meu método de inicialização lenta faria algo assim:

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

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

    return m_pMyOtherObject;
}

NOTA:. m_pMyOtherObject é uma variável membro do tipo CComPtr

A inicialização lenta pode não ser relevante para o meu problema aqui, mas eu estou incluindo-o para ser completo.

Problema

O que tenho notado é que, em algumas circunstâncias, eu me afirmações falhou quando meus aplicativo é desligado. No entanto, se eu alterar meu código para QueryInterface() chamada todas vez que eu preciso de acesso IID_IMyOtherOBject (em vez de armazená-lo como uma variável de membro) Isto evita que as afirmações.

Esta me parece ser um problema de vida útil do objeto COM. Minha hipótese é que porque eu sou storing um ponteiro COM, é necessário que haja algum tipo de sincronização entre a destruição da interface COM que estou apontando, e meu próprio ponteiro para ele.

O meu entendimento da classe CComPtr (que eu estou usando) é que ele tira um monte de dores de cabeça de lidar com questões da vida (ou seja, chamando AddRef() e Release()). Mas isso não parece estar funcionando no meu caso.

Alguém pode escolher o que eu posso estar fazendo errado?

Foi útil?

Solução

Em vez de implementar seu próprio singleton global, olhada através do IGlobalInterfaceTable interface em vez. É um solteirão que é fornecido pelo sistema operacional no nível do processo. Qualquer um dos seus DLLs podem colocar seus objetos COM na tabela, e as outras DLLs pode recuperá-los quando necessário. Tudo que você precisa para implementar em sua parte é uma maneira para que as DLLs para trocar os cookies DWORD da tabela com o outro.

Outras dicas

Você está retornando uma referência para o ponteiro inteligente que não pode estar aumentando a contagem de referência. Desculpe, eu iria verificar, mas é tarde aqui. Esse é o meu palpite e que se encaixa o que você está descrevendo -. Olhar para construtores de cópia para CComPtr

Espero que ajude,

K

Uma punhalada selvagem no escuro: É possível que CGlobalSingleton poderia ter destruído após CoUninitialize() é chamado, sob quaisquer circunstâncias? Se fosse, e m_pMyOtherObject foi, portanto, também destruída após uninitialization COM, seria uma outra maneira de provocar a violação de acesso que Igor mencionado.

Eu suspeito que o problema está na sua compreensão da semântica copy / atribuição da classe CComPtr; Eu não estou particularmente familiarizado com CComPtr, mas na minha experiência ponteiros inteligentes tendem a não funcionar da maneira que você pode esperar que eles. Primeiro você deve ler a documentação para CComPtr e certifique-se de compreender como ele funciona (ele não iria prejudicar a olhar para o código-fonte, qualquer um). Você também pode tentar colocar alguns pontos de interrupção no AddRef () e Release () membros da CComPtr para ver o que acontece durante e após a chamada para GetMyOtherObject (), especialmente se você está armazenando temporariamente o valor de retorno e sai do escopo.

Parece que m_pMyOtherObject ainda está vivo quando você desligar sua aplicação. Além de cópia questões construtor m_pMyOtherObject deve ser um CComPtr ou CGlobalSingleton deve chamar o método m_pMyOtherObject de Release sobre destruição.

editado para maior clareza.

Editar Apenas fiz um teste rápido e não encontrar quaisquer problemas com a função retornando uma referência a CComPtr. Enquanto isso é um pouco incomum não causar quaisquer problemas de contagem de referência.

Eu queria expandir embora sobre o que acontece se m_pMyOtherObject não é um ponteiro inteligente. Neste cenário que nunca vai ser liberado. Deixe-me mostrar-lhe porquê:

  1. Você chama QueryInterface em alguns ponteiro. Ele vai chamar AddRef nesse objeto.
  2. Você retornar tanto CComPtr & CComPtr & ou ponteiro de interface nu. Isso é em grande parte irrelevante. Nenhuma operação de contagem de ref ter lugar (a menos que você atribui o valor de retorno para outro CComPtr, que irá AddRef-lo. Mas desde que CComPtr vai equilibrar isso com uma chamada para liberá-lo não importa).
  3. O que você acabar com é 1 chamada para AddRef e 0 a liberação ou 2 chamadas para AddRef e 1 a Release. Em outras palavras, eles são desequilibrado e você tem um vazamento de referência.

Para evitar isso, você precisa estruturar o seu programa como este:

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;
}
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top