Domanda

Sto cercando di inviare un oggetto COM su un messaggio MSMQ in C ++. Questo è il mio oggetto:

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

Tutto va bene e posso ottenere l'intero oggetto tranne i tipi BSTR. Float e numeri interi sono inviati e ricevuti correttamente. Ma i tipi BSTR non funzionano. Sto cercando di inviare stringhe e non riesco a trovare la strada. Ho provato invece con VARIANT e anche il risultato è stato sbagliato. In qualche modo, sembra che le stringhe non siano serializzate.

Queste sono alcune delle funzioni get e set per il mio componente ATL:

Questo funziona benissimo:

STDMETHODIMP CAnalisis::getLight(FLOAT* light)
{

    *light=img.light;
    return S_OK;
}

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

Questo non lo fa:

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

    return S_OK;
}

STDMETHODIMP CAnalisis::setImgName(BSTR imgName)
{

    img.imgName=imgName;
    return S_OK;
}

e questo è il modo in cui creo il messaggio MSMQ e riempio i valori nel mio produttore:

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


ecco il codice di serializzazione

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 ottengo il valore BSTR nel produttore (prima della serializzazione), pAnalisis-getImgName () , funziona bene. Al contrario, quando provo a ottenerlo nel consumatore, dopo aver letto il messaggio dalla coda, non restituisce nulla. Gli altri valori, come la dimensione, vengono restituiti senza problemi.

qualcuno sa come inviare un valore BSTR all'interno di un oggetto COM tramite MSMQ?

Ho provato a trovare alcuni esempi simili ma totalmente invano.

il fatto è che sto ottenendo un valore molto strano con caratteri strani o un valore esadecimale, a seconda di come estraggo il valore ... il fatto è che non ottengo mai il valore giusto.

e mi chiedevo, tuttavia ... siamo sicuri che sia possibile inviare un valore BSTR? se non sbaglio, è un puntatore a una stringa ... Sto eseguendo due processi diversi (produttore e consumatore), quindi usano blocchi di memoria diversi e sono pensati per essere eseguiti su macchine diverse. ..

Stavo cercando di inviare queste informazioni come tipo VARIANT .. ma mi sono anche perso. Tuttavia, questo sembra un po 'meno inverosimile rispetto all'invio di un BSTR.

QUALUNQUE IDEE SU QUESTO?

È stato utile?

Soluzione

Il problema è che la serializzazione della classe Image la considera un blocco contiguo di memoria. Poiché BSTR è in realtà un puntatore, solo il valore del puntatore viene serializzato e il payload BSTR viene perso.

Invece dovresti scrivere tutti i campi tranne BSTR come binari ed elaborare BSTR separatamente. Ad esempio, è possibile scrivere prima la lunghezza BSTR come numero intero, quindi il relativo payload. Quando leggi prima leggerai la lunghezza, chiama SysAllocStringLen () per allocare un buffer, quindi leggi il payload.

Lascia la serializzazione di campi semplici così com'è (IPersistStreamInit :: Save ()):

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

Per i BSTR, procedere come segue:

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

Simile per la lettura (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;
}

Si noti che questo codice scrive / legge la lunghezza della stringa e quindi scrive / legge il payload costituito da caratteri Unicode. I caratteri Unicode occupano più di un byte ciascuno, quindi la moltiplicazione nella chiamata dei metodi di lettura / scrittura IStream.

Altri suggerimenti

Se si passa semplicemente un WCHAR, le informazioni sulla lunghezza vanno perse. Il BSTR è malformato e questo probabilmente ti sta causando tutto il dolore. È necessario utilizzare SysAllocString per utilizzarlo tra i componenti. Vedi MSDN - la sezione Osservazioni . Prova:

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

OK, questa risposta dipende dal fatto che stai facendo qualcosa di strano in questi giorni, ma potrebbe essere applicabile. Molto, molto tempo fa, ho dovuto passare una stringa VB sotto VB6 e VC ++ 6 (pro) da un'app VB. a un'app VC ++. La lunghezza è arrivata OK ma spesso ho ricevuto un personaggio dall'altra parte.

Il problema era che l'app di ricezione. non è stato compilato per Unicode, ma piuttosto come un progetto ANSI. Il codice del livello COM che lo ha decompresso sul lato opposto del trasferimento ha fatto un trucco interessante che ho trovato documentato solo in un angolo oscuro di MSDN in un estratto del libro: ha creato un ABSTR.

Un ABSTR non è in realtà un tipo. Non c'è modo di dichiararne uno. In realtà è una riformattazione dell'archivio sottostante di un BSTR in modo da poter far finta che sia un carattere ASCII * in C ++. Questo viene fatto assicurandosi innanzitutto che BSTR indichi il primo carattere dopo la sua intestazione (tipico comunque per tali strutture in C ++, IIRC) e quindi mescolando i dati della stringa effettiva in modo che contenga tutti i primi byte seguiti da tutti i secondi byte. Un altro nome per questo è "puro male".

Due molto cattivi cose possono accadere in questo modo: se questa conversione viene eseguita, e il risultato viene trattato come se fosse ancora ampio stringa di caratteri, diventa incomprensibile. Se non è fatto e tratti i risultati come un array di caratteri ASCII, di solito ottieni un singolo carattere, se l'originale conteneva solo caratteri di intervallo ASCII, poiché nella rappresentazione ampia ogni altro byte è uno zero e i byte alti vengono secondi.

Non riesco proprio a capire dalla tua descrizione se questo è quello che ti è successo. Ma consiglio di fermare la cosa in un debugger e guardare tutti i dati di stringa con quel valore ricevuto per vedere se sono stati rimescolati in qualche modo inaspettato. Se è stato mischiato, chiediti perché e guarda come hai realizzato il progetto.

Il fatto di un formato quasi non documentato incuneato in un tipo esistente come un layout di memoria alternativo che è davvero difficile da trovare, anche in base a quanti formati di stringhe siamo in grado di truccare gli standard di sviluppo MS, solo mi ha fatto urlare. È stato quasi negativo come provare a identificare " GetModuleFileName " per la prima volta come funzione da utilizzare per ottenere il percorso dell'eseguibile corrente.

Il tuo oggetto deve creare una copia della stringa sia nel getter che nel 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;
}

ovviamente, devi liberare la stringa nel distruttore, controllare i ptrs NULL ecc.

In alternativa puoi usare CComBSTR invece di BSTR. CComBSTR è più intelligente di BSTR, si occupa di allocare e deallocare la memoria.

Il mio suggerimento è di mettere i campi in una variante (anche se temporaneamente), quindi utilizzare il codice di streaming Variant per appiattire i dati e deserializzarli dall'altra parte.

Ecco il codice di streaming che puoi usare (mi dispiace che abbia circa 20 anni :))

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

Il codice è un po 'dettagliato da incollare qui.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top