Dr. Dobb's Journal February 2007
Developers these days are overloaded with ideas, but have limited time to implement them. Consequently, we've become increasingly reliant on third-party tools to encapsulate development work that can be easily utilized in COM-compliant applications such as with Visual Basic 6.
As owner of NC Software (www.nc-software.com) and developer of Logbook Pro pilot logbook and AVCataloger media cataloging software, I've found that third-party tools save development time and provide expertise that would have taken us years to acquire.
For instance, as Logbook Pro matured, we wanted to bring in a service-oriented architecture (SOA), but were challenged with legacy VB6 code paired against modern programming practices (aka .NET). We are web-service fanatics and take advantage of the SOA theme anywhere possible. However, our first challenge was to lose the integrated Update Service that we subscribed to from Macrovision. Although a great service, it was too expensive for our use. Moreover, we knew its functionality was something we could do ourselves via web services. With this in mind, we built a SQL Server database with a web-service middle tier, allowing our own update service to drive any application from our web applications, VB6 applications, and new editions of our software being developed in Visual Studio 2005.
Building the update system resulted in a huge cost savings because we were able to shed the Macrovision Update Service. The challenge we then faced was integrating not only web services into our VB6 applications, but also a download mechanism and XML parser with minimal effort. Because Logbook Pro has been in existence for nine years and all of our current development efforts are in .NET, we didn't want to expend too much effort. This is where the IP*Works! Internet Toolkit from /n Software (www.nsoftware.com/ipworks) came into the picture.
The software update system first involved making a web service call. Using the IP*Works! system was easy to integrate with only one item of interestthe requirement to add the SoapRpcMethod() attribute to the web methods; see Listing One.
<WebMethod(), SoapRpcMethod()> _
Public Function HelloWorld(ByVal p As String) As String
If p.ToLower = "test" Then
Return "Hello World"
Else
Return "Invalid Parameter"
End If
End Function
You can then use the Software Web Service Proxy Generator in the Helper Applications section (www.nsoftware.com/kb/default.aspx) in the knowledgebase, which opens the page at www.nsoftware.com/kb/apps/wspg. Enter the address to your web service and generates the code to make the web service call from your VB6 application. Listing Two makes our web service call using the IP*Works! SOAP component.
Public Function HasUpdates() As Boolean
On Error GoTo Err_Updates
Screen.MousePointer = 11
SOAP1.reset
SOAP1.ValueFormat = vfText
SOAP1.MethodURI = "http://
webservices.nc-software.com/UpdateService"
SOAP1.ActionURI =
"http://webservices.nc-software.com/
UpdateService/GetUpdateByMajMinRev"
SOAP1.URL =
"http://webservices.nc-software.com/20/
UpdateService/UpdateService.asmx"
SOAP1.method = "GetUpdateByMajMinRev"
SOAP1.AddParam "XMLProductCode", XMLProductCode
SOAP1.AddParam "iMajor", APP.Major
SOAP1.AddParam "iMinor", APP.Minor
SOAP1.AddParam "iRevision", APP.Revision
SOAP1.AddParam "p", "mypassword"
SOAP1.SendRequest
Dim strReturn As String
strReturn = SOAP1.returnValue
If Trim$(strReturn) = "" Then
HasUpdates = False
Screen.MousePointer = vbDefault
Exit Function
End If
If InStr(strReturn, "|") > 0 Then
HasUpdates = True
Dim arr As Variant
arr = Split(strReturn, "|")
With ncUpdateInfo
.UpdateTitle = arr(0)
.BriefDescription = arr(1)
.DetailedDescription = arr(2)
.DownloadURL = arr(3)
.InfoURL = arr(4)
.DownloadSize = arr(5)
.IsCritical = CBool(arr(6))
End With
Else
HasUpdates = False
End If
Screen.MousePointer = vbDefault
Exit Function
Err_Updates:
HasUpdates = False
Screen.MousePointer = vbDefault
End Function
As you can see, the SOAP component (one of more than 40 included in IP*Works!) makes web-service integration straightforward in any ActiveX-compliant application.
Once we could identify when an update was available, we needed an easy method to download the update file and show progress, which can be a challenge even in .NET. The HTTP control in the IP*Works! suite performs downloads, and the /n sample code shows how to display download progress, making it easy to wire up a progress bar to provide feedback to the end-user during download. Listing Three is the code for the update dialog and downloading of the available updates.
Private Sub DoDownload()
On Error GoTo Err_Download
Screen.MousePointer = 11
If Dir(updateFile) <> "" Then
FileSystem.Kill updateFile
End If
With HTTP1
.LocalFile = updateFile
.Get ncUpdateInfo.DownloadURL
End With
Exit Sub
Err_Download:
Screen.MousePointer = vbDefault
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, _
UnloadMode As Integer)
HTTP1.Interrupt
End Sub
Private Sub HTTP1_Disconnected(StatusCode As Integer, _
Description As String)
If StatusCode <> 0 Then
If Dir(updateFile) <> "" Then
FileSystem.Kill updateFile
End If
End If
EnableButtons
End Sub
Private Sub HTTP1_EndTransfer(Direction As Integer)
Me.cmd(1).Caption = "&Update Now"
Me.cmd(2).Caption = "&Close"
Me.cmd(1).ToolTipText =
"Click to begin the update and
close Logbook Pro"
Me.progBar.Visible = False
Screen.MousePointer = vbDefault
EnableButtons
End Sub
Private Sub HTTP1_Error(ErrorCode As Integer, _
Description As String)
If Dir(updateFile) <> "" Then
FileSystem.Kill updateFile
End If
EnableButtons
End Sub
Private Sub HTTP1_Header(Field As String, _
Value As String)
On Error Resume Next
If UCase(Field) = "CONTENT-LENGTH" Then
lngFileSize = CLng(Value)
End If
End Sub
Private Sub HTTP1_StartTransfer(Direction As Integer)
DisableButtons
Me.progBar.Visible = True
Me.progBar.FloodPercent = 0
End Sub
Private Sub HTTP1_Transfer(Direction As Integer, _
BytesTransferred As Long, _
Text As String)
Me.progBar.FloodPercent =
(BytesTransferred / lngFileSize) * 100
End Sub
Internet integration was also straightforward with IP*Works!, and as Logbook Pro continued to grow, we added support for both Palm OS and Pocket PC applications. We recently rewrote our Logbook Pro Pocket PC Companion using the .NET Compact Framework in Visual Studio 2005 and wanted to use a persisted DataSet for our data store on the device. We then wanted to be able to read this data on the PC after transferring the saved DataSet using RAPI and ActiveSync. The DataSet is a great storage means for making XML data persistent and readable, and in the small data requirement of the Logbook Pro Pocket PC Companion, this was ideal. However, this left us a challenge on the PC side of Logbook Prohaving to easily consume the XML-generated data using the DataSet.WriteXML method, parse it, and get it into the Microsoft Access back-end that Logbook Pro uses on the PC side. Of course, we could have used various XML options (xpath and the like). However, we have to assume our users don't have MSXML on their computers and we didn't want to bloat our installer any further. We're already pushing a 15-MB download and we truly wanted to keep this small, so the logical choice was to use the XMLp (XML Parser), which is part of the IP*Works! collection. Again, Software saved time by making the task of reading XML in VB6 simple; see Listing Four, which consumes the XML from our Pocket PC Companion product.
With XMLp1
.Input strXML
.XPath = "/dsLogbook"
Dim I As Integer
Dim j As Integer
For I = 1 To .XChildren
.XPath = "/dsLogbook/[" & I & "]"
Select Case .XElement
Case "PDACertificates"
Set rst =
mydb.OpenRecordset(
"PDACertificates", dbOpenDynaset)
rst.AddNew
rst.Fields("CertificateID").Value = .Attr("CertificateID")
rst.Fields("Grade").Value = .Attr("Grade")
rst.Fields("Number").Value = .Attr("Number")
rst.Fields("DateIssue").Value = GetPPCDate(.Attr("DateIssue"))
rst.Fields("Remarks").Value = .Attr("Remarks")
rst.Update
rst.Close
Set rst = Nothing
End Select
Next
End With
Taking advantage of third-party components such as IP*Works! can be a great time saver. And because I assume that the developers who created these components are smarter than me, I rest assured that I made the right choice in using available third-party technologies. DDJ