West-Wind Web Connection requires defining, in IIS, script-mapped extensions that execute without checking the existence of a file or folder; this post explains how to achieve that with a JavaScript script executing in Windows Script Host.
In most IIS installs, when creating a script-mapped handler, one gets this default setting:
As this setting does not work for West-Wind Web Connection, when defining a handler manually, one just need to un-check the box at the top:
However, when installing a Web application on a server programmatically, you need a programmatic way to do the same operation. We had to cope with this issue to install the FoxInCloud sample applications on the developer’s machine using InstallShield Professional 2015
(ISP
).
Using ISP
we can create applications within any site on the target machine (extent or new), such as the ‘Default Web Site’ mapped to 127.0.0.1:80, alias localhost
:
However, when defining a handler – aka script-mapped extension – ISP
no longer cares for the ‘check if file exists’ setting on IIS>6, thus defaulting to the dreaded ‘check that file exists’, not suitable for a West-Wind Web Connection application installation:
Fortunately, ISP
supports ‘custom actions’ that installation author can define and decide to execute at any step in the installation process:
We decided to execute, just after IIS applications have installed successfully, the following JavaScript in the Windows Script Host
context:
var junk
, adminManager = new ActiveXObject('Microsoft.ApplicationHost.WritableAdminManager')
, sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST")
, site = sitesSection.Collection.Item(0) /* alias "Default Web Site" */
, XMLparser = new ActiveXObject('MSXML.DOMDocument'), oXML
, fso = WScript.createObject('Scripting.FileSystemObject'), file, stream
, application, virtual
, handlers, handler, handlerChanged
, scriptProcessor
;
for (var i = 0; i < site.Collection.count; i++) {
application = site.Collection.Item(i);
if (application.Name === 'application' && application.Properties.Item('path').Value.length > 1){
for (var j = 0; j < application.Collection.count; j++) {
virtual = application.Collection.Item(j);
if (virtual.Name === "virtualDirectory" && virtual.Properties.Item('path').Value.length == 1){
/* Update web connection handlers in virtual's web.config*/
file = virtual.Properties.Item('physicalPath').Value;
file = file + (file.substr(file.length-1) === '\\' ? '' : '\\') + "web.config";
if (fso.FileExists(file)){
stream = fso.OpenTextFile(file, 1);
if (!stream.AtEndOfStream && XMLparser.loadXML(stream.ReadAll())){
handlers = XMLparser.documentElement;
if (handlers){
handlers = handlers.getElementsByTagName('system.webServer');
if (handlers.length){
handlers = handlers[0];
handlers = handlers.getElementsByTagName('handlers');
if (handlers.length){
handlers = handlers[0];
handlerChanged = false;
for (var k = 0; k < handlers.childNodes.length; k++) {
handler = handlers.childNodes[k];
scriptProcessor = handler.getAttribute("scriptProcessor");
scriptProcessor = scriptProcessor.substr(scriptProcessor.lastIndexOf('\\')+1).toLowerCase();
if (handler.getAttribute("resourceType") !== "Unspecified"
&& (scriptProcessor === 'wc.dll' || scriptProcessor === 'webconnectionmodule.dll')){ /* for west-wind web connect */
handler.setAttribute("resourceType", "Unspecified");
handlerChanged = true;
}
};
if (handlerChanged){
stream = fso.OpenTextFile(file, 2);
stream.Write(XMLparser.xml);
WScript.Echo(file);
}
}
}
}
}
}
}
}
}
};
Decoding:
adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST")
reads in "C:\Windows\System32\inetsrv\config\applicationHost.config"
the following section:
<site name="Default Web Site" id="1" serverAutoStart="true">
<application path="/" applicationPool="West Wind Web Connection">
<virtualDirectory path="/" physicalPath="%SystemDrive%\inetpub\wwwroot" />
<virtualDirectory path="/awScripts" physicalPath="C:\Program Files\VFP9\Tools\AB\AW\Scripts\" userName="" />
<virtualDirectory path="/blog" physicalPath="C:\aDossier\3718 FoxLANWeb\Interne\Web\Site\blog" />
<virtualDirectoryDefaults userName="ThierryNivelet" password="[...]" />
</application>
<application path="/tutoTest" applicationPool="FoxInCloud apps">
<virtualDirectory path="/" physicalPath="C:\Program Files\VFP9\Tools\AB\AW\Samples\FIC\FicTuto\site\" />
</application>
<application path="/demotest" applicationPool="FoxInCloud apps">
<virtualDirectory path="/" physicalPath="C:\Program Files\VFP9\Tools\AB\AW\Samples\FIC\FicDemo\site" />
</application>
<application path="/ttdoc" applicationPool="West Wind Web Connection">
<virtualDirectory path="/" physicalPath="C:\aDossier\3718 FoxLANWeb\Interne\Web\dokuwiki" />
</application>
<application path="/mpn/bin" applicationPool="West Wind Web Connection">
<virtualDirectory path="/" physicalPath="C:\aDossier\3681_MachPro_IntuiCat\Interne\Site\bin" />
</application>
<application path="/ctb" applicationPool="FoxInCloud apps">
<virtualDirectory path="/" physicalPath="C:\aDossier\3725 BioSilWeb\Interne\Compo\CTB" />
</application>
<application path="/cal" applicationPool="FoxInCloud apps">
<virtualDirectory path="/" physicalPath="C:\aDossier\3721 CAL\Client\Recu\BioVetoWeb\Site\CAL" />
</application>
<application path="/tt" applicationPool="FoxInCloud apps">
<virtualDirectory path="/" physicalPath="C:\aDossier\3718 FoxLANWeb\Interne\Web\Site" />
</application>
<application path="/ip" applicationPool="FoxInCloud apps">
<virtualDirectory path="/" physicalPath="C:\aDossier\3724 MCTG\Interne\Compo" />
</application>
<application path="/asl" applicationPool="FoxInCloud apps">
<virtualDirectory path="/" physicalPath="C:\aDossier\3728 JPB\Client\Recu\asl_Web\Site" />
</application>
<application path="/fttTest" applicationPool="FoxInCloud apps">
<virtualDirectory path="/" physicalPath="C:\Program Files\VFP9\Tools\AB\AW\Samples\Tastrade\Adapted\Site\fttTest" />
</application>
<application path="/wconnect" applicationPool="West Wind Web Connection">
<virtualDirectory path="/" physicalPath="C:\inetpub\wwwroot\wconnect" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:80:" />
<binding protocol="net.tcp" bindingInformation="808:*" />
<binding protocol="net.pipe" bindingInformation="*" />
<binding protocol="net.msmq" bindingInformation="localhost" />
<binding protocol="msmq.formatname" bindingInformation="localhost" />
</bindings>
<logFile period="MaxSize" truncateSize="4294967295" enabled="true" />
</site>
if (application.Name === 'application' && application.Properties.Item('path').Value.length > 1){
identifies the <application path="..." applicationPool="...">
nodes
for (var j = 0; j < application.Collection.count; j++) {
and if (virtual.Name === "virtualDirectory" && virtual.Properties.Item('path').Value.length == 1){
identify the virtual directories at the applications’ root
Then comes the tricky part… Each application’s settings are stored in a different place, in a file named web.config
stored in the virtual directory’s physical folder. Altering the application settings requires modifying this file, which Microsoft.ApplicationHost.WritableAdminManager
is no help for.
So we need to read and write the XML in this file, and rather than parsing the XML ourselves, we prefer rely on the standard MSXML.DOMDocument
activeX component:
file = virtual.Properties.Item('physicalPath').Value;
file = file + (file.substr(file.length-1) === '\\' ? '' : '\\') + "web.config";
if (fso.FileExists(file)){
stream = fso.OpenTextFile(file, 1);
if (!stream.AtEndOfStream && XMLparser.loadXML(stream.ReadAll())){
Then we navigate through the XML nodes down to the script-mapped extension handler defined on some west-wind web connect dll
and set its resourceType
attribute to Undefined
(case sensitive like everything in XML) if its value is different:
for (var k = 0; k < handlers.childNodes.length; k++) {
handler = handlers.childNodes[k];
scriptProcessor = handler.getAttribute("scriptProcessor");
scriptProcessor = scriptProcessor.substr(scriptProcessor.lastIndexOf('\\')+1).toLowerCase();
if (handler.getAttribute("resourceType") !== "Unspecified"
&& (scriptProcessor === 'wc.dll' || scriptProcessor === 'webconnectionmodule.dll')){ /* for west-wind web connect */
handler.setAttribute("resourceType", "Unspecified");
handlerChanged = true;
}
};
Finally we write the result XML back into the web.config
file:
if (handlerChanged){
stream = fso.OpenTextFile(file, 2);
stream.Write(XMLparser.xml);
}
Et voilà!
Update : here is the equivalent code for VFP:
local adminManager as Microsoft.ApplicationHost.WritableAdminManager;
, sitesSection, iApp;
, site;
, isapiCgiRestrictionSection;
, isapiCgiRestrictionCollection, iISAPI;
, XMLparser, oXML;
, fso, file, stream;
, app, virtual, iVirtual;
, handlers, handler, handlerChanged, iHand;
, scriptProcessor, scriptModule;
, scriptModuleEntry, scriptModuleListed, scriptModuleAllowed, isapiCgiRestrictionSectionChanged;
, true, false
adminManager = CreateObject('Microsoft.ApplicationHost.WritableAdminManager')
adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST"
sitesSection = adminManager.GetAdminSection("system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST")
site = sitesSection.Collection.Item(0) && alias "Default Web Site"
isapiCgiRestrictionSection = adminManager.GetAdminSection("system.webServer/security/isapiCgiRestriction", "MACHINE/WEBROOT/APPHOST")
isapiCgiRestrictionCollection = isapiCgiRestrictionSection.Collection
XMLparser = CreateObject('MSXML.DOMDocument')
fso = createObject('Scripting.FileSystemObject')
true = .T.
false = .F.
for iApp = 0 to site.Collection.Count-1
app = site.Collection.Item(m.iApp)
if app.Name == 'application' and Lenc(app.Properties.Item('path').Value) > 1
for iVirtual = 0 to app.Collection.count-1
virtual = app.Collection.Item(iVirtual)
if (virtual.Name == "virtualDirectory" and virtual.Properties.Item('path').Value == '/')
* Update web connection handlers in virtual's web.config
file = virtual.Properties.Item('physicalPath').Value
file = file + iif(substrc(file, Lenc(file)-1) == '\', '', '\') + "web.config"
if (fso.FileExists(file))
stream = fso.OpenTextFile(file, 1)
if (!stream.AtEndOfStream and XMLparser.loadXML(stream.ReadAll()))
handlers = XMLparser.documentElement
if vartype(handlers) == 'O'
handlers = handlers.getElementsByTagName('system.webServer')
if (!empty(handlers.length))
handlers = handlers.Item[0]
handlers = handlers.getElementsByTagName('handlers')
if !empty(handlers.length)
handlers = handlers.Item[0]
handlerChanged = false
for iHand = 0 to handlers.childNodes.length - 1
handler = handlers.childNodes.Item[m.iHand]
scriptProcessor = handler.getAttribute("scriptProcessor")
scriptModule = Lower(Substrc(scriptProcessor, Ratc('\', scriptProcessor)+1))
if (! handler.getAttribute("resourceType") == "Unspecified";
and (scriptModule == 'wc.dll' or scriptModule == 'webconnectionmodule.dll')) && west-wind web connect
handler.setAttribute("resourceType", "Unspecified")
handlerChanged = true
endif
&& make sure script processor is allowed && https://www.iis.net/configreference/system.webserver/security/isapicgirestriction
scriptModuleListed = false
scriptModuleAllowed = false
&& ? scriptProcessor
for iISAPI = 0 to isapiCgiRestrictionCollection.Count-1
scriptModuleEntry = isapiCgiRestrictionCollection.Item(m.iISAPI)
&& ? scriptModuleEntry.Properties.Item('path').Value
if (scriptModuleEntry.Name == 'add' and Lower(scriptModuleEntry.Properties.Item('path').Value) == Lower(scriptProcessor))
scriptModuleListed = true
scriptModuleAllowed = scriptModuleEntry.Properties.Item('allowed').Value == true
exit
endif
endfor
if !scriptModuleAllowed
if !scriptModuleListed
scriptModuleEntry = isapiCgiRestrictionCollection.CreateNewElement('add')
scriptModuleEntry.Properties.Item('path').Value = scriptProcessor
endif
scriptModuleEntry.Properties.Item('allowed').Value = true
scriptModuleEntry.Properties.Item('groupId').Value = handler.getAttribute('name')
scriptModuleEntry.Properties.Item('description').Value = 'FoxInCloud Application'
if(!scriptModuleListed)
isapiCgiRestrictionCollection.addElement(scriptModuleEntry)
endif
isapiCgiRestrictionSectionChanged = true
endif
endfor
if (handlerChanged)
stream.Close()
stream = fso.OpenTextFile(file, 2)
stream.Write(XMLparser.xml)
stream = fso.OpenTextFile(file, 1)
endif
endif
endif
endif
stream.Close()
endif
endif
endif
endfor
endif
endfor
if isapiCgiRestrictionSectionChanged
adminManager.CommitChanges()
endif
tags:
installation
JavaScript
West-Wind Web Connection
Watch FoxInCloud Marketing Videos :: Watch FoxInCloud Technical Videos :: Stay tuned on FoxInCloud Roadmap :: Learn how to use FoxInCloud :: Download FoxInCloud Adaptation Assistant for free :: Download FoxInCloud Web Application Studio for free
Please enable JavaScript to view the comments powered by Disqus.