接口的限制:
COM要求客户和服务器高度的分离,这已经由接口实现了,但是现在问题是,接口方法只提供了有限的几种数据类型. 如果接口是基于IDispatch的,我们的选择更加有限.请记住这些限制, C++对象只在下面几种情况下可以传递:
1. 客户和服务都是VC编译的;
2. 他们必须有共同的对象的定义,比如相同的头文件;
3. 通过传递C++对象简化应用的设计;
4. 在分布式环境中,需要注意你的COM必须具备远程激活, 本地/远程透明性, 安全性登方面的特性.
下面是一个例子:
1. 生成一个ATL DLL服务器
2. 添加一个继承于CObject的类.
3. 在类的头文件中加上DECLARE_SERIAL
4. 在类的CPP文件中加上IMPLEMENT_SERIAL
5. 重载Serialize方法
// 你的CSimpleObj类看起来这样子的
class CSimpleObj : public CObject
{
DECLARE_SERIAL( CSimpleObj )
public:
// constructor and destructor
CSimpleObj();
virtual ~CSimpleObj();
// 设置内部的字符串
void SetString( CString csData );
// 用于串行化你的数据
virtual void Serialize(CArchive& ar);
// 显示数据
void Show();
private:
CString m_strData;//这里定义一个字符串对象
};
// 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;
}
6. 下一步我们通过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 that tells the archive whether 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 (in 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文件里 定义2个方法:
[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;
}
最后,我们做一个带有2个按钮的对话框应用程序, 2个按钮的响应方法是:
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() );
}
}
这个例子里涵盖的内容比较多,包括了如何使用串行化,如何使用安全数组,如何通过接口传递C++对象.
作者原贴在这里:http://www.codeguru.com/Cpp/COM-Tech/atl/atl/article.php/c3587/