Problema con el almacenamiento de punteros COM en objeto singleton mundial
-
19-09-2019 - |
Pregunta
Fondo
La aplicación que estoy trabajando con tiene varias DLL COM.
Una de las DLL COM tiene un objeto único mundial, que almacena punteros a interfaces COM en otros archivos DLL. Debido a que es un objeto único mundial, he empleado la inicialización perezosa idioma, ya que es posible que la interfaz estoy tratando de obtener un puntero a un archivo DLL existe en el cual aún no se ha cargado.
( Side-nota: Esto es especialmente importante cuando se registra un solo archivo DLL, como los objetos globales serán construidos dentro del proceso regsvr32
, y no quiero que la DLL para tratar de obtener una interfaz a otro DLL durante este proceso.)
Por ejemplo, mi método de inicialización perezosa podría hacer algo como esto:
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
es una variable miembro del tipo de CComPtr
La inicialización perezosa puede no ser relevante para mi problema aquí, pero estoy incluyendo que esté completa.
Problema
Lo que he notado es que en algunas circunstancias, consigo aserciones fallen cuando mi aplicación se cierra. Sin embargo, si cambio de código para llamar QueryInterface()
todos tiempo que necesito para acceder IID_IMyOtherOBject
(en lugar de almacenarla como una variable miembro) esto evita que las afirmaciones.
Esto me parece ser un problema de duración de los objetos COM. Mi hipótesis es que porque soy un puntero storing
COM, es necesario que haya algún tipo de sincronización entre la destrucción de la interfaz COM que estoy señalando, y mi propio puntero a él.
Mi comprensión de la clase CComPtr
(que estoy utilizando) es que se quita una gran cantidad de los dolores de cabeza de tratar con los problemas de toda la vida (es decir, llamando AddRef()
y Release()
). Pero no parece estar funcionando en mi caso.
¿Alguien puede escoger lo que puedo estar haciendo mal?
Solución
En lugar de implementar su propio producto único global, buscar en el uso de la IGlobalInterfaceTable interfaz en lugar. Es un producto único que es proporcionada por el sistema operativo a nivel de proceso. Cualquiera de sus archivos DLL puede poner sus objetos COM en la tabla, y los otros archivos DLL seguir para recuperar cuando sea necesario. Todo lo que se necesita para poner en práctica de su parte es una manera para que los archivos DLL para el intercambio de galletas DWORD de la mesa entre sí.
Otros consejos
Usted está devolviendo una referencia al puntero inteligente que no podría estar aumentando el número de referencias. Lo siento, me gustaría comprobar pero es tarde aquí. Esa es mi corazonada y que se adapte a lo que usted está describiendo -. Mirar en constructores de copia de CComPtr
Espero que ayude,
K
Una puñalada salvaje en la oscuridad: ¿Es posible que CGlobalSingleton
podría quedar destruida después CoUninitialize()
se llama, bajo ninguna circunstancia? Si lo fuera, y por lo tanto también m_pMyOtherObject
fue destruida después de desinicialización COM, sería otra manera de causar la violación de acceso que Igor mencionado.
Sospecho que el problema está en su comprensión de la semántica copia / asignación de la clase CComPtr; No estoy particularmente familiarizado con CComPtr, pero en mi experiencia punteros inteligentes tienden a no funcionar de la manera que puede ser que ellos esperan. En primer lugar debe leer la documentación de CComPtr y asegúrese de entender cómo funciona (no estaría de más que mirar el código fuente, tampoco). También podría intentar poner algunos puntos de interrupción en el AddRef () y Release () miembros de CComPtr para ver lo que sucede durante y después de la llamada a GetMyOtherObject (), sobre todo si está almacenando temporalmente el valor de retorno y se sale del ámbito.
Suena como m_pMyOtherObject
está todavía vivo cuando apaga su aplicación. Además de copiar cuestiones constructor m_pMyOtherObject
debe ser un CComPtr
o CGlobalSingleton
debe llamar al método m_pMyOtherObject
de Release
a la destrucción.
Editado por claridad.
Editar Sólo hizo una prueba rápida y no tuve ningún problema usando una función que devuelve una referencia a CComPtr
. Mientras que esto es un poco inusual que no causa ningún problema de recuento de referencia.
quería ampliar pesar de lo que suceda si m_pMyOtherObject
no es un puntero inteligente. En este escenario nunca va a ser liberado. Te voy a enseñar por qué:
- Se llama a QueryInterface en algunos puntero. Se llamará AddRef en ese objeto.
- devuelven CComPtr y CComPtr y puntero de interfaz o desnudo. Esto es en gran medida irrelevante. No hay operaciones de recuento ref tienen lugar (a menos que se asigna el valor devuelto a otro CComPtr, que AddRef ella. Sin embargo, desde que se CComPtr equilibrarlo con una llamada a la versión no importa).
- ¿Qué se termina con 1 o bien es llamada a AddRef y 0 para liberar o 2 llamadas a AddRef y de 1 a lanzamiento. En otras palabras, son desequilibradas y usted tiene una fuga de referencia.
Para evitar esto es necesario estructurar su 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;
}
}