快来阿!!!分是大大的有!

解决方案 »

  1.   

      使用SAFEARRAY  我们将使用SAFEARRAY来通过COM传送串行化的对象。这部分将介绍如何建立和使用SAFEARRAY类型。  SAFEARRAY是数据结构的一种。这种结构也没有什么特别的,你只需要正确地设置它,当然你要知道其中一些颇为复杂的规定。为了确保SAFEARRAY被正确地使用,它有一系列用作管理的API函数。这些API的函数负责创建、调整大小和删除SAFEARRAY。不幸的是,有关这些API函数的文档是相当少的。  对于SAFEARRAY的内部结构我们并不关心,但了解一下是值得的。以下就是SAFEARRAY的Win32定义:  typedef struct tagSAFEARRAY   {    unsigned short cDims;    unsigned short fFeatures;    unsigned long cbElements;    unsigned long cLocks;    void * pvData;    SAFEARRAYBOUND rgsabound[ 1 ];   } SAFEARRAY;  这个结构的成员(cDims,cLocks等)是通过API函数来设置和管理的。真正的数据存放在pvData成员中,而SAFEARRAYBOUND结构定义该数组结构的细节。以下就是该结构成员的简要描述:
    成员 描述 
    cDims 数组的维数 
    fFeatures 用来描述数组如何分配和如何被释放的标志 
    cbElements 数组元素的大小 
    cLocks 一个计数器,用来跟踪该数组被锁定的次数 
    pvData  指向数据缓冲的指针 
    rgsabound 描述数组每维的数组结构,该数组的大小是可变的   rgsabound是一个有趣的成员,它的结构不太直观。它是数据范围的数组。该数组的大小依safe array维数的不同而有所区别。rgsabound成员是一个SAFEARRAYBOUND结构的数组--每个元素代表SAFEARRAY的一个维。  typedef struct tagSAFEARRAYBOUND   {    unsigned long cElements;    unsigned long lLbound;   } SAFEARRAYBOUND;  维数被定义在cDims成员中。例如,一个'C'类数组的维数可以是[3][4][5]-一个三维的数组。如果我们使用一个SAFEARRAY来表示这个结构,我们定义一个有三个元素的rgsabound数组--一个代表一维。  cDims = 3;
        ...
      SAFEARRAYBOUND rgsabound[ 3 ];  rgsabound[0]元素定义第一维。在这个例子中ILBOUND元素为0,是数组的下界。cElements成员的值等于三。数组的第二维([4])可以被rgsabound结构的第二个元素定义。下界也可以是0,元素的个数是4,第三维也是这样。要注意,由于这是一个"C"数组,因此由0开始,对于其它语言,例如Visual Basic,或者使用一个不同的开始。该数组的详细情况如下所示:
    元素 cElements ILbound 
    rgsabound[0]  3  0 
    rgsabound[1] 4  0 
    rgsabound[2] 5 0 
      关于SAFEARRAYBOUND结构其实还有很多没说的。我们将要使用的SAFEARRAY只是一个简单的单维字节数组。我们通过API函数创建数组的时候,SAFEARRAYBOUND将会被自动设置。只有在你需要使用复杂的多维数组的时候,你才需要操作这个结构。  还有一个名字为cLocks的成员变量。很明显,它与时间没有任何的关系--它是一个锁的计数器。该参数是用来控制访问数组数据的。在你访问它之前,你必须锁定数据。通过跟踪该计数器,系统可以在不需要该数组时安全地删除它。
    创建SAFEARRAY  创建一个单维SAFEARRAY的简单方法是通过使用SafeArrayCreateVector API函数。该函数可分配一个特定大小的连续内存块。  SAFEARRAY *psa;  file:// create a safe array to store the stream data  file:// llen is the number of bytes in the array.
      psa = SafeArrayCreateVector( VT_UI1, 0, llen );  SafeArrayCreateVector API创建一个SAFEARRAY,并且返回一个指向它的指针。首个参数用来定义数组的类型--它可以是任何有效的变量数据类型。为了传送一个串行化的对象,我们将使用最基本的类型--一个非负的字节数组。VT--UI1代表非负整形的变量类型,1个字节。  常数'0'定义数组的下界;在C++中,通常为0。最后的参数llen定义数组元素的个数。在我们的例子中,这与我们将要传送对象的字节数是一样的。我们还没有提数组大小(llen)是怎样来的,这将在我们重新考查串行化时提及。  在你访问SAFEARRAY数据之前,你必须调用SafeArrayAccessData。该函数锁定数据并且返回一个指针。在这里,锁定数组意味着增加该数组的内部计数器(cLocks)。  file:// define a pointer to a byte array  unsigned char *pData = NULL;  SafeArrayAccessData( psa, (void**)&pData );   ... use the safe array  SafeArrayUnaccessData(psa);  相应用来释放数据的函数是SafeArrayUnaccessData(),该功能释放该参数的计数。  定义COM接口  在COM中传送一个SAFEARRAY是很简单的,你需要设置自己的接口来传送SAFEARRAY结构。SAFEARRAY是一个本地的IDL数据类型。以下就是一个接口要处理的IDL代码:  [   object,    uuid(EEC6D3EF-32F7-11D3-9EA1-00105A132526),    dual,    helpstring("IBlobData Interface"),    pointer_default(unique)   ]   interface IBlobData : IUnknown  {   HRESULT GetArray([out] SAFEARRAY(unsigned char) *pData);   HRESULT SetArray([in] SAFEARRAY(unsigned char) pData );  };  只要你定义它包含的数据类型,SAFEARRAY就是一个有效的数据类型。语句SAFEARRAY(unsigned char)可用来传送任何类型的二进制数据。"Unsigned char"意味着该数据将会是二进制字节,它与VT_UI1变量类型相对应。  它的两个方法是相对的--GetArray方法从服务器得到一个对象。SetArray方法则发送一个对象给服务器。我们将不会谈及为该接口创建一个COM对象的基本问题。这个工作可通过使用ATL向导来完成。  接下来,我们会将串行化和SAFEARRAY两部分的知识结合起来,讲述一个例子。  Fig1.0数据类型允许用在IDispatch接口上。以下这些数据类型是可被一个类库调用的。类型 名字  描述 
    byte VT_UI1 非负字节 
    short VT_I2 有符号16位短整型 
    long VT_I4 有符号32位长整型 
    float VT_R4 一个IEEE 4字节实型数字 
    double VT_R8 一个IEEE 8字节实型数字 
    VARIANT_BOOL VT_BOOL 16位布尔 0=false, 0xFFFF=true 
    SCODE  VT_ERROR 16位错误码 
    CY  VT_CY  16位货币结构 
    DATE VT_DATE  使用双精度数字表示的日期 
    BSTR  VT_BSTR  visual basic风格的字符结构 
    DECIMAL VT_DECIMAL 一个十进制的结构 
    IUnknown VT_UNKNOWN  一个COM接口的指针 
    IDispatch VT_DISPATCH COM Dispatch接口的指针 
    SAFEARRAY * VT_ARRAY 一个用作传送数组数据的特别结构 
    VARIANT * VT_VARIANT 一个VARIANT结构的指针 
    void  * 普通的指针 
      VT_BYREF 任何类型(除指针外)的指针 
    首先我们来看一下服务器的GetArray方法。这个COM方法创建一个简单的对象,并将它送回给客户端。COM方法的本身挺简单,但它使用了一个颇复杂的称为CBlob的类。我将所有的串行化和安全数组代码放在CBlob类中。   file:// This method creates an object and sends it back to the client.  STDMETHODIMP CBlobData::GetArray(SAFEARRAY **pData)   {    // 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 object    delete pMyOb;    return S_OK;
       }  GetArray方法首先创建一个CSimpleObject对象。然后将它传送给CBlob的Load()方法。Load方法将会串行化该对象,然后将它放进一个安全数组中。这个方法返回一个指向该安全数组的指针,此指针会被发送回客户端。该方法负责繁琐的串行化处理。  // 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;    file:// create the archive using the memory file    CArchive ar(&memfile, lMode );    file:// 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();     file:// detach the buffer and close the file    unsigned char *pMemData = memfile.Detach();    file:// set up safearray - SAFEARRAY is defined OAIDL.H    SAFEARRAY *psa;    // create a safe array to store the stream data    psa = SafeArrayCreateVector( VT_UI1, 0, llen );    // pointer to byte array    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;   }   以下就是Load方法所做的事情。你应该可以认出大部分的代码,因为我们已经讨论过它。   1、创建一个准备用作存储的archive   2、使用archive串行化该对象到一个CMemFile中   3、存储CMemFile缓冲的长度   4、将数据缓冲由CMemFile中脱离   5、创建SAFEARRAY   6、拷贝数据缓冲到SAFEARRAY  该存储文件缓冲被串行化填充。一旦填好,该缓冲就会从CMemFile对象脱离。CMemFile的Detach()方法必须被执行,以访问一个指针,该指针指向包含有串行化对象的内存缓冲。Detach还会令内存缓冲由CMemFile脱离,并且关闭该文件。  我们可以使用该指针做一个简单的内存复制,该操作将内存复制到SAFEARRAY的数据缓冲中。  // copy the memory file into the safearray  memcpy( pData, pMemData, llen );  该个步骤看来比想象中复杂。其实,这是由于memcpy()函数仅能复制连续的数据,因此我们不能将对象直接拷贝到安全数组中--这个对象在内存中的存储并不是连续的。CMemFile缓冲可确保是连续的,因此我们可以使用memcpy(),这样数据可通过COM被发送到客户端,并在那里被还原(反串行化)。
      

  2.   

    上面的看了一半就没看了。
    我自己用程序试试。
    毕竟初中生不太好。
    所以先试看。