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

昨天在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

duilib修复ActiveXUI控件bug,以支持flash透明动态背景相关推荐

  1. duilib 修复Text控件无法设置宽度的bug,增加自动加算宽度的属性

    转载请说明原出处,谢谢~~: 今天有朋友反映CTextUI控件无法设置宽度,于是修复了这个bug,顺便给Text控件增加了一个自动计算宽度的属性,描述如下 <Attribute name=&qu ...

  2. 改进duilib的richedit控件的部分功能

    转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41208207 如果要使用透明异形窗体功能,首先要改进duilib库让他本 ...

  3. wpf控件设计时支持(2)

    原文:wpf控件设计时支持(2) 这篇介绍在wpf设计时集合项属性添加项的定义和自定义控件右键菜单的方法 集合项属性设计时支持 1.为集合属性设计器识别具体项类型 wpf设计器允许定义集合项的类型,如 ...

  4. wpf控件设计时支持(1)

    wpf控件设计时支持(1) 原文:wpf控件设计时支持(1) 这部分内容几乎是大家忽略的内容,我想还是来介绍一下. 本篇源码下载 1.属性元数据 在vs IDE中,在asp.net,winfrom等开 ...

  5. 设置ListCtrl列表控件其中某一行的字体和背景颜色

    设置ListCtrl列表控件其中某一行的字体和背景颜色,可以最终达到如下效果: 操作步骤如下所示: 1.先添加一个自定义消息 ON_NOTIFY ( NM_CUSTOMDRAW,IDC_V_H264_ ...

  6. 修复duilib库UISlider控件的4个bug

    转载级请注明原出处,谢谢~· 昨天封装好一个音频类,我在为dulib做音频播放demo时发现了一些问题,由CSliderUI控件导致的,进而发现了这个控件的好几样不足,他无法满 足我们做一个播放器的进 ...

  7. 仿酷狗音乐播放器开发日志二十三 修复Option控件显示状态不全的bug(附源码)...

    转载请说明原出处,谢谢~~ 整个仿酷狗工程的开发将近尾声,现在还差选项设置窗体的部分,显然在设置窗体里用的最多的就是OptionUI控件,我在写好大致的布局后去测试效果,发现Option控件的显示效果 ...

  8. Duilib教程-控件练习

    一.控件消息的响应. 在HelloDuilib例子中,程序不能退出,在这里,我将添加一个关闭按钮,当点击它时,调用PostQuitMessage进行退出. 首先在界面的右上角添加一个关闭按钮,并取名为 ...

  9. VS2013图片控件bug

    在VS2013中添加图片控件,经常会出现 error RC2108: expected numerical dialog constant这个错误. 个人感觉这应该是VS2013的一个bug.经过几番 ...

最新文章

  1. MySQL和Python交互
  2. IO和属性配置文件之组合拳
  3. 在SQL Server 2008中调用.net,dll
  4. Bob‘s Problem
  5. golang——strconv包常用函数
  6. android小灯泡实验代码,typecho常用代码片段收集
  7. 使用libhybris库linux调用android库
  8. 各种边缘检测算子特点比较(canny)
  9. c# 扩展方法奇思妙用高级篇三:Enumerable.CastT 应用
  10. 使用卡方分箱进行数据离散化-python实现
  11. 原创:CAD批量去除教育版戳记
  12. 转载:常见的15种音频格式
  13. 怎么配置环境变量?(保姆级教程)
  14. QAP,社会网络分析假设检验之一
  15. Java中JDBC详解
  16. FireMonkey 手机 APP 的手势
  17. 实时发布订阅协议(RTPS)DDS互操作网络协议规范-中文翻译_007
  18. 一开机checkingmedia_电脑开机就出现Checking media 这个怎么弄啊
  19. 【论文阅读】Dimensionality Reduction by Learning an Invariant Mapping
  20. 常见二叉树定义及其性质

热门文章

  1. 计算机应用生物,计算机应用生物信息学作业(10页)-原创力文档
  2. linux的pascal语言,Pascal语言究竟是什么语言
  3. 【美化§银河妖怪win7电脑主题下载§】
  4. QT实现两界面类似QQ聊天
  5. 贪吃蛇源码注释过程(未完成)
  6. ae编程语言as_【微教程】从编程的思路学习AE表达式
  7. 地下城与勇士(DNF)寂静城副本(倒悬的瞭望台、卢克的聚光镜、钢铁之臂、能源熔炉、光之舞会、王的书库)
  8. [054] SSL 3.0曝出Poodle漏洞的解决方案-----网民篇
  9. 日历相关的东西和算法
  10. [技美CG][DX12实战]Demo 运行【DX12】【VS2022】【龙书】【新手开箱可用】