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





1.00/5 (3投票s)
2002年6月17日
2分钟阅读

105534

838
本文解释了如何在 COM 服务器中传递 C++ 对象。
引言
几周前,我非常希望能找到一个示例,演示如何在 COM 接口之间传递 C++ 对象,但一直没有找到任何示例。这就是我决定在这里发布这篇文章的原因。
在 ATL DLL 中传递 C++ 对象并不困难,但当然也有点棘手和有趣。
在开始之前,请确保您的客户端和服务器都是 C++。其次,您必须了解如何设置 COM 客户端和服务器。
接口的局限性
COM 要求客户端和服务器之间高度分离,这是通过接口实现的。但问题是,接口在方法中只提供有限数量的数据类型。如果接口是基于 IDispatch
的,那么选择就更加有限了。考虑到这些限制,只有在以下情况下才能跨接口传递 C++ 对象:
- 客户端和服务器都是用 VC++ 编写的。
- 它们必须能够共享对象定义(即头文件)。
- 传递对象可以简化应用程序设计。
- 您的应用程序可能需要在分布式环境中运行。您希望 COM 具有远程激活、本地/远程透明性和安全性的功能。
我建议您在开始工作之前,先复习一下序列化主题。
现在让我们继续学习示例并执行以下操作
- 创建一个 ATL DLL 服务器。
- 添加从
CObject
派生的 MFC 类。 - 在类头中使用
DECLARE_SERIAL
宏。 - 在类体中使用
IMPLEMENT_SERIAL
宏。 - 重写
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
类型,我将在下一篇文章中讨论。
接下来的步骤如下
- 创建一个 COM 接口
- 创建一个
SAFEARRAY
对象 - 在 IDL 文件中定义
[helpstring("method SetArray")] HRESULT SetArray([in]SAFEARRAY (unsigned char) pData);[helpstring("method GetArray")] HRESULT GetArray([out/*,retval*/]SAFEARRAY(unsigned char) *pData);
。 - 创建基于 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,他的文章对我帮助很大。我本来打算更详细地解释这个主题,但由于时间有限,我无法这样做,但我会不时更新文档。在此期间,请随时与我联系。