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

在 ATL DLL 服务器中传递 C++ 对象

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (3投票s)

2002年6月17日

2分钟阅读

viewsIcon

105534

downloadIcon

838

本文解释了如何在 COM 服务器中传递 C++ 对象。

引言

几周前,我非常希望能找到一个示例,演示如何在 COM 接口之间传递 C++ 对象,但一直没有找到任何示例。这就是我决定在这里发布这篇文章的原因。

在 ATL DLL 中传递 C++ 对象并不困难,但当然也有点棘手和有趣。

在开始之前,请确保您的客户端和服务器都是 C++。其次,您必须了解如何设置 COM 客户端和服务器。

接口的局限性

COM 要求客户端和服务器之间高度分离,这是通过接口实现的。但问题是,接口在方法中只提供有限数量的数据类型。如果接口是基于 IDispatch 的,那么选择就更加有限了。考虑到这些限制,只有在以下情况下才能跨接口传递 C++ 对象:

  1. 客户端和服务器都是用 VC++ 编写的。
  2. 它们必须能够共享对象定义(即头文件)。
  3. 传递对象可以简化应用程序设计。
  4. 您的应用程序可能需要在分布式环境中运行。您希望 COM 具有远程激活、本地/远程透明性和安全性的功能。

我建议您在开始工作之前,先复习一下序列化主题。

现在让我们继续学习示例并执行以下操作

  1. 创建一个 ATL DLL 服务器。
  2. 添加从 CObject 派生的 MFC 类。
  3. 在类头中使用 DECLARE_SERIAL 宏。
  4. 在类体中使用 IMPLEMENT_SERIAL 宏。
  5. 重写 Serialize() 方法。
    // Your CSimpleObj class look like this
    class CSimpleObj : public CObject  
    {
        DECLARE_SERIAL( CSimpleObj )
    public:
        // constructor and destructor
        CSimpleObj();
        virtual ~CSimpleObj();
        // Set internal string data
        void SetString( CString csData );
        // Used to serialize data into an archive
        virtual void Serialize(CArchive& ar);
        // Display the data string
        void Show();
    private:
        CString m_strData;// Internal data string
    };
    // Write this object to an archive
    void CSimpleObj::Serialize(CArchive& ar)
    {
        CObject::Serialize( ar );
    
        if (ar.IsLoading())
        {
            // extract data from archive
            ar >> m_strData;
        }
        else
        {
            // store data into archive
            ar << m_strData;
        }
    }
    // Method to display data in this object
    void CSimpleObj::Show()
    {
        AfxMessageBox(m_strData);
    }
    
    // save a string in data member
    void CSimpleObj::SetString(CString csData)
    {
        m_strData = csData;
    }

    下一步是使用 CArchive 进行序列化和反序列化(加载和存储对象)。我使用了一个名为 CBlob 的不同类来完成它。

    class CBlob  
    {
    public:
        CBlob() {};
        virtual ~CBlob() {};
    
        // Extract data from a CObject and load it into a SAFEARRAY.
        SAFEARRAY* Load( CObject *pObj );
        // Re-create an object from a SAFEARRAY
        BOOL Expand( CObject * &pObj, SAFEARRAY *pVar );
    private:
    };
    // Extract data from a CObject and use it to create a SAFEARRAY.
    SAFEARRAY* CBlob::Load( CObject *pObj)
    {
        CMemFile memfile;   // memory file
    
        // define the flag which tells archive if it should load or store
        long lMode = CArchive::store | CArchive::bNoFlushOnDelete;
        // create the archive using the memory file
        CArchive ar(&memfile, lMode );
    
        // m_pDocument is not used
        ar.m_pDocument = NULL;
        // serialize the object into the archive
        ar.WriteObject(pObj);
        // close the archive - the data is now stored in memfile
        ar.Close();
    
        // get the length (bytes) of the memory file
        long llen = memfile.GetLength();
    
        // detach the buffer and close the file
        unsigned char *pMemData = memfile.Detach();
    
        // set up safearray
        SAFEARRAY *psa;
    
        // create a safe array to store the stream data
        psa = SafeArrayCreateVector( VT_UI1, 0, llen );
    
        // pointers to byte arrays
        unsigned char *pData = NULL;
    
        // get a pointer to the safe array. Locks the array.
        SafeArrayAccessData( psa, (void**)&pData );
    
        // copy the memory file into the safearray
        memcpy( pData, pMemData, llen );
    
        // clean up buffer
        delete pMemData;
    
        // unlock access to safearray
        SafeArrayUnaccessData(psa);
    
        // return a pointer to a SAFEARRAY allocated here
        return psa;
    }
    // Re-create an object from a SAFEARRAY
    BOOL CBlob::Expand(CObject * &rpObj, SAFEARRAY *psa)
    {
        CMemFile memfile;   // memory file for de-serailze
        long lLength;     // number of bytes
        char *pBuffer;  // buffer pointer
    
        // lock access to array data
        SafeArrayAccessData( psa, (void**)&pBuffer  );
    
        // get number of elements in array. This is the number of bytes
        lLength = psa->rgsabound->cElements;
    
        // attach the buffer to the memory file
        memfile.Attach((unsigned char*)pBuffer, lLength);
    
        // start at beginning of buffer
        memfile.SeekToBegin();
    
        // create an archive with the attached memory file
        CArchive ar(&memfile, CArchive::load | CArchive::bNoFlushOnDelete);
    
        // document pointer is not used
        ar.m_pDocument = NULL;
    
        // inflate the object and get the pointer
        rpObj = ar.ReadObject(0);
    
        // close the archive
        ar.Close();
    
        // Note: pBuffer is freed when the SAFEARRAY is destroyed
        // Detach the buffer and close the file
        pBuffer = (char*) memfile.Detach();
    
        // release the safearray buffer
        SafeArrayUnaccessData( psa );
    
        return TRUE;
    }

我在这里使用 SAFEARRAY,因为这是我们使用的最佳选择。它可以包含一些复杂的多维数组,但在本例中,我们只使用一个非常简单的数组。

SAFEARRAY 数据存在一个问题,即 MIDL 不识别这种数据类型。但最简单的方法是 VARIANT 类型,我将在下一篇文章中讨论。

接下来的步骤如下

  1. 创建一个 COM 接口
  2. 创建一个 SAFEARRAY 对象
  3. 在 IDL 文件中定义 [helpstring("method SetArray")] HRESULT SetArray([in]SAFEARRAY (unsigned char) pData);[helpstring("method GetArray")] HRESULT GetArray([out/*,retval*/]SAFEARRAY(unsigned char) *pData);
  4. 创建基于 MFC 的客户端来测试应用程序。

您的 IDL 文件应如下所示

    interface IBolbData : IUnknown
    {
        [helpstring("method SetArray")] HRESULT 
            SetArray([in]SAFEARRAY (unsigned char) pData);
        [helpstring("method GetArray")] HRESULT 
            GetArray([out/*,retval*/]SAFEARRAY(unsigned char) *pData);
    };

// Sets object.
STDMETHODIMP CBolbData::SetArray(SAFEARRAY *pData)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState())

  // create a dummy pointer of CSimpleObj
    CSimpleObj *dummy=NULL; 
    // create blob obect to expand/deserialize 
  CBlob blob;             
  // Init dummy object using safe array through this function
    blob.Expand( (CObject*&)dummy, pData );
  dummy->Show(); // Call show function to test the object.
  delete dummy;  // Delete the pointer.

    return S_OK;
}


// Creates Object and sends to client.
STDMETHODIMP CBolbData::GetArray(SAFEARRAY **pData)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState())
    // create object to send to server
    CSimpleObj *pMyOb = new CSimpleObj();

    // set the string data
    pMyOb->SetString( "A SAFEARRAY from the server!" );

    // create blob to serialize object
    CBlob blob;

    // load the object into the blob
    *pData = blob.Load( pMyOb );
    // delete the pMyOb pointer 
  delete pMyOb;

  return S_OK;
}

最后,编写一个基于对话框的 MFC 应用程序,其中有两个按钮并添加以下代码

void CClientDlg::OnOK() 
{
    // create COM smart pointer from CLSID string
    try
  {
    IBolbDataPtr pI( "Server.BolbData.1" );
        SAFEARRAY *psa ;

        // Get the safearray from the server
        pI->GetArray( &psa );

        // create a pointer to an object
        CSimpleObj *dummy=NULL;

        // blob object to expand 
        CBlob blob;

        // use the blob to expand the safearray into an object
        blob.Expand( (CObject *&)dummy, psa );
        // call a method on the object to test it
        dummy->Show();

        // delete the object
        delete dummy;
    }
    // Handle any COM exceptions from smart pointers
    catch (_com_error e)
    {
        // display the message string from the error
        AfxMessageBox( e.ErrorMessage() );
    }
}

void CClientDlg::OnLoad() 
{
    try
    {
        // create smart pointer from CLSID string
        IBolbDataPtr pI( "Server.BolbData.1" );
        SAFEARRAY *psa ;

        // create object to send to server
        CSimpleObj *pMyOb = new CSimpleObj();

        // set the string data
        pMyOb->SetString( "The client sent a SAFEARRAY!" );

        // create blob to serialize object
        CBlob blob;

        // load the object into the blob
        psa = blob.Load( pMyOb );

        // delete the object
//        delete pMyOb;

        pI->SetArray( psa );

    }
    // Handle any COM exceptions from smart pointers
    catch (_com_error e)
    {
        // display the message string from the error
        AfxMessageBox( e.ErrorMessage() );
    }
}

结论

本文涵盖了许多主题,即如何使用序列化、如何使用 SAFEARRAY 以及如何在接口之间传递 C++ 对象。我还想感谢 William Rubin,他的文章对我帮助很大。我本来打算更详细地解释这个主题,但由于时间有限,我无法这样做,但我会不时更新文档。在此期间,请随时与我联系。

© . All rights reserved.