Pergunta

Eu estou tentando enviar um objeto COM através de uma mensagem MSMQ em C ++. Este é o meu 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);

Tudo vai muito bem e eu posso obter o objeto inteiro, mas os tipos BSTR. Flutuadores e inteiros são devidamente enviados e recebidos. Mas os tipos BSTR não trabalho. Estou tentando enviar cadeias e não pode encontrar o caminho. Eu tentei com VARIANT vez eo resultado estava errado também. De alguma forma, parece que as cordas não são serializados.

Estas são algumas das funções get e set para o meu componente ATL:

Este funciona muito bem:

STDMETHODIMP CAnalisis::getLight(FLOAT* light)
{

    *light=img.light;
    return S_OK;
}

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

Este não:

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

    return S_OK;
}

STDMETHODIMP CAnalisis::setImgName(BSTR imgName)
{

    img.imgName=imgName;
    return S_OK;
}

e esta é a maneira que eu criar a mensagem MSMQ e preencher os valores no meu produtor:

// 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);


aqui é o código de serialização

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;
}

Se eu pegar o valor BSTR no produtor (antes de serialização), pAnalisis-getImgName(), ele funciona bem. Em contraste, quando eu tentar obtê-lo no consumidor, depois de ler a mensagem da fila, ele não retorna nada. Os outros valores, tais como o tamanho, são devolvidos sem problemas.

Alguém sabe como enviar um valor BSTR dentro de um objeto COM através MSMQ?

Eu tentei encontrar alguns exemplos semelhantes, mas totalmente em vão.

a coisa é que eu estou recebendo tanto um valor muito estranho com caracteres estranhos ou um valor hexadecimal, dependendo de como eu extrair o valor .. a coisa é que eu nunca chegar o valor correto.

e eu queria saber, no entanto ... estamos certeza de que é possível enviar um valor BSTR? se não estou errado, é um ponteiro para uma string ... Eu estou correndo dois processos diferentes (ie produtores e consumidores), para que eles usam diferentes blocos de memória, e eles são feitos para ser executado em máquinas diferentes assim. ..

Eu estava tentando enviar esta informação como um tipo VARIANT .. mas também se perdeu. No entanto, isso parece um pouco menos rebuscado do que enviar uma BSTR.

Todas as ideias sobre isso?

Foi útil?

Solução

O problema é que a serialização de guloseimas classe Imagem-lo como um bloco contíguo de memória. Desde BSTR é realmente um ponteiro apenas o valor do ponteiro é serializado ea carga BSTR está perdido.

Em vez disso você deve escrever todos os campos exceto BSTRs como binários e de processo BSTRs separadamente. Por exemplo, você pode escrever comprimento BSTR como inteiro em primeiro lugar, em seguida, sua carga útil. Ao ler você vai ler comprimento primeiro, chamar SysAllocStringLen () para alocar um buffer, em seguida, ler a carga útil.

Deixe a serialização de campos simples como ele é (o IPersistStreamInit :: Save ()):

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

Para BSTRs fazer isso:

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

Semelhante para leitura (o 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;
}

Note que grava essa código / lê comprimento da corda e, em seguida, escreve / lê a carga que consiste em caracteres Unicode. caracteres Unicode ocupam mais de um bytes cada - daí a multiplicação em IStream Read / Write métodos chamar

.

Outras dicas

Se você simplesmente passar um WCHAR - as informações comprimento está perdido. O BSTR é mal formado e este é, provavelmente, causando-lhe toda a tristeza. Você precisa usar SysAllocString para usá-lo em componentes. Consulte MSDN - Observações . Tente:

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

OK, esta resposta depende de você fazer algo estranho nos dias de hoje, mas pode ser aplicável. Há muito, muito tempo atrás eu tinha que passar uma cadeia VB sob VB6 e VC ++ 6 (pro) a partir de um aplicativo VB. para um aplicativo VC ++. O comprimento veio através OK, mas muitas vezes eu recebi um personagem do outro lado.

O problema era que o aplicativo de recepção. não foi compilado para unicode, mas sim como um projeto ANSI. O código da camada COM que descompactou no lado mais distante da transferência fez um truque interessante que eu só encontrei documentado em um canto obscuro do MSDN em um trecho do livro:. Criou uma Abstr

Um Abstr não é realmente um tipo. Não há maneira de declarar um. É realmente uma reformatação de armazenamento subjacente de um BSTR para que você pode fingir que é um caractere ASCII * em C ++. Isso é feito pela primeira certificando-se os pontos BSTR para o primeiro caractere após o seu cabeçalho (de qualquer maneira típica para tais estruturas em C ++, IIRC) e então baralhar os dados de seqüência de reais para que ele contém todo o primeiro bytes seguido por todos os segundos bytes. Outro nome para isso é "puro mal".

Dois muito mau coisas pode acontecer desta maneira: se esta conversão é feita, e você tratar o resultado como se ainda é uma grande cadeia de caracteres, você começa rabiscos. Se não for feito e você tratar os resultados como um array de caracteres ASCII, você normalmente obter um único caractere, se o original continha apenas caracteres ASCII alcance, uma vez que na ampla representação qualquer outro byte é um zero e os altos bytes vêm em segundo lugar.

Não consigo bastante dizer de sua descrição, se isso é o que aconteceu com você. Mas eu recomendo parar a coisa em um depurador e olhar para todos os dados de cadeia sob esse valor recebido para ver se ele foi reformulado, de alguma forma inesperada. Se ele tiver sido embaralhadas, pergunte-se por que e olhar para a forma como você construiu o projeto.

O fato de um formato quase não documentado encravado em um tipo existente como um layout de memória alternativa que é realmente difícil de encontrar, até mesmo pelos Como Muitos corda-formatos-can-we-make-up padrões de desenvolvimento MS, apenas sobre me fez gritar. Era quase tão ruim quanto tentar identificar "GetModuleFileName" pela primeira vez como a função a ser usada para obter o caminho do executável atual.

As suas necessidades de objeto para criar uma cópia da corda em ambos os getter e o 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;
}

é claro, que você precisa para liberar a corda no processo de destruição, para verificar NULL ptrs etc.

Como alternativa, você pode usar CComBSTR vez de BSTR. CComBSTR é um mais esperto do que BSTR, ele cuida de alocação e desalocação de memória.

A minha sugestão é colocar seus campos em um Variant (mesmo que temporariamente), em seguida, usar o código de streaming de Variant para achatar os dados e desserializá-lo na outra extremidade.

Aqui está o código que você pode usar de streaming (desculpe, é cerca de 20 anos :))

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

O código é um pouco detalhado para colar aqui.

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