XML Web services allow you to expose programmable services to other applications or programmers. You can build XML Web services in two ways. The first way is by using Microsoft Visual Studio 6.0 and the SOAP Toolkit. The second way is to use Visual Studio .NET, which has built-in support for building and consuming Web services. This section will look at both techniques ”you can decide which way is best for your needs.
To build a Web service using Visual Studio 6.0 and the SOAP Toolkit, you must first download and install the SOAP Toolkit from http://msdn.microsoft.com/soap . Using the toolkit, building a Web service is easy ”you simply create the DLL that will implement your functionality. The Toolkit takes your DLL and creates a Web Services Definition Language (WSDL) file that contains the description of the methods , their parameters, and their return values for the DLL that implements your Web service. For our example, we will implement a free/busy lookup Web service. The following code for our free/busy lookup DLL was written using Visual Basic. Notice that the DLL exposes a public function as the main entry point that other applications can call:
Public Function GetFreeBusy(strDirURL As String, _ strUserNames As String, _ strStartDate As String, _ strEndDate As String, _ strInterval As String) As String On Error Resume Next 'This function looks up free/busy information for users. 'User names must be email addresses separated by commas. 'The return value is a string of freebusy values for 'the users, separated by commas. 'Make sure a directory URL was passed If strDirURL = "" Then GetFreeBusy = "You must pass a directory URL " & _ "in the format LDAP://directory" Exit Function End If If Not (IsDate(strStartDate)) Then 'Not a date GetFreeBusy = "Start Date is not a valid date!" Exit Function End If If Not (IsDate(strEndDate)) Then 'Not a date GetFreeBusy = "End Date is not a valid date!" Exit Function End If If Not (IsNumeric(strInterval)) Then GetFreeBusy = "You must enter a number for the interval." Exit Function ElseIf CInt(strInterval) <= 0 Then GetFreeBusy = "You must enter a positive number for the interval." Exit Function End If 'Make sure that user names were passed arrUserNames = Split(strUserNames, ",") If UBound(arrUserNames) < 0 Then 'No usernames passed. Error and exit. GetFreeBusy = "There were no user names passed." Exit Function End If 'Start building the string array for the free/busy info strFreeBusy = "" For x = LBound(arrUserNames) To UBound(arrUserNames) If strFreeBusy = "" Then strFreeBusy = strFreeBusy & PrivGetFreeBusy(arrUserNames(x), _ strDirURL, strStartDate, strEndDate, strInterval) Else strFreeBusy = strFreeBusy & "," & _ PrivGetFreeBusy(arrUserNames(x), strDirURL, _ strStartDate, strEndDate, strInterval) End If Next GetFreeBusy = strFreeBusy End Function Private Function PrivGetFreeBusy(strUserName as String, _ strDirURL as String, _ strStartDate as String, _ strEndDate as String, _ strInterval as String) As String On Error Resume Next Dim oAddressee As CDO.Addressee Set oAddressee = CreateObject("CDO.Addressee") oAddressee.EmailAddress = strUserName bFound = oAddressee.CheckName(strDirURL) If bFound = False Then PrivGetFreeBusy = "Could not resolve username: " & strUserName Exit Function End If 'Get the freebusy information PrivGetFreeBusy = oAddressee.GetFreeBusy(strStartDate, strEndDate, _ strInterval) Set oAddressee = Nothing End Function
Next you generate a Web Service Description Language (WSDL) file and a Web Services Meta Language (WSML) file for your DLL. The WSDL file is an XML file that is a contract between the client and the server. It describes the format that the client should send to the server to call the Web service. The WSDL file for the free/busy Web service is shown here:
<?xml version='1.0' encoding='UTF-8' ?> <!-- Generated 12/10/01 by Microsoft SOAP Toolkit WSDL File Generator, Version 1.02.813.0 --> <definitions name ='VS6FB' targetNamespace = 'http://tempuri.org/wsdl/' xmlns:wsdlns='http://tempuri.org/wsdl/' xmlns:typens='http://tempuri.org/type' xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:stk='http://schemas.microsoft.com/soap-toolkit/wsdl-extension' xmlns='http://schemas.xmlsoap.org/wsdl/'> <types> <schema targetNamespace='http://tempuri.org/type' xmlns='http://www.w3.org/2001/XMLSchema' xmlns:SOAP-ENC='http://schemas.xmlsoap.org/soap/encoding/' xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/' elementFormDefault='qualified'> </schema> </types> <message name='FreeBusy.GetFreeBusy'> <part name='strDirURL' type='xsd:string'/> <part name='strUserNames' type='xsd:string'/> <part name='strStartDate' type='xsd:string'/> <part name='strEndDate' type='xsd:string'/> <part name='strInterval' type='xsd:string'/> </message> <message name='FreeBusy.GetFreeBusyResponse'> <part name='Result' type='xsd:string'/> <part name='strDirURL' type='xsd:string'/> <part name='strUserNames' type='xsd:string'/> <part name='strStartDate' type='xsd:string'/> <part name='strEndDate' type='xsd:string'/> <part name='strInterval' type='xsd:string'/> </message> <portType name='FreeBusySoapPort'> <operation name='GetFreeBusy' parameterOrder= 'strDirURL strUserNames strStartDate strEndDate strInterval'> <input message='wsdlns:FreeBusy.GetFreeBusy' /> <output message='wsdlns:FreeBusy.GetFreeBusyResponse' /> </operation> </portType> <binding name='FreeBusySoapBinding' type='wsdlns:FreeBusySoapPort' > <stk:binding preferredEncoding='UTF-8'/> <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http' /> <operation name='GetFreeBusy' > <soap:operation soapAction='http://tempuri.org/action/FreeBusy.GetFreeBusy' /> <input> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </input> <output> <soap:body use='encoded' namespace='http://tempuri.org/message/' encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' /> </output> </operation> </binding> <service name='VS6FB' > <port name='FreeBusySoapPort' binding='wsdlns:FreeBusySoapBinding' > <soap:address location='http://thomriznt52/vs6fb/VS6FB.WSDL' /> </port> </service> </definitions>
SOAP also requires a server-based WSML file, which provides the information that maps the operations of the service (as described in the WSDL file) to specific methods of the COM object. The WSML file determines which COM object and method should be used to service any requests . The WSML file for the sample is shown here:
<?xml version='1.0' encoding='UTF-8' ?> <!-- Generated 12/10/01 by Microsoft SOAP Toolkit WSDL File Generator, Version 1.02.813.0 --> <servicemapping name='VS6FB'> <service name='VS6FB'> <using PROGID='ExchWS.FreeBusy' cachable='0' ID='FreeBusyObject' /> <port name='FreeBusySoapPort'> <operation name='GetFreeBusy'> <execute uses='FreeBusyObject' method='GetFreeBusy' dispID='1610809344'> <parameter callIndex='1' name='strDirURL' elementName='strDirURL' /> <parameter callIndex='2' name='strUserNames' elementName='strUserNames' /> <parameter callIndex='3' name='strStartDate' elementName='strStartDate' /> <parameter callIndex='4' name='strEndDate' elementName='strEndDate' /> <parameter callIndex='5' name='strInterval' elementName='strInterval' /> <parameter callIndex='-1' name='retval' elementName='Result' /> </execute> </operation> </port> </service> </servicemapping>
You might wonder how to go about generating these files. Well, you could attempt to generate the files by hand, but a much simpler and less error-prone approach is to use the WSDL generator tool from the SOAP Toolkit (shown in Figure 14-1).
This tool outputs the files you need to create and call your Web service. The only other thing you need to do is test the service. The sample includes a consumer of the Web service. It is just a simple Visual Basic application that allows you to specify users, dates, the directory server, and the free/busy interval to use. The sample is shown in Figure 14-2.
The consumer application uses the Microsoft SOAP Type Library and its SOAPClient object to talk to the Web service. The SOAPClient object makes it easy to connect to and call methods on a Web service. As you can see in the following code from the consumer application, the SOAPClient object has an MSSOAPINIT method that takes a WSDL file and a WSML file as arguments. If the method can successfully open these files from the Web service, the SOAPClient object will be initialized and can be used against the Web service. The SOAPClient object can then call Web service methods directly, and you can retrieve any failures or errors through the SOAPClient object as well.
Option Explicit Private mSoapClient As SoapClient Dim strServerResponse As String Private Sub cmdConnect_Click() On Error GoTo ErrorHandler 'See if there are any users Dim NewSoapClient As New SoapClient Dim WSDL As String Dim WSML As String If frmConnect.Connect(Me, WSDL, WSML) Then Me.MousePointer = vbHourglass NewSoapClient.mssoapinit WSDL, , , WSML Set mSoapClient = NewSoapClient cmdRefresh.Enabled = True Me.MousePointer = vbNormal End If Exit Sub ErrorHandler: Me.MousePointer = vbDefault If NewSoapClient.faultstring <> "" Then MsgBox "Connect failed. " & NewSoapClient.faultstring, _ vbExclamation Else MsgBox "Connect failed. " & Err.Description, vbExclamation End If Err.Clear End Sub Private Sub cmdRefresh_Click() On Error GoTo ErrorHandler listResponse.Clear 'Make sure the end date is not before the start date If DateDiff("d", monthStart.Value, monthEnd.Value) < 0 Then MsgBox "The end date must be after the start date!", _ vbOKOnly + vbExclamation Exit Sub End If 'Make sure interval is a # If Not (IsNumeric(txtInterval.Text)) Then MsgBox "You must enter a number for the interval.", _ vbOKOnly + vbExclamation Exit Sub ElseIf CInt(txtInterval.Text) < 0 Then MsgBox "You must enter a number greater than zero.", _ vbOKOnly + vbExclamation Exit Sub End If 'Make sure there is a directory server If txtDirServer.Text = "" Then MsgBox "You must enter a directory server!", _ vbOKOnly + vbExclamation Exit Sub End If Me.MousePointer = vbHourglass Dim strStartDate as String Dim strEndDate as String Dim strSMTPAddresses as String 'Figure out start and end date strStartDate = monthStart.Value & " 12:00 AM" strEndDate = monthEnd.Value & " 11:59 PM" 'Get the SMTP Addresses If listUsers.ListCount > 0 Then 'Scroll through each user and separate with commas Dim i For i = 0 To listUsers.ListCount - 1 If i = 0 Then strSMTPAddresses = listUsers.List(i) Else strSMTPAddresses = strSMTPAddresses & ", " & _ listUsers.List(i) End If Next Else MsgBox "You must enter users in order to query the server!", _ vbOKOnly + vbExclamation Me.MousePointer = vbNormal Exit Sub End If 'Get the Response strServerResponse = mSoapClient.GetFreeBusy(txtDirServer.Text, _ strSMTPAddresses, strStartDate, strEndDate, _ txtInterval.Text) 'Loop through the response and add it to the listbox Dim arrResponse arrResponse = Split(strServerResponse, ",") For i = LBound(arrResponse) To UBound(arrResponse) listResponse.AddItem arrResponse(i) Next Me.MousePointer = vbDefault Exit Sub ErrorHandler: Me.MousePointer = vbDefault If mSoapClient.faultstring <> "" Then MsgBox "Could not refresh application. " & _ mSoapClient.faultstring, vbExclamation Else MsgBox "Could not refresh application. " & Err.Description End If Err.Clear End Sub