GenSCode updated to help use AhAdmin in C#, JavaScript

As promised in my previous post, I updated genscode tool to provide intellisense for AhAdmin code in C# and JavaScript. GenSCode now accepts an optional codetype switch which can be AhAJavaScript to help use AhAdmin in JavaScript, AhACSharp to help use AhAdmin in C# or MWACSharp (which is the default value) to ease using MWA in C#. Click here to download the updated tool.

Usage: genscode [/codetype:<value>] <schemaFilePath> [<sectionName>]
       /codeType switch is optional. Value can be AHAJavaScript | AHACSharp | MWACSharp [Default]
       <sectionName> is optional. If omitted, code is generated for all sections defined in schema file

With this you now have AppcmdUI to help write appcmd commands, genscode to help use MWA and also AhAdmin in your code. Last thing I want to do is make it easy to use IIS WMI provider. Lets see what can I come up with.

-Kanwal-

Enabling intellisense for AhAdmin code in scripts

MWA (Microsoft.Web.Administration) is pretty popular among developers who wish to read/write IIS configuration because of ease of writing managed code (which enables its use in powershell as well) and also because it provides a more intuitive wrapper over the IIS configuration system which is easier to work with. MWA has concept of application pool collection, sites, bindings etc while IIS configuration system only understands sections, elements, properties, location paths etc and not application pools, virtual directories which makes life of developers writing AhAdmin code very difficult. If you disagree, try adding a binding using MWA and then using AhAdmin. Presence of tools like genscode makes using MWA even more easier. Even though its painful to use AhAdmin interfaces, you don't get intellisense in scripts when working with COM components and you are required to refer configuration schema all the time, people do write AhAdmin code for variety of reasons. One reason is because people want to access IIS configuration in VBScript/JavaScript. Another reason is because people want to write code which works on server core as well (appcmd and WMI are alternatives as well). I have been thinking of ways to make life of AhAdmin developers easier for sometime. Below is how I could write a JavaScript class for system.webServer/httpErrors section which enabled intellisense and also captured section schema in the form of function calls.

//
// Emulate enum in JavaScript using anonymous types
//
var EnumHttpErrorsErrorMode =
{
    DetailedLocalOnly : 0,
    Custom : 1,
    Detailed : 2
};

//
// You pass a IAppHostElement for system.webServer/httpErrors section
// to this class which then enables intellisense for most operations with the section
//
function HttpErrorsSection(sectionElement)
{
    this.httpErrorsSectionName      = "system.webServer/httpErrors";
    this.httpErrorsSectionElement   = sectionElement;
   
    this.get_ErrorMode = function()
    {
        return this.httpErrorsSectionElement.GetPropertyByName("errorMode").Value;
    }
   
    this.set_ErrorMode = function(newValue)
    {
        this.httpErrorsSectionElement.GetPropertyByName("errorMode").Value = newValue;
    }

    /*
    // Code for other section level properties under system.webServer/httpErrors
    // Child elements if any will look as written below
    this.get_ElementName = function()
    {
        var elementNameElementObject = new ElementNameElement(this.httpErrorsSectionElement.GetElementByName("elementName"));
        return elementNameElementObject;
    }
    */
   
  
this.get_Errors = function()
    {
        var errorsCollectionObject = new HttpErrorsCollection(this.httpErrorsSectionElement.Collection);
        return errorsCollectionObject;
    }
}

//
// HttpErrorsCollectionElement class representing one error element
//
function HttpErrorsCollectionElement(errorElement)
{
    this.httpErrorElement = errorElement;
   
    this.get_StatusCode = function()
    {
        return this.httpErrorElement.GetPropertyByName("statusCode").Value;
    }
   
    this.set_StatusCode = function(newValue)
    {
        this.httpErrorElement.GetPropertyByName("statusCode").Value = newValue;
    }

    //
    // get/set methods for other error element properties
    //

}

var
EnumHttpErrorsResponseMode =
{
    File : 0,
    ExecuteURL : 1,
    Redirect : 2
};

//
// HttpErrorsCollection class which lets you Add/Delete error elements
//
function HttpErrorsCollection(collection)
{
    this.httpErrorsCollection = collection;    
    
    //
    // Create array for collection elements and fill it.
    // This code acts as JavaScript class constructor
    // 
    this._collectionElements = newArray();
    for(var i = 0; i < this.httpErrorsCollection.Count; i++)
    {
        this._collectionElements[i] = new HttpErrorsCollectionElement(collection.Item(i));
    }

    this.get_Count = function()
    {
        return this._collectionElements.length;
    }
   
    this.get_IndexOfElement = function(statusCode, subStatusCode)
    {
        for(var i = 0; i < this._collectionElements.length; i++)
        {
            var currentElement = this._collectionElements[i];
            if(currentElement.get_StatusCode() == statusCode &&
               currentElement.get_SubStatusCode() == subStatusCode)
           {
               return i;
           }
        }
       
        return -1;
    }
   
    this.get_Item = function(statusCode, subStatusCode)
    {
        var indexOfElement = this.get_IndexOfElement(statusCode, subStatusCode);
        if(indexOfElement == -1)
        {
            return null;
        } 

        //
        // Creating a new object and not returning _collectionElements object
        // to make it easier for visual studio to determine the type of return value
        // 
        var errorElement = new HttpErrorsCollectionElement(this.httpErrorsCollection.Item(indexOfElement));
        return errorElement;
    }
   
    this.Add = function(statusCode, subStatusCode, prefixLanguageFilePath, path, responseMode)
    {
        var newElement = new HttpErrorsCollectionElement(this.httpErrorsCollection.CreateNewElement("error"));
       
        newElement.set_StatusCode(statusCode);
        newElement.set_SubStatusCode(subStatusCode);
        newElement.set_PrefixLanguageFilePath(prefixLanguageFilePath);
        newElement.set_Path(path);
        newElement.set_ResponseMode(responseMode);

        this.httpErrorsCollection.AddElement(newElement.httpErrorElement, -1);
        return newElement;
    }
   
    this.Remove = function(statusCode, subStatusCode)
    {
        var indexOfElement = this.get_IndexOfElement(statusCode, subStatusCode);
        if(indexOfElement != -1)
        {
            this.httpErrorsCollection.DeleteElement(indexOfElement);
        }
    }
}

You can write similar code for enabling intellisense in VBScript as well. Once you have code representing a section as shown above, you can then use VS2008 (which provides nice intellisense for scripts) to write AhAdmin code easily. I tried going from section to child elements to collections and to collection elements and VS2008 provided intellisense for all returned types. Sadly you cannot move above code to a separate file and use eval. Visual studio doesn't evaluate eval statements and won't provide intellisense if you do that. There might be other JavaScript editors which will let you that. Also you need to keep this code above the code where you use the types defined above so that anonymous types are created before you use them. Below is how you can write AhAdmin code now.

try
{
    var serverManager = new ActiveXObject("Microsoft.ApplicationHost.WritableAdminManager");
    var sectionElement = serverManager.GetAdminSection(
        "system.webServer/httpErrors",
        "MACHINE/WEBROOT/APPHOST");
   
    var section = new HttpErrorsSection(sectionElement);
    section.set_ErrorMode(EnumHttpErrorsErrorMode.Detailed);
   
    var errorsCollection = section.get_Errors();
    errorsCollection.Add(
        "999",
        "-1",
        "",
        "C:\\Inetpub\\custerr\\en-US\\999.htm",
        EnumHttpErrorsResponseMode.File);
   
    //
    // Getting a particular error element will look like following
    //
    //var errorElement = errorsCollection.get_Item("999", "-1");
    //if(errorElement != null) {}
    //
   
  
serverManager.CommitChanges();
}
catch(e)
{
    WScript.Echo(e.description);
    WScript.Echo(e.number);
}

As you can probably guess, my next task is to extend genscode to generate JavaScript, C# code to enable intellisense for AhAdmin developers. I will post the updated tool soon.

Hope this helps.
– Kanwal –