long Size0;ubyte Data0[?];long Size1;ubyte Data1[?];long Size2;ubyte Data2[?];...
接收数据的那一方,如果只需要 遍历数据,那么使用这个结构是高效并且节省资源的。
Size0表示 Data0的长度,Data0中的问号?表示数据长度是不定长的,也因此节省了占用空间。
起始地址+Size0+4就会得到Size1的地址,Size1的地址+Size1+4就得到了Size2的地址,+4是因为long型数值Size 占4字节。有多少个数据,就连续定义多少个long Size和ubyte Data[?]。
这种结构对于“遍历”来说是高效的,但是,如果要获取Data1的数据,就必须从Size0开始计算,得出Size1的地址,Size1+4才是Data1的地址。最坏的情况,需要循环到结构最后,才能得到需要的数据,如果须考虑这种情况,必须再想一个结构,使得函数可以在O(1)内完成检索:
long Offset0;long Offset1;long Offset2;...
全是long型,表示数据n相对于起始地址的偏移地址,Offset0表示Size0的偏移地址,Offset1表示Size1的偏移地址。这里必须要设为偏移地址,而不能是绝对地址,因为进程间通信,数据发送到另一方时,起始地址的指针就会改变。如果要获取第n个数据,起始地址+[起始地址+(n-1)*4]就是数据n的地址。
综合起来:如果有3个数据需要发送,结构就是:
long Offset0;long Offset1;long Offset2;long Size0;ubyte Data0[?];long Size1;ubyte Data1[?];long Size2;ubyte Data2[?]
在使用时,问号?必须要改成一个合法的数值表示数据长度。
以上结构还有一个不足之处,就是 还没有一个字段来表示数据的数量。如果通信的另一方根本不知道数据量有多少,错误地想获取第4个数据,那么根据这个公式“起始地址+[起始地址+(4-1)*4]”就会计算出错误的地址而引发越界访问。为了确保程序的安全,最终的结构应该是:
long UBound;long Offset0;long Offset1;long Offset2;long Size0;ubyte Data0[?];long Size1;ubyte Data1[?];long Size2;ubyte Data2[?]
只在开头加了一个long UBound,表示数据的数量,这个结构能容纳3个,所以UBound的值就必须设为3。获取第n个数据的地址应该改用“起始地址+[起始地址+n*4]”。
示例代码:
#include <Memory.au3>
#include <Array.au3>
$aData = StringSplit("a,b,c,d,abc,abcd", ",", 3)
$pSendBuffer = _CreateSendBuffer($aData)
For $i = 0 To UBound($aData) - 1
MsgBox(0, $i, _ParseBuffer($pSendBuffer, $i))
Next
$aParsed = _ParseBuffer($pSendBuffer, -1)
_ArrayDisplay($aParsed)
; 解析结构
Func _ParseBuffer($pBuffer, $iIndex = -1)
Local $tData, $pData, $iSize, $tSize, $tOffset, $tUBound, $iUBound
$tUBound = DllStructCreate("long UBound", $pBuffer)
$iUBound = DllStructGetData($tUBound, "UBound") ; 数据量
If $iIndex < 0 Then ; 遍历
Local $aData[$iUBound]
For $i = 0 To $iUBound - 1
$aData[$i] = _ParseBuffer($pBuffer, $i)
Next
Return $aData
ElseIf $iIndex < $iUBound Then ; 获取其中第$iIndex个数据
; 获取偏移地址。
$tOffset = DllStructCreate("long EntryOffset", $pBuffer + $iIndex * 4 + 4)
$pData = $pBuffer + DllStructGetData($tOffset, "EntryOffset") ; 绝对地址
$tSize = DllStructCreate("long Size", $pData) ; 数据长度
$tData = DllStructCreate("wchar Data[" & DllStructGetData($tSize, "Size") / 2 & "]", $pData + 4)
Return DllStructGetData($tData, "Data") ; 返回实际数据
EndIf
EndFunc ;==>_ParseBuffer
; 创建结构
Func _CreateSendBuffer($aData)
Local $tBuffer, $pBuffer, $iBuffer, $tData, $iUBound, $aOffset[1]
$iUBound = UBound($aData) ; 数据量
Redim $aOffset[$iUBound]
For $i = 0 To $iUBound - 1
; 第$i个数据在结构中的偏移地址
$aOffset[$i] = $iBuffer + $iUBound * 4 + 4
; 数据所需长度
$iBuffer += StringLen($aData[$i]) * 2 + 6
Next
; 结构所需长度
$iBuffer += $iUBound * 4 + 4
; 分配内存,+4是为了避免引起越界访问。
$pBuffer = _MemGlobalAlloc($iBuffer + 4)
$tBuffer = DllStructCreate("long UBound", $pBuffer)
; 设置 数据量
DllStructSetData($tBuffer, "UBound", $iUBound)
; 填充结构
For $i = 0 To $iUBound - 1
$tBuffer = DllStructCreate("long EntryOffset", $pBuffer + $i * 4 + 4)
DllStructSetData($tBuffer, "EntryOffset", $aOffset[$i]) ; 偏移
$tData = DllStructCreate("long Size;wchar Data[" & StringLen($aData[$i]) + 1 & "]", $pBuffer + $aOffset[$i])
DllStructSetData($tData, "Size", DllStructGetSize($tData) - 4) ; 数据长度,-4即减去long Size所占长度。
DllStructSetData($tData, "Data", $aData[$i]) ; 实际数据
Next
Return $pBuffer
EndFunc ;==>_CreateSendBuffer
|