Pregunta

Estoy tratando de enviar un objeto COM a través de un mensaje MSMQ en C ++. Este es mi objeto:

class ATL_NO_VTABLE CAnalisis :
    public CComObjectRootEx,
    public CComCoClass,
    public ISupportErrorInfo,
    public IDispatchImpl,
    public IPersistStreamInit
{
private:
    typedef struct {
        DOUBLE size;
        float color;
        float light;

        BSTR imgName;

        BSTR  uname;

    } Image;

    Image img;
    STDMETHOD(Load)(IStream *pStm);
    STDMETHOD(Save)(IStream *pStm,  BOOL fClearDirty);

Todo va bien y puedo obtener todo el objeto excepto los tipos BSTR. Los flotantes y enteros se envían y reciben correctamente. Pero los tipos BSTR no funcionan. Estoy tratando de enviar cadenas y no puedo encontrar el camino. Intenté con VARIANT en su lugar y el resultado también fue incorrecto. De alguna manera, parece que las cadenas no están serializadas.

Estas son algunas de las funciones get y set para mi componente ATL:

Este funciona bien:

STDMETHODIMP CAnalisis::getLight(FLOAT* light)
{

    *light=img.light;
    return S_OK;
}

STDMETHODIMP CAnalisis::setLight(FLOAT light)
{
    img.light=light;
    return S_OK;
}

Este no:

STDMETHODIMP CAnalisis::getImgName(BSTR* imgName)
{
    *imgName = img.imgName;

    return S_OK;
}

STDMETHODIMP CAnalisis::setImgName(BSTR imgName)
{

    img.imgName=imgName;
    return S_OK;
}

y esta es la forma en que creo el mensaje MSMQ y relleno los valores en mi productor:

// For these ActiveX components we need only smart interface pointer
        IMSMQQueueInfosPtr  pQueueInfos; 
        IMSMQQueueInfoPtr   pQueueInfo; 
        IMSMQQueuePtr       pQueue;
        IUnknownPtr         pIUnknown;
        // Instanciate the follwing ActiveX components
        IMSMQQueryPtr       pQuery(__uuidof(MSMQQuery));
        IMSMQMessagePtr     pMessage(__uuidof(MSMQMessage));


        IAnalisisPtr pAnalisis(__uuidof(Analisis));

                WCHAR *  imagen;        
        imagen = L"imagen1.jpg";
                pAnalisis->setImgName(imagen);


                 (...)

                pAnalisis->setFruitSize(20.00);

                 (...)

                pQueueInfo = new IMSMQQueueInfoPtr( __uuidof(MSMQQueueInfo) );

        pQueueInfo->PathName = "MYCOMPUTER\\private$\\myprivatequeue";

            pQueue = pQueueInfo->Open(MQ_SEND_ACCESS, MQ_DENY_NONE);
        pMessage->Body = static_cast(pAnalisis);
                pMessage->Send(pQueue);


aquí está el código de serialización

STDMETHODIMP CAnalisis::Load( IStream *pStm )
{
    ULONG           cb;
    HRESULT         hr;
    if (NULL==pStm)
        return ResultFromScode(E_POINTER);
    // Read an object from the stream.
    //
    hr=pStm->Read(&img, sizeof(Image), &cb);
    if (FAILED(hr))
        return hr;
    if (sizeof(Image) != cb)
        return E_FAIL;

    return NOERROR;
}

STDMETHODIMP CAnalisis::Save( IStream *pStm, BOOL bClearDirty )
{
    ULONG           cb;
    HRESULT         hr;
    if (NULL==pStm)
        return ResultFromScode(E_POINTER);

    // Write an object into the stream.
    hr=pStm->Write(&img, (ULONG)sizeof(Image), &cb);
    if (FAILED(hr) || sizeof(Image)!=cb)
       return ResultFromScode(STG_E_WRITEFAULT);

    return NOERROR;
}

Si obtengo el valor BSTR en el productor (antes de la serialización), pAnalisis-getImgName () , funciona bien. Por el contrario, cuando intento obtenerlo en el consumidor, después de leer el mensaje de la cola, no devuelve nada. Los otros valores, como el tamaño, se devuelven sin problemas.

¿Alguien sabe cómo enviar un valor BSTR dentro de un objeto COM a través de MSMQ?

He intentado encontrar algunos ejemplos similares pero totalmente en vano.

la cuestión es que obtengo un valor muy extraño con caracteres extraños o un valor hexadecimal, dependiendo de cómo extraiga el valor ... la cuestión es que nunca obtengo el valor correcto.

y me preguntaba, sin embargo ... ¿estamos seguros de que es posible enviar un valor BSTR? si no me equivoco, es un puntero a una cadena ... Estoy ejecutando dos procesos diferentes (es decir, productor y consumidor), por lo que usan diferentes bloques de memoria y están destinados a ejecutarse en diferentes máquinas. ..

Estaba tratando de enviar esta información como un tipo VARIANTE ... pero también me perdí. Sin embargo, esto parece un poco menos descabellado que enviar un BSTR.

¿ALGUNA IDEAS SOBRE ESTO?

¿Fue útil?

Solución

El problema es que la serialización de la clase Image lo trata como un bloque contiguo de memoria. Dado que BSTR es realmente un puntero, solo se serializa el valor del puntero y se pierde la carga útil de BSTR.

En su lugar, debe escribir todos los campos excepto los BSTR como binarios y procesar los BSTR por separado. Por ejemplo, puede escribir la longitud BSTR como entero primero, luego su carga útil. Al leer, primero leerá la longitud, llame a SysAllocStringLen () para asignar un búfer, luego lea la carga útil.

Deje la serialización de campos simples como está (IPersistStreamInit :: Save ()):

pStm->Write(&(img.color), (ULONG)sizeof(float), &cb);

Para BSTR, haga esto:

int length = SysStringLen( img.uname );
pStm->Write(&length, (ULONG)sizeof(int), &cb);
if( length > 0 ) {
   pStm->Write( img.uname, (ULONG)(length * sizeof(WCHAR) ), &cb);
}

Similar para la lectura (IPersistStreamInit :: Load ()):

int length;
pStm->Read(&length, (ULONG)sizeof(int), &cb);
if( length > 0 ) {
   img.uname = SysAllocStringLen( 0, length );
   pStm->Read( img.uname, (ULONG)( length * sizeof( WCHAR) ), &cb);
} else {
   img.uname = 0;
}

Tenga en cuenta que este código escribe / lee la longitud de la cadena y luego escribe / lee la carga útil que consta de caracteres Unicode. Los caracteres Unicode ocupan más de un byte cada uno, de ahí la multiplicación en la llamada de métodos de lectura / escritura de IStream.

Otros consejos

Si simplemente pasa una WCHAR, la información de longitud se pierde. El BSTR está malformado y esto probablemente le esté causando todo el dolor. Debe usar SysAllocString para usarlo en todos los componentes. Consulte MSDN - la sección Comentarios . Prueba:

BSTR imagen = SysAllocString(L"imagen1.jpg");

OK, esta respuesta depende de que hagas algo extraño en estos días, pero podría ser aplicable. Hace mucho, mucho tiempo tuve que pasar una cadena VB bajo VB6 y VC ++ 6 (pro) desde una aplicación VB. a una aplicación VC ++. La longitud llegó bien, pero a menudo recibí un personaje en el otro lado.

El problema era que la aplicación receptora. no fue compilado para Unicode, sino más bien como un proyecto ANSI. El código de la capa COM que lo descomprimió en el otro lado de la transferencia hizo un truco interesante que solo encontré documentado en un rincón oscuro de MSDN en un extracto de libro: creó un ABSTR.

Un ABSTR no es realmente un tipo. No hay forma de declarar uno. En realidad, es un reformateo del almacenamiento subyacente de un BSTR para que pueda pretender que es un ASCII char * en C ++. Esto se hace primero asegurándose de que el BSTR apunte al primer carácter después de su encabezado (de todos modos, típico para tales estructuras en C ++, IIRC) y luego luego barajando los datos de cadena reales para que contengan todos los primeros bytes seguidos de todos los segundos bytes. Otro nombre para esto es "puro mal".

Dos muy malas cosas pueden suceder de esta manera: si se realiza esta conversión, y se trata el resultado como si todavía fuera un amplio cadena de caracteres, obtienes galimatías. Si no se hace y trata los resultados como una matriz de caracteres ASCII, generalmente obtiene un solo carácter, si el original contenía solo caracteres de rango ASCII, ya que en la representación amplia cada otro byte es un cero y los bytes altos ocupan el segundo lugar.

No puedo decir por tu descripción si esto es lo que te pasó. Pero recomiendo detener la cosa en un depurador y mirar todos los datos de cadena debajo de ese valor recibido para ver si se ha reorganizado de alguna manera inesperada. Si se ha barajado, pregúntese por qué y observe la forma en que creó el proyecto.

El hecho de un formato casi indocumentado encajado en un tipo existente como un diseño de memoria alternativo que es realmente difícil de encontrar, incluso por los estándares de cuántos formatos de cadena podemos inventar para el desarrollo de MS, solo sobre me hizo gritar. Era casi tan malo como tratar de identificar " GetModuleFileName " por primera vez como la función a usar para obtener la ruta del ejecutable actual.

Su objeto necesita crear una copia de la cadena tanto en el getter como en el setter:

STDMETHODIMP CAnalisis::getImgName(BSTR* imgName)
{
    *imgName = SysAllocString(img.imgName);

    return S_OK;
}

STDMETHODIMP CAnalisis::setImgName(BSTR imgName)
{
    SysFreeString(img.imgName);
    img.imgName=SysAllocString(imgName);
    return S_OK;
}

por supuesto, necesita liberar la cadena en el destructor, verificar ptrs NULL, etc.

Alternativamente, puede usar CComBSTR en lugar de BSTR. CComBSTR es más inteligente que BSTR, se encarga de asignar y desasignar la memoria.

Mi sugerencia es colocar sus campos en una Variante (aunque sea temporalmente) y luego usar el código de transmisión Variante para aplanar los datos y deserializarlos en el otro extremo.

Aquí está el código de transmisión que puede usar (lo siento, tiene aproximadamente 20 años :))

Enlace: https://github.com/kasajian/VariantStream/blob /master/VariantStream.h

El código es un poco detallado para pegar aquí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top