How can you survive without SOAP Toolkit when using WindowsPE for Vista?

A customer of mine recently came up with a tricky problem regarding SOAP Toolkit and Windows Vista: “We have computer naming system that uses SOAP query. It has worked fine with XP but now we have problems with Vista. We get the following error: ActiveX component can't create object 'MSSOAP.SoapClient'. What could be wrong?”

The first thing raised in my mind was to have a quick look at the supported operating systems in SOAP Toolkit 3.0 download page, and the answer was quickly obtained: Windows 2000; Windows 98; Windows ME; Windows Server 2003; Windows XP.

Basically, all SOAP Toolkits have been replaced by the Microsoft .NET Framework. SOAP Toolkit versions earlier than version 3.0 are no longer supported and standard support for SOAP Toolkit 3.0 expired March 31, 2005. All new development on the SOAP Toolkit has stopped. Consequentially, there will be no future improvements, fixes, or standard compliance updates made to it (there will be no implementation of the SOAP 2.0 specifications). Because of all this, any new development based on the SOAP Toolkit is not advised. Microsoft official recommendation for Web Services now is to use .NET to both access and publish Web Services. When there is no alternative option MS recommends using .NET in combination with COM for non .NET technologies when communicating with web services.

But the customer scenario was a bit more complex: I don’t know if you’ve noticed this ‘detail’ in his question: “We have computer naming system”. In fact the customer uses WindowsPE, a bootable tool from Microsoft that provides operating system features for installation, troubleshooting, and recovery (https://technet.microsoft.com/en-us/windowsvista/aa905120.aspx). It does NOT support .NET framework. You are allowed to run VB scripts with Windows PE.

Let’s wrap up:

1) Soap Toolkit is no longer supported on Windows Vista.
2) The customer uses WindowsPE, a bootable tool from Microsoft that provides operating system features for installation, troubleshooting, and recovery. It does NOT support .NET framework.
3) You can run VBScripts with WindowsPE and the customer used to connect a web service through soap toolkit in a vbscript.
4) The customer uses the web service to retrieve the computer name of the machine to be installed with sysprep.

I started thinking to all of the possibilities we had to easily connect a web service from an environment not including .NET, and several ideas, more or less sensible, rose up: producing a .NET client proxy, wrapping the assembly through COM Interop, invoking COM methods in the customer VB Script.. what a mess!

But why not discovering a way to directly connect the web service from the VB Script, even though I have to build SOAP messages by hand? The first impressions of my colleagues were something like “too much complex”, “what if you have to add methods?” “it seems to me you’re lately getting crazy” and so on.smile_eyeroll

In my opinion, the solution with the proxy client code generated by wsdl.exe is too much complex, and could be acceptable if no code changes were needed: in fact before compiling the .NET class library you have to modify the code generated by wsdl.exe, and it’s advisable to work with Visual Studio. Besides you have to register the .NET assembly with regasm.exe before being able to use the SOAP client. Too many steps to do, too many error chances.

My aim was to allow the customer to re-use his VBScript, and since the ideal solution didn’t exist for this problem, I considered some problem aspects.

1) Let’s try to re-use the work already done, in this case VBScript.

2) Reduce the pre-steps like object installation, registration.

3) The customer web service seemed to be quite simple, with just one GetName method in order to retrieve the machine name.

I guess the safest and simplest solution, given the aspects above, is to use the ServerXmlHttp object: it provides methods and properties that enable you to establish an HTTP connection between files or objects on different Web servers. It is implemented in msxml*.dll, so you can find it in every Vista installation, and no additional action is required.

The most difficult part is that invoking a web service means to use SOAP, i.e. an XML message formed in order to specify a method invocation together with parameter values. The response coming from the web service will be another XML message which contains the return value and the output parameters, and you’ll have to extract them to be able to use it.

However, working with strings shouldn’t be very hard in VBScript, and you can also use another msxml object, the DomDocument which simplify to handle with XML documents.

The request message was the following:

 POST /DataManager.asmx HTTP/1.1
SOAPAction: "https://and.it/wsnaming/GetName"
Content-Type: text/xml..User-Agent: SOAP Sdk
Host: 10.32.56.30
Content-Length: 433
Connection: Keep-Alive
Cache-Control: no-cache

<?xml version="1.0" encoding="UTF-8" standalone="no"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="https://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><GetName xmlns="https://andrea.org/wsnaming/"><sName xmlns:SOAPSDK1="https://and.it/wsnaming/">&apos;Precision M65                   &apos;,&apos;DH1XP2J&apos;,&apos;LAPTOP&apos;,&apos;00:15:C5:C2:80:BD&apos;,&apos;00:19:D2:02:F6:47&apos;</sName></GetName></SOAP-ENV:Body></SOAP-ENV:Envelope>

While the response was:

 HTTP/1.1 200 OK
Date: Tue, 18 Mar 2008 06:19:15 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 352

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema"><soap:Body><GetNameResponse xmlns="https://and.it/wsnaming/"><GetNameResult>FIL01709</GetNameResult></GetNameResponse></soap:Body></soap:Envelope>

So we should implement a vbscript which compose the request, and parses the response to get the host name.

I’ve implemented this:

 'DECLARE VARIABLES 
Dim objXMLDOC, objXMLDOM, SoapStr 
Dim model, serial, wsType, lanMacc, wLanMac
Dim hostName

model = "&apos;Precision M65                   &apos;"
serial = "&apos;DH1XP2J&apos;"
wsType = "&apos;LAPTOP&apos;"
lanMacc= "&apos;00:15:C5:C2:80:BD&apos;"
wLanMac ="&apos;00:19:D2:02:F6:47&apos;"


'BUILD CALL DATA 
SoapStr = "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""no""?>" 
SoapStr = SoapStr & "<SOAP-ENV:Envelope" 
SoapStr = SoapStr & " xmlns:SOAP-ENV=""https://schemas.xmlsoap.org/soap/envelope/""" 
SoapStr = SoapStr & " <SOAP-ENV:Body>" 
SoapStr = SoapStr & " <GetName xmlns=""https://and.it/wsnaming/"">" 
SoapStr = SoapStr & " <sName xmlns:SOAPSDK1=""https://and.it/wsnaming/"">"
SoapStr = SoapStr & model & "," & serial & "," & wsType & "," & lanMac & "," & wlanMac 
SoapStr = SoapStr & "</sName>" 
SoapStr = SoapStr & " </GetName>" 
SoapStr = SoapStr & " </SOAP-ENV:Body>" 
SoapStr = SoapStr & "</SOAP-ENV:Envelope>" 
 
'CREATE OBJECTS 
Set objXMLDOC = CreateObject("Msxml2.ServerXMLHTTP") 
Set objXMLDOM = CreateObject("Msxml2.DomDocument") 
Set oNode = CreateObject("Microsoft.XMLDOM") 


'MAKE THE CALL 
objXMLDOC.open "POST", "https://10.32.56.30/DataManager.asmx", False 
objXMLDOC.setRequestHeader "Content-Type", "text/xml" 
objXMLDOC.setRequestHeader "SOAPAction", "https://and.it/wsnaming/GetName" 
objXMLDOC.send(SoapStr) 
objXMLDOM.LoadXML objXMLDOC.responseText 

' SOAP RESPONSE FOR TESTING PURPOSE
'SoapStr = "<?xml version=""1.0"" encoding=""utf-8""?>"
'SoapStr = SoapStr & "<soap:Envelope xmlns:soap=""https://schemas.xmlsoap.org/soap/envelope/"" "
'SoapStr = SoapStr & "xmlns:xsi=""https://www.w3.org/2001/XMLSchema-instance"" "
'SoapStr = SoapStr & "xmlns:xsd=""https://www.w3.org/2001/XMLSchema"">"
'SoapStr = SoapStr & "<soap:Body><GetNameResponse xmlns=""https://and.it/wsnaming/""><GetNameResult>"
'SoapStr = SoapStr & "FIL01709"
'SoapStr = SoapStr & "</GetNameResult></GetNameResponse></soap:Body></soap:Envelope>"
'objXMLDOM.LoadXML SoapStr

hostName = ""

'PROCESS THE CALL 
If objXMLDOM.parseError.errorCode <> 0 Then 'parser error found 
'handle the error 
Else 'Call was successful, process 

Set oNode = objXMLDOM.getElementsByTagName("GetNameResult")
hostName =  oNode.Item(i).text
Wscript.Echo hostName

End If 


'CLEAN UP EVERYTHING 
Set objXMLDOC = Nothing 
Set objXMLDOM = Nothing 
Set oNode = Nothing 

Please consider this as just a track to follow: I suggest you to think on how to inject this code in your existent vbscript after having verified it’s working as is. Please also consider I'm not an expert VB Script programmer, so I know this sample doesn't contain any proper error handling, and maybe some unorthodox constructs Smile My purpose was mainly to show how some unusual problems can lead to the approach you would never have thought before.

Cheers,

Andrea