实现网络蚂蚁的实时监视剪贴板功能
作者:黑龙江省五大连池 马明臣
我们知道,网络蚂蚁能够实时地监视剪贴板,如果我们能够在自己的程序中实现这个功能,肯定为我们的程序增色不少。
那它到底是如何实现的呢?我们可以想到的最简单的方法是,直接开个计时器,定时检查剪贴板上的内容,或者另写个线程来检查。这两个方法都能够监视剪贴板,但遗憾的是实时性太差,又占用了系统的大量资源,很不合算。好一点的方法可以用Win32API中关于剪贴板钩子(HOOK)的函数,只要安装了剪贴板的钩子函数,就可以做到实时地监视剪贴板,因为任何剪贴板的改变都会触发一条消息,而钩子函数拦截了这条信息。然而,这要求当前程序必须要有机会获得CPU控制权才能处理这个消息。如果该程序这时并没有释放CPU,监视剪贴板的程序并不能处理消息。只有当该程序执行完了之后,别的程序才能获得处理机会,但此时,剪贴板只剩下最后一次粘贴的内容了。而且,使用钩子函数非常危险,一旦你的程序出了问题,整个系统也就跟着瘫痪了。
既然这些方法都不太理想,那我们不妨转换一下思维,不再去管剪贴板,而想想如何让剪贴板直接向自己的程序发消息,这样肯定能做到百分之百的实时。为此我仔细的查看了各种Windows的编程资料,结果是:因为剪贴板不具有自己的句柄(Handle),它本身也不是Windows的一个类,故剪贴板是不会自己发消息的。但是系统是如何能够接收剪切/拷贝到剪贴板等命令呢?也即是如何接收WM_COPY和WM_CUT,WM_PASTE等消息呢?经过研究发现能够发送消息的是剪贴板查看器,原来多个剪贴板查看器依次连接成剪贴板查看链。因此,要实现实时监视剪贴板必须将自己的程序注册成为剪贴板查看器(即加入链表中)。下面笔者以C++ Builder 5.0为例编程说明其具体实现过程。
首先新建一个工程,在Form1上增加一个Memo控件,然后在Form1的.h里面加上#include <vcl/Clipbrd.hpp>,因为下面要用到的API函数大多是在该头文件中定义的。
在Form1的.h的public加上:
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_DRAWCLIPBOARD,TMessage,ClipboardChanged) //映射这个消息
END_MESSAGE_MAP(TForm)
HWND LastHandle;// 用于保存剪贴板查看器链中下一个窗口的句柄。
接下来,当然是把自己的程序窗口注册成为剪贴板查看器了。在Form1的OnCreate事件中加入一句:
LastHandle=SetClipboardViewer(Form1->Handle);
该函数会将指定的窗口加入剪贴板查看器链,参数handle就是你欲新加入的窗口的句柄,返回值则为系统的剪贴板查看器链中下一个窗口的句柄。
当你自己的程序退出时,必须从剪贴板查看器链中删除本窗口。在Form1的OnClose事件中加入两句:
ChangeClipboardChain(Form1->Handle,LastHandle);
函数ChangeClipboardChain是SetClipboardViewer的逆操作,能将第一个参数handle指定的窗口从剪贴板查看器链中删除,第二个参数是剪贴板查看器链中下一个窗口的句柄,是供系统调整剪贴板查看器链表用的,我们不用管它。
最后一步,也是最关键的一步,如果剪贴板内容有变化,窗口将自动激活 WM_DRAWCLIPBOARD消息,也即间接地实现了让剪贴板向自己的程序发消息的功能,这就能够实时监视剪贴板,相信网络蚂蚁也应该是用这个方法。有一点要注意的是,在接收处理WM_DRAWCLIPBOARD消息时要将消息传递给剪贴板查看链中的下一个窗口,以便让其它程序也能监视剪贴板,因为该消息只直接发给链中的第一个程序,其他程序不会直接收到该消息。
程序如下,在Form1的.h的private加上:
void __fastcall ClipboardChanged(TMessage& Msg);
再编写自己的处理过程,这就是你自由发挥的地方了。本例设为自动将剪贴板的文本粘贴到Memo控件中。
在Form1的.cpp内加上:
void __fastcall TForm1::ClipboardChanged(TMessage& Msg)
{
if (Clipboard()->HasFormat(CF_TEXT))
Memo1->Text = Clipboard()->AsText;
}
这一过程中使用了 TClipboard 类的 Clipboard 函数获取剪贴板内文本内容,至于如何获取剪贴板内的其它非文本类型的内容则不在本文讨论范围内了,读者可自己在C++ Builder中输入TClipboard ,再按F1,键,就可以看到各个函数的详细用法了,不过这些帮助可都是英文的呀!
以上程序在C++ Builder 5.0+PWIN98系统下调试通过,由于笔者水平有限,上文如有不妥之处,请大家与我一起探讨。