zoukankan      html  css  js  c++  java
  • duilib修复ActiveXUI控件bug,以支持flash透明动态背景

    转载请说明原出处,谢谢~~

              昨天在QQ控件里和同学说起QQ2013登陆窗体的开发,从界面角度考虑,单单一个登陆界面是很容易做出来的。腾讯公司为了

    止各种盗号行为可谓煞费苦心,QQ2013采用了动态背景就是为了防止界面型盗号木马,这种盗号木马做起来很简单,容易骗过很

    多电脑小白。而才用动态背景后就加大了这种木马的开发难度。

              在Duiengine界面库中,已经有高手做出来一个高仿QQ界面的Demo。其中的登陆窗体只要使用flash做背景就可以了。在duilib

    中,已经有做好的ActiveXUI控件和flashUI控件,今天没事就准备做一个仿QQ登录器。

              先打开了duilib的flash demo,我准备测试一下在flash控件的上层是否可以绘制控件,但是问题出现了。在duilib中有两种播放

    flash的方法,第一是使用ActiveXUI控件去指定系统的Flash控件的clsid,然后在c++代码里再通过ActiveXUI控件的GetControl方法去

    获取IShockwaveFlash接口,进而进一步控制播放flash;第二是直接用duilib的flashUI控件。但是我发现,使用ActiveXUI控件播放的

    flash界面是透明无句柄的却是静态的,只是原flash文件的第一帧;而flashUI控件是动态的,但却自动创建了一个子窗体而不是透明

    无句柄界面,因为有了子窗体,就无法再把其他duilib控件绘制到Flash界面之上,所以这两个控件都无法满足我的需求。又是一场

    bug修复之旅(duilib的bug的确有点多了·····)。


    分析过程:

             首先我想修改一下FlashUI控件的源码,看看能否解决问题,在UIFlash.h文件的开头可以看到作者留下的这句话:

    class UILIB_API CFlashUI
    	: public CActiveXUI
    //	, public IOleInPlaceSiteWindowless // 透明模式绘图,需要实现这个接口
    	, public _IShockwaveFlashEvents
    	, public ITranslateAccelerator


             作者说要想让CFlashUI类实现透明模式绘图,需要实现IOleInPlaceSiteWindowless接口,这个接口是负责会绘制出无句柄的

    com组件并允许一个无窗口的对象处理window消息。这个接口在UIActiveX.cpp文件的CAvtiveXCtrl类中已经实现了,我查阅一些资料

    后给CFlashUI类补充了这个接口,却任然无法达到效果。debug后发现根本就没有进入到响应的函数中,我认为需要把另外

    的 IOleClientSite,  IOleControlSite,  IObjectWithSite, IOleContainer等接口也都实现了才会达到效果,但是这些接口的很多功能都已经

    CAvtiveXCtrl类中写好了,我再重写一遍显然不是个好办法。所以我把给CFlashUI写好的IOleInPlaceSiteWindowless接口代码都删

    掉,目标转向去修复CActiveXUI类的代码。

              通过debug模式下断点首先搞清楚了整个CActiveXUI.cpp文件中的几个类的执行流程。COM组件的主要绘制是在CAvtiveXCtrl

    类中,总体的执行流程为:com调用载体的IOleClientSite::QueryInterface,申请IOleInPlaceSite。在对象确定了载体是

    否具有定位能力之后,询问载体是否可以立即通过调用IOleInPlaceSite::CanInPlaceActivate定位激活该对象。在对象

    确定它可以进行定位激活之后,它通过调用IOleInPlaceSite::OnInPlaceActivate把自己的意图告诉载体。然后通过调

    IOleInPlaceSite::GetWindowContext,它得到指向其它两个载体接口----IOleInPlaceUIWindow(面向文档的)和

    IOleInPlaceFrame的指针,以及其他必要的信息(比如绘制的位置)。

              在duilib中,OnInPlaceActivate函数又调用了 OnInPlaceActivateEx函数,函数源码为:

     

           

    STDMETHODIMP CActiveXCtrl::OnInPlaceActivateEx(BOOL* pfNoRedraw, DWORD dwFlags)        
    {
        DUITRACE(_T("AX: CActiveXCtrl::OnInPlaceActivateEx"));
        ASSERT(m_pInPlaceObject==NULL);
        if( m_pOwner == NULL ) return E_UNEXPECTED;
        if( m_pOwner->m_pUnk == NULL ) return E_UNEXPECTED;
        ::OleLockRunning(m_pOwner->m_pUnk, TRUE, FALSE);
        HWND hWndFrame = m_pOwner->GetManager()->GetPaintWindow();
        HRESULT Hr = E_FAIL;
        if( (dwFlags & ACTIVATE_WINDOWLESS) != 0 ) {
            m_bWindowless = true;
            Hr = m_pOwner->m_pUnk->QueryInterface(IID_IOleInPlaceObjectWindowless, (LPVOID*) &m_pInPlaceObject);
            m_pOwner->m_hwndHost = hWndFrame;
            m_pOwner->GetManager()->AddMessageFilter(m_pOwner);
        }
        if( FAILED(Hr) ) {
            m_bWindowless = false;
            Hr = CreateActiveXWnd();
            if( FAILED(Hr) ) return Hr;
            Hr = m_pOwner->m_pUnk->QueryInterface(IID_IOleInPlaceObject, (LPVOID*) &m_pInPlaceObject);
        }
        if( m_pInPlaceObject != NULL ) {
            CDuiRect rcItem = m_pOwner->m_rcItem;
            if( !m_bWindowless ) rcItem.ResetOffset();
            m_pInPlaceObject->SetObjectRects(&rcItem, &rcItem);
        }
        m_bInPlaceActive = SUCCEEDED(Hr);
        return Hr;
    }
    

            在这里用过参数dwFlahs, if( (dwFlags & ACTIVATE_WINDOWLESS) != 0 )语句确定是否去试图创建无窗口的

    实例,而ACTIVATE_WINDOWLESS常量的值为1,如果dwFlags参数值不为1,就不回去视图创建无窗口实例,进而去执行后面的Hr = CreateActiveXWnd();语句,在这里调用了函数CreateActiveXWnd,这个函数的内容为:


    HRESULT CActiveXCtrl::CreateActiveXWnd()
    {
        if( m_pWindow != NULL ) return S_OK;
        m_pWindow = new CActiveXWnd;
        if( m_pWindow == NULL ) return E_OUTOFMEMORY;
        m_pOwner->m_hwndHost = m_pWindow->Init(this, m_pOwner->GetManager()->GetPaintWindow());
        return S_OK;
    }


             意思就是如果不创建无窗体的实例,就调用CreateActiveXWnd函数去创建一个CActiveXWnd实例,这是duilib自

    定义的窗体类,在类内建立了子窗体,让com组件附着到这个子窗体上,函数把类CActiveXWnd的实例赋值给

    m_pWindow变量,而他就是整个bug的核心关键。


    创建无窗体Flash的流程:

           前面说了一堆只是铺垫,现在我针对创建无窗体Flash这个点来说一下他的执行步骤,bug就在这里了!

            我直接使用FlashDemo来说明,在demo里,窗体收到_T("showactivex") 事件后就得知CAcviteXUI控件要显示出

    flash动画了,然后调用如下语句来初始化Flash:

    if( msg.pSender->GetName() == _T("flashActiveX") )
    {
    				
    	IShockwaveFlash* pFlash = NULL;
    	CActiveXUI* pActiveX = static_cast<CActiveXUI*>(msg.pSender);
    
    	pActiveX->GetControl(__uuidof(IShockwaveFlash), (void**)&pFlash);
    
    	if( pFlash != NULL )
    	{
    		pFlash->put_WMode( _bstr_t(_T("Transparent") ) );
    		pFlash->put_Movie( _bstr_t(CPaintManagerUI::GetInstancePath() + _T("\skin\FlashRes\test.swf")) );
    		pFlash->DisableLocalSecurity();
    		pFlash->put_AllowScriptAccess(L"always");
    
    		BSTR request,response;
    		request = SysAllocString(L"<invoke name="setButtonText" returntype="xml"><arguments><string>Click me!</string></arguments></invoke>");
    		response = SysAllocString(L"");
    		pFlash->CallFunction(request, &response);
    		SysFreeString(request);
    		SysFreeString(response);
    	}
    }

              当执行到pFlash->put_WMode( _bstr_t(_T("Transparent") ) );语句时,说明我们想创建一个透明无窗体的

    flash,这时就会先调用CActiveXCtrl类的CanWindowlessActivate函数来确定是否可以创建无窗体实例,此函数返回真

    ,在flash组件确认了可以创建无窗体实例后就回去主动调用OnInPlaceActivateEx函数并且把dwFlags参数设置为1,

    这时在OnInPlaceActivateEx函数内会去试图创建无窗体实例,如果创建成功了就不会执行CreateActiveXWnd函数,

    这个函数不执行,那么m_pWindow变量的值就是NULL。(而事实是可以创建成功,所以m_pWindow就为NULL)。

    此后flash组件会调用GetWindowContext函数去获取显示flash需要的必要信息,而这个函数就是bug的来源了!

    先看此函数的源码:

        

    STDMETHODIMP CActiveXCtrl::GetWindowContext(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
    {
        DUITRACE(_T("AX: CActiveXCtrl::GetWindowContext"));
        if( ppDoc == NULL ) return E_POINTER;
        if( ppFrame == NULL ) return E_POINTER;
        if( lprcPosRect == NULL ) return E_POINTER;
        if( lprcClipRect == NULL ) return E_POINTER;
    	if (m_pWindow)
    	{
    		::GetClientRect(m_pWindow->GetHWND(),lprcPosRect);
    		::GetClientRect(m_pWindow->GetHWND(),lprcClipRect);
    	}
        *ppFrame = new CActiveXFrameWnd(m_pOwner);
        *ppDoc = NULL;
        ACCEL ac = { 0 };
        HACCEL hac = ::CreateAcceleratorTable(&ac, 1);
        lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
        lpFrameInfo->fMDIApp = FALSE;
        lpFrameInfo->hwndFrame = m_pOwner->GetManager()->GetPaintWindow();
        lpFrameInfo->haccel = hac;
        lpFrameInfo->cAccelEntries = 1;
        return S_OK;
    }


           可以看到,代码里有一处判断

    if (m_pWindow)
    {
    	::GetClientRect(m_pWindow->GetHWND(),lprcPosRect);
    	::GetClientRect(m_pWindow->GetHWND(),lprcClipRect);
    }

           当m_pWindow不为NULL时就为lprcPosRect和lprcClipRect参数赋值,这两个参数决定了flash组件的输出的位

    置。而我前面分析了,m_pWindow恰好就是NULL,所以这两个参数没有被赋值,所以最终无法正常输出flash动画,

    我们就只能得到静态的flash效果了,此bug的修复方法很简单,就是如果m_pWindow为NULL,就把这两个参数赋值为

    CActiveXUI控件的位置,修复后的代码为:


    STDMETHODIMP CActiveXCtrl::GetWindowContext(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
    {
        DUITRACE(_T("AX: CActiveXCtrl::GetWindowContext"));
        if( ppDoc == NULL ) return E_POINTER;
        if( ppFrame == NULL ) return E_POINTER;
        if( lprcPosRect == NULL ) return E_POINTER;
        if( lprcClipRect == NULL ) return E_POINTER;
    	if (m_pWindow)
    	{
    		::GetClientRect(m_pWindow->GetHWND(),lprcPosRect);
    		::GetClientRect(m_pWindow->GetHWND(),lprcClipRect);
    	}
    	else
    	{
    		RECT rcItem = m_pOwner->GetPos();
    		memcpy(lprcPosRect, &rcItem, sizeof(rcItem));
    		memcpy(lprcClipRect, &rcItem, sizeof(rcItem));
    	}
        *ppFrame = new CActiveXFrameWnd(m_pOwner);
        *ppDoc = NULL;
        ACCEL ac = { 0 };
        HACCEL hac = ::CreateAcceleratorTable(&ac, 1);
        lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
        lpFrameInfo->fMDIApp = FALSE;
        lpFrameInfo->hwndFrame = m_pOwner->GetManager()->GetPaintWindow();
        lpFrameInfo->haccel = hac;
        lpFrameInfo->cAccelEntries = 1;
        return S_OK;
    }


            只需要修复这一处代码,我们用FlashDemo就可以创建出无窗体的透明flash背景了。效果如下:

         

           这个透明无窗体的Flash问题解决了,就可以很容易做出个仿QQ2013登陆界面了,这是我简单做得一个:


              这个仿QQ2013登录器的背景是动态的,不过好像放到博客上就成了静态的了······因为这个QQ登陆器修复bug

    无关,我就在下一篇博客里说明一下QQ2013登录器了。

               个人水平有限,如果发现我的博客里有说明不当的地方,请提醒我!


             Redrain   2014.8.10    QQ:491646717

  • 相关阅读:
    Quicksum -SilverN
    uva 140 bandwidth (好题) ——yhx
    uva 129 krypton factors ——yhx
    uva 524 prime ring problem——yhx
    uva 10976 fractions again(水题)——yhx
    uva 11059 maximum product(水题)——yhx
    uva 725 division(水题)——yhx
    uva 11853 paintball(好题)——yhx
    uva 1599 ideal path(好题)——yhx
    uva 1572 self-assembly ——yhx
  • 原文地址:https://www.cnblogs.com/redrainblog/p/3902685.html
Copyright © 2011-2022 走看看