作者: ZDNET CHINA 特稿
在vb程序间进行跨进程通信不是一件容易的事。我曾经见过许多的程序开发员试图通过各种各样的方法来完成这一工作,这些办法包括从将信息写入共享的文本文档或注册表键到使用成熟的activeX程序通信服务器。每一种办法都有缺点:以前的办法容易产生错误,而现在的这些方法又由于在服务器中呼叫每一个对象的时候都会发生跨进程操作,又使其效率变得非常低。有一个经过实践检验的方法是,将要跨进程的通信挂接到你自己的VB程序的消息序列中,同时倾听从其它进程中传来的自定义窗口消息,一种被大家称之为"子类"的技术。
我曾经描述过如何以及为什么在VB编程语言中要运用"子类",而且我还展示了如何使用视窗应用编程接口来将消息发送到你的应用程序以及解锁的隐藏控制功能中去。在本篇文章中,我会向大家解释如何才能向其他的应用程序发送消息以及如何发送消息才能解决跨进程的通信难题。同时,我还会给大家提供可再度使用的ActiveX DLL, Messenger.dll,来让各位在将自定义消息运用于自己的程序的时候尽可能的简单起来。
传送信息时与其它程序之间的相互作用
因为消息传送是Windows和在它上面运行的程序之间通信的最基本方式,任何窗口或者控制任何的应用程序都可以通过使用SendMessage或者相关的软件来发送消息。此外,那些标准的消息对于所有运行于Windows的应用程序都有着相同的含义。有了这两点,我们就能得出一个令人吃惊的结论:通过发送消息,人们可以轻而易举的控制任何的当前正在运行的应用程序,甚至还包括那些并不应该被别人操控的应用程序。举个例子来说:
当你使用WM_xBUTTONDOWN 以及 WM_xBUTTONUP来传送消息的时候,你可以在别的应用程序中模拟鼠标的点击操作。
发送WM_KEYDOWN 以及 WM_KEYUP消息的时候,会模拟出按键盘的操作。
有恶意的程序员能够通过发送一条WM_CLOSE消息到最高级窗口来迫使其它的应用程序关闭,或者通过使用WM_ENDSESSION来让其它的应用程序误以为Windows操作系统正在关闭。
指出你是在和谁谈话
发送消息到另一个窗口的关键并不取决于你的应用程序,而是取决于窗口句柄。Windows操作系统的应用编程接口有许多的功能能够重新得到特定的窗口句柄,其中最经常使用的一个就是FindWindow功能。这个功能能够在标题说明中找到基于文本的最高级别的窗口(lpWindowName),并且返回到它的窗口句柄。在VB编程语言中关于FindWindow是如下这么描述的:
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
找到子窗体
你能通过使用FindWindowEx来找到子窗体句柄,就象是一个表格中的特殊控制一样。
自定义消息提供了一种共通的语言
现在让我们来复习一下:发送消息是一种简单有效的与其它应用程序进行交流的方法,而且你能够轻松的通过发送标准的Windows消息来模拟出用户与图形用户界面程序之间的相互作用。但是,如果你需要分程传递一些非标准的消息到你的另一个也许已经准备就绪进行处理的程序的时候又该如何去做呢?当然,这是完全能够做到的,重新定义一个标准消息的含义来表达"数据已经准备就绪,"使其它程序的主窗口子类化,并且对此消息进行特殊的处理。
这么做真的有效果吗?是的,答案是肯定的。但是这远远不是最佳的解决办法。首先,你的数据处理程序不会对你决定进行重定义的任何消息做出像正常时一样的反应。如果你能够定义一条制有你的程序能够明白的自定义消息的话,会比较好一些。
令人感到愉快的是,RegisterWindowMessageAPI函数就是这样的。你只需要传递给它一个单独的文本消息标识符,剩下的工作就可以让这个功能自己去完成了。其中的工作包括,向Windows注册此消息,返回一条专门的消息标识符,这条专门的消息标识符必须被保证是唯一的而且对于当前的Windows是有益的。之后,用相同的消息标识符呼叫RegisterWindowMessage功能,无论是从相同的还是从不相同的程序中,会把这个分配给第一次呼叫的相同的标识符返回。VB编程语言对于RegisterWindowMessage有如下声明:
Private Declare Function RegisterWindowMessage _
Lib "user32" Alias "RegisterWindowMessageA" _
(ByVal lpString As String) As Long
把碎片组合在一起
这是解决跨进程通信的最后一块碎片。任何需要从别的程序接收自定义消息的程序会子类化它的主窗口,并且提供一个单独的窗口标题来让它的hWnd能被FindWindow轻松的获得。(第一个应用程序应该用另一种方法来定位第二个程序的hWnd。)两个程序都注册相同的自定义窗口消息,并且悬挂到他们接收到的标识符上去。当一个程序需要将自定义消息锁代表的通知给另一个程序的时候,它会发现另一个程序的窗口句柄,并且通过使用SendMessage变量来发送自定义消息。任何额外的信息都会由于SendMessage注释:swParam 以及lParam参数或者它的返回码而相互联络。
根据我所说的,我编了一个在VB6程序中能够被用来实现自定义消息特别的Messenger组件。这个组件的源代码可以从下边的列表中找到:
Listing A是这个组件主要类别的源代码,cMessenger。
为个人使用的源代码被称之为cMessageInfo,可以在Listing B中被找到。
在Listing C中你能找到的是用来function addresses以及需要功能模块的时候的组件源代码。
Messenger负责为用户子类,注册,接听,以及发送自定义消息。这是相当安全的,因此,多个应用程序都能够使用此组件的同一拷贝。
StartListening方法被看作是一个窗口对子类的句柄中唯一的参量。它为此窗口嵌入它自己的窗口程序,并且开始等待你用RegisterMessage方法定义的自定义消息。这样能够通过提高IncomingMessage事件以及只回应你使用RegisterMessage注册的自定义消息来确定是否收到一条自定义消息。在关闭窗口之前,通过使用StopListening功能,确保已经把cMessenger从窗口消息序列中去掉了。
你可以使用SendMessage来传送消息到其它的应用程序。SendMessage认可你希望通报的窗口标题,同时也认可你使用RegisterMessage注册的消息文本。你能够通过使用RegisterWindow或者 RegisterWindowByHandle手动将新的窗口添加到高速缓存中去。