在客户端重新创建对象

2016-02-19 12:40 6 1 收藏

只要你有一台电脑或者手机,都能关注图老师为大家精心推荐的在客户端重新创建对象,手机电脑控们准备好了吗?一起看过来吧!

【 tulaoshi.com - 编程语言 】


  至今我们还没有涉及到客户部分的操作,现在就讨论一下。客户端通过调用服务器端的GetArray()方法来开始整个处理。客户端将会接收我们在服务器创建的安全对象。客户端的程序则负责将这块字节流变成为一个有效的C++对象。以下摘录了做这部分工作的客户端代码:
  
  // create COM smart pointer from CLSID string
  
  IBlobDataPtr pI( "TestServer.BlobData.1" );
  
  SAFEARRAY *psa ;
  
  // Get the safearray from the server
  
  pI-GetArray( &psa );
  
  file:// create a pointer to an object
  
  CSimpleObj *dummy=NULL;
  
  file:// blob object to eXPand
  
  CBlob blob;
  
  file:// use the blob to expand the safearray into an object
  
  blob.Expand( (CObject*&)dummy, psa );
  
  file:// call a method on the object to test it
  
  dummy-Show(this-m_hWnd, "Client Message");
  
  // delete the object
  
  delete dummy;
  
  这段代码挺简单的。它使用一个COM智能指针与服务器进行连接。假如你对智能指针不熟悉,这些方便的小对象通过调用CoCreateInstance()就可做所有的工作,与服务器进行连接。
  
  CBlob类再一次完成这个步骤,这个类与服务器端的那一个是完全相同的。该类代码事实上是由服务器工程中得到的。将一个安全数组变成一个CSimpleObject的工作由Expand()方法完成。该方法与服务器端调用的Load()方法是相对的。
  
  // Re-create an object from a SAFEARRAY
  
  BOOL CBlob::Expand(CObject * &rpObj, SAFEARRAY *psa)
  
   {
  
  CMemFile memfile; // memory file for de-serialize
  
  long lLength; // number of bytes
  
  char *pBuffer; // buffer pointer
  
  file:// 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);
  
  file:// start at beginning of buffer
  
  memfile.SeekToBegin();
  
  file:// create an archive with the attached memory file
  
  CArchive ar(&memfile, CArchive::loadCArchive::bNoFlushOnDelete);
  
  // document pointer is not used
  
  ar.m_pDocument = NULL;
  
  file:// inflate the object and get the pointer
  
  rpObj = ar.ReadObject(0);
  
  // close the archive
  
  ar.Close();
  
  file:// Note: pBuffer is freed when the SAFEARRAY is destroyed
  
  file:// Detach the buffer and close the file
  
  pBuffer = (char*) memfile.Detach();
  
  file:// release the safearray buffer
  
  SafeArrayUnaccessData( psa );
  
  return TRUE;
  
  }
  
  这个方法的大部分代码你也在前面见过了,该方法接收一个安全数组的输入。我们由安全数组中取出缓冲的数据。CArchive对象负责将缓冲的数据重新创建为对象。
  
  以下就是我们在刚才的Expand()方法中完成的事情
  
   1、锁定访问安全数组结构,并且得到它的长度(以字节计)。
  
   2、将安全数组的数据缓冲与一个CMemFile关联
  
   3、将CMemFile与一个archive相关联
  
   4、使用Archives的ReadObject()方法重新创建对象
  
   5、将SAFEARRAY的数据缓冲由CMemFile脱离
  
   6、返回一个指向新创建对象的指针给调用者
  
  我们在这里谈到了许多基层的东西。CBlob类负责串行化和还原对象的繁重工作。COM部分的程序确实是很易懂的。
    性能问题
  
  我并不是劝说你不要使用这里提出的技巧,不过在性能方面,我要提醒你一下。以上我们提到的东西,对于小和中等大小的对象,可工作得很好的。不过,大型的对象将会有问题,我指的是超过一兆的大型对象。
  
  问题是,这些大型对象必须放在内存中,它可令系统开始使用分页。你可能也知道,分页的效率是很低的,非凡是对于内存少的机器。
  
  这些问题产生的原因是几个这样大的对象都必须同时放在内存中。假如客户和服务器应用都运行在同一部机器(或者一个进程中),那么在内存中同时可有高达三个这些对象的拷贝。这样系统开销就会很大,但这又是不可避免的。
  
  对于由许多小对象组成的大对象,在串行化处理方面可以作一些调整。你可以调用自己的CArchive,并且用更大的哈希表调用SetStoreParams和SetLoadParams。CArchive使用一个哈希表来存储类的信息。假如哈希表填满了,那么串行化的处理将会变得很慢。默认的大小适合用在1000或者更少的对象,要了解如何做到这一点,可看一下sample目录的CBigArchive类。你可以使用这个类来代替CArchive。在这个工程的CBigArchive类中,我也包含了它的一个例子(在我的例子中并没有使用CBigArchive)。
  
  安全数组外的选择
  
  安全数组并不是传送二进行数据的唯一方法。我还简要提一下其它三个办法。我可以肯定还有其它的方式。
  
  1、使用一个VARIANT
  
  调用VariantInit API函数可创建一个VARIANT数据。该函数初始化VARIANT的数据结构。你还需要设置variant的类型,这可以通过设置它的"vt"成员做到。以下的代码片段创建一个variant并且将它放到一个安全数组中。
  
  VARIANT *pVar
  
  // initialize the variant
  
  VariantInit( pVar );
  
  file:// set the variant type to an array of bytes
  
  pVar-vt = VT_ARRAY + VT_UI1;
  
  file:// create the safe array pointer
  
  SAFEARRAY *psa;
  
  psa = SafeArrayCreateVector( VT_UI1, 0, llen );
  
  file://Code to load the SAFEARRAY with data
  
   ...
  
  file:// assign the SAFEARRAY pointer to the VARIANT
  
  pVar-parray = psa;
  
  2、使用MIDL提供的非自动类型
  
  我们使用SAFEARRAY和VARIANT的原因是可以利用MIDL产生的类库。该类库可以简化调用数据的操作,还提供COM的智能指针。使用一个类库并不是必须的,但是不这样做的话,你需要做一些额外的工作。
  
  MIDL答应一些"custom"(自定义)的数据类型。这些数据类型并不兼容类库和"automation"的接口。一个二进制对象的MIDL接口可以使用一个byte *,而不是一个安全数组。该接口规范需要包含一些关于调用的明确信息,这些信息在[IN]和[OUT]属性中定义。
  
  在TesServer.IDL文件中,我提到了一个关于如何定义MIDL接口的例子。该串行化的处理与这篇文章谈到的是一样的,不过缓冲分配有点不同。以下是IDL的定义。
  
  file:// get data from server
  
  HRESULT GetData([out] long* pcLen,[out,size_is(,*pcLen)] byte **pBuffer );
  
  file:// send data to server
  
  HRESULT SetData([in] long cLen,[in,unique,size_is(cLen)] byte Buffer[]);
  
  要使用这些接口你将必须做一些调用。"size_is"属性告诉MIDL如何调用数据。(IDL属性定义的信息与安全数组的成员变量存储的信息是一样的)。调用通过一个由MIDL产生的Proxy/Stub DLL完成。Proxy/Stub DLL需要建立,并且在客户和服务器端使用。
  
  3、使用IStream接口
  
   一个完全不同的方法是使用标准的IStream接口。我没有研究过,不过一些编程者坚持说这是唯一的方法。我觉得安全数组也不错,因此没有尝试过,这可能会在未来的文章中提到。

来源:https://www.tulaoshi.com/n/20160219/1601481.html

延伸阅读
标签: 电脑入门
apple官方的客户端默认是只支持windows vista往上的操作系统,windows xp用户很蛋疼,下面教大家如何简单的几步在xp上安装icloud控制面板: 第一步: 去苹果官网下载icloud客户端for windows 第二步: 下载 ORCA MSI editer 并安装(新手不能发链接所以请自行搜索下载) 第三步: 用unrar或WinRAR解压缩这个下载好的icloud安装文件,可...
标签: ASP
  这个比较长,只要将下列代码加入<body</body之间就行: <form Name="InputForm" <div align="center"<center<p<script language="JavaScript" var FirstForm; function StartSearch() { document.forms[FirstForm+document.InputForm.SearchSelect. selectedIndex].elements[0].val...
Outlook客户端设置范例 如果您使用Outlook客户端收发邮件,请参照以下流程进行设置(以Outlook2007为例): 第一步:添加新的电子邮件账户 点击工具菜单,选择帐户设置,帐户设置页面如下图所示,在帐户设置页面点击新建添加新的电子邮件帐户: 在添加新的电子邮件帐户的第一个页面选择电子邮件服务器类型:Microsoft Ex...
标签: ASP
  /** 描述:在使用ASP Request对象时需要注意的小问题 作者:慈勤强 Email : cqq1978@yeah.net **/ 在ASP中Request对象是获取客户端提交数据的一个很重要的对象,大家对他也是非常熟悉了。 虽然如此,还是经常有人问我下面的几种写法有什么不同,到底应该怎么写? strMessage = Request("msg") strMessage = Request.Form("msg") 而...
SQL Server 2008 “ 阻止保存要求重新创建表的更改 ”的错误的解决方案是本文我们主要要介绍的内容,情况是这样的:我们在用SQL Server 2008 建完表后,插入或修改任意列时,提示:当用户在在SQL Server 2008企业管理器中更改表结构时,必须要先删除原来的表,然后重新创建新表,才能完成表的更改。 如果强行更改会出现以下提示:不允许保存...

经验教程

62

收藏

21
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部