2009-03-09 14 views
2

J'essaie d'envoyer un objet COM sur un message MSMQ en C++. C'est mon objet:envoie un objet COM avec un type de valeur BSTR dans un message MSMQ

 
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 va bien et je peux obtenir l'objet entier mais les types BSTR. Les flottants et les nombres entiers sont correctement envoyés et reçus. Mais les types BSTR ne fonctionnent pas. J'essaie d'envoyer des chaînes et ne peux pas trouver le chemin. J'ai essayé avec VARIANT à la place et le résultat était faux aussi. D'une certaine manière, il semble que les chaînes ne sont pas sérialisées.

Voici quelques-uns des get et fonctions définies pour mon composant ATL:

Celui-ci fonctionne très 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 compte 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 la façon dont je crée le message MSMQ et 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); 


ici est 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 je reçois la valeur BSTR dans le producteur (avant sérialisation), pAnalisis-getImgName(), il fonctionne très bien. En revanche, lorsque j'essaie de l'obtenir chez le consommateur, après avoir lu le message de la file d'attente, il ne renvoie rien. Les autres valeurs, telles que la taille, sont renvoyées sans problème.

Est-ce que quelqu'un sait comment envoyer une valeur BSTR dans un objet COM via MSMQ?

J'ai essayé de trouver des exemples similaires mais totalement en vain. Le fait est que je reçois soit une valeur très bizarre avec des caractères étranges ou une valeur hexadécimale, selon la façon dont j'extrais la valeur .. le fait est que je n'ai jamais la bonne 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'exécute deux processus différents (ie producteur et consommateur), donc ils utilisent des blocs de mémoire différents, et ils sont destinés à être exécutés sur des machines différentes. J'ai essayé d'envoyer cette info comme un type VARIANT .. mais aussi perdu. Cependant, cela semble un peu moins tiré par les cheveux que d'envoyer un BSTR.

TOUTES LES IDÉES?

Répondre

1

Le problème est que la sérialisation de la classe Image la traite comme un bloc de mémoire contigu. Comme BSTR est réellement un pointeur, seule la valeur du pointeur est sérialisée et la charge utile BSTR est perdue. Au lieu de cela, vous devez écrire tous les champs sauf BSTR en tant que binaire et traiter les BSTR séparément. Par exemple, vous pouvez écrire d'abord la longueur BSTR en entier, puis sa charge utile. Lorsque vous lisez, vous lisez d'abord la longueur, appelez SysAllocStringLen() pour allouer un tampon, puis lisez la charge utile.

congé sérialisation des champs simples comme il est (le :: IPersistStreamInit Save()):

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

Pour BSTRs faire ceci:

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

similaires pour la lecture (la 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 longueur de la chaîne et écrit/lit alors le salaire charge composée de caractères Unicode. Les caractères Unicode occupent plus d'un octet chacun - d'où la multiplication dans l'appel de méthodes IStream Read/Write.

+0

donc vous voulez dire que je devrais enlever ceci de la structure, si je ne me trompe pas:

 private: typedef struct { \t DOUBLE size; \t float color; \t float light; \t \t VARIANT origin; } Image; long imgNameLenght; long unameLenght; BSTR imgName; BSTR uname; 
markitus82

+0

Non, modifiez le code de sérialisation. Réponse éditée pour clarifier. – sharptooth

+0

IL A TRAVAILLÉ !!!! J'ai abandonné il y a environ deux semaines, après avoir lutté pendant quelques jours. maintenant c'est fait dans un moment. Merci beaucoup. Je viens de marquer cette réponse comme la bonne, et totalement utile. Je suis sûr que cela sera utile pour de nombreux autres programmeurs car il n'y a pas d'exemples du tout.AMAZING! ThxAmillion! – markitus82

0

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 entre les composants. Voir MSDN - la section Remarques. Essayez:

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

Eh bien, je l'ai déjà fait ... dans certains de mes autres essais, et j'obtiens un résultat similaire. J'ai encore essayé avec SysAllocString et aucun changement. Cependant, je vais le laisser ainsi. Je pense que l'erreur est lorsque vous essayez de retirer la valeur de l'objet sérialisé, sinon je ne comprends pas. – markitus82

0

OK, cette réponse dépend de vous faire quelque chose d'étrange 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 a traversé OK mais j'ai souvent reçu un personnage de l'autre côté.

Le problème était que l'application de réception. n'a pas été compilé pour unicode, mais plutôt comme un 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 un type. Il n'y a aucun moyen d'en déclarer un. C'est en fait 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 s'assurant d'abord que le BSTR pointe vers le premier caractère après son en-tête (typique pour C++, IIRC) et puis en mélangeant les données de chaîne réelles pour qu'elles contiennent tous les premiers octets suivis de tous les deuxièmes octets. Un autre nom pour ceci est "le mal pur".

Deux trèsmauvaischoses peuvent se produire de cette façon: si cette conversion est faite, et vous traitez le résultat comme si elle est encore une grande chaîne de caractères, vous obtenez du 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 contient que des caractères ASCII, car dans la représentation large, tous les autres octets sont nuls et les octets supérieurs sont seconds.

Je ne peux pas dire à partir de votre description si c'est ce qui vous est arrivé. Mais je recommande d'arrêter la chose dans un débogueur et d'examiner toutes les données de chaîne sous cette valeur reçue pour voir si elle a été remaniée d'une manière inattendue. Si elle a été mélangée, demandez-vous pourquoi et regardez comment vous avez construit le projet. Le fait d'un format presque non documenté coincé dans un type existant comme une disposition de la mémoire alternative qui est vraiment difficile à trouver, même selon les normes de développement de MS, comment pouvons-nous formater les formats de chaîne, à peu près m'a fait crier. C'était presque aussi mauvais que d'essayer d'identifier "GetModuleFileName" pour la première fois comme la fonction à utiliser pour obtenir le chemin de l'exécutable actuel.

0

Votre objet a besoin de créer une copie de la chaîne à la fois le getter et le 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; 
} 

bien sûr, vous devez libérer la chaîne dans la destructor, vérifiez NULL REE etc.

0

Vous pouvez également utiliser CComBSTR au lieu de BSTR. CComBSTR est plus intelligent que BSTR, il prend soin d'allouer et de libérer la mémoire.

0

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

code ici se vous pouvez utiliser le streaming (désolé, il est environ 20 ans :))

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

Le code est un peu bavard pour coller ici.