Friday, June 25, 2010

Introduction to InfoPath 2010 Software Development

 

Table of Contents

Hello World Example

Exercise - Moving Items Between ListBoxes

Exercise - Repeating Sections handled Manually

Changed Events

Developing with Visual Studio 2010

 

Hello World Example

I will start by opening up InfoPath 2010 and allowing for custom development. First go to File –> Options –> More Options –> Design and choose C#.


Then create a new blank form and immediately save it as Form.xsn.

Now I’ll add three controls to my InfoPath form, a text box, a calculated value with a blank calculation and a button. The text box will automatically bind to a field named field1. Create a new text field named field2 and bind it to the calculated value.

Now open up the buttons properties window and click Edit Form Code. This will open the form’s Visual Studio Tools for Applications (VSTA) project for editing.

Note that you may have to install VSTA by going to Add / Remove programs and right clicking on Microsoft Office (Or InfoPath) and choosing “Add / Remove Features

Here is what you should see when the VSTA project opens.

In keeping with the spirit of Hello World examples, I’m not going to explain anything about what you’re looking at. Replace the text // Write your code here. with the following code:

public void CTRL3_5_Clicked(object sender, ClickedEventArgs e)

{
XPathNavigator mainNavigator = MainDataSource.CreateNavigator();
XPathNodeIterator textBoxNodeIterator = mainNavigator.Select("my:myFields/my:field1", NamespaceManager);
textBoxNodeIterator.MoveNext();
XPathNodeIterator calculatedValueNodeIterator = mainNavigator.Select("my:myFields/my:field2", NamespaceManager);
calculatedValueNodeIterator.MoveNext();
calculatedValueNodeIterator.Current.SetValue(textBoxNodeIterator.Current.Value);
}

Here is what it should look like inside of VSTA.

Now press F5 to run your form in debug mode with your new custom code. Enter a value inside the textbox and see your value returned to you inside the calculated value.

This simple example could have been accomplished without any code, but it opens the door to endless opportunities. The pattern of querying for data using xpath, iterating through the results with an XPathNodeIterator, navigating around parent, child and sibling nodes with an XPathNavigator and setting  the Value or InnerXml properties of xml nodes is basically all you will be doing with custom InfoPath code.
The important thing to realize when doing custom InfoPath development is that you are writing code to manipulate an xml document. After your xml manipulation code runs, InfoPath will read the xml you’ve manipulated and render a user interface on top of it.

Moving Items Between ListBoxes

Here is an example I’d like to see someone accomplish without custom code. Take a look at the form below. I have a list on the left named ThingsToChooseList and a list on the right named ThingsChosenList. In between them I have a MoveRight button and a MoveLeft button. Under the ThingsChosenList I have two more buttons, a MoveUpButton and MoveDownButton. I am also going to use the form loading event to load up some items into the ThingsToChooseList. 

Notice inside the button properties you have the ability to name the buttons. Choosing readable names instead of CTRL#_# will make things a lot easier on you and anyone else that might have to read your code.
Here is what VSTA should look like before you start writing any code

Here is the code to make it all work:

public void FormEvents_Loading(object sender, LoadingEventArgs e)
{
XPathNavigator mainNavigator = MainDataSource.CreateNavigator();
XPathNodeIterator thingsToChooseNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsToChoose", NamespaceManager);
thingsToChooseNodeIterator.MoveNext();

// add some values to the ListBox
thingsToChooseNodeIterator.Current.InnerXml = @"Money
Power
Strength
Wisdom
Charm";

// delete any existing white space nodes
XPathNodeIterator choosenThingsNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsChosen", NamespaceManager);
choosenThingsNodeIterator.MoveNext();
while (choosenThingsNodeIterator.Current.MoveToFirstChild())
{
    choosenThingsNodeIterator.Current.DeleteSelf();
}

XPathNodeIterator EventsFromRepeatingSections = mainNavigator.Select("/my:myFields/my:EventsFromRepeatingSections", NamespaceManager);
EventsFromRepeatingSections.MoveNext();
while (EventsFromRepeatingSections.Current.MoveToFirstChild())
{
    EventsFromRepeatingSections.Current.DeleteSelf();
}
}

public void MoveRightButton_Clicked(object sender, ClickedEventArgs e)
{
XPathNavigator mainNavigator = MainDataSource.CreateNavigator();
XPathNodeIterator selectedThingToChooseNodeIterator = mainNavigator.Select("/my:myFields/my:SelectedThingToChoose", NamespaceManager);
selectedThingToChooseNodeIterator.MoveNext();
     
// make sure there is something selected
if (!string.IsNullOrEmpty(selectedThingToChooseNodeIterator.Current.Value))
{
    // get the item in the list box with the same name as the selected item
    XPathNodeIterator thingsToChooseNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsToChoose/my:ThingToChoose[.='" + selectedThingToChooseNodeIterator.Current.Value + "']", NamespaceManager);
    thingsToChooseNodeIterator.MoveNext();
     
    // add new item to ThingsChosenListBox
    XPathNodeIterator thingsChosenNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsChosen", NamespaceManager);
    thingsChosenNodeIterator.MoveNext();
    thingsChosenNodeIterator.Current.AppendChild("" + selectedThingToChooseNodeIterator.Current.Value + "");
     
    // delete the selected item in ThingsToChooseListBox
    thingsToChooseNodeIterator.Current.DeleteSelf();
     
    // select the top item in the ThingsToChooseListBox or select nothing
    thingsToChooseNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsToChoose", NamespaceManager);
    thingsToChooseNodeIterator.MoveNext();
    thingsToChooseNodeIterator.Current.MoveToFirstChild();
    selectedThingToChooseNodeIterator.Current.SetValue(thingsToChooseNodeIterator.Current.Value);
}
}

public void MoveLeftButton_Clicked(object sender, ClickedEventArgs e)
{
XPathNavigator mainNavigator = MainDataSource.CreateNavigator();
XPathNodeIterator selectedThingChosenNodeIterator = mainNavigator.Select("/my:myFields/my:SelectedChosenThing", NamespaceManager);
selectedThingChosenNodeIterator.MoveNext();
     
// make sure there is something selected
if (!string.IsNullOrEmpty(selectedThingChosenNodeIterator.Current.Value))
{
    // get the item in the list box with the same name as the selected item
    XPathNodeIterator thingChosenNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsChosen/my:ThingChosen[.='" + selectedThingChosenNodeIterator.Current.Value + "']", NamespaceManager);
    thingChosenNodeIterator.MoveNext();
     
    // add new item to ThingsToChooseListBox
    XPathNodeIterator thingsToChooseNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsToChoose", NamespaceManager);
    thingsToChooseNodeIterator.MoveNext();
    thingsToChooseNodeIterator.Current.AppendChildElement("my", "ThingToChoose", thingsToChooseNodeIterator.Current.NamespaceURI, selectedThingChosenNodeIterator.Current.Value);
     
    // delete the selected item in ThingsChosenListBox
    thingChosenNodeIterator.Current.DeleteSelf();
     
    // select the top item in the ThingsChosenListBox or select nothing
    thingChosenNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsChosen", NamespaceManager);
    thingChosenNodeIterator.MoveNext();
    thingChosenNodeIterator.Current.MoveToFirstChild();
    selectedThingChosenNodeIterator.Current.SetValue(thingChosenNodeIterator.Current.Value);
}
}
     
public void MoveUpButton_Clicked(object sender, ClickedEventArgs e)
{
XPathNavigator mainNavigator = MainDataSource.CreateNavigator();
XPathNodeIterator selectedThingChosenNodeIterator = mainNavigator.Select("/my:myFields/my:SelectedChosenThing", NamespaceManager);
selectedThingChosenNodeIterator.MoveNext();
     
// get the item in the list box with the same name as the selected item
XPathNodeIterator thingsChosenNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsChosen/my:ThingChosen[.='" + selectedThingChosenNodeIterator.Current.Value + "']", NamespaceManager);
thingsChosenNodeIterator.MoveNext();
     
// Move to the items previous sibling to see if there is anything before it
if (thingsChosenNodeIterator.Current.MoveToPrevious())
{
    // Move back to the item we want to move
    thingsChosenNodeIterator.Current.MoveToNext();
     
    // Swap the item with the item above it
    string value = thingsChosenNodeIterator.Current.Value;
    thingsChosenNodeIterator.Current.MoveToPrevious();
    thingsChosenNodeIterator.Current.InsertBefore("" + value + "");
    thingsChosenNodeIterator.Current.MoveToNext();
    thingsChosenNodeIterator.Current.DeleteSelf();
}
}
     
public void MoveDownButton_Clicked(object sender, ClickedEventArgs e)
{
XPathNavigator mainNavigator = MainDataSource.CreateNavigator();
XPathNodeIterator selectedThingChosenNodeIterator = mainNavigator.Select("/my:myFields/my:SelectedChosenThing", NamespaceManager);
selectedThingChosenNodeIterator.MoveNext();
     
// get the item in the list box with the same name as the selected item
XPathNodeIterator thingsChoosenNodeIterator = mainNavigator.Select("/my:myFields/my:ThingsChosen/my:ThingChosen[.='" + selectedThingChosenNodeIterator.Current.Value + "']", NamespaceManager);
thingsChoosenNodeIterator.MoveNext();
     
// Move to the items next sibling to see if there is anything after it
if (thingsChoosenNodeIterator.Current.MoveToNext())
{
    // Move back to the item we want to move
    thingsChoosenNodeIterator.Current.MoveToPrevious();
     
    // Swap the item with the item below it
    string value = thingsChoosenNodeIterator.Current.Value;
    thingsChoosenNodeIterator.Current.MoveToNext();
    thingsChoosenNodeIterator.Current.InsertAfter("" + value + "");
    thingsChoosenNodeIterator.Current.MoveToPrevious();
    thingsChoosenNodeIterator.Current.DeleteSelf();
}
}

And the functionality should look like this

Let’s look at the recurring themes inside the code.

this.MainDataSource – The this property refers to the form itself, which has a MainDataSource property. The MainDataSource is the data source that you design when you drop controls into the form.

XPathNavigator – Variables of type XPathNavigator are the real workhorses for xml manipulation. They can execute xpath, add and delete child xml elements. They can also navigate around an xml document, moving between parent nodes and child nodes, as well as between sibling nodes. They can also add, edit and remove xml elements or attributes.

XPathNodeIterator – When you execute an xpath expression the results go into a XPathNodeIterator variable. XPathNodeIterator has the count of items found and a MoveNext() method that loads each items into the Current property. The Current property is an XPathNavigator.

The code to drive moving the items left and right and up and down is basically identical to the Hello World example. I make queries using xpath, I iterate through the results using the XPathNodeIterator, and I navigate around the document using the XPathNavigator setting values where appropriate.

Repeating Sections handled Manually

Here’s one piece of functionality I needed almost immediately when using InfoPath for a client. InfoPath allows some menus for adding and deleting sections in a repeating section, but you can’t show or hide the menus based off of logic with out of the box InfoPath. I’ll show you how using code.

Create a repeating section inside an InfoPath form and bind it to a repeating group called RepeatingSection inside a group called RepeatingSections. Add a button named AddNewSectionButton above the repeating section and a button inside the repeating section named DeleteThisSectionButton.

Here is the code to make it work.

public void AddNewSectionButton_Clicked(object sender, ClickedEventArgs e)
{
XPathNavigator mainNavigator = MainDataSource.CreateNavigator();
XPathNodeIterator repeatingSectionsNodeIterator = mainNavigator.Select("my:myFields/my:RepeatingSections", NamespaceManager);
repeatingSectionsNodeIterator.MoveNext();
repeatingSectionsNodeIterator.Current.AppendChild(@"");
}

public void DeleteThisSectionButton_Clicked(object sender, ClickedEventArgs e)
{
e.Source.DeleteSelf();
}

And the functionality should look like this

Now you have control over adding and deleting sections.

Changed Events

Some events like button events and the form loading event are straightforward but the changed events can get tricky. The trick with the changed events is that they bubble up through the xml document. For example, if you created a changed event at the root of your main data source, any change to the document would fire an event. Because events can come from any child node, you’ll probably want to examine the sender variable and the e.Operation variable inside your event handler  to make sure you are responding to the correct event.

lets add a changed event to our RepeatingSections element. I will also add a group named EventsFromRepeatingSections with a repeating text field named EventFromRepeatingSections to display some information about what is happening inside of our changed event handler.

public void RepeatingSections_Changed(object sender, XmlEventArgs e)
{
XPathNavigator mainNavigator = MainDataSource.CreateNavigator();
XPathNodeIterator EventsFromRepeatingSectionsNodeIterator = mainNavigator.Select("/my:myFields/my:EventsFromRepeatingSections", NamespaceManager);
EventsFromRepeatingSectionsNodeIterator.MoveNext();
EventsFromRepeatingSectionsNodeIterator.Current.AppendChild("" + " Source: " + (sender as XPathNavigator).Name + " Operation: " + e.Operation + " OldValue: " + e.OldValue + " NewValue: " + e.NewValue + "");
}

And now I will just click around my repeating sections and we can see the events bubbling up to my RepeatingSections element.

Developing with Visual Studio 2010

The visual studio that comes with InfoPath is called VSTA and is much different than the Visual Studio developers around the world adore. VSTA is at least 5 years old and allows for only .NET 2.0 development. It doesn’t support some of the most amazing .NET features such as LINQ. Now I will show you a little hack that will allow you to use .NET 3.5 and Visual Studio 2010 (VS2010) for your InfoPath development.

First you will need Visual Studio 2010. There is a free express edition that should suffice but if you are doing real development beg or borrow your way to a higher edition.

Create a new windows class library inside of VS2010 by going to File->New->Project->Windows Class Library. Make sure to give your class library the same name as your form! 

Next we need to copy some references from VSTA into our VS201o project. The references we need to move over are Microsoft.Office.Infopath, Microsoft.VisualStudio.Tools.Applications.Adapter, Microsoft.VisualStudio.Tools.Applications.Contract and System.AddIn.Contract. Simply copy the paths of from the properties window of your VSTA project when you click on the reference, and paste the path into the Browse tab of the add reference window inside of VS2010.

Here are the paths to the references on my computer

C:\Program Files\Microsoft Office\Office14\InfoPathOM\InfoPathOMFormServices\InfoPathOMFormServicesV12\Microsoft.Office.Infopath.dll

C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Tools.Applications.Adapter.dll

C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.Tools.Applications.Contract\8.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Tools.Applications.Contract.dll

C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.AddIn.Contract.dll
Next open up VSTA and right click on your project in the solution explorer. In  the Build Events section add the following commands. Your paths will be different but you should get the idea

"C:\Windows\Microsoft.NET\Framework64\v3.5\MSBuild.exe" "c:\users\administrator\documents\visual studio 2010\Projects\Form\Form\Form.csproj"

xcopy /y "C:\Users\administrator\Documents\visual studio 2010\Projects\Form\Form\bin\Debug\Form.dll" "$(TargetDir)"
Now copy the files FormCode.cs, FormCode.designer.cs and InfoPath.snk into the root of your VS2010 project.

Finally, right click on your project inside the VS2010 solution explorer, selecting signing, checking New, and selecting InfoPath.snk.

And your done!

One downside I’ve noticed is that debugging still needs to occur inside VSTA. This is actually a plus if you only have VS2010 express, which cannot attach to processes for debugging. But if you have a higher edition the debugging experience in VS2010 is much better than VSTA. If anyone can show me how to debug inside VS2010 I’d appreciate it!

Saturday, June 19, 2010

Enabling Kerberos in SharePoint 2010

 

Table of Contents

Enabling Kerberos for a SharePoint 2010 web site

Enabling Kerberos for SQL Server

Enabling Kerberos for a SharePoint 2010 Service Application

Practical Examples

     RSS Viewer Web Parts
     SharePoint 2010 External List

An excellent Kerberos Debugging Tool: DelegConfig

Advanced Kerberos Topics

     Double hop discussion
     Kerberos and IIS7 Kernel Mode

 

Enabling Kerberos for a SharePoint 2010 web site

lets first look at my server environment.
http://public.blu.livefilestore.com/y1peAAjcSBDLpXPVIBHMsOvgLW6uHBQQ1VVqXcKnkDdKLcwYg8Gdz-vDB0FnE-hhi9dcavePKgEPHIRVv_KLiviDQ/1.png?psid=1
I have four virtual machines. In the upper left I have a domain controller named DomainControl, in the upper right I have a SharePoint 2010 server named SharePoint2010, in the lower right I have a second SharePoint 2010 web front end named SharePoint2010W and in the lower left I have an SQL Server named SQL2008R2.

Lets start by logging into DomainControl and creating a new domain user that will host the application pool for our SharePoint 2010 web site. I'll name this user SP2010ServiceAccount. 
Now I will open up ADSI edit and go into the properties of SP2010ServiceAccount and add some service principal names (SPNs). DelegConfig (covered later in this blog) recommends you have both the NetBIOS name and fully qualified domain name (FQDN). Refer to MSDN for more information on SPNs. Once a domain account has SPNs a new Delegation tab will appear when you open the properties window from Active Directory Users and Computers. I’m going to be lazy here and trust delegation to any service. I’ll show you how to add individual services in the Enabling Kerberos for a SharePoint 2010 Service Application section.
I will then add SP2010ServiceAccount as a managed account inside of central administration.
Now I'm going to make a new web application on port 80.When you choose Kerberos you will get a popup warning about extra configuration you need to perform. If you followed the steps above you performed all the necessary steps.I'll use the Team Site definition for my site collection.
Now I’ll connect to http://SharePoint2010 from another computer (in my case I browsed from SQLServer2008R2) and then go look at the event log for SharePoint2010. In the security log for Logon events I see Kerberos for the logon process in the entry.

Enabling Kerberos for SQL Server

While I am using SQL Server 2008 R2, the same steps apply for previous editions. I already have a domain account SSDEServiceAccount running the MSSQLSERVER service. Lets hop on DomainControl and add the necessary SPNs. These SPNs may already exist from the SQL Server installation.Again I’m going to be lazy and trust this account to delegate to any service. See the Enabling Kerberos for a SharePoint 2010 Service Application section for details on how to add individual services instead.Here is a query you can execute to prove that SQL Server is enabled for Kerberos. You'll have to perform a join with other system tables for more information.

Enabling Kerberos for a SharePoint 2010 Service Application

Setting up Kerberos for a service application is the same for all service applications. I am going to use PerformancePoint services because its easy to get going.

I’ll start by creating a new domain account, SP2010PerfPointNow I’m going to do something funny. I’m going to make a dummy SPN called HTTP/PerfPoint. What I actually need is just some SPN here to give me the Delegation tab for the domain user.Now I’m going to do some configuration that may seem peculiar because its unrelated to Kerberos. I’m doing non-Kerberos configuration here because SharePoint web sites communicate with service applications using Claims authentication instead of Windows authentication. For the user SP2010PerfPoint, allow delegation for any authentication protocol.click the Add button and then click the User or Computer button, which will pop up a window where you can type in the service accounts of the services your service application needs to authenticate to. I am just going to communicate with SQL Server for this example.After you choose the user or computer and click OK, all the services that user or computer executes present themselves. Select the ones you want and click OK.When a service application needs to authenticate to a system through Windows authentication, it needs to convert the claims token to a Windows token. There is a Windows service, disabled by default, called the Claims to Windows Token Service. Start the service inside of SharePoint.Then open up the Windows services list and ensure the Claims to Windows Token Service is enabled and starts automatically. Also notice that this service runs as Local System. This is important because in order for this service to delegate we need to allow delegation for the computer instead of a domain account.There shouldn’t be any need to open up ADSI edit to assign any SPNs to the computer, the computer should already have some in there such as HOST. Open up Active Directory Users and Computers.Here I need to repeat the same steps performed for SP2010PerfPoint in regards to delegation. I need to allow delegation for any authentication provider, and choose the SPNs that I want. Because this service could be used to connect to an SSDE database, an SSAS Cube or a web service, I chose all three services.Now for the fruits of our labors. This blog is not about PerformancePoint Services so I’ll just glance over how to get it running.

Get the secure store service running first and generate a new key inside the secure store service. Then create a new PerformancePoint service application. Configure the PerformancePoint service to use an account as the unattended service account that has access to SQL Server. Then activate the PerformancePoint site collection and site features. Then create a PerformancePoint data sources library and in the New Document dropdown menu choose New PerformancePoint Data Source. A one-click application will launch and you can open your performance point data source library and choose to add a new data source.I’ll connect to the Microsoft provided AdventureWorks database for this example. The important thing to note here is that we are choosing the Per-user Identity. Test your connection and hope for the best. It worked for me!

Practical Examples

RSS viewer web part

In order to use RSS Viewer web parts that consume RSS feeds from SharePoint lists, we need Kerberos enabled.

I’ll start by creating a new list called RSS Data, and placing a single item inside the list called RSS Item. I will then get the RSS feed url for the list and I'll use that feed url in my RSS Viewer web parts. I will actually use two RSSVWPs with slightly different feed urls. The first feed url will start with http://sharepoint2010/ and the second feed url will start with http://sharepoint2010w/. This way I force a double hop scenario. See the Double Hop Discussion section for more details.

I’m going to go back into central admin and switch back to NTLM for a minute to show you what happens when we try to drive RSS viewer web parts from SharePoint list feeds without using Kerberos authentication.Now I will re-enable Kerberos authentication and everything works fine.

SharePoint 2010 External Lists

In order to create an external list inside SharePoint where people authenticate to the external system, we needed Kerberos enabled.

First I'll open up SharePoint designer 2010 and create an external content type based off of the AdventureWorks customers table. Notice how I've chosen to connect as the current user.
and now I’ll create an external list from my external content type… 

and I’ll make sure I have permission to access it… 

And here is the customer table. In case you are curious, if I switch back to NTLM this is what you would have seen; the classic SQL Server Anonymous User error.

An excellent Kerberos Debugging Tool: DelegConfig

Brian Murphy-Booth of the IIS team created a great tool called DelegConfig. it will give a report of common issues when trying to set up Kerberos for different services.

Download the zip file and you’ll find the contents of an ASP.NET web application. navigate to your WSS virtual directory and just add a 1 to all the default files. Then paste the DelegConfig files in there. Navigate to your SharePoint web site url and you should see the DelegConfig page instead. Run the wizard. The first phase in the wizard is for your SharePoint web site. any subsequent phases are for other systems like SQL Server.I’m going to simulate a screw up. Lets pretend I was absent minded when I entered the SPNs and put two forwards slashes instead of one.This is the report I would see from DelegConfig.Here is another hard to find problem: Your Internet Explorer might not have the Windows authentication check box checked.this report is the best you can ask for.As mentioned before, DelegConfig can also be used with SQL Server. Lets pretend that I messed up the SPNs for SQL Server.This is the report I would see if I added SQL Server to the DelegConfig report.

Advanced Kerberos Topics

Double hop discussion

Lets take a normal SharePoint environment. A user opens up a browser on their computer and requests a web page from a SharePoint 2010 web site and the SharePoint 2010 web site connects to SQL Server to get its data.

Browser –> SharePoint web site –> SQL Server.

This works fine with NTLM. Now I’m going to tell you that NTLM is incapable of passing credentials across two server boundaries, AKA a double hop. “But wait” you say, “On almost every SharePoint environment running NTLM there is a double hop. The scenario you just described previously is a double hop and it works fine with NTLM.”

The previous scenario works fine because SharePoint doesn’t connect to SQL Server as the user in the browser. It impersonates the app pool account whenever it needs data from the database.If the SharePoint web site needed to connect to any system as the user from the browser with NTLM, it would fail. The RSS Web Part example from a previous section would fail without Kerberos, because the web front end serving the RSS data needs to authenticate the browser user.Similarly, If SharePoint needs to get data from a non-SharePoint database like AdventureWorks in the examples from previous sections, Kerberos must be enabled.The diagram for a service application is even more complicated, as you now know. A browser request starts out with Windows authentication, then goes into Claims authentication, and then back to Windows authentication for any external services. 

Note that this diagram was borrowed from this excellent blog.

Kerberos and IIS7 Kernel Mode

IIS7 has something called Kernel mode which was supposed to make Kerberos much easier to configure. Unfortunately, SharePoint 2010 web sites can’t use it for reasons stated here.  However, SharePoint 2010 service applications do use Kernel mode authentication, but do not use Kerberos.