Borland最新推出的C++Builder 6.0(以下简称BCB)和Delphi/Kylix一样,有着强大的Web Service处理能力,但现在关于如何用BCB创建和调用Web Service的资料并不多,现在我就示范一下如何使用BCB6.0实现一个简单的Web Service客户端,而且在我的例子中使用的是多线程的调用方式。由于自己使用BCB的时间非常短,开发经验还不足,所以本文仅当是抛砖引玉。如果文中有错误或可以改进的地方,还请大家多提意见。
现在我们就开始。我们要调用的是大名鼎鼎的Dr.Bob用Delphi6写的一个自动获取Delphi、CppBuilder、Kylix、Jbuilder等Borland产品信息的Web Service,它的WSDL是http://www.ebob42.com/cgi-bin/DrBobsClinic.exe/wsdl/IHeadline。你可以在www.xmethods.com上看到它以及更多Web Service的有关信息。
1、首先创建一个新项目:File-New-Application,在Form1上分别放上Memo、ComboBox和Button控件,界面效果如下图如示。然后以默认名称保存单元和项目(你也可以给单元和项目分别命上有意义的名称)。
2、设定ComboBox1的一些属性:我们要调用不同的产品信息,这个通过ComboBox1中的值来决定。打开它的Items属性编辑器,在其中输入下面的内容:
Delphi
C++Builder
JBuilder
Kylix
SOAP
Borcon
然后,把ComboBox1的ItemIndex属性设为0,这样“Delphi”就成了默认值。
3、现在就要通过WSDL Importer来导入Web Service的接口了。保持现有项目不变,再选择File-New-Other,在Web Service页中,点选WSDL Importer项,确定。在随后出现的向导中,在Location of WSDL file or URL中输入:http://www.ebob42.com/cgi-bin/DrBobsClinic.exe/wsdl/IHeadline,点击Next。WSDL Importer就会自动获取有关的接口信息,产生的结果如下图所示:
点击Finish退出。以默认名称IheadLine.cpp把单元保存在项目目录之下。4、现在,我们有了接口信息,就开始对它进行调用了。在前面我说过,要用多线程来调用。为什么要这样?在此之前,我一直在Delphi中进行Web Service调用,那时看的资料(包括李维老师写的书)都是用单线程的,但我在使用过程中发现效果并不理想。因为Web Service是非常消耗资源的,加上XML的封装、转换,服务端与客户端的数据处理,往往一个简单的调用会很费时间,而且客户端界面会像死掉了一样停滞一段时间。基于以上考虑,我想通过建立另一个线程来解决问题,通过新线程在后台进行调用和数据处理,最后把结果反应到主线程的VCL界面上来。这是我的考虑,当然也可能有不成熟的地方。
5、接下来就是创建一个新的线程。同样保持现有项目不变,点选File-New-Other,在New页中点选Thread Object项确定。在随后的对话框中输入该线程的类和名称。在我机器上的效果是这样的:
确定后,以GetNews.cpp来保存单元至同一目录下。
6、现在来具体编写新线程的代码。首先要明确的是,我们要在这一线程内用到刚才自动导入的接口信息,同时又要与主线程交换信息,所以请点击File-Include Unit Hdr…,在随后的对话框中把Unit和GetNews两个头文件都选上,否则编译时会报错。
然后,在GetNews.h头文件中声明这样一个函数:
void __fastcall ThreadGetNews();
你可以把它声明为GetNews类的私有函数。然后在GetNews.cpp文件中实现这一函数,代码如下:
//------------------------------------------------------------------------
void __fastcall TGetNews::ThreadGetNews()
{
Form1->Button1->Enabled=false; //目的是使新线程运行期间只有一个实例
AnsiString s;
switch (Form1->ComboBox1 ->ItemIndex) /*通过ComboBox中的不同项目来决定要调用哪个函数*/
{
case 0:// delphi
s=GetIHeadLine()->DelphiNews(0);
break;
case 1:// cppBuilder
s=GetIHeadLine()->CBuilderNews(0);
break;
case 2: // JBuilder
s=GetIHeadLine()->JBuilderNews(0);
break;
case 3: // Kylix
s=GetIHeadLine()->KylixNews(0);
break;
case 4: // SOAP
s=GetIHeadLine()->SOAPNews(0);
break;
case 5: // Borcon
s=GetIHeadLine()->BorConNews(0);
break;
default:
s="Invalide direction" ;
}
Form1->Memo1->Lines ->Clear();
Form1->Memo1->Lines->Add(s);
Form1->Button1 ->Enabled=true; /*线程结束(不严格意义上说),让Button1可以再用*/
s="";
}
//------------------------------------------------------------------------
注意:如果你上面忘了引用两个头文件,那么这一步编译肯定通不过。
7、接着设定线程如何启动。首先请在GetNews.cpp中找到__fastcall TGetNews::TGetNews(bool CreateSuspended): TThread(CreateSuspended)的定义,然后在{}中加入:
Priority=tpNormal;
设定线程以正常优先级来运行。你也可以另外设定其它的优先级。
然后,在Execute()函数中加入代码,使之成为下面的样子:
//-----------------------------------------------------------------------
void __fastcall TGetNews::Execute()
{
SetName();
//---- Place thread code here ----
//下面的代码是新加的
FreeOnTerminate=true;
if(!Terminated)
Synchronize(ThreadGetNews);
}
//-----------------------------------------------------------------------
这样,关于新线程的设定就完成了。可以看出,一但主线程中创建了一个新线程的实例,并设置CreateSuspended为false时,新线程就会执行Execute()中的代码,如果线程没有结束,那么就会通过Synchronize来调用ThreadGetNews,通过该函数来调用Web Service并获得结果,实现对主线程VCL界面的更新。一但线程调用完成,就会自动回收资源。(因为我们设定了FreeOnTerminate为true)。
8、现在,我们就可以在主线程中调用这个新线程了。切换到Form1,先加入对线程头文件的引用:#include "GetNews.h"(或者同样用上面的方法通过File菜单加入),然后在Button1的OnClick事件中输入如下代码:
//-----------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TGetNews *gNews=new TGetNews(false);
}
//----------------------------------------------------------------------
OK!大功即将告成。现在就可以编译项目了。运行后,在ComboBox中点选喜欢的主题,然后按Button,过一会Memo中就会有信息传回来了。在我的机器上是这样的:
当然,这里返回的是ASCII字符,在这个Web Service中,你也可以选择返回HTML格式,甚至是可以用在PDA上的WML。你可以在调用函数比如说DelphiNews(int)中设定,以上三种格式的参数值分别为0,1,2。为了方便起见,我在本示例中设为0。
不过,还有一点似乎是个问题,那就是运行这个程序时,新线程在后台进行Web Service调用,但主界面依然有点停滞,这和普通的多线程程序多少有点不一样,至于为什么还请大家一起给解决一下。我的mail是:hada@shcei.com.cn。