2009-08-19 21 views
6

Tengo un servidor COM fuera de proceso escrito en C++, que es llamado por algún código de cliente C#. Un método en una de las interfaces del servidor devuelve un BSTR grande al cliente, y sospecho que esto está causando una pérdida de memoria. El código funciona, pero estoy buscando ayuda para organizar BSTRs.BSTR de clasificación de C++ a C# con interoperabilidad COM

Simplificando un poco, el IDL para el método de servidor es

HRESULT ProcessRequest([in] BSTR request, [out] BSTR* pResponse); 

y la puesta en práctica se parece a:

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse) 
{ 
    USES_CONVERSION; 
    char* pszRequest = OLE2A(request); 
    char* pszResponse = BuildResponse(pszRequest); 
    delete pszRequest; 
    *pResponse = A2BSTR(pszResponse); 
    delete pszResponse; 
    return S_OK; 
} 

A2BSTR asigna internamente el BSTR usando SysAllocStringLen().

En el cliente de C# simplemente hacer lo siguiente:

string request = "something"; 
string response = ""; 
myserver.ProcessRequest(request, out response); 
DoSomething(response); 

Esto funciona, en el que las cadenas de solicitud se envían al servidor COM y cadenas de respuesta correctas son devueltos al cliente C#. Pero cada viaje de ida y vuelta al servidor pierde memoria en el proceso del servidor. El soporte de detección de fugas crt no muestra fugas significativas en el montón crt, por lo que sospecho que la fuga se asignó con IMalloc.

¿Estoy haciendo algo mal aquí? He encontrado comentarios vagos de que "todos los parámetros deben asignarse con CoTaskMemAlloc, de lo contrario el marshaller de interoperabilidad no los liberará", pero sin detalles.

Andy

+0

Gracias por esta pregunta y las respuestas que estoy usando BSTR con un objeto COM de ATL y C++. Una cosa que encontré fue que si especifica un BSTR * como [out] en el IDL, si se ha inicializado el BSTR * se obtendrá una fuga de memoria. Por lo tanto, debe declarar el BSTR * como [in, out] en el archivo IDL. Consulte http://msdn.microsoft.com/en-us/library/bdyd6xz6.aspx –

Respuesta

2

anelson ha cubierto esta bastante bien, pero quería añadir un par de puntos;

  • CoTaskMemAlloc no es el único asignador COM-amigable - BSTR son reconocidos por el contador de referencias por defecto, y serán liberados/re-asignado usando SysAllocString & amigos.

  • Evitar USES_CONVERSION (debido a apilar los riesgos de desbordamiento - véase la respuesta de anelson), el código completo debe ser algo como esto [1]

(tenga en cuenta que A2BSTR es seguro de usar, ya que llama SysAllocString después de la conversión, y no utiliza asignación de pila dinámica. Además, usar una matriz a borrar (delete []) como BuildResponse probable asigna una serie de caracteres)

  • El asignador BSTR tiene una caché que puede hacer que parezca como si hubiera una pérdida de memoria. Consulte http://support.microsoft.com/kb/139071 para obtener más información o Google para OANOCACHE. Podría intentar deshabilitar el caché y ver si la 'fuga' desaparece.

[1]

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse) 
{ 
    char* pszResponse = BuildResponse(CW2A(request)); 
    *pResponse = A2BSTR(pszResponse); 
    delete[] pszResponse; 
    return S_OK; 
} 
+0

Gracias. Hice los cambios que sugirió. Parece que la fuga es otra cosa. –

-4

supongo que se necesita para destruir request con ::SysFreeString(). Esta memoria se asigna en el lado del servidor.

Además, OLE2A puede asignar memoria debido a la conversión (échele un vistazo). Usted no lo libera también.

+0

Gracias por responder. Re destruyendo la "solicitud", pensé que [in] los parámetros fueron asignados y liberados por la persona que llama? –

+0

No, según las reglas COM, el destinatario nunca libera los parámetros [en]. – anelson

+0

OLE2A usa _alloca, que asigna memoria dinámicamente en la pila. Puede ser peligroso, si la cadena es de un tamaño desconocido, pero no es necesaria la desasignación: el espacio de pila se recupera cuando la función finaliza. –

3

No veo un problema obvio con su código. Sugerimos que modifique el método ProcessRequest para descartar interoperabilidad COM como el origen de la fuga:

HRESULT MyClass::ProcessRequest(BSTR request, BSTR* pResponse) 
{ 
    *psResponse = ::SysAllocStringLen(L"[suitably long string here]"); 
    return S_OK; 
} 

Sospecho que no se escape, en cuyo caso se haya reducido la fuga a su código.

También señalaría que OLE2A asigna memoria en la pila, por lo que no solo no debe eliminar pszRequest, sino que no debe usar OLE2A en absoluto, debido a la posibilidad de desbordamiento de la pila. Consulte this article para obtener alternativas más seguras.

También me gustaría sugerir que cambie A2BSTR con :: SysAllocString (CA2W (pszResponse))

+0

Gracias. Parece que la fuga no está en el manejo de la memoria COM, aunque no estoy seguro de dónde. Es hora de probar el limitchecker ... –

Cuestiones relacionadas