posts - 3,comments - 2,trackbacks - 0

最近作者用 20 天写了一个小软件,在 csdn Bcb 论坛和 chinabcb 发表,得到了大家的支持,并有很多朋友询问是否可以公开源代码,作者其实也不是一个保守的人,以前就公开了自己的游戏源代码,但后来这个游戏被别人盗用,还那他当共享软件收费,我非常生气,也算有了教训,这次作者在短期之内是不会公开源代码了,但面对那么多网友的支持,作者认为有责任写一些东西来帮助广大 bcber 爱好者共同进步,所以就写了这篇开发手记,希望对初学者有一定帮助,如果你是高手,就当我班门弄斧好了。

 

如果你还没有见过 MySpy ,你可以从 http://www.chinabcb.com/bbs/viewtopic.php?t=6445 下载,这个地址需要你注册登录才能看到下载地址,否则只有介绍。

 

作者不打算介绍整个开发过程,就拿出问的最多的问题来介绍一下吧:

1.  如何将图标保存为真彩色图标的?

其实,这本来不改算是个问题,但在 Bcb Delphi 里这可真是一个非常难解决的问题,不知大家是否知道, Bcb Delphi TIcon 类保存的图标仅支持 16 色,这样不管是 256 色还是真彩色的图标,都只能用 16 色来显示,这样的保存结果不要也罢,作者曾一度想放弃保存图标的功能,但最后还是没有这样做,那么作者是怎么解决的呢?

首先作者想到了 Internet ,既然我有这样的问题,那么世界上肯定还有其他人遇到了这样的问题,去 google 上应该可以找到其他人解决问题的方法,作者花了一个下午的时间,在各大 Delphi 站点、 faq 、控件区都没有找到合适的解决方法,最后在一个日本网战上找到了一个 TIconEx ,从几个认识的中文里,我猜出了这个东西正好是我要的 , 我立刻开着快车把他下了下来,很可惜没有源代码,日本人真 tmd 的讨厌,怀着对日本人无限的愤怒之情,我把那个 TIconEx 扔进了垃圾箱。

看来他山之石是借不出来了,那么作者只好从底层出发寻找解决方案了,从这个地址 http://www.csdn.net/dev/format/ ,作者找到了 Icon 的文件格式和一段微软工程师写的例子代码,作者用了一个晚上来研究这个 Icon 格式和工程师的代码,明白后,作者决定把他用 Bcb 封装起来,因为原来的代码是用 c 语言编写的,而作者是个 oo 爱好者,所以作者决定把他类化然后加入了从 HICON 保存文件的代码(原来的只能从模块中抽取出来保存)。

代码是写完了,但是把他用到 Bcb 还真是不容易,不过这已经不是技术问题了,大家如果有兴趣就自己试一试吧,对了,上面介绍 Icon 格式的文章是英文的,如果作者有时间了,会把他翻译成为中文的,那篇文章写的相当好。

 

2.  如何 HOOK

HOOK MySpy 里是用的最多的技术,从获取密码到截获系统的消息都是 HOOK 技术,可以说这个技术已经不是什么技术了,网上随便一个编程站点都可以找到相关介绍资料,但截获系统消息的还是比较麻烦的,作者就介绍一下它吧。

截获系统消息无非是安装系统钩子 WH_CALLWNDPROC WH_CALLWNDPROCRET WH_GETMESSAGE ,为什么要安装这么多,因为系统消息有很多不同的类型,有 Send Post 还有处理消息返回也是需要截获的,当然如果你对系统菜单也感兴趣,还需要安装 WH_SYSMSGFILTER 钩子,因为这是一个系统范围的钩子,所以必须编写成为 DLL ,而核心代码也不过就是:

 

LRESULT CALLBACK MsgHookProc1(int nCode, WPARAM wParam,LPARAM lParam)

{

 

  if (nCode>=0)

  {

    CWPSTRUCT* mm=(CWPSTRUCT*)lParam;

    MsgData data;

    data.msg=mm->message;

    data.wparam=(int)mm->wParam;

    data.lparam=(int)mm->lParam;

    data.type=1;

    data.result=0;

    COPYDATASTRUCT copydata;

    copydata.dwData=0;

    copydata.cbData=sizeof(MsgData);

    copydata.lpData=&data;

    if(mm->hwnd==HookStruct->HookedWnd)

    {

      SendMessage(HookStruct->ParentWnd,WM_COPYDATA,0,(LPARAM)&copydata);

 

    }

  }

  return(CallNextHookEx(HookStruct->MsgHook1,nCode,wParam,lParam));

}

可以看到作者是使用 WM_COPYDATA MySpy 发送通知消息的,上面是其中一个 HOOK 的回调函数,其他类似。 MySpy 收到 WM_COPYDATA 消息后,会将 lParam 参数转换成为 MsgData ,而这个结构体封装了关于该消息全部有用的信息,就等着 MySpy 处理了。 HOOK 其实真的很简单,就是调试麻烦,作者在调试过程中,至少死机 10 多次,如果使用 Win9x 系统,可能你就光死机了。

到此你可能认为写个截获消息的功能就算完成了,如果你注意 MySpy 的话,并对 Win32API 有一定了解,你会发现,我们得到的消息是个整数的 id ,而我们不可能只给用户显示个 id ,我们需要 WM_XXX 这个的友好表示,但 API 里并没有提供把整数 id 转换成为友好字符串的函数,这该如何解决呢?作者最后使用了一个记录文件,文件格式如下:

0x0000 WM_NULL

0x0001 WM_CREATE

0x0002 WM_DESTROY

0x0003 WM_MOVE

0x0005 WM_SIZE

0x0006 WM_ACTIVATE

……

有了这个文件,查找对应的友好字符串就简单了,而且扩展这个记录文件非常容易,你可以加入你已知 id 的对应字符串。关键在于查找效率,这个也不难,排序后快速查找再合适不过了。到现在,作者也不知道我的解决方法是不是最好的,还请各位赐教?

 

3.  如何得到网页上的元素,并取得他的相关信息?

这个问题还是有一点难度的,特别对于不懂 COM 的人,这个问题就更难了。

想要操作 HTML 首先要得到 IHtmlDocument2 接口,这个接口是 IE4.0 以上提供用来专门与 Html 交互的 COM 对象表露的,如果我们用 asp JavaScript 来操作 Html ,非常简单,当然得到网页里的元素也非常简单,而使用 Bcb 这样的开发工具来操作 Html ,没有微软的帮助恐怕是不行(至少作者不知道),还好微软确实提供了这方面的支持。这个支持就是 OLEACC.DLL

这个 DLL 的功能很多,其中之一就是从 HWND 得到关联的 COM 对象的接口,那么代码到底是什么样的呢?

CComPtr<IAccessible> spAccess;

hr=AccessibleObjectFromWindow(hwnd,0,IID_IAccessible,(void**) &spAccess);

if ( SUCCEEDED(hr) )

{

CComPtr<IServiceProvider> spServiceProv;

hr=spAccess->QueryInterface(IID_IServiceProvider,(void**)&spServiceProv);

if(hr==S_OK)

{

CComPtr<IHTMLWindow2> spWin;

hr=spServiceProv->QueryService(IID_IHTMLWindow2,IID_IHTMLWindow2,(void**)&spWin);

if(hr==S_OK)

spWin->get_document(&pDoc2);

}

}

 

上面就是从 HWND 取得 IHTMLDocument2 接口的核心代码了,当然你需要加入 Oleacc.h Oleacc.lib 文件,得到 IHTMLDocument2 接口后我们就用了同 ASP 同样强大的能力去造作 HTML 了,关于如何操作 HTML 这里就不谈了,有 ASP 知识的朋友一看到这里我想都明白了吧。

这里的关键函数就是 Oleacc.dll 导出的 AccessibleObjectFromWindow 这个函数的作用看一下名字就知道了, MSDN 里给的说明如下:

Retrieves the address of the specified interface to the object associated with the given window.

所以这个函数不光可以得到 IHTMLDocument2 接口还可以得到 Office 等接口,有兴趣的可以试一试 .

 

4.  如何编写插件?

MySpy 支持插件,而且为 MySpy 开发插件很容易,当然这主要靠作者的合理设计和完整的开发文档(大家不要砸我:)),编写插件本来不应该算 MySpy 的特色功能,或者说绝对不是什么核心技术,不过既然 MySpy 写了,又有人问,我就顺便介绍一下如何开发插件吧。

作者在开始设计插件结构的时候,徘徊在使用 COM 插还是普通 DLL 插的交叉路口,使用 COM 会容易编写代码,对于开发人员容易理解,而且现在的大多数软件都是使用 COM 作为插件的,但使用 COM 的最大问题是,将大大增加 MySpy 的身体臃肿度和增加 MySpy 的开发时间,因为作者需要单独编写一个 DLL 来封装整个 MySpy ,然后表露接口,这样就额外带来了将近 1 倍额外脂肪,这是作者不能接受的,还有就是编写 COM 插件对于开发人员要求太高了;使用 DLL 来做插件又有很多方法,是导出若干函数来负责若干功能还是导出 1 2 个函数来泛化设计呢?还有就是要不要插件同 MySpy 有交互呢(存在双向数据访问吗?)

最后作者选择了现在的解决方法,就是仅导出一个函数的泛化设计,这个设计类似于 Windows 的消息机制,就是 MySpy 发送若干消息给插件来通知插件做什么事情,插件也可以发送消息给 MySpy 来控制 MySpy ,而开发人员仅需要处理相应消息并返回必要的值,剩下的事情由 MySpy 完成,这样的解决方法不需要额外的 DLL ,在 MySpy 代码内部的适当位置发送适当的消息给插件,然后处理返回值就可以了。我们来看一下导出函数的格式:

extern "C" __declspec(dllexport)

int MMPlugin(MMSG msg,MPARAM wParam,MPARAM lParam)

{

  switch(msg)

  {

    case MM_INIT:

      //place your init code here

      break;

    case MM_DESTROY:

      //place your destroy code here

      break;

   

  }

}

 

可以看到这个导出函数,很像 WndProc 函数,作者这样设计的灵感完全来源于 Windows 消息机制的巧妙,一个整型的 MPARAM 参数可以传递任何类型,任何大小的数据,作者真的很佩服 Windows 的消息机制,不知各位朋友有什么好的方法,不妨大家一起讨论一下。

 

至此 MySpy 的关键技术已经全部介绍完了,大家可能也看到了,作者对所有的技术介绍都是点到为止,并没有讨论的很详细,这并不是作者不愿意给出源代码,而是没有必要,在作者看来解决问题的方法是思想,如果大家有兴趣,自己试一试,像作者一样花个 10 20 天,肯定大有收获。

作者非常欢迎大家来信讨论 siney@yeah.net

感谢Aweay提供的技术知识。

posted on 2006-10-23 10:30 风籁 阅读(187) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: