Wednesday, April 04, 2007 3:08 PM bart

Extending IIS 7.0 - Part 4: Creating a custom module for IIS Manager


Note: This post is part of the follow-up for the Developer & IT Pro Days 2007 session entitled "Internet Information Services (IIS) 7.0 - End-to-End Overview of Microsoft's New Web Application Server". Attendees will be able to download the slides and will get the video recording of the entire session as well. Using these posts, attendees should be capable of reproducing the entire demo part of the session. Furthermore, others who didn't attend the session will be able to get a better idea of what IIS 7.0 provides by following the demo steps outlined in these posts. In order to implement the demos, one should have IIS 7.0 installed on Windows Vista (or Windows "Longhorn" Server).



In the previous part of this series, we focused on IIS 7.0's configuration system by extending our image handler sample with web.config-based configuration. Today we'll take it one step further by making the configuration of our handle available through IIS Manager by means of a custom module. As you'll see, creating custom modules for IIS Manager is pretty straightforward, thanks to the managed code support.


IIS 7.0 Manager

We've been faced with the IIS Manager of IIS 7.0 quite a lot already. Just press WIN+R, enter inetmgr, click OK and accept the UAC dialog to refresh your mind:

Our goal is to add an "icon" to the middle area of the screen, in order to make the settings for the image copyright handler accessible directly. Such an addition to IIS Manager is called a module, maybe a bit of a misnomer due to the conflicting name with the module-handler stuff we discussed before.


A custom module

Right, here we are with our image copyright handler and support for configuration through web.config (see the previous part of this series). Let's extend it with an IIS Manager module:

  1. In Visual Studio 2005 - still running as administrator - add a new C# class library project named PictureHandlerConfiguration to the solution by right-clicking the solution root node in Solution Explorer and choosing Add, New Project...
  2. Strong-name the assembly generated by the class library project by opening the Properties node of the PictureHandlerConfiguration project

    and going to the Signing tab. Mark the checkbox "Sign the assembly" and choose new from the "Choose a strong name key file:" dropdown box:

    In the dialog that appears, give the key file a name "key.snk" and disable the password protection for sake of the demo:

  3. Next, add a few references to the PictureHandlerConfiguration project by right-clicking it and choosing Add Reference... Go to the .NET tab and choose System.Windows.Forms. Click OK.

    Open up the Add Reference... dialog again and go to the Browse tab, navigate to %windir%\system32\inetsrv and select both Microsoft.Web.Administration.dll and Microsoft.Web.Management.dll. Click OK.

  4. Right-click the PictureHandlerConfiguration project and choose Add, User Control... Call it ImageCopyrightConfiguration.cs and press OK:


    This control will be displayed in the IIS Manager upon completion of this demo script. It will display the settings and allow settings to be changed.
  5. Design the control as displayed below:


    The controls are the following (see numbering above):

    0. chkEnabled
    1. lblMessage  2. txtMessage
    3. lblColor    4. txtColor (ReadOnly=true)    5. btnColor
    6. btnSave   7. lblStatus

  6. Add a ColorDialog control named colorDialog to the designer surface too:

  7. Double-click the btnColor button and add the following piece of code (pretty straightforward to understand):
    private void btnColor_Click(object sender, EventArgs e) { if (txtColor.Text != "") colorDialog.Color = Color.FromName(txtColor.Text); if (colorDialog.ShowDialog() == DialogResult.OK) txtColor.Text = colorDialog.Color.Name; }

  8. Select the Class1.cs file in the Solution Explorer and rename it to PictureHandlerSettings.cs. Open it and replace the Class1 with the code below (make sure to keep the namespace):
    class PictureHandlerSettings : ConfigurationSection { public bool Enabled { get { return (bool)base["enabled"]; } set { base["enabled"] = value; } } public string Message { get { return (string)base["message"]; } set { base["message"] = value; } } public System.Drawing.Color Color { get { return System.Drawing.Color.FromName((string)base["color"]); } set { base["color"] = value.Name; } } }

    You'll need to import the Microsoft.Web.Administration namespace to have the ConfigurationSection base class in scope.

  9. Go back to the ImageCopyrightConfiguration control's code view and add the following private members to the class definition:
    private ServerManager mgr; private string siteName; private string virtualPath; private PictureHandlerSettings settings;

    Again, you'll need to import the Microsoft.Web.Administration namespace, this time for the ServerManager type.
  10. Change the constructor of the control like this:
    public ImageCopyrightConfiguration(ServerManager mgr) { this.mgr = mgr; InitializeComponent(); }

    Notice we didn't set the siteName and virtualPath yet; the reason for this will become apparent further on.
  11. Next, we'll add a ReadSettings method that uses the ServerManager instance to query IIS for the current settings:
    private void ReadSettings() { Configuration config = mgr.GetWebConfiguration(siteName, virtualPath); settings = (PictureHandlerSettings)config.GetSection( "system.webServer/imageCopyright", typeof(PictureHandlerSettings)); }

    This code is completely the same as the one we used in the previous post to get the applicable settings inside our handler implementation.
  12. Now, we need to use the settings to populate the control's UI elements with the values. We'll do this in an Initialize method that calls our ReadSettings method from step 11:
    public void Initialize(string siteName, string virtualPath) { this.siteName = siteName; this.virtualPath = virtualPath; ReadSettings(); txtColor.Text = settings.Color.Name; txtMessage.Text = settings.Message; chkEnabled.Checked = settings.Enabled; }

    This is where the siteName and virtualPath values are set. The Initialize method will be called by the module page we'll talk about further on.
  13. Finally, for what the control definition is concerned, go back to the designer and double-click the btnSave button. Add code to the event handler like this:
    private void btnSave_Click(object sender, EventArgs e) { try { settings.Color = Color.FromName(txtColor.Text); settings.Message = txtMessage.Text; settings.Enabled = chkEnabled.Checked; mgr.CommitChanges(); lblStatus.Text = "Settings saved successfully!"; lblStatus.ForeColor = Color.DarkGreen; } catch (Exception ex) { lblStatus.Text = ex.Message; lblStatus.ForeColor = Color.DarkRed; } ReadSettings(); }

    In this code, the settings object is modified by assigning the values from the UI element to the various properties. Using the ServerManager instance, changes are committed by calling CommitChanges. Finally, if everything goes right, a status message is displayed in the lblStatus label. One thing that's rather important is to call ReadSettings again, to re-read the settings from configuration. This is required because you can only commit changes of a settings object once, so you have to obtain a new settings object for subsequent changes.
  14. Now it's time to wrap the control in a module page. It's okay to think of such a page as the right-hand side portion of IIS Manager where the real functionality is displayed when opening a module:

    Such a module page is a Windows Forms control, on which we'll put our own ImageCopyrightConfiguration control this time. This approach allows us to use the User Control designer in Visual Studio 2005. To create the module page, add a new class file to the PictureHandlerConfiguration project by right-clicking the project in the Solution Explorer and choosing Add, Class... giving it a name of ModuleStuff.cs. Replace the ModuleStuff class definition by the following (make sure the namespace is kept):
    class ImageCopyrightUIPage : ModulePage { private ServerManager mgr; private ImageCopyrightConfiguration c; public ImageCopyrightUIPage() { mgr = new ServerManager(); c = new ImageCopyrightConfiguration(mgr); Controls.Add(c); } // // Override OnActivated // }

    You'll need to import the Microsoft.Web.Management.Client.Win32 namespace:

    The constructor of the ImageCopyrightUIPage class creates an instance of ServerManager (for which you need to import the Microsoft.Web.Administrator namespace again). Furthermore, it creates an instance of our control created in steps 4-13 and adds it to the Controls collection (effectively putting the control in the left top corner of the module page's surface).
  15. Next, override OnActivated in the class definition from step 14 and add a bit of code:
    protected override void OnActivated(bool initialActivation) { base.OnActivated(initialActivation); if (initialActivation) c.Initialize(Connection.ConfigurationPath.SiteName, Connection.ConfigurationPath.ApplicationPath + Connection.ConfigurationPath.FolderPath); }

    This is where we do our initialization work for the control by calling Initialize. The reason for doing the initialization over here is that only from this point on, we have access to the Connection object, which is our context object to get information about the currently selected node in the IIS Manager's tree on the left. Basically, we query the connection for the site's name as well as the complete path to the currently selected node. Notice we only perform the initialization when the module page is activated for the first time ("initialActivation").
  16. Once we have our module page definition, it's time to wrap it inside a module. A module wraps the module page (which is the module's user interface) together with information to create a shortcut ("icon") in the IIS Manager's control panel. Add the following class definition to the ModuleStuff.cs file, inside the namespace definition:
    class ImageCopyrightUI : Module { // // Override Initialize // // // Override IsPageEnabled // }

    Again you need to import a namespace, this time the Microsoft.Web.Management.Client namespace.

  17. Now, we'll override both the Initialize and IsPageEnabled methods, as displayed below:
    class ImageCopyrightUI : Module { protected override void Initialize(IServiceProvider serviceProvider, Microsoft.Web.Management.Server.ModuleInfo moduleInfo) { base.Initialize(serviceProvider, moduleInfo); IControlPanel controlPanel = (IControlPanel)GetService(typeof(IControlPanel)); controlPanel.RegisterPage(new ModulePageInfo(this, typeof(ImageCopyrightUIPage), "Image Copyright", "Configuration for the image copyright demo.")); } protected override bool IsPageEnabled(ModulePageInfo pageInfo) { Connection conn = (Connection)GetService(typeof(Connection)); ConfigurationPathType pt = conn.ConfigurationPath.PathType; return pt == ConfigurationPathType.Site || pt == ConfigurationPathType.Application; } }

    The Initialize method obtains a reference to the control panel of the IIS Manager. In here, the icons are displayed that provide access to the underlying module's functionality, represented by the module page. By using the GetService method, a service is retrieved; this allows to write very flexible code. In this case, we retrieve the control panel service, but you could also hook up your own services and retrieve a bunch of other services. An example of a custom service would be a class that allows communication to some database to import settings. The ModulePageInfo object is used to represent the "shortcut" to the underlying module page (you could have more than one module page too, each represented by one instance of ModulePageInfo), containing the caption of the icon, the descrption of the page (which will be shown in a tooltip when you hover over the icon), as well as the type. Other overloads to the ModulePageInfo constructor exist, that also take images for the icon. In our case, we didn't specify an icon, so a default one will be chosen by IIS Manager as you'll see further on.

    The IsPageEnabled method is used by IIS Manager to query the module whether or not it should make some module page (passed in through the pageInfo parameter) available. In this case we have a one-on-one mapping between the module and a module page, so we don't bother about the pageInfo parameter. In this demo, we've chosen to make the configuration module page only available when a site or application node is selected (thus not on the server root level). Notice that the Connection object is retrieved through GetService too.

    As a side-remark, modules are capable of hosting tasks as well. These tasks are displayed in the right-hand side column of the IIS Manager. We won't elaborate on this for now.
  18. Right, we have a module page and a module. Last but not least, we have arrived at the uppermost layer in the hierarchy: the module provider. Again, add it to the ModuleStuff.cs file inside the namespace scope:
    class ImageCopyrightUIModuleProvider : ModuleProvider { public override Type ServiceType { get { return null; } } public override bool SupportsScope(ManagementScope scope) { return true; } public override ModuleDefinition GetModuleDefinition(IManagementContext context) { return new ModuleDefinition(Name, typeof(ImageCopyrightUI).AssemblyQualifiedName); } }

    Again, a namespace needs to be imported, this time Microsoft.Web.Management.Server:

    This is the most "dirty" one of the three classes since it's the least visual of the three. Basically it wraps a module by means of a ModuleDefinition object that points to the fully qualified type name of the module and the assembly implementing it. Just accept this piece of plubming; it's required for IIS Manager to load the module itself (amongst a custom service if you require one).
  19. Finally, compile the class library by pressing CTRL-SHIFT-B. Next, go to the Visual Studio 2005 Command Prompt (running as Administrator) and cd into the bin\Debug folder of the PictureHandlerConfiguration project. Execute gacutil -i PictureHandlerConfiguration.dll to put the assembly in the GAC to make it available for IIS Manager:

  20. In order to make the module available for IIS Manager we need to modify the XML-based configuration of the IIS Manager. To do so, open the %windir%\system32\inetsrv\config\administration.config file in Visual Studio 2005, running as Administrator. Locate the <configuration>/<moduleProviders> section and add the following:
    <!-- !!! CHANGE PKT !!! --> <add name="ImageCopyright" type="PictureHandlerConfiguration.ImageCopyrightUIModuleProvider, PictureHandlerConfiguration, Version=, Culture=neutral, PublicKeyToken=5be231eaf2cd0956, processorArchitecture=MSIL" />

    Important! Change the PublicKeyToken value by the value of your assembly's public key token (it will be different from the one shown above since you've generated a key yourself). You can get the value either by using gacutil -l or sn -T as shown below:

    In my case, the configuration entry becomes:
    <add name="ImageCopyright" type="PictureHandlerConfiguration.ImageCopyrightUIModuleProvider, PictureHandlerConfiguration, Version=, Culture=neutral, PublicKeyToken=e549d40da8d9d26f, processorArchitecture=MSIL" />

    Finally, locate the <configuration>/<location path=".">/<modules> section and add the module in there as follows:
    <add name="ImageCopyright" />

  21. Time to test! Make sure the configuration file from step 20 is saved properly and open up IIS Manager. Go to Web Sites\Default Web Site\devdays and observe the Image Copyright icon being available on the right:


    As you can see, the icon's caption as well as the description are visible. When you double-click the icon, the module page appears:


    Change the color to Green by clicking the ... button and finally hit Save:

    You'll see that the web.config file of devdays has changed like this:
    <imageCopyright enabled="true" message="Copyright (C) Developer &amp; IT Pro Days 2007" color="Green" />

    Refresh the waterfall.jpg file in the browser (http://localhost/devdays/waterfall.jpg?percent=30):

Congratulations, you've completed your first IIS 7.0 handler with its own configuration and management module! Download the code for this final episode over here. | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:


# re: Extending IIS 7.0 - Part 4: Creating a custom module for IIS Manager

Wednesday, April 04, 2007 10:03 PM by Gabriel Lozano-Moran

Hello Bart, just a quick question off-topic. Since you are using Community Server as well I was asking myself what tool(s) you use to write these blog posts? How do you upload the images and add the code blocks with code highlighting?

# re: Extending IIS 7.0 - Part 4: Creating a custom module for IIS Manager

Thursday, April 05, 2007 3:32 AM by bart

Hi Gabriel,

I'm using Windows Live Writer together with SyntaxColor4Writer; just Live Search for these two and you're ready to go. Pictures are uploaded by Live Writer through FTP btw.


# IIS7 Administration and Customization talk | - Advanced Technology Views, News &amp; Guides

Pingback from  IIS7 Administration and Customization talk | - Advanced Technology Views, News &amp; Guides

# Blogtech &#8211; Advanced Technology Views, News &amp; Guides | - Advanced Technology Views, News &amp; Guides

Pingback from  Blogtech &#8211; Advanced Technology Views, News &amp; Guides | - Advanced Technology Views, News &amp; Guides