ATL中传递C++对象

http://www.vckbase.com/document/viewdoc/?id=1259

原文出处:Passing   C++   Object   in   ATL   DLL

简介
  几个星期以前,我拼命的寻找一个能够通过COM接口传递C++对象的例子,但是,没有找到.这就是我发表这篇文章的原因。

  向ATL的DLL中传递一个C++对象参数并不是非常之难,但是,当然也会有点难度,也很有趣。

  在开始一个工程以前,首先你得确信客户机和服务器组件都是适应C++的程序,其次,你必须知道怎样设置你的客户机和服务器。

接口的局限性
  COM技术要求客户机和服务器高度的分离,这是通过接口实现的,但是问题出在:接口的方法中只提供了有限个参数数据类型,如果这个接口是基于IDispatch的,参数类型的可选范围就更加受到限制了,由于这些局限性,C++对象只有在满足以下条件时才能够传递:

客户机和服务器都是由VC++编写。  
它们必须共享对象的定义(比如   头文件)。  
传递应用程序设计的简单的对象。  
你的应用程序可能需要运行在一个分布式环境下。你希望COM的远程活动,本地/远程活动是透明的,安全的。  
我建议,在开始工作之前,先顺序的看一下各个标题,现在,我列出实例,并作以下事情:

创建一个ATL   DLL服务器,  
添加一个MFC类,从CObject类派生,  
在类的头部使用   DECLARE_SERIAL   宏,  
在类的中间使用   IMPLEMENT_SERI   宏,  
覆盖Serialize()   方法,   //   你的   CSimpleObj   类应该像这样:
class   CSimpleObj   :   public   CObject
{
  DECLARE_SERIAL(   CSimpleObj   )
public:
//   构造函数和析构函数
  CSimpleObj();
  virtual   ~CSimpleObj();
//   设置内部字符串数据
  void   SetString(   CString   csData   );
//   用来向存档文件串行输入数据(序列化)
  virtual   void   Serialize(CArchive&   ar);
//   现实字符串数据
  void   Show();
private:
  CString   m_strData;//   内部字符串数据
};
//   把这个数据对象写入到文档中
void   CSimpleObj::Serialize(CArchive&   ar)
{
  CObject::Serialize(   ar   );
if   (ar.IsLoading())
{
//   从档案文件提取数据
  ar   >>   m_strData;
}
else
{
//   把数据存入档案文件
  ar   <<   m_strData;
}
}
//   显示对象数据的方法
void   CSimpleObj::Show()
{
  AfxMessageBox(m_strData);
}
//把字符串数据保存到一个变量中
void   CSimpleObj::SetString(CString   csData)
{
  m_strData   =   csData;
}            
现在,下一步就是用一个CArchive对象来进行序列化和反序列化(载入和存储对象),我用了一个叫CBlob的新类来实现的   class   CBlob
{
public:
  CBlob()   {};
  virtual   ~CBlob()   {};
//   从一个   CObject对象中提取数据并载入到一个   SAFEARRAY对象中.
  SAFEARRAY*   Load(   CObject   *pObj   );
//   重新创建一个SAFEARRAY对象
  BOOL   Expand(   CObject   *   &pObj,   SAFEARRAY   *pVar   );
private:
};
//   从一个   CObject对象中提取数据并用它构建一个   SAFEARRAY对象.
SAFEARRAY*   CBlob::Load(   CObject   *pObj)
{
  CMemFile   memfile;   //   内存文件
//   定义一个用来标记档案文件是读取还是存储的标志
  long   lMode   =   CArchive::store   |   CArchive::bNoFlushOndelete;
//   用内存文件创建档案文件
  CArchive   ar(&memfile,   lMode   );
//   m_pDocument   不使用
  ar.m_pDocument   =   NULL;
//   序列化对象到档案文件中
  ar.WriteObject(pObj);
//   关闭档案文件--现在,数据在内存文件中
  ar.Close();
//   取得内存文件的长度(以字节为单位)
  long   llen   =   memfile.GetLength();
//   释放缓冲区   关闭文件
  unsigned   char   *pMemData   =   memfile.Detach();
//   设定safearray
  SAFEARRAY   *psa;
//   创建safearray对象存取流数据
  psa   =   SafeArrayCreateVector(   VT_UI1,   0,   llen   );
//   指向字节数组的指针
  unsigned   char   *pData   =   NULL;
//   取得一个   safe   array的指针.   锁定数组.
  SafeArrayAccessData(   psa,   (void**)&pData   );
//   拷贝内存文件到   safearray
  memcpy(   pData,   pMemData,   llen   );
//   清理缓冲区
  delete   pMemData;
//   锁定对   safearray的访问
  SafeArrayUnaccessData(psa);
//   返回一个在这分配的SAFEARRAY的指针
  return   psa;
}
//   重新创建一个SAFEARRAY对象
BOOL   CBlob::Expand(CObject   *   &rpObj,   SAFEARRAY   *psa)
{
  CMemFile   memfile;   //   反序列化的内存文件
  long   lLength;   //   字节数
  char   *pBuffer;   //   缓冲区指针
//   锁定数组数据的访问
  SafeArrayAccessData(   psa,   (void**)&pBuffer   );
//   取得数组中元素个数.   是字节数
  lLength   =   psa->rgsabound->cElements;
//   连接缓冲区到内存文件
  memfile.Attach((unsigned   char*)pBuffer,   lLength);
//   从缓冲区头部开始
  memfile.SeekToBegin();
//   创建一个连接到内存文件上的档案文件
  CArchive   ar(&memfile,   CArchive::load   |   CArchive::bNoFlushOndelete);
//   不使用文档指针
  ar.m_pDocument   =   NULL;
//   填充对象   取得指针
  rpObj   =   ar.ReadObject(0);
//   关闭档案文件
  ar.Close();
//   注意:   当SAFEARRAY被毁坏时   pBuffer   被释放
//   释放缓冲区   关闭文件
  pBuffer   =   (char*)   memfile.Detach();
//   释放safearray   缓冲区
  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);
};
//   设定对象
STDMETHODIMP   CBolbData::SetArray(SAFEARRAY   *pData)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState())
//   创建CSimpleObj的亚元指针
  CSimpleObj   *dummy=NULL;  
//   创建   blob   对象   用来填充、反序列化
  CBlob   blob;
//   使用   safearray   创建亚元对象
  blob.Expand(   (CObject*&)dummy,   pData   );
  dummy->Show();   //   调用显示函数测试对象
  delete   dummy;   //删除指针
  return   S_OK;
}
//   创建对象   并发送给客户机.
STDMETHODIMP   CBolbData::GetArray(SAFEARRAY   **pData)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState())
//   创建对象并发送给服务器
  CSimpleObj   *pMyOb   =   new   CSimpleObj();
//设定字符串数据
  pMyOb->SetString(   "A   SAFEARRAY   from   the   server!"   );
//   创建blob来序列化对象
  CBlob   blob;
//   将对象载入blob
  *pData   =   blob.Load(   pMyOb   );
//   删除pMyOb指针
  delete   pMyOb;
  return   S_OK;
}
             
最后,写一个有两个按钮的基于对话框的   MFC   应用程序   并添加如下代码:    void   CClientDlg::OnOK()
{
//   从CLSID串创建COM智能指针
try
{
  IBolbDataPtr   pI(   "Server.BolbData.1"   );
  SAFEARRAY   *psa   ;
//   从服务器取得   safearray
  pI->GetArray(   &psa   );
//   创建指针
  CSimpleObj   *dummy=NULL;
//   blob   对象
  CBlob   blob;
//使用blob   扩展   safearray   到一个对象里
  blob.Expand(   (CObject   *&)dummy,   psa   );
//通过调用一个对象的方法来测试它
  dummy->Show();
//   删除对象
  delete   dummy;
}
//   通过智能指针处理任意   COM   异常
catch   (_com_error   e)
{
//   显示错误信息
  AfxMessageBox(   e.ErrorMessage()   );
}
}
void   CClientDlg::OnLoad()
{
try
{
//   从CLSID   串创建智能指针
  IBolbDataPtr   pI(   "Server.BolbData.1"   );
  SAFEARRAY   *psa   ;
//   创建送给服务器的对象
  CSimpleObj   *pMyOb   =   new   CSimpleObj();
//   设置字符串数据
  pMyOb->SetString(   "The   client   sent   a   SAFEARRAY!"   );
//   创建   blob   用来序列化对象
  CBlob   blob;
//   将对象载入到   blob
  psa   =   blob.Load(   pMyOb   );
//删除对象
  delete   pMyOb;
  pI->SetArray(   psa   );
}
catch   (_com_error   e)
{
//   显示错误信息
  AfxMessageBox(   e.ErrorMessage()   );
}
}            
总结
  这篇文章包含了很多的主题:例如   怎样使用序列化,怎样使用   SAFEARRAY,和怎样通过接口传递C++对象。我要感谢William   Rubin,他的文章对我帮助很大,我曾经计划把这个主题解释的更详细,但由于时间不足我无法完成,然而我会不断的更新这篇文章,在这期间,请不用客气的跟我联系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值