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
duilib修复ActiveXUI控件bug,以支持flash透明动态背景的更多相关文章
- duilib 修复Text控件无法设置宽度的bug,增加自动加算宽度的属性
转载请说明原出处,谢谢~~: 今天有朋友反映CTextUI控件无法设置宽度,于是修复了这个bug,顺便给Text控件增加了一个自动计算宽度的属性,描述如下 <Attribute name=&qu ...
- duilib 修复CTreeViewUI控件动态添加子控件时,对是否显示判断不足的bug
转载请说明出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/42264947 这个bug我在仿酷狗开发日志里提到过,不过后来发现修复的不够 ...
- 将webkit内核封装为duilib的浏览器控件
转载请说明出处,谢谢~~ 原本的duilib是自带浏览器控件的,但是使用了IE内核,我在做仿酷狗音乐播放器时,在右侧乐库要用到浏览器控件,而我使用自带的IE控件却发现了不少缺点,这也是duilib一直 ...
- 改进duilib的richedit控件的部分功能
转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/41208207 如果要使用透明异形窗体功能,首先要改进duilib库让他本 ...
- 基于Bootstrap的JQuery TreeView树形控件,数据支持json字符串、list集合(MVC5)<二>
上篇博客给大家介绍了基于Bootstrap的JQuery TreeView树形控件,数据支持json字符串.list集合(MVC5)<一>, 其中的两种方式都显得有些冗余.接着上篇博客继续 ...
- wpf控件设计时支持(3)
原文:wpf控件设计时支持(3) wpf设计时调试 编辑模型 装饰器 1.wpf设计时调试 为了更好的了解wpf设计时框架,那么调试则非常重要,通过以下配置可以调试控件的设计时代码 (1)将启动项目配 ...
- wpf控件设计时支持(1)
原文:wpf控件设计时支持(1) 这部分内容几乎是大家忽略的内容,我想还是来介绍一下. 本篇源码下载 1.属性元数据 在vs IDE中,在asp.net,winfrom等开发环境下,右侧的Proper ...
- wpf控件设计时支持(2)
原文:wpf控件设计时支持(2) 这篇介绍在wpf设计时集合项属性添加项的定义和自定义控件右键菜单的方法 集合项属性设计时支持 1.为集合属性设计器识别具体项类型 wpf设计器允许定义集合项的类型,如 ...
- 在带(继承)TextView的控件中,在代码中动态更改TextView的文字颜色
今天由于公司项目需求,须要实现一种类似tab的选项卡,当时直接想到的就是使用RadioGroup和RadioButton来实现. 这种方法全然没问题.可是在后来的开发过程中,却遇到了一些困扰非常久的小 ...
随机推荐
- http://www.oschina.net/translate/elasticsearch-getting-started?cmp
http://www.oschina.net/translate/elasticsearch-getting-started?cmp
- jackson基于注解的简单使用
Jackson提供了一系列注解,方便对JSON序列化和反序列化进行控制,下面介绍一些常用的注解. 1.@JsonIgnore 此注解用于属性上,作用是进行JSON操作时忽略该属性. 2.@JsonFo ...
- My_Plan part1 小结
数位DP AC十道题目以上 成就达成 八月份!三个月!想想就令人兴奋呢 开始写总结啦 貌似简单的数位DP只需要改改模板就可以啦 就按照我的做题顺序开始总结吧 先是学习了一发模板:http://www. ...
- 欧拉工程第70题:Totient permutation
题目链接 和上面几题差不多的 Euler's Totient function, φ(n) [sometimes called the phi function]:小于等于n的数并且和n是互质的数的个 ...
- Sina App Engine(SAE)入门教程(6)- memcache使用
Memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像.视频.文件以及数据库检索的结果等.简单的说就是将数据调用到内 ...
- 编写自己的TRACE函数
TRACE函数是MFC里面的一个宏,是对OutputDebugString的封装. OutputDebugString的作用是输出调试信息,不要以为这个函数只有在Debug版本才会打日志,即使是Rel ...
- Zookeeper核心机制
(如果感觉有帮助,请帮忙点推荐,添加关注,谢谢!你的支持是我不断更新文章的动力.本博客会逐步推出一系列的关于大型网站架构.分布式应用.设计模式.架构模式等方面的系列文章) Zookeeper是Hado ...
- Linux系统下统计目录及其子目录文件个数
(1)查看某目录下文件的个数: ls -l |grep "^-"|wc -l 或 find ./company -type f | wc -l (2)查看某目录下文件的个数,包括子 ...
- 自己动手实现STL:前言
一.前言 最近,刚看完<STL源码剖析>,深深被实现STL库的那些的大牛们所折服.同时又感觉自己与大牛们差距之大,便萌生深入学习之意.如果仅仅只是看看<STL源码剖析>的话,又 ...
- oracle视图总结(转)
视图简介: 视图是基于一个表或多个表或视图的逻辑表,本身不包含数据,通过它可以对表里面的数据进行查询和修改.视图基于的表称为基表.视图是存储在数据字典里的一条select语句. 通过创建视图可以提取数 ...