这一步本来要往上走的,因为感觉上面还有很多风景,可是一次意外,发现我的msdn竟然无法找到网上流行的SafeArrayCreateEx这个函数,大感意外,因此就决定继续横爬了,来看看怎么用SAFEARRAY包装自定义结构了。
开始之前,先推荐一文章
VB真是想不到系列之四:VB指针葵花宝典之SafeArray
网址就不提供了,网上搜搜就找到了,绝对不是做广告的,只是觉得挺有意思的,从VB的眼光来看SAFEARRAY。
很显然COleSafeArray并没有封装SafeArrayCreateEx函数,所以这回咱直接用API了。
1.为CLiteGridCtrl添加上Rects属性,VARIANT类型,还是用Get/Set methods吧,这个属性功能就不用多说了,为我们所有的CCell设置和返回m_rect成员。
2.实现Rects属性
VARIANT CLiteGridCtrl::GetRects()
{
VARIANT vaResult;
VariantInit(&vaResult);
// TODO: Add your property handler here
IRecordInfo* pRecInfo = NULL;
GetRecordInfoFromGuids(GUID_Lib, 1, 0, LOCALE_USER_DEFAULT, GUID_Rect, &pRecInfo);
SAFEARRAYBOUND sab[2];
sab[0].cElements = 10;
sab[0].lLbound = 0;
sab[1].cElements = 10;
sab[1].lLbound = 0;
SAFEARRAY* psa = SafeArrayCreateEx(VT_RECORD, 2, sab, pRecInfo);
long lindex[2] = {0};
RECT* prect = NULL;
SafeArrayLock(psa);
for(int i=0; i<10; i++){
lindex[0] = i;
for(int j=0; j<10; j++){
lindex[1] = j;
SafeArrayPtrOfIndex(psa, lindex, (void**)&prect);
*prect = m_cells[i][j].m_rect;
}
}
SafeArrayUnlock(psa);
vaResult.vt = VT_ARRAY | VT_RECORD;
vaResult.pRecInfo = pRecInfo;
vaResult.parray = psa;
return vaResult;
}
void CLiteGridCtrl::SetRects(const VARIANT FAR& newValue)
{
// TODO: Add your property handler here
COleSafeArray sa(newValue);
ASSERT(sa.GetDim() == 2);
long llb1 = 0;
long lub1 = 0;
long llb2 = 0;
long lub2 = 0;
long l1 = 0;
long l2 = 0;
sa.GetLBound(1, &llb1);
sa.GetUBound(1, &lub1);
l1 = lub1-llb1+1;
ASSERT(l1 == 10);
sa.GetLBound(2, &llb2);
sa.GetUBound(2, &lub2);
l2 = lub2-llb2+1;
ASSERT(l2 == 10);
long lindex[2] = {0};
RECT* prect = NULL;
sa.Lock();
for(int i=llb1; i<=lub1; i++){
lindex[0] = i;
for(int j=llb2; j<=lub2; j++){
lindex[1] = j;
sa.PtrOfIndex(lindex, (void**)&prect);
m_cells[i-llb1][j-llb2].m_rect = prect;
}
}
sa.Unlock();
InvalidateControl();
SetModifiedFlag();
}
其实只要将Texts属性的代码拷贝过来,稍微修改一下就可以了,这里的GUID_Lib和GUID_Rect定义在LiteGridCtrl.cpp中,如下:
const GUID GUID_Lib =
{ 0x191618F9, 0xEBF9, 0x4538, { 0x9E, 0x10, 0xD9, 0xC5, 0x62, 0x7E, 0xAE, 0xA9 } };
const GUID GUID_Rect =
{ 0x6BF5EE0C, 0x373A, 0x4893, { 0x89, 0xEB, 0x2C, 0x02, 0x08, 0xD3, 0xD4, 0xEB } };
3.在VB的Form_Load中添加如下代码
Private Sub Form_Load()
Dim str(0 To 9, 0 To 9) As String
Dim stro() As String
Dim i As Integer
Dim j As Integer
For i = 0 To 9
For j = 0 To 9
str(i, j) = i & " : " & j
Next
Next
LiteGrid1.Texts = str
stro = LiteGrid1.Texts
Dim v() As Rect
Dim x As Integer
Dim y As Integer
x = 0
y = 0
v = LiteGrid1.Rects
For i = LBound(v) To UBound(v)
y = 0
For j = LBound(v, 2) To UBound(v, 2)
v(i, j).Left = x
v(i, j).Top = y
v(i, j).Right = x + 40
v(i, j).bottom = y + 16
y = y + 20
Next
x = x + 50
Next
LiteGrid1.Rects = v
End Sub
前面的一段是上一步中的内容,也给列了出来,运行一下会发现框框没有并在一起了,分开了些,证明Rect属性的Get和Set都是调用成功了。
所以,其实还是蛮简单的
本来是想要用SafeArrayAllocDescriptor和SafeArrayAllocData来模拟SafeArrayCreateEx的,而且事实上好象也不会出错的,SAFEARRAY的各个成员的值两种方法得到的都一样,如下:
SAFEARRAY* psa = NULL;
SafeArrayAllocDescriptor(2, &psa);
psa->rgsabound[0].lLbound = 0;
psa->rgsabound[0].cElements = 10;
psa->rgsabound[1].lLbound = 0;
psa->rgsabound[1].cElements = 10;
psa->cbElements = sizeof(RECT);
psa->fFeatures = FADF_RECORD;
HRESULT hresult = SafeArrayAllocData(psa);
if(FAILED(hresult)){
SafeArrayDestroyDescriptor(psa);
return vaResult;
}
可是,意外的是竟然GetRects和SetRects全都没有正确执行
然后在后面加上
SafeArraySetRecordInfo(psa, pRecInfo);
就OK了。
所以看起来似乎这个IRecordInfo是隐藏在SAFEARRAY结构中的,具体放哪里,就不去管它了,研究这个好象也没什么意义的,如果哪位朋友碰巧熟悉这个,烦情告知一下,感谢。
差点忘了,因为这里还是用COleSafeArray来解析的,所以没用到SafeArrayGetVartype,这个函数好象也是msdn中没有的,可以用它来判断是否是你所需要的VARTYPE,应该是比较有用的。
VARTYPE vt;
SafeArrayGetVarType(psa, &vt);
这里psa为SAFEARRAY*。