ActiveX控件的MFC设计之旅-第3步 .

来源:互联网 发布:国家税务局网络 编辑:程序博客网 时间:2024/05/29 13:34
 
这一步本来要往上走的,因为感觉上面还有很多风景,可是一次意外,发现我的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*。