Question

J'essaie d'envoyer un objet COM via un message MSMQ en C ++. Ceci est mon objet:

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

Tout se passe bien et je peux obtenir l’objet entier mais les types BSTR. Les flottants et les entiers sont correctement envoyés et reçus. Mais les types BSTR ne fonctionnent pas. J'essaie d'envoyer des chaînes et ne trouve pas le chemin. J'ai essayé avec VARIANT à la place et le résultat était aussi faux. D'une manière ou d'une autre, il semble que les chaînes ne soient pas sérialisées.

Voici certaines des fonctions get et set de mon composant ATL:

Celui-ci fonctionne bien:

STDMETHODIMP CAnalisis::getLight(FLOAT* light)
{

    *light=img.light;
    return S_OK;
}

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

Celui-ci ne le fait pas:

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

    return S_OK;
}

STDMETHODIMP CAnalisis::setImgName(BSTR imgName)
{

    img.imgName=imgName;
    return S_OK;
}

et c’est ainsi que je crée le message MSMQ et que je remplis les valeurs dans mon producteur:

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


voici le code de sérialisation

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 j'obtiens la valeur BSTR dans le producteur (avant la sérialisation), pAnalisis-getImgName () , cela fonctionne correctement. En revanche, lorsque j'essaie de le récupérer chez le consommateur, après avoir lu le message dans la file d'attente, il ne renvoie rien. Les autres valeurs, telles que la taille, sont renvoyées sans problème.

Quelqu'un sait-il comment envoyer une valeur BSTR à l'intérieur d'un objet COM via MSMQ?

J'ai essayé de trouver des exemples similaires, mais en vain.

Le fait est que je reçois soit une valeur très étrange avec des caractères étranges, soit une valeur hexadécimale, en fonction de la façon dont j'extrais la valeur.

et je me demandais cependant ... sommes-nous sûrs qu’il est possible d’envoyer une valeur BSTR? si je ne me trompe pas, c'est un pointeur sur une chaîne ... J'utilise deux processus différents (producteur et consommateur), ils utilisent donc des blocs de mémoire différents, et ils sont destinés à être exécutés sur des machines différentes. ..

J'essayais d'envoyer cette information en tant que type VARIANT .. mais je me suis aussi perdu. Cependant, cela semble un peu moins tiré par les cheveux que d'envoyer un BSTR.

DES IDÉES SUR CETTE?

Était-ce utile?

La solution

Le problème est que la sérialisation de la classe Image la traite comme un bloc de mémoire contigu. Puisque BSTR est vraiment un pointeur, seule la valeur du pointeur est sérialisée et la charge BSTR est perdue.

À la place, vous devez écrire tous les champs sauf les BSTR en tant que fichiers binaires et les traiter séparément. Par exemple, vous pouvez d'abord écrire la longueur de BSTR sous forme d'entier, puis sa charge utile. Lors de la lecture, vous lirez d’abord la longueur, appelez SysAllocStringLen () pour allouer un tampon, puis lisez les données utiles.

Laissez la sérialisation des champs simples telle quelle (the IPersistStreamInit :: Save ()):

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

Pour les BSTR, procédez comme suit:

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

Similaire pour la lecture (the 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;
}

Notez que ce code écrit / lit la longueur de la chaîne, puis écrit / lit la charge utile constituée de caractères Unicode. Les caractères Unicode occupent chacun plus d’un octet - d’où la multiplication dans l’appel aux méthodes de lecture / écriture IStream.

Autres conseils

Si vous passez simplement un WCHAR, les informations de longueur sont perdues. Le BSTR est mal formé et cela vous cause probablement tout le chagrin. Vous devez utiliser SysAllocString pour l’utiliser dans tous les composants. Voir MSDN - la section Remarques . . Essayez:

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

Bien, cette réponse dépend du fait que vous fassiez quelque chose de bizarre ces jours-ci, mais pourrait être applicable. Il y a très très longtemps, j'ai dû passer une chaîne VB sous VB6 et VC ++ 6 (pro) à partir d'une application VB. à une application VC ++. La longueur est correcte, mais j'ai souvent reçu un caractère de l'autre côté.

Le problème était que l'application réceptrice. n’a pas été compilé pour unicode, mais plutôt en tant que projet ANSI. Le code de couche COM qui l'a décompressé du côté éloigné du transfert a fait une astuce intéressante que je n'ai trouvée que documentée dans un coin obscur du MSDN dans un extrait de livre: il a créé un ABSTR.

Un ABSTR n'est pas réellement un type. Il n'y a aucun moyen de le déclarer. Il s'agit en fait d'un reformatage du stockage sous-jacent d'un BSTR afin que vous puissiez prétendre qu'il s'agit d'un caractère * ASCII en C ++. Ceci est fait en vérifiant d’abord que le BSTR pointe sur le premier caractère après son en-tête (ce qui est typique pour de telles structures en C ++, IIRC) et puis en mélangeant les données de chaîne réelles de manière à ce qu’elles contiennent la totalité du premier. octets suivis par tous les seconds octets. Un autre nom pour cela est "pur mal".

Deux très mauvaises choses peuvent se produire de cette façon: si cette conversion est effectuée, et que vous traitez le résultat comme si le résultat était large chaîne de caractères, vous obtenez charabia. Si ce n'est pas fait et que vous traitez les résultats comme un tableau de caractères ASCII, vous obtenez généralement un seul caractère, si l'original ne contenait que des caractères de plage ASCII, car dans la représentation large, chaque octet est égal à zéro et les octets de poids fort viennent en deuxième.

Je ne peux pas vraiment dire d'après votre description si c'est ce qui vous est arrivé. Mais je vous recommande d’arrêter la chose dans un débogueur et d’examiner toutes les données de chaîne sous la valeur reçue pour voir si elles ont été remaniées de manière inattendue. S'il a été mélangé, demandez-vous pourquoi et regardez comment vous avez construit le projet.

Le fait qu'un format presque non documenté soit calé dans un type existant en tant que format de mémoire alternatif qui est vraiment difficile à trouver, même par les normes de formats de chaîne multiples permettant de constituer le développement MS, seulement à propos m'a fait crier. C’était presque aussi grave que d’essayer d’identifier & Get; GetModuleFileName " pour la première fois en tant que fonction à utiliser pour obtenir le chemin d'accès de l'exécutable actuel.

Votre objet doit créer une copie de la chaîne à la fois dans le getter et dans le séparateur:

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

bien sûr, vous devez libérer la chaîne dans le destructeur, vérifier les ptrs NULL, etc.

Vous pouvez également utiliser CComBSTR au lieu de BSTR. CComBSTR est plus intelligent que BSTR, il s’occupe de l’allocation et de la désallocation de la mémoire.

Ma suggestion est de placer vos champs dans un Variant (même temporairement), puis d'utiliser le code de streaming de Variant pour aplatir les données et les désérialiser à l'autre extrémité.

Voici le code de streaming que vous pouvez utiliser (désolé il a environ 20 ans :))

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

Le code est un peu détaillé à coller ici.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top