Chinaunix首页 | 论坛 | 博客
  • 博客访问: 184160
  • 博文数量: 90
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2017-08-23 16:48
文章分类

全部博文(90)

文章存档

2015年(1)

2011年(21)

2010年(59)

2009年(9)

我的朋友

分类: C/C++

2010-07-14 10:03:19

在上一步中我们实际是通过IDispatch接口的Invoke方法来访问控件的属性和方法的,虽然有COleDispatchDriver和其它一些辅助函数,但还是有些繁杂,能不能直接得到控件对象的指针,这样就可以直接访问控件了,甚至于控件的内部变量了?答案是肯定了。
我们的控件派生自COleControl,COleControl派生自CWnd,CWnd又派生自CCmdTarget,我们的IDispatch接口就是在CCmdTarget中实现的(其实应该是实现在COleDispatchImpl类中),实现的方法比较特殊,这里不细细分析了,有兴趣的朋友可以自己看看MFC源码或者网上搜搜资料,应该能知道的。
我们能从CCmdTarget中获得IDispatch接口指针(GetIDispatch函数),同样的,我们也能从IDispatch接口指针中反向导出CCmdTarget对象指针,用到的是CCmdTarget的静态成员函数
static CCmdTarget* FromIDispatch( LPDISPATCH lpDispatch );
前面我们已经知道可以通过属性页的GetObjectArray来获得控件的IDispatch接口指针,而有了上面的FromIDispatch这个函数,我们又可以从IDispatch接口指针中来获得控件的指针了,那么问题就解决了。
另外,这里我们还用到COlePropertyPage的一个虚拟函数OnObjectsChanged,在控件变化(包括控件加载上和卸载掉时)时,都会调用到这个函数。(其实,并不是一定要用这个函数的,只是想介绍一下,就用上了)
还是用上一步中的例子topp
1.添加控件类指针为属性页类的成员变量(#include "ToppCtl.h",应该不用说的吧)
CToppCtrl* m_pctrl;
2.添加获得控件类指针的函数CToppCtrl* GetCtrlPtr();
CToppCtrl* CToppPropPage::GetCtrlPtr()
{
    ULONG ulObjects;
    LPDISPATCH* lpObjectArray = GetObjectArray( &ulObjects );
    ASSERT( lpObjectArray != NULL );
    LPDISPATCH lpdisp = NULL;
    if(ulObjects > 0){//在这个函数中,并不一定能保证就有控件.
        lpdisp = lpObjectArray[0];//这里省事了,就假设只有一个控件了
    }
    CToppCtrl* pctrl = NULL;
    if(lpdisp){
        pctrl = (CToppCtrl*)CCmdTarget::FromIDispatch(lpdisp);
    }
    return pctrl;
}
3.重载OnObjectsChanged,设置m_pctrl(在其它地方,如OnInitDialog中设置也无所谓,甚至即用即获得也无所谓)
void CToppPropPage::OnObjectsChanged()
{
    ULONG ulObjects;
    LPDISPATCH* lpObjectArray = GetObjectArray( &ulObjects );
    ASSERT( lpObjectArray != NULL );
    LPDISPATCH lpctl = NULL;
    if(ulObjects > 0){//在这个函数中,并不一定能保证就有控件.
        lpctl = lpObjectArray[0];//这里省事了,就假设只有一个控件了
    }
    if(lpctl){
        m_pctrl = (CToppCtrl*)CCmdTarget::FromIDispatch(lpctl);
    }
    else{
        m_pctrl = NULL;
    }
}
3.修改上一步中的几个成员函数
BOOL CToppPropPage::AddItem(LPCTSTR lpszItem)
{
    if(m_pctrl)    m_pctrl->m_saItems.Add(lpszItem);
    return m_pctrl != NULL;
}
CString CToppPropPage::GetItem(long lItem)
{
    if(m_pctrl) {
        if(lItem >= 0 && lItem < m_pctrl->m_saItems.GetSize()){
            return m_pctrl->m_saItems[lItem];
        }
        else{
            return _T("");
        }
    }
    else{
        return _T("");
    }
}
//在获得m_color时,是直接将m_color从protected拉到了public下的,可恶的MFC,再添加新的属性或方法时可能会重新再添加一个,会给你带来一个小小的麻烦。
COLORREF CToppPropPage::GetColor()
{
    if(m_pctrl) return m_pctrl->TranslateColor(m_pctrl->m_color);
    return RGB(0, 0, 0);
}
void CToppPropPage::SetColor(COLORREF cr)
{
    if(m_pctrl) m_pctrl->m_color = cr;
}
long CToppPropPage::GetCount()
{
    if(m_pctrl) return m_pctrl->m_saItems.GetSize();
    return 0;
}
4.编译,新建一VB工程,在VB下测试,可以发现没有问题
5.在ActiveX Control Container下测试,咦,出问题了,没有反应,为什么?
根据最后给出的资料上中的意思,大致是因为容器聚合了控件的原因(呵呵,懒得去想了,有兴趣的自己思考吧)
6.为控件添加一只读属性long CtrlPtr
long CToppCtrl::GetCtrlPtr()
{
    // TODO: Add your property handler here
    return reinterpret_cast(this);
    return 0;
}
7.编译,在ClassWizard的Automation下,点击Add Class,然后选择From a type library,找到编译时为控件生成的.tlb文件,这里应该是在\Debug目录下,选择这个topp.tlb,然后只选择其中的_DTopp,点确定,这样将生成一个_DTopp类,和我们上一步中的介绍比较一下,可以发现,其实就是派生自一个COleDispatchDriver类,也就是说,只是一个帮助我们调用控件属性和方法的类(也就是说,我们上一步中其实完全不用自己来编代码的,ClassWizard可以为我们生成一个方便的可操作的类:))。
8.修改属性页的GetCtrlPtr函数如下:
CToppCtrl* CToppPropPage::GetCtrlPtr()
{
    ULONG ulObjects;
    LPDISPATCH* lpObjectArray = GetObjectArray( &ulObjects );
    ASSERT( lpObjectArray != NULL );
    LPDISPATCH lpdisp = NULL;
    if(ulObjects > 0){//在这个函数中,并不一定能保证就有控件.
        lpdisp = lpObjectArray[0];//这里省事了,就假设只有一个控件了
    }
    CToppCtrl* pctrl = NULL;
    if(lpdisp){
        pctrl = (CToppCtrl*)CCmdTarget::FromIDispatch(lpdisp);
        if(!pctrl){
            long ControlPointer;
            _DTopp control(lpdisp);
            // GetObjectArray() docs state must not release pointer.
            control.m_bAutoRelease = FALSE;
            ControlPointer = control.GetCtrlPtr();
            pctrl = reinterpret_cast(ControlPointer);
            //这里其实可以简单用如下的方法调用,之所以用上面的方法,只是用来介绍而已
            /*
            long p;
            GetPropText("CtrlPtr", &p);
            pctrl = reinterpret_cast(p);
            */
        }
    }
    return pctrl;
}
9.编译,在ActiveX Control Container中测试,OK!
注释:这里用ClassWizard导入了一个辅助类是用来帮助从IDispatch中获得控件的CtrlPtr属性值的,其实并不一定需要这个辅助类的,见CToppCtrl* CToppPropPage::GetCtrlPtr()函数中被注释掉的代码,之所以引入这个辅助类,也是介绍起见。
参考资料:

 
阅读(1554) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~