快来阿!!!分是大大的有!
解决方案 »
- 女友老说糊里糊涂就跟我在一起了,咋办?
- 心情不好散分!!!
- 大家过年好,想请教一个关于数据显示问题,请各位帮帮忙
- 如何写DELPHI一个数据库程序的安装程序
- 在一个比较费时的操作时,自做的一个显示进度信息的窗口显示不完全?
- BatchMove拷贝access数据库中的table时自动编号的字段类型变成了数字型,而且。。。
- 【紧急求救】动态创建TXMLDocument后,其的DocumentElement不能使用。报Access violation错误!
- 30分求WINDOWS优化大师的注册号!!!
- 不是高手请不要进来!!!!谢谢
- 用于控制edit,dbedit数字录入的问题
- 在报表打印时,怎么样才能控制不换页,就是说报表有多少记录就打印多少记录
- 为了显示我的高超,将发送一个我自己用delphi做的程序给大家。不过没有带源程序。需要的话付点服务费。(仅20元)
成员 描述
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被发送到客户端,并在那里被还原(反串行化)。
我自己用程序试试。
毕竟初中生不太好。
所以先试看。