65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (2投票s)

1999年11月23日

2分钟阅读

viewsIcon

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++ 客户端(更多不可移植的丑陋之处)。挑战在于为数组、映射、列表等正确地完成所有序列化部分。

© . All rights reserved.