Problema com o armazenamento de ponteiros COM no objeto singleton mundial
-
19-09-2019 - |
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?
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ê:
- Você chama QueryInterface em alguns ponteiro. Ele vai chamar AddRef nesse objeto.
- 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).
- 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;
}
}