深入Brew编程之——接口函数替换技术及其应用

今天我在这里要讲的我这几天工作的一个总结,希望对大家有帮助。不过我这个人有个毛病,在开宗明义之前总喜欢发点满腹牢骚。请大家满足一下我这个小小癖好。
   Brew实际上是一个再简单不过的系统了,只不过搭上手机开发这个比较新鲜的事物,所以有那么一点神秘感。各位新人们,我要告诉你们,开发Brew比开发Win32程序要简单的多,容易得多(当然,要想通过那个有点变态的UBT测试是另外一回事),完全无需心虚害怕。如果你对VC恶熟,程序写得很溜,那么恭喜你,你只需要最多一周的时间,就可以把Brew玩得很溜了。大家切忌有那种学了Brew就是捡了金矿的想法。
   好了,牢骚发完,进入正题。我们发帖子的,要讲的东西当然就不能讲那些Program Guide、API Refrence上能看到的东西,怎么也得是类似于深入XX编程这一层次的东西才对。我今天要讲的主题,是Brew接口函数替换技术和一个实际的例子:怎么为 IHtmlViewer 控件加上背景图片。我的方法可能不是唯一的方法,更不是最好的方法,但是应该有一些独特的地方。如果这个方法能给大家一点启示,作为抛砖引玉,让大家能更深入地挖掘Brew的玩法,然后贡献出来共同讨论,那就是最好不过的了。
   IHtmlViewer这个东西是一个好东西,但是也有很多让人出离愤怒的地方,包括不支持背景色和背景图片,令人郁闷不已。解决这个问题,我用到了两个关键技术:
(1) 接口函数替换技术

JAVA手机网[www.cnjm.net]

(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]

}
把宏定义展开就是这样:

JAVA手机网[www.cnjm.net]

typedef struct IDisplayVtbl IDisplayVtbl;
struct IDisplayVtbl
{

JAVA手机网[www.cnjm.net]

  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);

JAVA手机网[www.cnjm.net]

//替换
pIDisplayVtbl->Update = IDISPLAY_MyUpdateEx;
*(( IDisplayVtbl**) pIDisplay) = pIDisplayVtbl;
   这样IDISPLAY_UpdateEx就替换成自己定义的函数了。下面看一下IDISPLAY_ MyUpdateEx这个函数怎么实现。直接修改显示缓冲区的方法在过去已经有人讨论过,在这里就不详细说明了,直接给出IDISPLAY_ MyUpdateEx函数的参考实现。在参考实现中没有做任何错误处理,请注意。
void IDISPLAY_MyUpdateEx(IDisplay * po, boolean bDefer)
{
   IBitmap* pScrBitmap;

JAVA手机网[www.cnjm.net]

   IDIB* pScrDIB;
   IBitmap* pBgBitmap;
   IDID* pBgDIB;
   IImage* pBgImage;
   // 获取显示器位图
   IDISPLAY_GetDeviceBitmap(po, &pScrBitmap);

JAVA手机网[www.cnjm.net]

   // 获取显示缓冲区

JAVA手机网[www.cnjm.net]

   IBITMAP_QueryInterface(pScrBitmap, AEECLSID_DIB, (void**)& pScrDIB);
   // 创建背景位图
   IBITMAP_CreateCompatibleBitmap(pScrBitmap, &pBgBitmap,pDIB->cx,pDIB->cy);
   // 获取背景位图的缓冲区
   IBITMAP_QueryInterface(pBgBitmap, AEECLSID_DIB, (void**)&pBgDIB);

JAVA手机网[www.cnjm.net]

   // 载入背景图片
   pBgImage = ISHELL_LoadImage(
                          ((AEEApplet*)GETAPPINSTANCE() )->m_pIShell,

JAVA手机网[www.cnjm.net]

     “bg.png”
      );
   // 替换IDisplay接口的目标位图
   IDISPLAY_SetDestination(po, pBgBitmap);

JAVA手机网[www.cnjm.net]

   IBITMAP_Release(pScrBitmap); // 引用计数减1

JAVA手机网[www.cnjm.net]

   // 贴背景图
   IIMAGE_Draw(pBgImage);
   // 还原IDisplay接口的目标位图
   IDISPLAY_SetDestination(po, pScrBitmap);
   IBITMAP_Release(pBgBitmap); // 引用计数减1
   IIMAGE_Release(pBgImage);
   // 这时,pScrDIB里面是原来IHtmlViewer载入的页面的位图数据,pBgDIB里是背景图片

JAVA手机网[www.cnjm.net]

   // 的位图数据,我们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;
       }
   }

JAVA手机网[www.cnjm.net]

   // 调用真正的屏幕刷新函数
   (GetOldUpdate())(po, bDefer);
   // 释放资源
   IDIB_Release(pBgDIB);
   IBITMAP_Release(pBgBitmap);
}
   好了,到此这篇文章也该结束了。最好我想狗尾续貂一下。上面讲的我想应该是一种较具普篇性的,应该不只限于为IHtmlViewer控件添加背景图(包括背景色),通过举一反三,还可以用于其它目的。希望大家可以共同探讨一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值