Just a short, simple blog for Bob to share his thoughts.
28 January 2012 • by Bob • IIS, LogParser, Scripting
I recently had a situation where I wanted to customize the chart output from Log Parser, and after a bunch of research I eventually arrived at the conclusion that configuration scripts to create customized charts are probably the least-documented feature of Log Parser. After a lot of experimentation, (and a bit of frustration), I finally managed to achieve the results that I wanted. With that in mind, I thought that it would make a great blog series if I documented some of the settings that I used.
When you look in the Log Parser help file, it makes mention of using configuration scripts to customize charts, and it provides the following small JavaScript sample:
// Add a caption
chartSpace.HasChartSpaceTitle = true;
chartSpace.ChartSpaceTitle.Caption = "Generated by Log Parser 2.2";
chartSpace.ChartSpaceTitle.Font.Size = 6;
chartSpace.ChartSpaceTitle.Position = chartSpace.Constants.chTitlePositionBottom;
// Change the background color
chart.PlotArea.Interior.Color = chartSpace.Constants.chColorNone;
Unfortunately, this sample isn't very useful, although I found dozens of forum posts that quote this sample as a way to do things - but it's the only sample that most people cite. The Log Parser help file mentions looking at the MSDN ChartSpace Object Model documentation, but that documentation is only slightly more useful. These two references are what led me to my earlier conclusion that chart configuration scripts are not well-documented, and especially when you are trying to do something with Log Parser.
What I found to be particularly helpful was to use the Log Parser COM interface and write scripts by using Adersoft's VbsEdit and JsEdit. In case you haven't used either of those applications, they are great IDEs for writing scripts; they both give you a great debugging environment, and they have a great object browser that I used to discover what options were available to me. In the end, these two editors made it possible to create the chart configuration scripts that I will discuss in this blog series.
By the way, chart configuration scripts can be written in VBScript or JavaScript, but for this blog I will use VBScript for the Log Parser COM samples and JavaScript for the configuration script samples. I didn't have to do it that way, but it seemed like a good idea to help differentiate between the samples.
For the samples in this blog series, I will use Log Parser's COM interface and VBScript to create my charts, but this is not necessary; everything that I am documenting can be done from the command-line version of Log parser, and I'll give you some quick examples to see the differences.
The following examples generate some simple area charts that plot the total number of hits by day, and both examples do exactly the same thing:
logparser.exe "
Option Explicit
Dim objLogQuery, strSQL
Dim objInputW3CFormat, objOutputChartFormat
Set objLogQuery = WScript.CreateObject("MSUtil.LogQuery")
Set objInputW3CFormat = WScript.CreateObject("MSUtil.LogQuery.W3CInputFormat")
Set objOutputChartFormat = WScript.CreateObject("MSUtil.LogQuery.ChartOutputFormat")
strSQL = "SELECT Date, COUNT(*) AS Hits " & _
" INTO HitsByDay.gif " & _
" FROM *.log " & _
" GROUP BY Date " & _
" ORDER BY Date"
objOutputChartFormat.groupSize = "800x600"
objOutputChartFormat.fileType = "GIF"
objOutputChartFormat.chartType = "Area"
objOutputChartFormat.categories = "ON"
objOutputChartFormat.values = "ON"
objOutputChartFormat.legend = "OFF"
objLogQuery.ExecuteBatch strSQL, objInputW3CFormat, objOutputChartFormat
Using some of the log files from one of my websites, the above samples created the following basic chart:
Taking a look at this chart makes it easy to see why you would want to customize your output; that light blue is pretty awful, and those values are pretty hard to read.
If you remember the incredibly basic configuration script from earlier, you only need to add one parameter to each example in order to specify the configuration script:
logparser.exe "
Option Explicit
Dim objLogQuery, strSQL
Dim objInputW3CFormat, objOutputChartFormat
Set objLogQuery = WScript.CreateObject("MSUtil.LogQuery")
Set objInputW3CFormat = WScript.CreateObject("MSUtil.LogQuery.W3CInputFormat")
Set objOutputChartFormat = WScript.CreateObject("MSUtil.LogQuery.ChartOutputFormat")
strSQL = "SELECT Date, COUNT(*) AS Hits " & _
" INTO HitsByDay.gif " & _
" FROM *.log " & _
" GROUP BY Date " & _
" ORDER BY Date"
objOutputChartFormat.groupSize = "800x600"
objOutputChartFormat.fileType = "GIF"
objOutputChartFormat.chartType = "Area"
objOutputChartFormat.categories = "ON"
objOutputChartFormat.values = "ON"
objOutputChartFormat.legend = "OFF"
objOutputChartFormat.config = "HitsByDay.js"
objLogQuery.ExecuteBatch strSQL, objInputW3CFormat, objOutputChartFormat
Taking a look at the resulting chart, you can see why I mentioned earlier that the configuration script wasn't very useful; all it does is add a centered title to the bottom of the chart:
Yup - that's a pretty useless sample configuration script for chart customization.
In my subsequent posts, I'll show how to make this chart (and several other types of charts) look a lot better.
Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/
07 October 2011 • by Bob • IIS, Scripting, WebDAV
I've mentioned in previous blog posts that I use the Windows WebDAV Redirector a lot. (And believe me, I use it a lot.) Having said that, there are a lot of registry settings that control how the Windows WebDAV Redirector operates, and I tend to tweak those settings fairly often.
I documented all of those registry settings in my Using the WebDAV Redirector walkthrough, but unfortunately there isn't a built-in interface for managing the settings. With that in mind, I decided to write my own user interface.
I knew that it would be pretty simple to create a basic Windows Form application that does everything, but my trouble is that I would want to share the code in a blog, and the steps create a Windows application are probably more than I would want to write in such a short space. So I decided to reach into my scripting past and create an HTML Application for Windows that configures all of the Windows WebDAV Redirector settings.
It should be noted, like everything else these days, that this code is provided as-is. ;-]
When you run the application, it will present you with the following user interface, which allows you to configure most of the useful Windows WebDAV Redirector settings:
To create this HTML Application, save the following HTMLA code as "WebDAV Redirector Settings.hta" to your computer, and then double-click its icon to run the application.
<html> <head> <title>WebDAV Redirector Settings</title> <HTA:APPLICATION APPLICATIONNAME="WebDAV Redirector Settings" ID="WebDAV Redirector Settings" VERSION="1.0" BORDER="dialog" BORDERSTYLE="static" INNERBORDER="no" SYSMENU="no" MAXIMIZEBUTTON="no" MINIMIZEBUTTON="no" SCROLL="no" SCROLLFLAT="yes" SINGLEINSTANCE="yes" CONTEXTMENU="no" SELECTION="no"/> <script language="vbscript"> ' ---------------------------------------- ' Start of main code section. ' ---------------------------------------- Option Explicit Const intDialogWidth = 700 Const intDialogHeight = 620 Const HKEY_LOCAL_MACHINE = &H80000002 Const strWebClientKeyPath = "SYSTEM\CurrentControlSet\Services\WebClient\Parameters" Const strLuaKeyPath = "Software\Microsoft\Windows\CurrentVersion\Policies\System" Dim objRegistry Dim blnHasChanges ' ---------------------------------------- ' Start the application. ' ---------------------------------------- Sub Window_OnLoad On Error Resume Next ' Set up the UI dimensions. Self.resizeTo intDialogWidth,intDialogHeight Self.moveTo (Screen.AvailWidth - intDialogWidth) / 2, _ (Screen.AvailHeight - intDialogHeight) / 2 ' Retrieve the current settings. Document.all.TheBody.ClassName = "hide" Set objRegistry = GetObject( _ "winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv") Call CheckForLUA() Call GetValues() Document.All.TheBody.ClassName = "show" End Sub ' ---------------------------------------- ' Check for User Access Control ' ---------------------------------------- Sub CheckForLUA() If GetRegistryDWORD(strLuaKeyPath,"EnableLUA",1)<> 0 Then MsgBox "User Access Control (UAC) is enabled on this computer." & _ vbCrLf & vbCrLf & "UAC must be disabled in order to edit " & _ "the registry and restart the service for the WebDAV Redirector. " & _ "Please disable UAC before running this application again. " & _ "This application will now exit.", _ vbCritical, "User Access Control" Self.close End If End Sub ' ---------------------------------------- ' Exit the application. ' ---------------------------------------- Sub ExitApplication() If blnHasChanges = False Then If MsgBox("Are you sure you want to exit?", _ vbQuestion Or vbYesNo Or vbDefaultButton2, _ "Exit Application") = vbNo Then Exit Sub End If Else Dim intRetVal intRetVal = MsgBox("You have unsaved changes. " & _ "Do you want to save them before you exit?", _ vbQuestion Or vbYesNoCancel Or vbDefaultButton1, _ "Exit Application") If intRetVal = vbYes Then Call SetValues() ElseIf intRetVal = vbCancel Then Exit Sub End If End If Self.close End Sub ' ---------------------------------------- ' Flag the application as having changes. ' ---------------------------------------- Sub FlagChanges() blnHasChanges = True End Sub ' ---------------------------------------- ' Retrieve the settings from the registry. ' ---------------------------------------- Sub GetValues() On Error Resume Next Dim tmpCount,tmpArray,tmpString ' Get the radio button values Call SetRadioValue(Document.all.BasicAuthLevel, _ GetRegistryDWORD(strWebClientKeyPath, _ "BasicAuthLevel",1)) Call SetRadioValue(Document.all.SupportLocking, _ GetRegistryDWORD(strWebClientKeyPath, _ "SupportLocking",1)) ' Get the text box values Document.all.InternetServerTimeoutInSec.Value = _ GetRegistryDWORD(strWebClientKeyPath, _ "InternetServerTimeoutInSec",30) Document.all.FileAttributesLimitInBytes.Value = _ GetRegistryDWORD(strWebClientKeyPath, _ "FileAttributesLimitInBytes",1000000) Document.all.FileSizeLimitInBytes.Value = _ GetRegistryDWORD(strWebClientKeyPath, _ "FileSizeLimitInBytes",50000000) Document.all.LocalServerTimeoutInSec.Value = _ GetRegistryDWORD(strWebClientKeyPath, _ "LocalServerTimeoutInSec",15) Document.all.SendReceiveTimeoutInSec.Value = _ GetRegistryDWORD(strWebClientKeyPath, _ "SendReceiveTimeoutInSec",60) Document.all.ServerNotFoundCacheLifeTimeInSec.Value = _ GetRegistryDWORD(strWebClientKeyPath, _ "ServerNotFoundCacheLifeTimeInSec",60) ' Get the text area values tmpArray = GetRegistryMULTISZ( _ strWebClientKeyPath,"AuthForwardServerList") For tmpCount = 0 To UBound(tmpArray) tmpString = tmpString & tmpArray(tmpCount) & vbTab Next If Len(tmpString)>0 Then Document.all.AuthForwardServerList.Value = _ Replace(Left(tmpString,Len(tmpString)-1),vbTab,vbCrLf) End If blnHasChanges = False End Sub ' ---------------------------------------- ' Save the settings in the registry. ' ---------------------------------------- Sub SetValues() On Error Resume Next ' Set the radio button values Call SetRegistryDWORD( _ strWebClientKeyPath, _ "BasicAuthLevel", _ GetRadioValue(Document.all.BasicAuthLevel)) Call SetRegistryDWORD( _ strWebClientKeyPath, _ "SupportLocking", _ GetRadioValue(Document.all.SupportLocking)) ' Set the text box values Call SetRegistryDWORD( _ strWebClientKeyPath, _ "InternetServerTimeoutInSec", _ Document.all.InternetServerTimeoutInSec.Value) Call SetRegistryDWORD( _ strWebClientKeyPath, _ "FileAttributesLimitInBytes", _ Document.all.FileAttributesLimitInBytes.Value) Call SetRegistryDWORD( _ strWebClientKeyPath, _ "FileSizeLimitInBytes", _ Document.all.FileSizeLimitInBytes.Value) Call SetRegistryDWORD( _ strWebClientKeyPath, _ "LocalServerTimeoutInSec", _ Document.all.LocalServerTimeoutInSec.Value) Call SetRegistryDWORD( _ strWebClientKeyPath, _ "SendReceiveTimeoutInSec", _ Document.all.SendReceiveTimeoutInSec.Value) Call SetRegistryDWORD( _ strWebClientKeyPath, _ "ServerNotFoundCacheLifeTimeInSec", _ Document.all.ServerNotFoundCacheLifeTimeInSec.Value) ' Set the text area values Call SetRegistryMULTISZ( _ strWebClientKeyPath, _ "AuthForwardServerList", _ Split(Document.all.AuthForwardServerList.Value,vbCrLf)) ' Prompt to restart the WebClient service If MsgBox("Do you want to restart the WebDAV Redirector " & _ "service so your settings will take effect?", _ vbQuestion Or vbYesNo Or vbDefaultButton2, _ "Restart WebDAV Redirector") = vbYes Then ' Restart the WebClient service. Call RestartWebClient() End If Call GetValues() End Sub ' ---------------------------------------- ' Start the WebClient service. ' ---------------------------------------- Sub RestartWebClient() On Error Resume Next Dim objWMIService,colServices,objService Document.All.TheBody.ClassName = "hide" Set objWMIService = GetObject( _ "winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") Set colServices = objWMIService.ExecQuery( _ "Select * from Win32_Service Where Name='WebClient'") For Each objService in colServices objService.StopService() objService.StartService() Next Document.All.TheBody.ClassName = "show" End Sub ' ---------------------------------------- ' Retrieve a DWORD value from the registry. ' ---------------------------------------- Function GetRegistryDWORD( _ ByVal tmpKeyPath, _ ByVal tmpValueName, _ ByVal tmpDefaultValue) On Error Resume Next Dim tmpDwordValue If objRegistry.GetDWORDValue( _ HKEY_LOCAL_MACHINE, _ tmpKeyPath, _ tmpValueName, _ tmpDwordValue)=0 Then GetRegistryDWORD = CLng(tmpDwordValue) Else GetRegistryDWORD = CLng(tmpDefaultValue) End If End Function ' ---------------------------------------- ' Set a DWORD value in the registry. ' ---------------------------------------- Sub SetRegistryDWORD( _ ByVal tmpKeyPath, _ ByVal tmpValueName, _ ByVal tmpDwordValue) On Error Resume Next Call objRegistry.SetDWORDValue( _ HKEY_LOCAL_MACHINE, _ tmpKeyPath, _ tmpValueName, _ CLng(tmpDwordValue)) End Sub ' ---------------------------------------- ' Retrieve a MULTISZ value from the registry. ' ---------------------------------------- Function GetRegistryMULTISZ( _ ByVal tmpKeyPath, _ ByVal tmpValueName) On Error Resume Next Dim tmpMultiSzValue If objRegistry.GetMultiStringValue( _ HKEY_LOCAL_MACHINE, _ tmpKeyPath, _ tmpValueName, _ tmpMultiSzValue)=0 Then GetRegistryMULTISZ = tmpMultiSzValue Else GetRegistryMULTISZ = Array() End If End Function ' ---------------------------------------- ' Set a MULTISZ value in the registry. ' ---------------------------------------- Sub SetRegistryMULTISZ( _ ByVal tmpKeyPath, _ ByVal tmpValueName, _ ByVal tmpMultiSzValue) On Error Resume Next Call objRegistry.SetMultiStringValue( _ HKEY_LOCAL_MACHINE, _ tmpKeyPath, _ tmpValueName, _ tmpMultiSzValue) End Sub ' ---------------------------------------- ' Retrieve the value of a radio button group. ' ---------------------------------------- Function GetRadioValue(ByVal tmpRadio) On Error Resume Next Dim tmpCount For tmpCount = 0 To (tmpRadio.Length-1) If tmpRadio(tmpCount).Checked Then GetRadioValue = CLng(tmpRadio(tmpCount).Value) Exit For End If Next End Function ' ---------------------------------------- ' Set the value for a radio button group. ' ---------------------------------------- Sub SetRadioValue(ByVal tmpRadio, ByVal tmpValue) On Error Resume Next Dim tmpCount For tmpCount = 0 To (tmpRadio.Length-1) If CLng(tmpRadio(tmpCount).Value) = CLng(tmpValue) Then tmpRadio(tmpCount).Checked = True Exit For End If Next End Sub ' ---------------------------------------- ' ' ---------------------------------------- Sub Validate(tmpField) Dim tmpRegEx, tmpMatches Set tmpRegEx = New RegExp tmpRegEx.Pattern = "[0-9]" tmpRegEx.IgnoreCase = True tmpRegEx.Global = True Set tmpMatches = tmpRegEx.Execute(tmpField.Value) If tmpMatches.Count = Len(CStr(tmpField.Value)) Then If CDbl(tmpField.Value) => 0 And _ CDbl(tmpField.Value) =< 4294967295 Then Exit Sub End If End If MsgBox "Please enter a whole number between 0 and 4294967295.", _ vbCritical, "Validation Error" tmpField.Focus End Sub ' ---------------------------------------- ' ' ---------------------------------------- Sub BasicAuthWarning() MsgBox "WARNING:" & vbCrLf & vbCrLf & _ "Using Basic Authentication over non-SSL connections can cause " & _ "serious security issues. Usernames and passwords are transmitted " & _ "in clear text, therefore the use of Basic Authentication with " & _ "WebDAV is disabled by default for non-SSL connections. That " & _ "being said, this setting can override the default behavior for " & _ "Basic Authentication, but it is strongly discouraged.", _ vbCritical, "Basic Authentication Warning" End Sub ' ---------------------------------------- ' End of main code section. ' ---------------------------------------- </script> <style> body { color:#000000; background-color:#cccccc; font-family:'Segoe UI',Tahoma,Verdana,Arial; font-size:9pt; } fieldset { padding:10px; width:640px; } .button { width:150px; } .textbox { width:200px; height:22px; text-align:right; } .textarea { width:300px; height:50px; text-align:left; } .radio { margin-left:-5px; margin-top: -2px; } .hide { display:none; } .show { display:block; } select { width:300px; text-align:left; } table { border-collapse:collapse; empty-cells:hide; } h1 { font-size:14pt; } th { font-size:9pt; text-align:left; vertical-align:top; padding:2px; } td { font-size:9pt; text-align:left; vertical-align:top; padding:2px; } big { font-size:11pt; } small { font-size:8pt; } </style> </head> <body id="TheBody" class="hide"> <h1 align="center" id="TheTitle" style="margin-bottom:-20px;">WebDAV Redirector Settings</h1> <div align="center"> <p style="margin-bottom:-20px;"><i><small><b>Note</b>: See <a target="_blank" href="https://docs.microsoft.com/iis/publish/using-webdav/using-the-webdav-redirector/">Using the WebDAV Redirector</a> for additional details.</small></i></p> <form> <center> <table border="0" cellpadding="2" cellspacing="2" style="width:600px;"> <tr> <td style="width:600px;text-align:left"><fieldset title="Security Settings"> <legend> <b>Security Settings</b> </legend> These values affect the security behavior for the WebDAV Redirector.<br> <table style="width:600px;"> <tr title="Specifies whether the WebDAV Redirector can use Basic Authentication to communicate with a server."> <td style="width:300px"> <table border="0"> <tr> <td style="width:300px"><b>Basic Authentication Level</b></td> </tr> <tr> <td style="width:300px;"><span style="width:280px;padding-left:20px;"><small><i><b>Note</b>: Using basic authentication can cause <u>serious security issues</u> as the username and password are transmitted in clear text, therefore the use of basic authentication over WebDAV is disabled by default unless the connection is using SSL.</i></small></span></td> </tr> </table> </td> <td style="width:300px"> <table style="width:300px"> <tr> <td style="width:020px"><input class="radio" type="radio" value="0" name="BasicAuthLevel" onchange="VBScript:FlagChanges()" id="BasicAuthLevel0"></td> <td style="width:280px"><label for="BasicAuthLevel0">Basic Authentication is disabled</label></td> </tr> <tr> <td style="width:020px"><input class="radio" type="radio" value="1" checked name="BasicAuthLevel" onchange="VBScript:FlagChanges()" id="BasicAuthLevel1"></td> <td style="width:280px"><label for="BasicAuthLevel1">Basic Authentication is enabled for SSL web sites only</label></td> </tr> <tr> <td style="width:020px"><input class="radio" type="radio" value="2" name="BasicAuthLevel" onchange="VBScript:FlagChanges()" id="BasicAuthLevel2" onClick="VBScript:BasicAuthWarning()"></td> <td style="width:280px"><label for="BasicAuthLevel2">Basic Authentication is enabled for SSL and non-SSL web sites</label></td> </tr> </table> </td> </tr> <tr title="Specifies a list of local URLs for forwarding credentials that bypasses any proxy settings. (Note: This requires Windows Vista SP1 or later.)"> <td style="width:300px"> <table border="0"> <tr> <td style="width:300px"><b>Authentication Forwarding Server List</b></td> </tr> <tr> <td style="width:300px;"><span style="width:280px;padding-left:20px;"><small><i><b>Note</b>: Include one server name per line.</i></small></span></td> </tr> </table> </td> <td style="width:300px"><textarea class="textarea" name="AuthForwardServerList" onchange="VBScript:FlagChanges()"></textarea></td> </tr> <tr title="Specifies whether the WebDAV Redirector supports locking."> <td style="width:300px"><b>Support for WebDAV Locking</b></td> <td style="width:300px"> <table style="width:300px"> <tr> <td style="width:020px"><input class="radio" type="radio" value="1" checked name="SupportLocking" onchange="VBScript:FlagChanges()" id="SupportLocking1"></td> <td style="width:280px"><label for="SupportLocking1">Enable Lock Support</label></td> </tr> <tr> <td style="width:020px"><input class="radio" type="radio" value="0" name="SupportLocking" onchange="VBScript:FlagChanges()" id="SupportLocking0"></td> <td style="width:280px"><label for="SupportLocking0">Disable Lock Support</label></td> </tr> </table> </td> </tr> </table> </fieldset> </td> </tr> <tr> <td style="width:600px;text-align:left"><fieldset title="Time-outs"> <legend> <b>Time-outs and Maximum Sizes</b> </legend> These values affect the behavior for WebDAV Client/Server operations.<br> <table border="0" style="width:600px;"> <tr title="Specifies the connection time-out for the WebDAV Redirector uses when communicating with non-local WebDAV servers."> <td style="width:300px"><b>Internet Server Time-out</b> <small>(In Seconds)</small></td> <td style="width:300px"><input class="textbox" type="text" name="InternetServerTimeoutInSec" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="30"></td> </tr> <tr title="Specifies the connection time-out for the WebDAV Redirector uses when communicating with a local WebDAV server."> <td style="width:300px"><b>Local Server Time-out</b> <small>(In Seconds)</small></td> <td style="width:300px"><input class="textbox" type="text" name="LocalServerTimeoutInSec" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="15"></td> </tr> <tr title="Specifies the time-out in seconds that the WebDAV Redirector uses after issuing a request."> <td style="width:300px"><b>Send/Receive Time-out</b> <small>(In Seconds)</small></td> <td style="width:300px"><input class="textbox" type="text" name="SendReceiveTimeoutInSec" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="60"></td> </tr> <tr title="Specifies the period of time that a server is cached as non-WebDAV by the WebDAV Redirector. If a server is found in this list, a fail is returned immediately without attempting to contact the server."> <td style="width:300px"><b>Server Not Found Cache Time-out</b> <small>(In Seconds)</small></td> <td style="width:300px"><input class="textbox" type="text" name="ServerNotFoundCacheLifeTimeInSec" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="60"></td> </tr> <tr title="Specifies the maximum size in bytes that the WebDAV Redirector allows for file transfers."> <td style="width:300px"><b>Maximum File Size</b> <small>(In Bytes)</small></td> <td style="width:300px"><input class="textbox" type="text" name="FileSizeLimitInBytes" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="50000000"></td> </tr> <tr title="Specifies the maximum size that is allowed by the WebDAV Redirector for all properties on a specific collection."> <td style="width:300px"><b>Maximum Attributes Size</b> <small>(In Bytes)</small></td> <td style="width:300px"><input class="textbox" type="text" name="FileAttributesLimitInBytes" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="1000000"></td> </tr> </table> </fieldset> </td> </tr> <tr> <td style="text-align:center"> <table border="0"> <tr> <td style="text-align:center"><input class="button" type="button" value="Apply Settings" onclick="VBScript:SetValues()"> <td style="text-align:center"><input class="button" type="button" value="Exit Application" onclick="VBScript:ExitApplication()"> </tr> </table> </td> </tr> </table> </center> </form> </div> </body> </html>
You will need to run this HTML Application as an administrator in order to save the settings and restart the Windows WebDAV Redirector. (Which is listed as the "WebClient" service in your Administrative Tools.)
This HTML Application performs basic validation for the numeric fields, and it prevents you from exiting the application when you have unsaved changes, but apart from that there's not much functionality other than setting and retrieving the registry values. How else can you get away with posting an application in a blog with only 500 lines of code and no compilation required? ;-]
Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/
01 June 2011 • by Bob • IIS, Scripting
I had a question recently where someone was trying to add <clear />
or <remove />
elements to a collection in their IIS 7 configuration settings. With that in mind, for today's blog I thought that I would discuss a couple of ways to add <clear />
and <remove />
elements by using two specific scripting methods: AppCmd and VBScript.
It should be noted that you can also use JavaScript or PowerShell, but I'm not covering those because the syntax for those is available elsewhere. (JavaScript syntax is available in the Configuration Editor in IIS Manager, and the PowerShell syntax is available through the Web Server (IIS) Administration Cmdlet Reference.) You can also use Managed-Code, and the syntax for that is also available in the Configuration Editor in IIS Manager; but compiled code isn't scripting, is it? :-)
Here's the scenario, IIS makes it possible to modify the contents of an inherited collection in two ways:
<configuration> <system.webServer> <defaultDocument enabled="true"> <files> <clear /> </files> </defaultDocument> </system.webServer> </configuration>
<configuration> <system.webServer> <defaultDocument enabled="true"> <files> <remove value="index.html" /> </files> </defaultDocument> </system.webServer> </configuration>
With that in mind, let's look at scripting those settings.
AppCmd.exe is a great utility that ships with IIS 7, which allows editing the configuration settings for IIS from a command line. This also allows you to create batch scripts that automate large numbers of configuration changes. For example, the following batch file enables ASP session state, sets the maximum number of ASP sessions to 1000, and then sets the session time-out to 10 minutes for the Default Web Site:
appcmd.exe set config "Default Web Site" -section:system.webServer/asp /session.allowSessionState:"True" /commit:apphost
appcmd.exe set config "Default Web Site" -section:system.webServer/asp /session.max:"1000" /commit:apphost
appcmd.exe set config "Default Web Site" -section:system.webServer/asp
I'm a big fan of IIS 7's AppCmd.exe, but unfortunately it has two rather large limitations:
These limitations have caused me some grief from time to time, because I often want to script the modification of collections, and I would love to remove items or clear a collection.
<clear />
element using AppCmd:Although it's kind of a hack, there is a way to force AppCmd.exe to add a <clear />
element.
Here's what you need to do in order to clear the list of default documents for the Default Web Site:
<?xml version="1.0" encoding="UTF-8"?> <appcmd> <CONFIG CONFIG.SECTION="system.webServer/defaultDocument" path="MACHINE/WEBROOT/APPHOST" overrideMode="Allow" locked="false"> <system.webServer-defaultDocument enabled="true"> <files> <clear /> </files> </system.webServer-defaultDocument> </CONFIG> </appcmd>
appcmd.exe set config /in "Default Web Site" < CLEAR.xml
Unfortunately this technique does not work for <remove />
elements. :-( But that being said, you can add a <remove />
element through VBScript; for more information, see the Using VBScript section.
Fortunately, VBScript doesn't have AppCmd.exe's limitations, so you can add both <clear />
and <remove />
elements.
<clear />
element in VBScript:The following steps will clear the list of default documents for the Default Web Site:
Set adminManager = WScript.CreateObject("Microsoft.ApplicationHost.WritableAdminManager") adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST/Default Web Site" Set defaultDocumentSection = adminManager.GetAdminSection("system.webServer/defaultDocument", _ "MACHINE/WEBROOT/APPHOST/Default Web Site") Set filesCollection = defaultDocumentSection.ChildElements.Item("files").Collection filesCollection.Clear adminManager.CommitChanges
<remove />
element in VBScript:The following steps will remove a single item from the list of default documents for the Default Web Site:
Set adminManager = WScript.CreateObject("Microsoft.ApplicationHost.WritableAdminManager") adminManager.CommitPath = "MACHINE/WEBROOT/APPHOST/Default Web Site" Set defaultDocumentSection = adminManager.GetAdminSection("system.webServer/defaultDocument", _ "MACHINE/WEBROOT/APPHOST/Default Web Site") Set filesCollection = defaultDocumentSection.ChildElements.Item("files").Collection addElementPos = FindElement(filesCollection, "add", Array("value", "index.html")) If (addElementPos = -1) Then WScript.Echo "Element not found!" WScript.Quit End If filesCollection.DeleteElement(addElementPos) adminManager.CommitChanges Function FindElement(collection, elementTagName, valuesToMatch) For i = 0 To CInt(collection.Count) - 1 Set element = collection.Item(i) If element.Name = elementTagName Then matches = True For iVal = 0 To UBound(valuesToMatch) Step 2 Set property = element.GetPropertyByName(valuesToMatch(iVal)) value = property.Value If Not IsNull(value) Then value = CStr(value) End If If Not value = CStr(valuesToMatch(iVal + 1)) Then matches = False Exit For End If Next If matches Then Exit For End If End If Next If matches Then FindElement = i Else FindElement = -1 End If End Function
For more information about scripting and IIS configuration settings, see the following:
Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/
06 July 2007 • by Bob • FTP, Scripting
A few weeks ago my friend Jaroslav posted a blog entry about viewing the current FTP7 sessions using Javascript, and I followed that up with a blog post about viewing the current FTP7 sessions using C#.
This blog entry follows up on those postings by showing you how to view the current FTP7 sessions using VBScript. To do so, copy the following VBScript code to Windows Notepad and save the file as "ftp_sessions.vbs" on a computer running Windows Server 2008 with the new FTP7 server installed:
Option Explicit Dim objAdminManager, objSiteCollection, objFtpSiteElement Dim objSite, objFtpSession, objFtpSessions, objFtpProperty Dim intSite, intFtpSession, intFtpProperty Dim intSiteCount, intFtpSessionCount, intFtpPropertyCount Set objAdminManager = WScript.CreateObject("Microsoft.ApplicationHost.AdminManager") ' get the collection of sites Set objSiteCollection = objAdminManager.GetAdminSection( _ "system.applicationHost/sites", "MACHINE/WEBROOT/APPHOST" ) intSiteCount = CInt(objSiteCollection.Collection.Count) WScript.Echo String(40,"*") WScript.Echo "Site count: " & intSiteCount WScript.Echo String(40,"*") ' loop through the sites collection For intSite = 0 To intSiteCount-1 ' get a site Set objSite = objSiteCollection.Collection.Item(intSite) ' get the FTP section Set objFtpSiteElement = objSite.ChildElements.Item("ftpServer") ' get the sessions collection Set objFtpSessions = objFtpSiteElement.ChildElements.Item("sessions") intFtpSessionCount = CInt(objFtpSessions.Collection.Count) WScript.Echo String(40,"=") WScript.Echo "FTP sessions for " & _ objSite.Properties.Item("name").Value & _ ": " & intFtpSessionCount WScript.Echo String(40,"=") ' loop through the sessions For intFtpSession = 0 To intFtpSessionCount - 1 Set objFtpSession = objFtpSessions.Collection.Item(intFtpSession) intFtpPropertyCount = CInt(objFtpSession.Properties.Count) ' loop through each session's properties For intFtpProperty = 0 To intFtpPropertyCount - 1 Set objFtpProperty = objFtpSession.Properties.Item(intFtpProperty) WScript.Echo CStr(objFtpProperty.Name) & ": " & CStr(objFtpProperty.Value) Next WScript.Echo String(40,"-") Next Next
To make sure that you don't see any message box pop-ups, run the script from the command-line using the following syntax:
cscript.exe ftp_sessions.vbs
That's about it for this post - have fun!
21 February 2006 • by Bob • IIS, Scripting
One of my servers has a large number of individual web sites on it, and each of these web sites has several server bindings for different IP addresses, Port Assignments, and Host Headers. As I continue to add more web sites on the server, it becomes increasingly difficult to keep track of all the details using the IIS user interface.
With that in mind, I wrote the following ADSI script which creates a text file that contains an itemized list of all server bindings on a server.
Option Explicit On Error Resume Next Dim objBaseNode, objChildNode Dim objBindings, intBindings Dim objFSO, objFile, strOutput ' get a base object Set objBaseNode = GetObject("IIS://LOCALHOST/W3SVC") Set objFSO = WScript.CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.CreateTextFile("ServerBindings.txt") ' check if if have an error ... If (Err.Number <> 0) Then ' ... and output the error. strOutput = "Error " & Hex(Err.Number) & "(" strOutput = strOutput & Err.Description & ") occurred." ' ... otherwise, continue processing. Else ' loop through the child nodes For Each objChildNode In objBaseNode ' is this node for a web site? If objChildNode.class = "IIsWebServer" Then ' get the name of the node strOutput = strOutput & "LM/W3SVC/" & _ objChildNode.Name ' get the server comment strOutput = strOutput & " (" & _ objChildNode.ServerComment & ")" & vbCrLf ' get the bindings objBindings = objChildNode.ServerBindings ' loop through the bindings For intBindings = 0 To UBound(objBindings) strOutput = strOutput & vbTab & _ Chr(34) & objBindings(intBindings) & _ Chr(34) & vbCrLf Next End If ' try not to be a CPU hog Wscript.Sleep 10 Next End If objFile.Write strOutput objFile.Close Set objBaseNode = Nothing Set objFSO = Nothing
Hope this helps!
11 January 2006 • by Bob • IIS, Scripting
(Note: I had originally posted this information on a blog that I kept on http://weblogs.asp.net, but it makes more sense to post it here. [:)] )
Like many web programmers, I host several hobby web sites for fun. (They make a wonderful test bed for new code. ;-] )
And like many computer enthusiasts, I sometimes change my ISP for one reason or another. If you are hosting web sites in a similar situation, I'm sure that you can identify the pain of trying to manually update each old IP address to your new IP address. This situation can be made even more difficult when any number of your web sites are using several host headers because the user interface for the IIS administration tool only lists the first host header. This means that you have to manually view the properties for every site just to locate the IP addresses that you are required to change.
Well, I'm a big believer in replacing any repetitive task with code when it is possible, and a recent change of ISP provided just the right level of inspiration for me to write a simple Active Directory Service Interfaces (ADSI) script that locates IP addresses that have to be changed and updates them to their new values.
To use the example script, I would first suggest that you make a backup copy of your metabase. (The script works fine, but it is always better to have a backup. ;-] ) As soon as your metabase has been backed up, copy the example script into notepad or some other text editor, update the old and new IP addresses that are defined as constants, and then run the script.
Option Explicit On Error Resume Next Dim objIIS Dim objSite Dim varBindings Dim intBindings Dim blnChanged Const strOldIP = "10.0.0.1" Const strNewIP = "192.168.0.1" Set objIIS = GetObject("IIS://LOCALHOST/W3SVC") If (Err <> 0) Then WScript.Echo "Error " & Hex(Err.Number) & "(" & _ Err.Description & ") occurred." WScript.Quit Else For Each objSite In objIIS blnChanged = False If objSite.class = "IIsWebServer" Then varBindings = objSite.ServerBindings For intBindings = 0 To UBound(varBindings) If InStr(varBindings(intBindings),strOldIP) Then blnChanged = True varBindings(intBindings) = Replace(varBindings(intBindings),strOldIP,strNewIP) End If Next End If If blnChanged = True Then objSite.ServerBindings = varBindings objSite.Setinfo End If Next End If MsgBox "Finished!"
That's all for now. Happy coding!
Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/