Pergunta

Temos um componente COM cuja implementação e definição de interface existem no código gerenciado, mas é impulsionado por um componente nativo. O componente gerenciado está retornando um SafeArray de volta ao código nativo através da seguinte declaração do método.

interface IExample {
  <return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UNKNOWN)>
  object[] DoSomeOperation()
}

A assinatura nativa gerada passa corretamente SafeArray.

Durante uma revisão de código, tenhamos apresentado algumas perguntas sobre chamadas para a matriz resultante com o SafeArrayGetElement. A questão é se o retorno de salva -se de salvações ou não um IUnknown instância que é addref'd ou não. Essencialmente, ele se resume a qual das seguintes opções está correto

Exemplo 1:

CComPtr<IUnknown> spUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&spUnk));

Exemplo 2:

IUnknown* pUnk;
hr = SafeArrayGetElement(pArray, &bounds, reinterpret_cast<void**>(&pUnk));

A documentação é muito fina nesse assunto. Inclui apenas a seguinte linha.

Se o elemento de dados for uma string, objeto ou variante, a função copia o elemento da maneira correta.

A definição de correto é um pouco ambígua.

Foi útil?

Solução

O primeiro método deve estar correto e estaria de acordo com o manuseio de objetos em todo o COM, presumivelmente a definição que você encontrou assumindo que o consumidor conhece a maneira correta.

Os outros itens mencionados exigem. Copiar uma variante ou um SafeArray carrega um addref implícito () quando eles contêm objetos. A variante não exige quando o VT_BYREF está presente, no entanto.

VariantCopy @ msdn
SafeArraycopy @ msdn

Esse comportamento não é intrínseco para proteger os raios ou variantes, pois faz parte das regras do manuseio de parâmetros no COM. Não há nada para impedir que alguém tente contornar as regras.

Para os parâmetros de entrada, não é responsabilidade do callee addref (), a menos que eles pretendam manter o ponteiro da interface para uso posterior. No entanto, outros casos de uso do parâmetro exigem.

Por exemplo, as interfaces colocadas em variantes ou outros contêineres devem ter pelo menos uma chamada addref () aplicada, caso contrário, isso criaria problemas ao usar variantes como parâmetros de saída dos métodos COM, pois a transferência de dados/referências é unidirecional. O objeto original poderá expirar quando a chamada chega ao seu destino. Da mesma forma, o organização de uma interface em um fluxo também requer um addref ().

Da mesma forma, a chamada por referência exige pelo menos uma chamada Addref também. Se não fosse esse o caso, qualquer chamada de longa duração adequada (digamos, por meio da DCOM) pode não chegar ao seu destino com a garantia de que o objeto referenciado ainda está vivo. As chamadas extras addref ()/release () são frequentemente ignoradas aqui, pois o objeto já deve estar em mais de 1 devido à criação no ou antes do escopo de chamada.

Se for possível modificar o componente e suas chamadas estiverem em processo, pode ser desejável usar o git. Isso permite que você passe um token, e será mais fácil marcar a interface nos apartamentos com. A vida útil dos objetos envolvidos se torna de responsabilidade do chamador durante a duração da chamada, e você poderá prender casos em que um objeto não poderia ser organizado.

Criando a tabela de interface global @ msdn

Também interessante é a nota de rodapé para o BSTRS.

Se a implementação de uma função que levar um parâmetro de referência BSTR atribuir um novo BSTR ao parâmetro, ele deverá liberar o BSTR anteriormente referenciado.

Funções de manipulação de string (COM)

Outras dicas

Deve ser addref: ED, mas não tenho informações em primeira mão de que esse é o caso (por exemplo, não li a fonte).

Eu acho que a documentação é bastante clara - copiar um ponteiro de interface 'corretamente' é addref: ing.

Se você quiser ter certeza, construa um objeto ATL super simples que implementa IUnknown, encher vários deles em um SAFEARRAY e colocar um ponto de interrupção em CComObjectBase<>::InternalAddRef (Se minha memória servir). Em seguida, depra uma chamada para SafeArrayGetElement E veja se o seu ponto de interrupção é atingido.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top