|
|
|
| 深入Brew编程之一——接口函数替换技术及其应用 |
编辑:choujs 审核:rocks 文章来源:CNJM论坛精华
关键词:无 发表日期:2005-05-04 12:30:08 浏览次数:6760次 |
|
|
|
|
本文版权归原作者,中国JAVA手机网收录本文的目的是让更多人阅读到此文章。转载请注明出处为中国JAVA手机网<www.cnjm.net>
来自:http://www.cnjm.net/tech/article750.html [这个贴子最后由choujs在 2005/05/04 12:38pm 第 1 次编辑]
今天我在这里要讲的我这几天工作的一个总结,希望对大家有帮助。不过我这个人有个毛病,在开宗明义之前总喜欢发点满腹牢骚。请大家满足一下我这个小小癖好。 Brew实际上是一个再简单不过的系统了,只不过搭上手机开发这个比较新鲜的事物,所以有那么一点神秘感。各位新人们,我要告诉你们,开发Brew比开发Win32程序要简单的多,容易得多(当然,要想通过那个有点变态的UBT测试是另外一回事),完全无需心虚害怕。如果你对VC恶熟,程序写得很溜,那么恭喜你,你只需要最多一周的时间,就可以把Brew玩得很溜了。大家切忌有那种学了Brew就是捡了金矿的想法。 好了,牢骚发完,进入正题。我们发帖子的,要讲的东西当然就不能讲那些Program Guide、API Refrence上能看到的东西,怎么也得是类似于深入XX编程这一层次的东西才对。我今天要讲的主题,是Brew接口函数替换技术和一个实际的例子:怎么为 IHtmlViewer 控件加上背景图片。我的方法可能不是唯一的方法,更不是最好的方法,但是应该有一些独特的地方。如果这个方法能给大家一点启示,作为抛砖引玉,让大家能更深入地挖掘Brew的玩法,然后贡献出来共同讨论,那就是最好不过的了。 IHtmlViewer这个东西是一个好东西,但是也有很多让人出离愤怒的地方,包括不支持背景色和背景图片,令人郁闷不已。解决这个问题,我用到了两个关键技术: (1) 接口函数替换技术 (2) 直接修改显示缓冲区技术 接口函数替换类似于一种钩子方法(Hook),我通过这种方法用一个自定义的函数(IDISPLAYER_MyUpdateEx)替换IDisplay接口的IDISPLAY_UpdateEx函数。大家知道所有显示输出操作最后都需要调用IDISPLAY_UpdateEx函数刷新屏幕,IHtmlViewer也不例外。使用IDISPLAYER_MyUpdateEx替换IDISPLAY_UpdateEx函数后,IHtmlViewer在刷新屏幕的时候就会首先调用IDISPLAYER_MyUpdateEx,在这个函数中我修改显示缓冲区,添加背景图片,然后再调用真正的IDISPLAY_UpdateEx函数刷新屏幕,这样,IHtmlViewer控件就有背景图片了。 首先看看怎么替换IDISPLAY_UpdateEx接口函数。打开AEEDisp.h,可以看到IDISPLAY_ UpdateEx的定义如下。 #define IDISPLAY_UpdateEx(p, bDefer) AEEGETPVTBL(p,IDisplay)->Update((p), (bDefer)) 再把AEEGETPVTBL宏定义展开,实际的定义应该是这样的: #define IDISPLAY_UpdateEx(p, bDefer) (*((IDisplayVtbl**)p))-> Update((p), (bDefer)) 从这里我们可以看到很多问题。我们知道,每一个应用都需要创建一个IDisplay接口的实例指针(假设是pIDisplay),那么这个指针指向怎么样一个数据结构呢?在AEEDisp.h中我们可以看到IDisplay的定义: typedef struct _IDisplay IDisplay; 可以看到,IDisplay实际上是一个伪数据类型,没有任何意义(所有的Ixxxxx数据类型定义都是如此),我们无从知道其具体数据结构定义。幸运的是,从接口函数的定义中我们可以知道两个事实: (1) pIDisplay指针所指向的数据的前四个字节的值是一个指针 (2) 这个指针的类型为IDisplayVtbl(记为pIDisplay->pIDisplayVtbl)。 IDisplayVtbl数据结构实际上是IDisplay接口的函数表(所有其它接口也都一样),在AEEDisp.h中定义。 AEEINTERFACE(IDisplay)
JAVA手机网[www.cnjm.net] { INHERIT_IBase(IDisplay); … void (*Update)(IDisplay * po, boolean bDefer); …
JAVA手机网[www.cnjm.net] } 把宏定义展开就是这样: typedef struct IDisplayVtbl IDisplayVtbl; struct IDisplayVtbl { uint32 (*AddRef) (IDisplay*); uint32 (*Release) (IDisplay*); … void (*Update)(IDisplay * po, boolean bDefer); … }
JAVA手机网[www.cnjm.net] 其它函数我们就不关心了,跟这篇文章有关的就是这个 Update 函数指针。很显然,我们只要把这个指针替换为IDISPLAY_MyUpdateEx就可以了。但是且慢,这里还有一个问题。pIDisplay->pIDisplayVtbl这个指针指向的地址位于代码段,是不能修改的。因此我们还需要做一些处理。方法是重新构造一个函数表,用于替换原来的函数表。 函数替换的代码如下。 IDisplay* pIDisplay = ((AEEApplet*) GETAPPINSTANCE() )->m_pIDisplay; IDisplayVtbl* pIDisplayVtbl = (IDisplayVtbl*)MALLOC(sizeof(IDisplayVtbl*)); MEMCPY(pIDisplayVtbl, *(( IDisplayVtbl**) pIDisplay), sizeof(IDisplayVtbl)); //在这里保存原来的Update函数指针 SaveOldUpdate(pIDisplayVtbl->Update); //替换 pIDisplayVtbl->Update = IDISPLAY_MyUpdateEx; *(( IDisplayVtbl**) pIDisplay) = pIDisplayVtbl; 这样IDISPLAY_UpdateEx就替换成自己定义的函数了。下面看一下IDISPLAY_ MyUpdateEx这个函数怎么实现。直接修改显示缓冲区的方法在过去已经有人讨论过,在这里就不详细说明了,直接给出IDISPLAY_ MyUpdateEx函数的参考实现。在参考实现中没有做任何错误处理,请注意。 void IDISPLAY_MyUpdateEx(IDisplay * po, boolean bDefer) { IBitmap* pScrBitmap; IDIB* pScrDIB; IBitmap* pBgBitmap; IDID* pBgDIB; IImage* pBgImage; // 获取显示器位图 IDISPLAY_GetDeviceBitmap(po, &pScrBitmap); // 获取显示缓冲区 IBITMAP_QueryInterface(pScrBitmap, AEECLSID_DIB, (void**)& pScrDIB); // 创建背景位图
JAVA手机网[www.cnjm.net] IBITMAP_CreateCompatibleBitmap(pScrBitmap, &pBgBitmap,pDIB->cx,pDIB->cy); // 获取背景位图的缓冲区 IBITMAP_QueryInterface(pBgBitmap, AEECLSID_DIB, (void**)&pBgDIB); // 载入背景图片 pBgImage = ISHELL_LoadImage(
JAVA手机网[www.cnjm.net] ((AEEApplet*)GETAPPINSTANCE() )->m_pIShell,
JAVA手机网[www.cnjm.net] “bg.png” ); // 替换IDisplay接口的目标位图 IDISPLAY_SetDestination(po, pBgBitmap); IBITMAP_Release(pScrBitmap); // 引用计数减1 // 贴背景图 IIMAGE_Draw(pBgImage); // 还原IDisplay接口的目标位图
JAVA手机网[www.cnjm.net] IDISPLAY_SetDestination(po, pScrBitmap); IBITMAP_Release(pBgBitmap); // 引用计数减1 IIMAGE_Release(pBgImage); // 这时,pScrDIB里面是原来IHtmlViewer载入的页面的位图数据,pBgDIB里是背景图片 // 的位图数据,我们pScrDIB里白色的点用pBgDIB对应的点来替换。这里假设设备的颜色 // 深度为16bit { uint16* pixel = (uint16*)pScrDIB->pBmp; uint16* bgpixel = (uint16*)pBgDIB->pBmp; int pcount = pScrDIB->cx * pScrDIB->cy; for(; pcount; --pcount) { if(*pixel == 0xffff) *pixel = *bgpixel; ++pixel; ++bgpixel; } } // 调用真正的屏幕刷新函数 (GetOldUpdate())(po, bDefer); // 释放资源 IDIB_Release(pBgDIB); IBITMAP_Release(pBgBitmap); } 好了,到此这篇文章也该结束了。最好我想狗尾续貂一下。上面讲的我想应该是一种较具普篇性的,应该不只限于为IHtmlViewer控件添加背景图(包括背景色),通过举一反三,还可以用于其它目的。希望大家可以共同探讨一下。
JAVA手机网[www.cnjm.net]
来自:http://www.cnjm.net/tech/article750.html
|
|
|
|
|
|
相关文章
暂无相关文章
|
|
| 最新评论
[查看全部评论(10条)]
|
| tiger3927 在 2006-09-06 16:27:24 发表的评论: |
| 试验了,不好使,htmlviewer变化的时候不会调用displayupdate的,空欢喜了。 |
| joe8019 在 2005-08-16 20:52:48 发表的评论: |
| 楼主,这个方法是不是不行呢,我试了,不行,有没有人使用过,楼主能不能给详细的代码呢? |
| joe8019 在 2005-08-16 15:37:58 发表的评论: |
| 是怎么用的呀,兄弟能说说不,我想在菜单中用背景图 |
| lql3 在 2005-08-02 09:17:19 发表的评论: |
thanks |
| rainwell 在 2005-08-01 16:27:02 发表的评论: |
| 谢了 |
| migliang 在 2005-06-06 09:58:21 发表的评论: |
| 多谢!又学到了一招 |
|
|
|