使用 BSTR 在 DCOM 接口上传输 C++ 对象






4.40/5 (2投票s)
1999年11月23日
2分钟阅读

137676
一个让我感到沮丧的问题是,在 SQL/MTS 服务器上构建大型对象,然后使用 DCOM 将所有这些内容传递到客户端的难度。目标是在服务器上提交一个查询,然后通过数据库进行处理,构建一个复杂的对象,即包含嵌套的映射、列表和其他对象的数组的对象,并将整个结果以可用的形式返回给客户端。
使用 SAFEARRAY 或大量 COM 属性的语义对我来说令人望而却步。由于我不需要将这些复杂对象提供给 VB 或 Java 应用程序,因此我可以将所有代码保留在 C++ 中。
这种方法利用了 BSTR 的简单设计,据我所知,BSTR 只是一个长指针。它指向的是一个长整型和一个内存块之间的地址,实际上是内存块的第一个字节。在字符串内存之前包含的长整型包含内存块的字节长度。通常,内存块包含一个以空字符结尾的 2 字节字符数组,但实际上它可以包含任何内容。由于 COM/DCOM 知道如何在网络和进程边界上编组 BSTR,因此可以打包到 BSTR 中的任何内容都可以跨网络编组。
第一步是将任意复杂的对象打包到 BSTR 中。我使用 CMemFile 接收序列化的对象,然后将 CMemFile 填充到 BSTR 中。
代码如下:
BSTR CMyView::FetchObject(long lParam) // pseudo-server function { MyComplexClass *p_mCC; // a serializable class BSTR bstrHoldThis; CMemFile mfFile; // works just like a real // file CArchive ar((CFile*)&mfFile, CArchive::store); // Create the complex object. I include this stub here to indicate // that what is being passed is really an an object and not a pointer. // It could be a megabyte or more in size. p_mCC = new MyComplexObject(lParam); // A small, simple object could have been created on the stack.That's // not best place for a REALLY big object, although it // guarantees that it will go away when the function returns. // I generally put objects on the heap with 'new' and // delete them after serialization. // Serialize the object to the memory file p_mCC->Serialize(ar); // You need to flush the archive and file buffers ar.Flush(); // flush the archive first mfFile.Flush(); // then the file // The step that creates the BSTR needs the length of the file ULONG lArchiveLength = mfFile.GetLength(); // Stuff the archive into the BSTR with SysAllocStringByteLen() // CMemFile.Detach() returns a pointer to the memory block of the file // which is used to create the BSTR. bstrHoldThis = SysAllocStringByteLen((const char*)mfFile.Detach(), lArchiveLength); // Free the object. The CMemFile will clean up after itself. delete p_mCC; // Return the BSTR result; return bstrHoldThis; // send the whole thing into the aether }
现在,调用者需要能够解包 BSTR 并重新创建对象。这只是上述步骤的反向操作,我将其留作读者的练习,但我实际上使其工作了,所以这是它。
Void CMyView::OnButton1() { BSTR bstrA; MyComplexClass mRC; CMemFile mfFile; // error checking omitted for clarity - but necessary // go get the BSTR wrapped object from the pseudo-server BstrA = FetchObject(m_lTestVal); // here's the ugly part. I back into the length of the BSTR. ULONG *p_lLength = (ULONG*) bstrA; // points at the byte AFTER the // BSTR length --p_lLength; // now it points at the length of the // BSTR. Like Foster's - crude but // effective. // attach the memory part of the BSTR to the CMemFile memory block mfFile.Attach((unsigned char*) bstrA, *p_lLength); // now the object is in the memfile and it can be loaded CArchive ar(&mfFile, CArchive::load); mRC.Serialize(ar); // The object is now fully instantiated on the stack. // In this sample code it would be destroyed when this // function returned so you'd really want to build it with 'new' // on the heap and assign it to a member variable, or a // Document data member. // We still need to free the memory used by the BSTR and the // CMemFile. They both point to the same memory. The CMemFile // won't automatically free the memory that was attached when it // is destroyed, so freeing the BSTR memory takes care of that for us. SysFreeString(bstrA); }
可以将此方法与更通用的 COM 接口上的其他方法结合起来,并同意将其使用限制为 MFC C++ 客户端(更多不可移植的丑陋之处)。挑战在于为数组、映射、列表等正确地完成所有序列化部分。