Author Archives: Tobint

I might be Speaking at CodeStock

OK, so I had intended to announce that I posted a session submission to CodeStock and that voting was open. However, I procrastinated and Shawn Wildermuth beat me to the punch. So in an act of utter creepiness, I am modeling my post after his and invoking his name for extra copy-cat points.

For those of you that don’t know what CodeStock is, think of it as a CodeCamp done better – in two days instead of one. In their own words:

CodeStock is about Community. For Developers, by Developers (with love for SysAdmins and DBAs too!). Last year and idea started at CodeStock to mix Open Spaces within a traditional conference. This year we’re going to crank things up to 11 and rip off the knob – and you’re being drafted to help.

This two-day conference, of sorts, will be kicked off June 26th and costs only $25 to register.

My sessions are in the running, right along side Shawn’s. As he so eloquently pointed out, CodeStock attendees get to pick what sessions they wish to see from all of those submitted by would-be speakers. If my session doesn’t get picked, perhaps I can just watch Shawn. I’m told he has given a presentation or two in his career 🙂

So if you haven’t already, please go register for CodeStock so you can vote on those sessions while you can! Session voting ends on May 15th.

(Thanks, Shawn!)

Installing CakePHP on IIS 7

Recently I spoke with someone on Twitter who was having issues running CakePHP on IIS. With all the talk about ASP.NET MVC on IIS, folks forget that the MVC pattern works in other languages as well. CakePHP provides MVC  development on PHP. That said, I wanted to dive in and see what the issues were involved in getting this project up and running on IIS 7. I managed to get it installed pretty quickly, but it does take a little tweaking to get you up and running. I’ve chronicled my adventures with CakePHP below in case anyone else is having issues. That said, I must first say that I am not an expert working with CakePHP. This was my first experience with the project, so this information is provided “as-is” and should be taken with a grain of salt. With this demo, I’ll be walking through the “Cake Blog Tutorial” offered on cakephp.org, and modifying it as needed to work with IIS 7. That said, let’s get started.

Prerequisites

Yes, there ARE a lot of Prerequisites, but these are pretty typical for any MVC app on any platform.

Assumptions / Conventions

For the purposes of this post, I will use the convention/assumption that you have unzipped CakePHP to c:inetpubCakePHP . You should have the following paths now:

  • c:inetpubCakePHP
    • app
    • cake
    • vendors
    • .htaccess
    • index.php
    • version.txt

I will also use the assumption that this is being installed on the “Default Web Site”. This is unlikely what you are doing, so you’ll want to replace the “Default Web Site” instances in the steps below with your site or application path.

Lastly, I will assume that you are using and have already installed MySQL. You may use another database if you please, but this blog will reference MySQL.

Installing the Blog Sample

Pointing IIS to the cake document root

First, you’ll need to configure your website to point to the correct location. Using the assumptions above, the correct location would be c:inetpubCakePHPappwebroot .

Creating a Blog Database

Second, configure your database connection. To do this, you’ll need to create a blog database, and then point your configuration to that new catalog.

Start by creating a new MySQL Catalog using your favorite tool. I used MySQL Administrator. Simply right click in the catalogs and click “create new schema.”

Create a schema named “CakeBlog”. Once the schema is created, click on the “Tools” menu and select “MySQL Query Browser” and execute the following script:

/* First, create our posts table: */
CREATE TABLE posts (
     id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
     title VARCHAR(50),
     body TEXT,
     created DATETIME DEFAULT NOT NULL,
     modified DATETIME DEFAULT NOT NULL8.);

/* Then insert some posts for testing: */
INSERT INTO posts (title,body,created)
      VALUES ('The title',
                  'This is the post body.', NOW());
INSERT INTO posts (title,body,created)
      VALUES ('A title once again',
                  'And the post body follows.', NOW());
INSERT INTO posts (title,body,created)
      VALUES ('Title strikes back',
                  'This is really exciting! Not.', NOW());

* This SQL code copied verbatim from tutorial found here:

You’ve now created your database and a blog posts table with some default posts. Time to configure CakePHP to read from the database:

Cake Database Configuration

We’ll need to let CakePHP know where the database is. Copy database.php.default in c:inetpubCakePHPappconfig to database.php

Open c:inetpubcakephpappconfigdatabase.php and change the $default variable to point to your database:

var $default = array( 'driver'    => 'mysql',
                      'connect'   => 'mysql_connect',
                      'host'      => 'localhost',
                      'login'     => 'CakeBlog',
                      'password'  => 'c4ke-1z-k00l',
                      'database'  => 'CakeBlog',
                      'prefix'    => '' );

* This PHP code copied nearly verbatim from tutorial found here:
http://book.cakephp.org/view/326/The-Cake-Blog-Tutorial

You should now be able to open your browser to your application and see the default cake configuration page.

Setting up Rewriting Rules

CakePHP uses mod_rewrite, but also provides the ability to use Cake’s built-in ‘pretty URLs’. We’ll be importing the mod_rewrite rules from the .htaccess files from the default cakephp installation into the IIS URL Rewrite module. We’ll then have to modify those rules.

Start this process by opening the IIS Management Console. Open your application path. In this instance, we are using “Default Web Site”.

  1. Click on the “Default Web Site
  2. Open the “URL Rewrite” module
  3. Click on “Import Rules…” in the Actions pane
  4. Click the “” button next to the “Configuration file” textbox.
  5. Select the c:inetpubcakephp.htaccess file and click “OK
  6. Click the “Import” button
  7. Click the “Apply” button in the “Actions” pane
  8. Repeat steps 4, 5, 6 and 7 for c:inetpubcakephpapp.htaccess and c:inetpubcakephpappwebroot.htaccess files.

The rules are imported, but now you’ll need to edit the rules.

  1. Click the “Back to Rules” button in the “Actions” pane
  2. Edit the two rules with the action starting with “webroot/
  3. Remove the “webroot” portion of the “Rewrite URL”. Your paths should now look as follows:

Creating your MVC Application

The remainder of your application setup should follow the steps found in the original “Cake Blog Tutorial”. There is nothing different between IIS and Apache at this point, so copying the steps would be a bit redundant. Start with the step named “Create a Post Model”. Much like ASP.NET MVC, Cake provides an MVC pattern for developing PHP applications.

Once you have completed the steps, you should have a default site that looks something like the following:

Summary

Installing CakePHP on IIS is actually not much different from installing on Apache. The main difference lies in the implementation of mod_rewrite on Apache vs URL Rewriter in IIS. Obviously the installation of PHP differs from Apache. IIS makes the installation of PHP simple with Web Application Installer. If you are using CakePHP on IIS, I would be interested to hear if your experience was different than mine.

Charlotte Code Camp – Spring 2009

I flew to Charlotte from my home near Pittsburgh to attend the spring code camp this year.

When I arrived I found out that a Pete Mourfield was not going to make it in time to give one of his presentations. Brian Hitney suggested that I do a presentation at the last minute to fill in. I took the dare and agreed to do a presentation on MGrammar — something I had been checking out for about a week. I had about 50 minutes to prepare. In that time I downloaded the Oslo bits over the slow wifi at the school and installed them on my laptop. I had no slide deck and I had very little idea of how I would procede.

I managed to give a fairly fun presentation and got some great reviews in the process.

You can find some photos of the event on flickr.

A matter of pixels

I’ve been sick a lot lately. In fact, in my entirely life, I don’t ever remember being this sick for this long. It’s been weeks with the same cold – months if you consider the on-again/off-again problems I’ve been having. I guess it could be my fault. I’ve never been a big fan of drugs or doctors.

During my sick time, I try to do some work and often find myself barely able to concentrate. You know how it is, your head feels like its floating, and then you cough or sneeze and you feel as though your body has shaken apart into a million pieces. I’ve always equated that feeling with the visualization of my head becoming pixilated or snowy. That brings me, ever so strangely, to my current topic.

It’s been years since I focused on the front of web development issues. Back in the late 1990’s and early 2000’s I tinkered with client-side web development a great deal. I was writing “Ajax” applications before they were known as that. Of course, they all ran on intranet sites for my clients because they would only work in IE. I learned something back then. It was all a matter of pixels. In many cases, you had to take a box model and calculate precisely where things were and how they should line up. Of course, back then I was still using tables for layouts and cutting up images into manageable slices to put in individual or spanned table cells. Making those images line up, then, was extremely important to work on all browsers.

Over the years, I drifted away from client-side development. I have been concentrating on the back end of the web development stack. IIS, Apache, SQL Server, MySQL, MSMQ, MQ Series, BizTalk, WF, Remoting=>WCF, and associated technologies have been where I spent my time. What happened with data once my server pieces were done was beyond me. As a 3-year veteran working with IIS at Microsoft, you can see why this is important to me. That said, to help make IIS a better web server, it is always useful to actually know what is being served by your webserver, and why it may be important to you. I started playing with ASP.NET MVC which of course means I had to look at the “View” side of things again. To get a better grip on ASP.NET MVC, I decided to implement my blog in Oxite. Oxite isn’t a product, its a technology sample that is being followed to its logical conclusion – full implementation. This project fills the void of most samples by truly evangelizing the technology in a real-world implementation that can (and should) be implemented by others. Many of the samples out there are great examples of how to start an app, but never demonstrate that you can fully implement your project in the technology.

So, as I decided to dive in again and start playing with client-side development, I realized quickly that I needed to relearn everything I once knew. Box models were still important, but so was understanding the nuances of where my pixels within the context of their containers. Unlike with tables, that are fairly predictable, I learned very quickly that CSS-based DIV layouts can break down quickly if you aren’t using the proper attributes in your styles. div wrapping, overflow, z-indexes, display styles all became important topics for me to learn. I was trying to learn All this while I was laying sick in my bed!

I must say that I’m hooked now. I love technologies like jQuery. I’ve written a few plug-ins already. Some of them you see on this site, others will be on my site soon. Some will be coming in next revisions and available for download from this site. You can get the twitter client plug-in that scrolls at the top of this site’s page by downloading the Oxite skin on this website. I love that the community has developed many resources such as Yahoo’s CSS reset and associated style sheets.

Don’t get me wrong, I’m not an expert in these technologies overnight. I’ve got a LOT to learn. But I just keep telling myself, its all a matter of pixels. Count them out and everything will turn out ok!

Some resources for your own journey in client-side design/development:

  • jQuery – jQuery is a technology rooted in Microsoft’s leading XmlHttpRequest object. jQuery native core allows you to assign behavior to elements of the document object, perform common tasks such as layout, animations, fades and much more. This site’s plug-in repository is worth checking out and scouring for examples.
  • jQueryUI – If you like jQuery, this framework of UI plug-ins will help you get a clean, professional look for any website. The site allows you to create a customized theme, download it, and apply it to your layout. Absolutely brilliant.
  • YUI – Yahoo User Interface Library provides many user interface modules. I highly recommend the reset CSS. It helps all browser get on the same footing as far as padding, margin, borders, and spacing go. Very useful tool to help you achieve your desired look/feel on all browsers.
  • The Ajaxian – Great blog with useful information about developing dynamic content on the client side.
  • Introducing JSON – A good starting spot to learn about JSON (JavaScript Object Notation) and associated extensions such as JSONP. JSON allows you to serialize objects across Ajax requests. jQuery provides support for JSON/JSONP.
  • Microsoft: Introduction to JSON in JavaScript and .NET – Useful information to further your understanding of JSON.
  • HTML 5 Draft Recommendation – It always helps to know where your rendering specification is heading. HTML 5 supports things such as canvases that can render objects of irregular shapes and even manipulate individual pixels.
  • Common layouts – This useful site provides the code needed to produce common layouts in fixed and fluid mode.
  • Firebug – This tool is useful if you need to inspect a website’s output and determine what isn’t working. Often times it is hard to tell what setting is making our layout go wildly wrong. Firebug allows you to view the aggregate of your CSS calls for a given element, and let you see where your settings are coming from. The tool is invaluable to any client-side developer.
  • FireUnit – Provides JavaScript debugging. This is a necessary tool for anyone developing modern, dynamic, and interactive web UI today.
  • Color Schemer – This site provides a great tool to create color pallets for your site.

 

There is so much more to look at that I’m leaving out here. Obviously I could make the list extremely long. However, emphasizing everything means I am emphasizing nothing. I suggest those sites above as a good starting point.

Happy Coding!

IIS cannot start after upgrade to Vista SP1

Some time ago, I tried to start up the IIS Management Console on my Vista machine and I received an exception. After trying a few quick fixes, I gave up. I had several VPC images that I worked from and I didn’t particularly need this machine to work when I had so many others.  However, I’ve been doing a great deal of development on this machine now, and having IIS working is obviously rather handy. So, I tracked down the exception and solved my problem. I thought I’d go ahead and chronical my adventures for anyone else who is having this issue and needs help.

So first of all, the error that was displayed when I started inetmgr was as follows:

---------------------------
Failed to connect
---------------------------
There was an error when trying to connect. Do you want to retype your credentials and try again?
Details:
Creating an instance of the COM component with CLSID {2B72133B-3F5B-4602-8952-803546CE3344}
from the IClassFactory failed due to the following error: 8007000d.
---------------------------
Yes   No
---------------------------

So I thought that this might be a configuration error. I tried to roll back to a known good configuration but appcmd failed with the following exception:

ERROR ( hresult:80070426, message:Command execution failed.
The service has not been started. )

I tried to start the service with a good old fashion iisreset command and the following exception occurred:

Attempting stop…
Internet services successfully stopped
Attempting start…
Restart attempt failed.
The IIS Admin Service or the World Wide Web Publishing Service, or a service dep
endent on them failed to start.  The service, or dependent services, may had an
error during its startup or may be disabled.

Once again, I thought I’d go into the services console and try to start IIS manually. After trying to start it manually, it failed, stating that a dependent service failed. So I looked at Windows Process Activation Services (WAS) and noticed it was stopped. I tried to start it manually and found the following:

—————————
Services
—————————
Windows could not start the Windows Process Activation Service service on Local Computer.
Error 13:
The data is invalid.
—————————
OK �
—————————

So I checked out the event log and found the following four entries:

First Event :

Log Name:      System
Source:        Service Control Manager
Date:          9/21/2008 11:52:00 PM
Event ID:      7001
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Description:
The World Wide Web Publishing Service service depends on the Windows Process Activation Service service which failed to start because of the following error:
The data is invalid.

Second Event:

Log Name:      System
Source:        Service Control Manager
Date:          9/21/2008 11:52:00 PM
Event ID:      7023
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Description:
The Windows Process Activation Service service terminated with the following error:
The data is invalid.

Third Event:

Log Name:      System
Source:        Microsoft-Windows-WAS
Date:          9/21/2008 11:51:59 PM
Event ID:      5005
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Description:
Windows Process Activation Service (WAS) is stopping because it encountered an error. The data field contains the error number.

Fourt Event:

Log Name:      System
Source:        Microsoft-Windows-WAS
Date:          9/21/2008 11:51:59 PM
Event ID:      5036
Task Category: None
Level:         Error
Keywords:      Classic
User:          N/A
Description:
The configuration manager for Windows Process Activation Service (WAS) did not initialize. The data field contains the error number.

I checked through my event logs and realized that this started happening after I had upgraded to SP1. I hadn’t recognized this as the issue because, as I said, don’t typically use IIS on this machine on a regular basis. I decided to go back to my initial assessment that this had to be configuration related. I started doing comparisons between configuration of a pure Vista installation vs a Vista machine with SP1. I found that the schema had changed during the install, but something had been left out.  The schema file was updated to add the configurationHistory configuration section, but a corresponding section definition was not added to the applicationHost.config file.  

Whether this was what was causing the problem or not, I knew this was going to cause a problem. I added the following configuration entry to the applicationHost.config file under the section group for “system.applicationHost”. 

<section name="configHistory" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />

After this I started the WAS service, the World Wide Publishing Service and the IIS Admin Service. I opened up the IIS Management Console and everything was working just fine.

I’m not sure what exactly happened during the SP1 upgrade that caused this file to not be updated, but I’m pleased to report that all things are up and running again.

Enabling VS.NET 2008 to work with IIS 7.0

This is likely old ground for some, but I thought I’d cover it again just in case. As you may know Visual Studio allows you to create a new web site on IIS. However, there are some minor steps that you need to complete before it will work appropriately.

Let’s walk through this.

  1. Open Visual Studio .NET 2008
  2. Go to File | Web Site…
  3. Click the Browse… button to choose a Location
    1 - vs08-newwebsite-sm
  4. Click on the Local IIS button on the left
    2 - vs08-iis-sm
    You’ll notice the IIS 6 Metabase and IIS Configuration Compatibility need to be
    installed as well as ASP.NET. The next steps we’ll go through will enable this for
    you.
  5. Go to Start | Control Panel and click on the
    Programs and Features
    applet
  6. Click on the Turn Windows features on or off button on the left
  7. From the Windows Features window, select the IIS Metabase
    and IIS 6 configuration compatibility
    option under IIS 6 Management
    Compatibility
    as well as ASP.NET under Application
    Development Features

    3 - vs08-features-sm
  8. Click OK and wait as Windows configures the service
    4 - vs08-configuring
  9. Once Windows is done configuring IIS, it may ask you to restart. Click Restart
    Now
    .
    5 - vs08-restart
  10. After rebooting, you should be able to walk through steps 1-4 again and create a
    Web site with the Local IIS option.
    6 - vs08-working-sm

This should be all you need to do to enable Local IIS integration with VS.NET 2008.

IIS 7.0 Error Support

I know that most of you reading the blogs on this site have already seen how handy the errors are in IIS 7.0 as compared to previous error conditions. This weekend, I was playing with BlogEngine.NET and thought I’d put it on my personal site (tobint.com) to try it out. I downloaded the software and tried setting it up on my server. After following the brief instructions, I hit an error:

HTTP Error 500.22 – Internal Server Error
An ASP.NET setting has been detected that does not apply in Integrated managed pipeline mode.

In previous versions of IIS, this might have been all that I had to go on. I’d then search one or several search engines for that exact error and read through blog posts, forums, knowledge base articles and FAQ’s before I found what I needed. In IIS 7.0, I found the friendly error page awaiting me:

Error 500.22

 I looked at the “Things you can try” and a picked the first “fix” on the list. Since my application was not in the Default Web Site, I ran the appcmd for my application in “tobint.com/”. 

AppCmd

 As you can see, the error page correctly identified that my configuration needed to be migrated, and gave me the steps I needed to migrate it effectively. No searching the web. No trying to trace the problem down. No turning on tracing for ASP.NET pages. 

I love this feature. It’s a huge time saver and I don’t think it gets bragged on enough.

Accessing Custom IIS 7 Configuration at Runtime

I’ve been working on a series of posts and Webcasts in my “spare time” directed at some of the areas in which I am most frequently asked questions. I am currently working on developing topics dealing with IIS configuration. It seems logical that this would bring me a great deal of questions considering that IIS 7.0 is using a completely different configuration system than it did in previous versions. That said, I wanted to follow up to my Webcast (“Extending IIS Configuration“) with an article, and another webcast dealing with accessing custom configuration data at runtime.

In the webcast, I described how to extend an existing IIS configuration system object. I want to talk a little more about that. In this blog post I will take on four topics:

  1. Give you some warnings that were not provided in the Webcast regarding extending native IIS configuration
  2. Show you how to access your extended configuration data at runtime in a managed-code HTTP module
  3. Describe how to improve the example used in the Webcast in a custom configuration section.
  4. Show you how to access your custom configuration section at runtime in a managed-code HTTP module

Extending Native Configuration Elements

Introduction

I talked with Carlos today to discuss the validity of extending native configuration elements for our own purposes. While there are certainly valid reasons to do this, it doesn’t come without a risk to your application. Any time that you modify a piece of data that was created by another party — Microsoft or otherwise — you run the risk of having your data overwritten or corrupting the data that the other party needs to use. That said, lets revisit the sample that is frequently given in many articles and was reused in my webcast.

Warnings

In our Webcast, we discussed the idea that you could extend a native configuration element simply by creating a new configuration element schema and placing it in our %windir%sytem32inetsrvconfigschema directory. For our example, we created a file named SiteOwner_schema.xml and put the following code into it:

<configSchema>
   
<sectionSchema name=”system.applicationHost/sites”>
       
<attribute name=”ownerName” type=”string”/>
        <
attribute name=”ownerEmail” type=”string”/>
        <
attribute name=”ownerPhone” type=”string”/>    
    </
sectionSchema>
</configSchema>

All we have done is matched the same section schema path as the existing “system.applicationHost/sites” sectionSchema element in our IIS_schema.xml file. We then added schema information for our three new attributes.

This simple step allows us to immediately start adding attributes to our site configuration, and to access that data programmatically, through PowerShell, and through the AppCmd utility. However, you should be aware of a few dangers before you do something of this nature.

The first problem is that many of our native configuration elements have associated “default” configuration elements. For instance, the <site/> element has an associated <siteDefaults/> element. When modifying the configuration schema for <site/> , you should also modify the <siteDefaults/> configuration schema to match. That said, you might consider changing the above sample configuration schema as follows:

<configSchema>
   
<sectionSchema name=”system.applicationHost/sites”>
       
<attribute name=”ownerName” type=”string”/>
        <
attribute name=”ownerEmail” type=”string”/>
        <
attribute name=”ownerPhone” type=”string”/>
    </sectionSchema>    
    <element name=”siteDefaults”>       
       
<attribute name=”ownerName” defaultValue=”undefined” type=”string”/>
        <
attribute name=”ownerEmail” defaultValue=”undefined”  type
=”string”/>
        <
attribute name=”ownerPhone” defaultValue=”undefined” type=”string”/>    
     </element>
</configSchema>

As you can see, in the bolded section above we have now modified the <siteDefaults/> configuration element to match our changes to the <site/> element.  This isn’t the end of the story, however. While changing this might have seemed simple, modifying other elements become much more complex. Consider the fact that when you modify the schema for <application/> elements, there are several places where <applicationDefaults/> are kept. You will find <applicationDefaults/> under both the sites collection definition, as well as under the associated a site element definition. If you modify the application schema definition, you must be sure to edit the schema for both of the applicationDefaults definitions. This gets even more complex if you try to modify the <virtualDirectoryDefaults/> definition. You’ll find that defined three times in the native configuration: under the sites collection definition, under the application element definition, and under the site element definition itself.

You can see how you can quickly start adding complexity to your changes. Making sure that you make changes everywhere you need to becomes all the more risky.

The second problem you may have is that you may not be able to rely on your third-party to keep their schema definition in line with your own expectations. From one minor revision to the next, a configuration schema you may be depending on can change before your very eyes. This becomes a huge issue. Your future installation may need to make many dependency and version checks to decide what schema document to install, and your module may even need to make checks in case the user upgrades the third-party component without installing the upgraded schema. This can get very tricky, to say the least.

The third problem we can run into when we take a dependency on existing configuration is that external code may not behave the way we want it to. For instance, there is no guarantee that utilities and code written to handle those elements will know what to do if it encounters data that it doesn’t expect. Sure, we can cause IIS to validate our changes through schema, but there is no guarantee that the components which handle those elements can take on the challenge of handling the existing data. There is even a good chance that you could cause an Access Violation (read: crash) if the data is not handled properly. There is also a good chance that the components which normally handle those elements may overwrite your custom data.

So before we go on to show you how to access this extended configuration element data, please be warned that it may not be the best solution to your problem. Later in this article, I will describe how to modify our example to give us the same functionality without altering the schema definition of the existing elements.

Accessing Extended Native Configuration Elements at Runtime

Introduction

With the warnings having been fairly posted, I’ll show you quickly how to access your custom configuration data at runtime from an HTTP module. Once you have saved your schema file to the appropriate directory, its time to write the HTTP module. That module is going to be very simple. We need to create a module that uses the PreSendRequestHeaders event. In this event we will get our “system.applicationHost/sites” configuration section, loop through the configured sites looking for the site on which the current HTTP request is executing, and then write HTTP headers out to the client. Just to make sure that our sample is working, we’ll wrap it up by adding JavaScript to an HTML page that will request the headers from the Web site and display them in a message box.

Getting Started

For this sample, I used Visual Studio 2008, but you can use plain old notepad and command-line compilation if your are feeling particularly froggy.

Visual Studio 2008 Walk-through

The following walk-through will create an HTTP module that can display the site owner details of the configuration system

  1. Click on the File | New | Project …  menu item.
  2. In the “Project types” tree on the left, drill down to the C# | Windows tree element
  3. In the “Templates” selector on the right, select the “Class Library” template
  4. Change the Name of the project to “SiteOwner
  5. Change the Location to “c:samples”
  6. Verify that the “Create directory for solution” checkbox is checked
  7. Click OK
  8. Click on the Project | Add Reference … menu item.
  9. Under the .NET tab, select the component with the Component Name of “System.Web
  10. Click OK
  11. Rename the “Class1.cs” file in the Solution Explorer to “SiteOwnerHeaderModule.cs
  12. Paste the following code in “SiteOwnerHeaderModule.cs” file:
    using System;
    using System.Web;
    using System.Web.Hosting;
    using Microsoft.Web.Administration;
    using System.Collections.Specialized;

    namespace SiteOwner
    {
       
    public class SiteOwnerHeaderModule : IHttpModule {
           
    public void Dispose() {}
           
    public void Init(HttpApplication context) {
               
    context.PreSendRequestHeaders += new EventHandler(OnPreSendRequestHeaders);
           
    }
           
    void OnPreSendRequestHeaders(object sender, EventArgs e){
                // Get the site collection

               
    ConfigurationSection section =
                   
    WebConfigurationManager.GetSection(“system.applicationHost/sites”);
               
    ConfigurationElementCollection cfgSites = section.GetCollection();
                HttpApplication app = (HttpApplication)sender;
                HttpContext context = app.Context;
                // Loop through the sites to find the site of 
                // the currently executing request
               
    foreach (ConfigurationElement cfgSite in cfgSites) {
                   
    if (HostingEnvironment.SiteName == (string)cfgSite.GetAttributeValue(“name”)) {
                        // This is the correct site, get the site owner attributes
                       
    SiteOwner siteOwner = new SiteOwner(cfgSite);
                       
    NameValueCollection headers = context.Response.Headers;
                       
    headers.Add(“Site-Owner-Name”, siteOwner.Name);
                       
    headers.Add(“Site-Owner-Email”, siteOwner.Email);
                       
    headers.Add(“Site-Owner-Phone”, siteOwner.Phone);
                       
    break;
                   
    }
               
    }
           
    }
       
    }
       
    class SiteOwner {
           
    public string Name { get; set; }
           
    public string Email { get; set; }
           
    public string Phone { get; set; }
           
    public SiteOwner(ConfigurationElement site) {
               
    this.Name = site.GetAttributeValue(“ownerName”) as string ;
               
    this.Email = site.GetAttributeValue(“ownerEmail”) as string ;
               
    this.Phone = site.GetAttributeValue(“ownerPhone”) as string;
           
    }
       
    }
    } 

  13. Click the File | Save menu item.
  14. Right-click the SiteOwner project in the Solution Explorer window and select Properties from the menu
  15. Click the Signing tab
  16. Check the Sign the assembly checkbox
  17. Select “<new>” from the Choose a strong name key file dropdown box
  18. Type “KeyFile” in the Key file name box
  19. Uncheck the checkbox labeled Protect my key file with a password
  20. Click OK
  21. Click the File | Save SiteOwner menu item
  22. Click the Build | Build SiteOwner menu item.

Notepad.exe and Command-line Walk-through

The following walk-through will create an HTTP module that can display the site owner details of the configuration system. This walk-through assumes that you have the Microosft .NET Framework SDK or Windows SDK installed. These include the C# compilers needed to build. For more help on building with csc.exe, see “Command-line building With csc.exe

  1. Paste the code from step 10 of the Visual Studio .NET 2008 walk-through into a new notepad instance
  2. Click File | Save As
  3. Browse to the “C:samples” folder if it exists; create the folder if it does not
  4. Type “SiteOwnerHeaderModule.cs” in the File name box
  5. Click the Save button
  6. Open a new SDK Command Prompt (Windows SDK) or Visual Studio Command Prompt (Visual Studio 2008).
  7. At the command prompt type:
    csc.exe /target:module /out:SiteOwner.netmodule /debug /r:System.Web.dll /r:%windir%system32inetsrvMicrosoft.Web.Administration.dll SiteOwnerHeaderModule.cs
  8. Press ENTER then type:
    sn.exe -k keyfile.snk
  9. Press ENTER then type:
    al.exe /out:SiteOwner.dll SiteOwner.netmodule /keyfile:keyfile.snk

Deploying Your HTTP Module

Once you have built your assembly using either Visual Studio 2008 or csc.exe, its time to deploy it to your system. This sample assumes that you don’t want all of your customers to have to install this same component to each Web site, so we’ll install it as a default module on the system.

Registering the Assembly in the GAC

To do this we first need to copy the assembly file(s) to the target system, and register them in the Global Assembly Cache. Using your preferred method to copy the files, copy the SiteOwner.dll (and SiteOwner.netmodule if you used command-line compilation) to the %windir%system32inetsrv directory on the target installation machine. Next open up a command prompt and type:

gacutil -i %windir%system32InetSrvSiteOwner.dll

Now you’ll need to get a copy of the “Public Key Token” for the assembly so that you can install this “strong-named assembly” module in IIS. To do this open up your %windir%assembly directory in Windows Explorer. Scroll down to the SiteOwner “file” in that window and copy the “Public Key Token”. You can do this easily by right-clicking the “file” and selecting Properties from the menu. Double click on the Public Key Token to select the entire contents, then right click the highlighted text and select Copy from the menu.

image

Installing the Module as a Default Module in IIS 7.0

Now that you have your assembly registered and your Public Key Token copied, its time to add this module to all the Web sites on your IIS 7.0 server. To accomplish this, open up your %windir%system32inetsrvconfigapplicationHost.config file in notepad. Scroll down near the bottom of the file to the default location path that looks something like this:

<location path=”” overrideMode=”Allow”>

Inside of that element scroll down to the <modules> element under the <system.webServer> section. You should find a number of modules already listed. To add yours, simply scroll to the bottom of the list and type the following:

<add name=”SiteOwner” type=”SiteOwner.SiteOwnerHeaderModule, SiteOwner, Version=1.0.0.0, Culture=neutral, PublicKeyToken=” />

Once you have typed this line in, you can paste your Public Key Token in right after “PublicKeyToken=” in the line you just typed. Save the applicationHost.config file.

Testing the Module

Choose a Web site on your server to test your application. If you want to run this module for files other than ASP.NET files, you’ll need to choose a Web site running in Integrated Pipeline. For our sample, we are going to make an HTML file to test our code. Our HTML file will contain some JavaScript that will get the headers from the server and display them in a message box. We could use a number of diagnostic tools to do this same work, but this will serve our purposes.

Open up Notepad and paste the following markup into it:

<html>
<body>
<script language=”JavaScript” type=”text/javascript”>
<!–
var req = new ActiveXObject(‘Microsoft.XMLHTTP’);
req.open(‘HEAD’, location.href, false);
req.send();
alert(req.getAllResponseHeaders())
//–>
</script>
</body>
</html>

Save the file to the physical root of the Web site you wish to test. Type in the corresponding URL to your test file in your web browser. Your result should be a JavaScript alert that looks something like the following:

image 

You’ll notice in my results, I have already set the Site’s ownerName attribute. (see the Webcast titled “Extending IIS Configuration” to see how).

Improving our Sample

Introduction

As I stated earlier in this post, this demonstrates something that is definitely possible, but somewhat risky to do. We now want to improve our sample to limit our dependencies on native and/or third-party configuration schema definitions, and prevent us from encroaching on another module’s configuration territory. In concept, what we will do is move our configuration data to its own configuration section and use the site’s ID to allow us to map the data from the site collection to our configuration section.

Modifying the Schema

First, you’ll want to remove the custom site owner configuration data from the site nodes. Otherwise, once we modify the schema, the configuration will not validate. Once you’ve removed that data open the SiteOwner_schema.xml file in notepad and change it to the following:

<configSchema> 
   
<sectionSchema name=”system.applicationHost/siteOwner”> 
       
<collection addElement=”site”> 
       
<attribute name=”id” type=”uint” required=”true” isUniqueKey=”true” />
            <
attribute name=”ownerName” type=”string” /> 
            <
attribute name=”ownerEmail” type=”string” /> 
            <
attribute name=”ownerPhone” type=”string” /> 
        </
collection> 
   
</sectionSchema> 
</configSchema>

Save the file in the same location as you did previously. Now we need to tell the applicationHost.config file about our new section schema. To do this, we add a new section declaration underneath of the system.applicationHost sectionGroup in the applicationHost.config file.

<sectionGroup name=”system.applicationHost”>
    …
    <section name=”siteOwner” allowDefinition=”AppHostOnly” overrideModeDefault=”Deny” />
    …
</
sectionGroup>

Next we need to modify our HTTP module to read from the new configuration section:

using System;
using System.Web;
using System.Web.Hosting;
using Microsoft.Web.Administration;
using System.Collections.Specialized;

namespace SiteOwner{
   
public class SiteOwnerHeaderModule : IHttpModule{
       
public void Dispose() { }
       
public void Init(HttpApplication context){
           
context.PreSendRequestHeaders
               
+= new EventHandler(OnPreSendRequestHeaders);
       
}
       
void OnPreSendRequestHeaders(object sender, EventArgs e){
           
// Get the site collection
            ConfigurationSection section = 
                WebConfigurationManager.GetSection(“system.applicationHost/sites”);
           
ConfigurationElementCollection cfgSites = section.GetCollection(); 
            HttpApplication app = (HttpApplication)sender;
            HttpContext context = app.Context;

           
// Loop through the sites to find the site of 
            // the currently executing request
            foreach (ConfigurationElement cfgSite in cfgSites){
                if (HostingEnvironment.SiteName == (string)cfgSite.GetAttributeValue(“name”)){
                   
// This is the correct site, find the associated
                    // Site Owner record in our custom config section
                    SiteOwner siteOwner = SiteOwner.Find((long)cfgSite.GetAttributeValue(“id”));
                   
if (siteOwner != null) {
                       
// Add the headers if we found a matching
                        // site owner record configured
                        NameValueCollection headers = context.Response.Headers;
                       
headers.Add(“Site-Owner-Name”, siteOwner.Name);
                       
headers.Add(“Site-Owner-Email”, siteOwner.Email);
                       
headers.Add(“Site-Owner-Phone”, siteOwner.Phone);
                   
}
                   
break;
               
}
           
}
       
}
   
}
   
class SiteOwner{
       
public string Name { get; set; }
       
public string Email { get; set; }
       
public string Phone { get; set; }
       
public SiteOwner(ConfigurationElement owner){
           
this.Name = owner.GetAttributeValue(“ownerName”) as string;
           
this.Email = owner.GetAttributeValue(“ownerEmail”) as string;
           
this.Phone = owner.GetAttributeValue(“ownerPhone”) as string;
       
}
       
public static SiteOwner Find(long siteId){
             // Find the matching siteOwner record for this siteId         
           
ConfigurationSection section = 
                        WebConfigurationManager.GetSection(“system.applicationHost/siteOwner”);
           
ConfigurationElementCollection cfgSiteOwners = section.GetCollection();
           
foreach (ConfigurationElement cfgOwner in cfgSiteOwners) {
               
if (siteId == (long)cfgOwner.GetAttributeValue(“id”)){
                   
return new SiteOwner(cfgOwner);
               
}
           
}
           
return null;
       
}
   
}
}

 

There isn’t much changed in this compared to the last. The difference, apart from a few code comments, is that I’ve modified the OnPreRequestHeaders function to get the current site ID, and pass that into the static SiteOwner.Find function. Then I modified the SiteOwner.Find function to read from our new configuration section. Please note that production code would likely set SiteOwner to extend from ConfigurationElement, and a special ConfigurationSection would be developed. Go ahead and compile and deploy this assembly the same way you did with the previous version. Make sure that you run gacutil on the new assembly once it is copied. Also, since we are running against a static HTML file, you might need to run iisreset.exe from a command-line in order to clear the cache from the server.

Once you have deployed your assembly and the schema file, its time to test the application. If we refresh our Web page, we will get a JavaScript alert without any site owner headers listed:

image

This is because we haven’t created any site owner configuration entries yet. So lets add a record directly to our config. I added the following configuration directly after the <sites> node in the applicationHost.config file:

<siteOwner>
   
<site id=”1″ ownerName=”Tobin Titus” ownerEmail=”tobint@microsoft.com” ownerPhone=”555-121-2121″ />
</
siteOwner>       

Save your file and refresh your Web browser. You should now see your site owner configuration data.

image

Conclusions

In this blog post, we’ve walked through extending existing IIS configuration objects, and accessing the custom data at runtime through an HTTP module. We’ve also talked about the risks of this approach and have demonstrated a better approach. This approach gives us the same ability to customize our IIS 7.0 configuration system, but gives us a much smaller dependency on the native configuration schema. We have little worry about schema changes, we don’t have to worry about other modules or utilities stomping on our custom data, and most importantly, we are far less likely to cause any Access Violations with the later approach. 

Many thanks go to Carlos once again for his help in putting this post together as well as for his CodeColorizer plug-in for Live Writer. High-fives and cookies in his office!

Reading IIS.NET Blogs (or any RSS) with Powershell

Being a member of the IIS team, I often find myself checking blog posts to see what the members of the product team are blogging about.  However, since Powershell came out, I find myself doing more and more work on my scripts. It’s a bit annoying to have to jump out of Powershell to go read blog posts.  As such, I’ve written a few quick scripts to help me read IIS.NET from my pretty blue shell. For those of you who are already familiar with powershell and don’t want to read the long blog post, you can download my blog script from the DownloadCENTER: http://www.iis.net/downloads/default.aspx?tabid=34&g=6&i=1387 

Setting up your Powershell environment


To start, I’ve written a few supporting functions in my profile.  These functions help me keep my scripts organized and, since I change my scripts quite often, it helps me to sign them as well. 
First off, if you haven’t created your own certificate for signing code, please go back and take a look at my first Powershell blog post that give you the details on how to do this.  
Next, we need to add a few things to your Powershell profile.  To open your Powershell profile from within Powershell, type: 

PS > notepad $profile 
First, I add a function to allow us to easily sign our scripts (assuming you have created a cert to sign them wth): 

## Sign a file
##-------------------------------------------
function global:Sign-Script ( [string] $file )
{
     $cert = @(Get-ChildItem cert:CurrentUserMy -codesigning)[0]
     Set-AuthenticodeSignature $file $cert
}
set-alias -name sign -value sign-script

The next function is used to help me organize things. I have several scripts for various work environments.  I like to organize them by function. So, I keep my IIS scripts in an “IIS” directory, my common scripts in a “common” directory and so on.  Inside each of my script directories, I keep a “load.ps1” script that I can  use to initialize any of my work environments.  Lastly, I create a Powershell drive that matches the work environment name so I can get to my scripts easily. The function below does all the work for me. 

## Create a Drive
##-------------------------------------------
function global:New-Drive([string]$alias)
{
     $path = (join-path -path $global:profhome -childpath $alias)
     if( !(Test-Path $path ) )
     {
          ## Create the drive's directory if it doesn't exist
          new-item -path $global:profhome -name $alias -type directory
     }
     else
     {
          ## Execute the load script for this drive if one exists
          $loadscript = (join-path -path $path -childpath "load.ps1")
          if( Test-Path $loadscript)
          {
               $load = &$loadscript
          }
     }
     # Create the drive
     new-Psdrive -name $alias -scope global -Psprovider FileSystem -root $path
}

Within my profile, I simply call this function and pass in an alias. When the function executes it will create a directory with the alias name, if it doesn’t exist already. If the directory does exist, it will check for the load.ps1 file inside that path and execute it. Lastly, it will create powershell drive. I have the following calls added to my profile below: 

## Custom PS Drives
##-------------------------------------------
New-Drive -alias "common"
New-Drive -alias "iis"

Go ahead and save your profile now and type these commands: 

PS > Set-ExecutionPolicy Unrestricted
PS > &$profile
PS > Sign $profile
PS > Set-ExecutionPolicy AllSigned

 
The first command sets Powershell into unrestricted mode. This is because we need to execute the profile script and it hasn’t been signed yet.  The next command executes the profile. The third command uses the “sign” function that our profile script loaded. Since our profile is now signed, we can set our execution policy back to AllSigned. AllSigned means that Powershell will execute scripts as long as they are signed. 

From this point on, we can make changes to our profile and simply call our sign function again before we close our Powershell instance. The next instance of powershell that is opened will have our changes.  

Creating / Using Blog Functionality


Now that we have our environment set up, lets get to the blogging part.  If you’ve set up your environment right, you can execute the following command: 

PS > cd iis: 

This command will put you in the iis scripts directory.  Next, create a new blogs script by typing: 

PS > notepad blogs.ps1 

You’ll be prompted if you want to create the file. Go ahead and say yes.  Next, paste the following into the the notepad and save it: 
  

## Sets up all custom feeds from feeds.txt
##---------------------------------------------------
function global:Import-Feed
{
     if( $global:RssFeeds -eq $null )
     {
          $global:RssFeeds = @{};
     }
     $RssFeeds.Add( "iisblogs", "http://blogs.iis.net/rawmainfeed.aspx" );
     $RssFeeds.Add( "iisdownloads", "http://www.iis.net/DownloadCENTER/all/rss.aspx" );
}
Import-Feed ## Call Import-Feed so we are ready to go
## Gets a feed or lists available feeds
##---------------------------------------------------
function global:Get-Feed( [string] $name )
{
     if( $RssFeeds.ContainsKey( $name ) )
     {
          return $RssFeeds[$name];
     }
     else
     {
          Write-Host "The path requested does not exist";
          Write-Output $RssFeeds;
     }
}
## Gets IIS Blogs
##---------------------------------------------------
function global:Get-Blog([int]$index, [int]$last, [int]$first, [int]$open)
{
     $url = (Get-Feed iisblogs)
     return (Get-RSS $url $index $last $first $open)
}
## Gets a specific blog
##---------------------------------------------------
function global:Get-AuthorBlog([string]$creator)
{
     Get-Blog | Where-Object {$_.creator -eq $creator}
}
## Gets Downloads from IIS
##---------------------------------------------------
function global:Get-Download([int]$index, [int]$last, [int]$first, [int]$open)
{
     $url = (Get-Feed iisdownloads)
     return (Get-RSS $url $index $last $first $open)
}
## Gets a generic RSS Feed
##---------------------------------------------------
function global:Get-RSS([string]$url, [int]$index, [int]$last, [int]$first, [int]$open)
{
     $feed = [/xml](new-object System.Net.WebClient).DownloadString($url)
     if($index)
     {
          return $feed.rss.channel.item[$index]
     }
     if($open)
     {
          $ieaddr = $env:programfiles + "internet exploreriexplore.exe"
          return &(get-item $ieaddr) $feed.rss.channel.item[$open].link
     }
     if($last)
     {
          return ($feed.rss.channel.item | Select -last $last)
     }
     if($first)
     {
          return ($feed.rss.channel.item | Select -first $first)
     }
     return $feed.rss.channel.item
}

Once you’ve saved this file, close it.  We need to sign this script and execute it by typing: 

PS IIS:> sign blogs.ps1
PS IIS:> ./blogs.ps1 

Now lets start reading.  

  • Read all Blogs
PS iis:> Get-Blog 
  • Read the last five blog posts
PS iis:> Get-Blog -last 5 
  • Read the first five blog posts
PS iis:> Get-Blog -first 5 
  • Read the 8th blog post
PS iis:> Get-Blog -index 8 
  • Open the 12th blog post and open in Internet Explorer
PS iis:> Get-Blog -open 12 
  • Read all blog posts by Bill Staples
PS iis:> Get-AuthorBlog bills 
  • Read all items in DownloadCENTER
PS iis:> Get-Download 
  • Get titles of all items in DownloadCENTER
PS iis:> Get-Download | Select Title 

Of course, all the laws of Powershell still apply, so I can still do fun stuff like like listing only the blog titles from my blog. 

PS iis:> Get-AuthorBlog TobinTitus | Select Title 

I can do the same witht he raw blog output: 

PS iis:> Get-Blog -last 5 | Select pubDate, Creator, Title 

Happy reading. 

IIS.NET Blogs in Powershell

OT: Cat and Mouse

For those of you looking for IIS information, this blog post is not for you. For those of you that just like general computer information, read on and enjoy.

The computer mouse has come a long way over the years.  When Doug Engelbart invented the computer mouse, it was rather crude looking but was none-the-less very revolutionary.  Engelbart showed his invention to the world in 1968 during a presentation now know as “The Mother of All Demos.”  The mouse that Engelbart demoed was an electromechanical device that leveraged a large sphere to turn x and y coordinate counters as the device was rolled across a surface. Each click of the counter would tell the computer how far the mouse had “traveled”.  For several years the mouse kept the same basic principal. We improved on the original idea and replaced a large sphere with small rubbery balls with x,  i and often an additional diagonal gear wheel. The diagonal indicator was used to help correct the cursor movement if the mouse was rotated or tilted. The rubber helped the ball move across slick surfaces when a mouse pad just wasn’t cutting it. The downside to this rubbery surface was that pet owners ended up with a lot of cat (or dog) fur rolling into the mouse.  You would often have to open the bottom portion of the mouse and clean the hair and other debris out to make your pointing device work efficiently again.  Playing Doom or Quake with a junked-up mouse was an instant indication of a n00b that needed serious pwning.

Roll forward to the present day and we find optical mice taking the electromechanical device’s place. The optical approach solves a lot of the problems associated with older mice. For one, the new mice don’t have the rolling dowel-like rollers (counters) that can get gunked up anymore.  A rubbery ball is not picking up every piece of debris and yanking it into the mouse cavity as though it were a time-capsule for desktop debris.  So, why does your mouse still freak out when a piece of fur gets trapped under your optical mouse? 

The answer is pretty interesting and I’m sure will be solved with the next iteration of mouse invention. Many optical mice are created using a camera or an optoelectric sensor, an optical processor to compare images taken by the camera/sensor, and an LED  that illuminates the surface under your mouse.  The camera/sensor takes ~1500 picture samples a second!  The pictures are small (usually 10-20 square pixels) and gray-scale (usually registering fewer than 100 shades).  The optical processor examines and compares the picture samples to determine the relative position of the mouse to its previous position.

Now introduce your cat’s hair to the equation.  The cat hair gets entangled in the small cavity of the mouse where the optical sensor lives. As the mouse rolls across the desk, static builds up and flips the hair around wildly. As your mouse snaps those thousands of pictures, the hair position is captured and the optical processor gets confused by the sudden movement of the cat hair in the picture comparisons.  You can move your mouse slowly to the right, but if the hair is flipped around within the mouse cavity, the processor will think that you have jerked the mouse to the left or sharply downward.  If your cat is watching the computer screen as mine often does, these sharp movements may cause the cat to attack your monitor — truly creating an interesting game of cat and mouse.