摘要: 本文介绍了使用Windows消息机制实现由不同语言编制的程序之间的相互通讯、联系,并以当前较为流行的两种语言Microsoft Visual C++ 6.0和Borland delphi 5.0为对象,用这两种语言各编制一应用程序,并能很好的通过消息进行交互。
一、 引言
编制较大型的程序往往需要将一个项目分割成若干个模块,由若干个开发小组共同完成。笔者曾参加过几个大型项目的研发工作,根据需要往往要将项目分为解为几大部分,分到三、四家科研单位共同完成,由于各单位较分散,彼此之间的协调只能根据预先制定好的接口协议来完成,即每个单位同其他单位的程序之间的交互只通过几个接口来完成。而且各单位采用的开发语言也不同,这就为使这些不同语言的程序之间的交互增加了困难。笔者根据实际工作摸索出一些解决上述问题的一些方法,下面就对此作些介绍。
二、 程序设计思路
虽然各个单位采取不同的开发语言,从Microsoft Visual C++ 6.0到Borland delphi 5.0乃至Microsoft Visual Basic 6.0,但有一点是相同的:都是工作于Win 32操作系统下的。而Win 32操作系统有一个很重要的机制--消息。我们可以用Microsoft Visual Studio 6.0附带的DDE Spy工具去拦截运行于Windows操作系统下的应用程序所发出的各种消息,而不管这些应用程序是使用何种语言编制的。所以我们可以明确一点:即只要捕获到目标程序的窗口句柄,就能向其发送消息,并可附带两个消息参数将信息传递过去。而用于发送消息的两个函数PostMessage和SendMessage在定义上也很好的体现了这一点:
BOOL PostMessage( HWND hWnd, // 目标窗口句柄 UINT Msg, //发送的消息 WPARAM wParam, // 第一个消息参数 LPARAM lParam // 第二个消息参数); BOOL SendMessage( HWND hWnd, // 目标窗口句柄 |
可以看出,从原理上采用消息是可以沟通两个由不同语言编制的程序的,下面通过两个分别由Microsoft Visual C++ 6.0和Borland delphi 5.0编制的模拟程序来简要介绍一下该方法的实现过程。
三、 由Borland delphi 5.0编制的模拟程序的实现
新建一个工程,由于在Microsoft Visual C++ 6.0下要捕获其他应用程序的窗口句柄要通过API函数FindWindow(……)来实现,而此函数是根据目标窗口的窗口标题来判断是哪个窗口的,所以要保证两种语言编制的程序能可靠的通信,必须要保证各自窗口标题不发生改变。所以在本程序中需要通过修改窗口的属性来改变窗体的标题,将其设为"delphi消息接收、发送程序"。
在Borland delphi 5.0下向Microsoft Visual C++ 6.0发送消息比较简单,只须调用Win32 API函数findwindow(……)在当前的所有的窗口中根据对方程序窗体的标题进行搜寻即可。当获取到对方程序的窗口句柄后可以用postmessage(……)或是sendmessage(……)函数对其发送消息了,需要说明的是这两个函数虽然都是向指定窗口发送消息,但前者是"邮递"性质,只要把消息发出去了就算干完了,而不关心对方接收到消息后是否处理;而后者却是一直等待对方把消息处理完,如若对方不处理该消息,那么sendmessage(……)函数也不会返回。虽然差别很细微,但在实际应用中如能选取恰当的消息发送函数,往往会使程序更加完美。发送消息的关键代码如下:
…… {nil参数指定搜寻所有的窗口,捕获窗口标题为"vc消息接收、发送程序"的应用程序的句柄} hwnd:=findwindow(nil,pchar(’vc消息接收、发送程序’)); postmessage(hwnd,2000,0,1); |
要响应从Microsoft Visual C++ 6.0发送来的消息,则比较麻烦,首先要添加一个用来描述消息的数据结构,可以定义如下:
type Tmymessage=record a:cardinal; b:integer; c:integer; d:integer; end; |
在Borland delphi 5.0里响应消息不需要有消息映射,只须象添加一个普通的过程一样,只是其入口参数必须为刚才所添加的消息结构对象并在其后添加一个消息号即可,比如 procedure receive(var message:Tmymessage);message 2000;在消息响应函数里可以通过判断入口参数的消息结构里的 b、c两数据成员变量来获取随消息发送来的两个消息参数。并根据特定的数值作出相应的反应。
四、 由Microsoft Visual C++ 6.0 编制的模拟程序的实现
在Microsoft Visual C++ 6.0下实现消息发送、响应的设计思路和实现方法与前面提到的方法基本类似,也是将自己的标题固定以便对方获取自己的窗口句柄来向自己发消息,但响应消息时需要靠消息映射来显式实现。
固定自己的程序窗口标题一般是在工程的应用程序类的初始化函数OnInitial()里作如下修改:
…… m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->SetWindowText("vc消息接收、发送程序"); m_pMainWnd->UpdateWindow(); …… |
需要特别说明的是并非在所有的类里都可以添加从其他程序发来消息的响应函数,只有在主框架类里才能接收到外部程序发送来的消息,因为外部程序捕获的句柄只是根据程序标题捕获到的程序主框架的句柄,所以只能将消息发送到主框架类了。自定义消息和消息映射的添加实现情况如下:
在主框架类的头文件中修改如下片段:
…… #define WM_MYMSG 2000 …… //{{AFX_MSG(CMainFrame) // NOTE - the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG void OnRecvMsg(WPARAM wParam,LPARAM lParam); DECLARE_MESSAGE_MAP() …… |
再在主框架类的实现文件里修改如下片段:
…… BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG_MAP ON_MESSAGE(WM_TOdelphi,OnRecvMsg) END_MESSAGE_MAP() …… void CMainFrame::OnRecvMsg(WPARAM wParam,LPARAM lParam) |
其中,从wParam 传来的参数是接收到的消息附带的第一个参数;从lParam传来的参数是接收到的消息附带的第二个参数。可以通过这两个参数从对方获取更多的附加信息。
至于向对方发送消息则和前一个程序非常类似,寻找对方窗口可以用Win32 API函数的FindWindow(……),也可以使用MFC的CWnd类的FindWindow(……),后者返回的直接是窗口指针,实现的主要代码如下:
…… //获取以"delphi消息接收、发送程序"为标题的delphi程序的窗口句柄 CWnd *pWnd=CWnd::FindWindow(NULL,"delphi消息接收、发送程序"); //只有当确实捕获到窗口时才向其发送消息,否则会引起异常错误。 if (pWnd) pWnd->PostMessage(WM_MYMSG,0,1); …… |
WM_MYMSG是自定义消息,0,1是附带的两个消息参数。具体意义可以通过接口协议自行制定。
五、 检验程序的交互情况
首先在各自的编程环境下编译完毕,运行后打开Microsoft Visual Studio 6.0附带的调试工具包DDE Spy,当一个程序从另外一个程序接收到发送来的消息,执行完特定的工作后。可以通过DDE Spy监视到确实是从一个进程发出自定义消息,然后经过Win 32系统的消息队列被另外一个进程所接收并响应。
结论:
本程序通过一个简单的例子对在异种语言编制的程序中使用消息作为通讯工具的方法做了简单的介绍,在实际应用中还要考虑许多具体的细节问题,实现起来也要远比本程序烦琐的多。但通过这种方式来解决混合编程中的小数据量的频繁数据通讯无疑是很有优势的。
http://soft.zhiding.cn/software_zone/2007/1017/560580.shtml