2009-03-09 4 views
2

Ich versuche, ein COM-Objekt über eine MSMQ-Nachricht in C++ zu senden. Das ist mein Objekt:senden Sie ein COM-Objekt mit einem BSTR-Werttyp in einer MSMQ-Nachricht

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

Alles geht gut und ich kann das ganze Objekt aber die BSTR-Typen bekommen. Floats und Integer werden ordnungsgemäß gesendet und empfangen. Aber die BSTR-Typen funktionieren nicht. Ich versuche, Strings zu senden und kann den Weg nicht finden. Ich habe es stattdessen mit VARIANT versucht und das Ergebnis war auch falsch. Irgendwie sieht es so aus, als ob die Strings nicht serialisiert sind.

Dies sind einige der Get- und Set-Funktionen für meine ATL-Komponente:

Dieses funktioniert gut:

 
STDMETHODIMP CAnalisis::getLight(FLOAT* light) 
{ 

    *light=img.light; 
    return S_OK; 
} 

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

Dieses nicht:

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

    return S_OK; 
} 

STDMETHODIMP CAnalisis::setImgName(BSTR imgName) 
{ 

    img.imgName=imgName; 
    return S_OK; 
} 

und das ist die Art, wie ich die MSMQ-Nachricht erstelle und die Werte in meinem Producer fülle:

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


hier ist die Serialisierungscode

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

Wenn ich den BSTR Wert in dem Erzeuger (vor der Serialisierung) erhalten, pAnalisis-getImgName(), es funktioniert gut. Im Gegensatz dazu, wenn ich versuche, es in den Verbraucher zu bekommen, nach dem Lesen der Nachricht aus der Warteschlange, gibt es nichts zurück. Die anderen Werte, z. B. die Größe, werden ohne Probleme zurückgegeben.

weiß jemand, wie man einen BSTR-Wert innerhalb eines COM-Objekts durch MSMQ sendet?

Ich habe versucht, einige ähnliche Beispiele zu finden, aber völlig umsonst.

die Sache ist, dass ich entweder einen sehr seltsamen Wert mit komischen Zeichen oder einem hexadezimalen Wert bekomme, abhängig davon, wie ich den Wert extrahiere .. die Sache ist, dass ich nie den richtigen Wert bekomme.

und ich frage mich, aber ... sind wir sicher, dass es möglich ist, einen BSTR-Wert zu senden? Wenn ich nicht falsch liege, ist es ein Zeiger auf eine Zeichenkette ... Ich führe zwei verschiedene Prozesse aus (dh Producer und Consumer), also benutzen sie verschiedene Speicherblöcke und sie sollen auf verschiedenen Maschinen laufen. ..

Ich habe versucht, diese Informationen als VARIANT-Typ zu senden .. ging aber auch verloren. Dies erscheint jedoch etwas weniger weit hergeholt als das Senden eines BSTR.

JEDE IDEEN ZU DIESEM?

Antwort

1

Das Problem besteht darin, dass die Serialisierung der Image-Klasse es als einen zusammenhängenden Speicherblock behandelt. Da BSTR wirklich ein Zeiger ist, wird nur der Zeigerwert serialisiert und die BSTR-Nutzlast ist verloren.

Stattdessen sollten Sie alle Felder außer BSTRs als Binärdateien schreiben und BSTRs separat verarbeiten. Beispielsweise können Sie die BSTR-Länge zuerst als Integer und dann als Payload schreiben. Wenn Sie zuerst die Länge lesen, rufen Sie SysAllocStringLen() auf, um einen Puffer zuzuweisen, und lesen Sie dann die Nutzdaten.

Leave Serialisierung von einfachen Felder, wie sie (die IPersistStreamInit :: Save()) ist:

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

Für BSTRs dies tun:

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

Ähnliche zum Lesen (die IPersistStreamInit :: Load()):

Beachten Sie, dass dieser Code schreibt/liest Zeichenfolge Länge und dann schreibt/liest die Bezahlung Laden, das aus Unicode-Zeichen besteht. Unicode-Zeichen belegen jeweils mehr als 1 Byte - daher die Multiplikation in IStream Read/Write-Methoden.

+0

so meinst du, dass ich das aus der Struktur nehmen sollte, wenn ich mich nicht irre:

 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

Nein, ändern Sie den Serialisierungscode. Bearbeitete Antwort zu klären. – sharptooth

+0

ES GEARBEITET !!!! Ich gab vor ungefähr 2 Wochen auf, nachdem ich für ein paar Tage gekämpft habe. jetzt ist es schon lange fertig. ich danke dir sehr. Ich habe diese Antwort gerade als die richtige markiert, und total hilfreich.Ich bin sicher, dass dies für viele andere Programmierer nützlich sein wird, da es überhaupt keine Beispiele gibt. Erstaunlich! ThxAmillion! – markitus82

0

Wenn Sie einfach einen WCHAR übergeben - die Längeninformation ist verloren. Der BSTR ist fehlerhaft und dies verursacht wahrscheinlich alle Trauer. Sie müssen SysAllocString verwenden, um es über Komponenten hinweg zu verwenden. Siehe MSDN - die Bemerkungen Abschnitt. Versuchen Sie:

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

Nun, ich habe das schon getan ... in einigen meiner anderen Versuche, und ich bekomme ein ähnliches Ergebnis. Ich habe es erneut mit SysAllocString versucht und keine Änderung. Allerdings werde ich es so lassen. Ich denke, der Fehler ist, wenn Sie versuchen, den Wert aus dem serialisierten Objekt herauszunehmen, sonst verstehe ich nicht. – markitus82

0

OK, diese Antwort hängt davon ab, dass Sie etwas seltsam in diesen Tagen tun, könnte aber anwendbar sein. Vor langer, langer Zeit musste ich eine VB-Zeichenfolge unter VB6 und VC++ 6 (Pro) von einer VB-App übergeben. zu einer VC++ App. Die Länge kam durch OK aber ich bekam oft einen Charakter auf der anderen Seite.

Das Problem war, dass die empfangende App. wurde nicht für Unicode, sondern als ANSI-Projekt kompiliert. Der COM-Layer-Code, der ihn auf der anderen Seite der Übertragung entpackt hat, hat einen interessanten Trick ergeben, den ich nur in einer obskuren Ecke der MSDN in einem Buchauszug dokumentiert gefunden habe: Er hat einen ABSTR erstellt.

Ein ABSTR ist eigentlich kein Typ. Es gibt keine Möglichkeit, einen zu deklarieren. Es ist eigentlich eine Neuformatierung des zugrundeliegenden Speichers eines BSTR, so dass Sie vorgeben können, dass es ein ASCII-Zeichen * in C++ ist. Dies wird getan, indem zuerst sichergestellt wird, dass der BSTR auf das erste Zeichen nach seinem Header zeigt (typisch für solche Strukturen in C++, IIRC) und dann Mischen der tatsächlichen Zeichenfolge Daten, so dass es alle der ersten Bytes gefolgt von allen enthält die zweiten Bytes. Ein anderer Name dafür ist "Pure Evil".

Zwei sehrschlechtDinge auf diese Weise passieren kann: Wenn diese Konvertierung abgeschlossen ist, und Sie behandeln das Ergebnis, als ob es immer noch eine große Zeichenfolge, Sie Kauderwelsch erhalten. Wenn dies nicht geschieht und Sie die Ergebnisse als ASCII-Zeichenarray behandeln, erhalten Sie normalerweise ein einzelnes Zeichen, wenn das Original nur Zeichen im ASCII-Bereich enthält, da in der breiten Darstellung jedes zweite Byte eine Null und die hohen Bytes an zweiter Stelle stehen.

Ich kann nicht ganz aus Ihrer Beschreibung sagen, wenn das ist, was Ihnen passiert ist. Ich empfehle jedoch, die Sache in einem Debugger anzuhalten und alle String-Daten unter diesem empfangenen Wert zu betrachten, um zu sehen, ob sie auf unerwartete Weise neu gemischt wurden. Wenn es gemischt wurde, fragen Sie sich warum und schauen Sie sich an, wie Sie das Projekt gebaut haben.

Die Tatsache eines fast undokumentierten Formats eingekeilt in einen bestehenden Typ als alternatives Speicherlayout, das selbst durch die Wie-viele-String-Formate-Kann-wir-Make-up-Standards der MS-Entwicklung wirklich schwer zu finden ist, Ungefähr brachte mich zum Schreien. Es war fast so schlimm wie der Versuch, "GetModuleFileName" zum ersten Mal als die Funktion zu identifizieren, die verwendet werden soll, um den Pfad der aktuellen ausführbaren Datei abzurufen.

0

Ihr Objekt muss sowohl eine Kopie des Strings in dem Getter erstellen und die Setzer:

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

natürlich, müssen Sie die Zeichenfolge im Destruktor befreien, überprüfen Sie für NULL PTR usw.

0

Alternativ können Sie CComBSTR anstelle von BSTR verwenden. CComBSTR ist ein schlauer als BSTR, es kümmert sich um die Zuweisung und Freigabe des Speichers.

0

Mein Vorschlag ist, Ihre Felder in eine Variante (auch wenn vorübergehend) zu setzen, dann Variant-Streaming-Code verwenden, um die Daten zu glätten und am anderen Ende deserialisieren.

Hier Streaming-Code, den Sie (sorry, es ist etwa 20 Jahre alt :))

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

Der Code ist ein bisschen ausführlicher hier einzufügen.

Verwandte Themen