Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9548896
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-04-23 21:53:05

向ATL DLL中传递C 对象

原著:Uttam Kumar
翻译:



原文出处:

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

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

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

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

  1. 客户机和服务器都是由VC 编写。
  2. 它们必须共享对象的定义(比如 头文件)。
  3. 传递应用程序设计的简单的对象。
  4. 你的应用程序可能需要运行在一个分布式环境下。你希望COM的远程活动,本地/远程活动是透明的,安全的。

我建议,在开始工作之前,先顺序的看一下各个标题,现在,我列出实例,并作以下事情:

  1. 创建一个ATL DLL服务器,
  2. 添加一个MFC类,从CObject类派生,
  3. 在类的头部使用 DECLARE_SERIAL 宏,
  4. 在类的中间使用 IMPLEMENT_SERI 宏,
  5. 覆盖Serialize() 方法,
  6. // 你的 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;
    
    }      
  7. 现在,下一步就是用一个CArchive对象来进行序列化和反序列化(载入和存储对象),我用了一个叫CBlob的新类来实现的
  8. 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数据类型。

下一步如下:
  1. 创建一个COM接口,
  2. 创建一个SAFEARRAY对象,
  3. 在IDL文件中定义:
  4. [helpstring("method SetArray")] 
    
    HRESULT SetArray([in]SAFEARRAY (unsigned char) pData);[helpstring("method GetArray")] 
    
    HRESULT GetArray([out/*,retval*/]SAFEARRAY(unsigned char) *pData); 
  5. 创建一个基于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,他的文章对我帮助很大,我曾经计划把这个主题解释的更详细,但由于时间不足我无法完成,然而我会不断的更新这篇文章,在这期间,请不用客气的跟我联系。

译者:王先生(MrWang2000)
Email : yuanzhaowang@sohu.com
    或yuanzhaowang@163.com

阅读(307) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~