2009-11-22 7 views
11

Tengo una función COM que debe devolver un SafeArray a través de un parámetro de salida LPSAFEARRAY*. La función crea SafeArray usando la clase de plantilla CComSafeArray de ATL. Mi aplicación ingenua utiliza CComSafeArray<T>::Detach() con el fin de mover la propiedad de la variable local con el parámetro de salida:¿Cómo se devuelve un CComSafeArray local a un parámetro de salida LPSAFEARRAY?

void foo(LPSAFEARRAY* psa) 
{ 
    CComSafeArray<VARIANT> ret; 
    ret.Add(CComVariant(42)); 
    *psa = ret.Detach(); 
} 

int main() 
{ 
    CComSafeArray<VARIANT> sa; 
    foo(sa.GetSafeArrayPtr()); 

    std::cout << sa[0].lVal << std::endl; 
} 

El problema es que CComSafeArray::Detach() realiza una operación Unlock de modo que cuando el nuevo propietario de la SafeArray (principal sa en este caso) se destruye, el bloqueo no es cero y Destroy no puede desbloquear el SafeArray con E_UNEXPECTED (esto provoca una pérdida de memoria debido a que SafeArray no está desasignado).

¿Cuál es la forma correcta de transferir la propiedad entre CComSafeArrays a través de un límite de método COM?


Editar: De la única respuesta hasta el momento parece que el error está en el lado del cliente (main) y no del lado del servidor (foo), pero me resulta difícil de creer que CComSafeArray wasn Diseñado para este caso de uso trivial, debe haber una manera elegante de obtener un SafeArray de un método COM en un CComSafeArray.

+0

¿Qué versión de Visual Studio estás usando? –

+0

Esto sucede tanto para VS8 (2005) como para VS9 (2008) – Motti

+2

Basado en mi experiencia, creo que quien diseñó CComSafeArray nunca lo usó realmente. Puede usar su propia clase contenedora si lo desea. – Amnon

Respuesta

10

El problema es que establece el puntero interno de recepción CComSafeArray directamente. utilizar el método de Attach() adjuntar un SAFEARRAY existente a un CComSafeArray:

LPSAFEARRAY ar; 
foo(&ar); 
CComSafeArray<VARIANT> sa; 
sa.Attach(ar); 
+0

Seguramente esta no es la forma en que se supone que se usa 'CComSafeArray', va contra el grano de' CComVariant' y 'CComBSTR'. – Motti

+0

Como vio en el código, el CComSafeArray espera que el SAFEARRAY se bloquee. Tienes que bloquearlo de una forma u otra. – Amnon

+0

Y no hay una funcionalidad de tipo Attach que se bloquee y tampoco una función similar a Detach que no se Desbloquee, por lo que el trabajo debe realizarse tanto en el lado de la persona que llama como en el de la calle. –

1

supongo que donde hubo intención de permitir que un caso tal uso. Probablemente no era el mismo desarrollador que escribió CComVariant & CComPtr :)

Creo que el autor considera la semántica de valor CComSafeArray 's como objetivo principal; Adjuntar/Separar podría ser simplemente una característica de "bonificación".

+1

E incluso con este razonamiento, todavía siento que el ctor predeterminado de 'CComSafeArray' y' GetSafeArrayPtr' son fallas de diseño/soluciones temporales ... – Andrey

5

Solo para confirmar que la respuesta correcta es la correcta. Los envoltorios RAII no pueden funcionar a través de los límites COM.

La implementación del método publicado no es correcta, no puede suponer que la persona que llama va a proporcionar un SAFEARRAY válido. Just [out] no es un atributo válido en Automation, debe ser [out, retval] o [in, out]. Si es [out, retval], que es lo que parece, entonces el método debe crear una nueva matriz desde cero. Si es [in, out], el método debe destruir la matriz pasada si no coincide con el tipo de matriz esperado y crear una nueva.

Cuestiones relacionadas