case study: how hotmail used "atlas" and asp.net to build a great user experience walter...

25
Case Study: How Hotmail Used Case Study: How Hotmail Used "Atlas" and ASP.NET to Build a "Atlas" and ASP.NET to Build a Great User Experience Great User Experience Walter Hsueh Walter Hsueh PRSL02 PRSL02 Development Lead Development Lead Microsoft Corporation Microsoft Corporation

Upload: derrick-bradley

Post on 25-Dec-2015

214 views

Category:

Documents


0 download

TRANSCRIPT

Case Study: How Hotmail Used "Atlas" and Case Study: How Hotmail Used "Atlas" and ASP.NET to Build a Great User Experience ASP.NET to Build a Great User Experience

Walter HsuehWalter HsuehPRSL02 PRSL02 Development LeadDevelopment LeadMicrosoft CorporationMicrosoft Corporation

2

Building The Microsoft Building The Microsoft Mail Beta In ASP.NET And Mail Beta In ASP.NET And "Atlas""Atlas"Web Mail LandscapeWeb Mail Landscape

Design Goals for Web Mail BetaDesign Goals for Web Mail Beta

Server and ClientServer and ClientProblem spaceProblem space

Issues that we faced and how we resolved.Issues that we faced and how we resolved.

"Atlas" Programming Model"Atlas" Programming ModelWhat is "Atlas" and why did we need it?What is "Atlas" and why did we need it?

How we converged on "Atlas"How we converged on "Atlas"

Tips and TricksTips and Tricks

3

The Internet Mail The Internet Mail LandscapeLandscapeCompetitive CharacteristicsCompetitive Characteristics

Cheap storageCheap storage

Large Inboxes, Large attachmentsLarge Inboxes, Large attachments

Unified Inbox, Unified StorageUnified Inbox, Unified Storage

Rich DHTML browser applicationsRich DHTML browser applications

4

Microsoft Web Mail BetaMicrosoft Web Mail Beta

Walter HsuehWalter HsuehDevelopment LeadDevelopment LeadHotmail FrontdoorHotmail Frontdoor

5

Web Mail Client Web Mail Client ArchitectureArchitectureBrowser Client GoalsBrowser Client Goals

Optimize the Perceived PerformanceOptimize the Perceived PerformanceThe user experience must seem fastThe user experience must seem fast

Non-postback design using XMLHTTPNon-postback design using XMLHTTPMinimize the wire (protocol) costMinimize the wire (protocol) cost

Pure data? HTML? XML?Pure data? HTML? XML?

Utilize the client capabilitiesUtilize the client capabilitiesCPU capacity and memory (move RegEx to client)CPU capacity and memory (move RegEx to client)The browser DOM as stateThe browser DOM as state

Leverage CSSLeverage CSSPositioning/layoutPositioning/layoutThemeing/typology Themeing/typology

6

Web Mail Client Web Mail Client ArchitectureArchitectureBuilt upon "Atlas", Part IBuilt upon "Atlas", Part I

MSN organizational driveMSN organizational driveDevelop a viable client development platform, or Develop a viable client development platform, or "JavaScript runtime""JavaScript runtime"Used across MSN properties (Hotmail, MSN.com Spaces, Used across MSN properties (Hotmail, MSN.com Spaces, Start.com)Start.com)

Strategic company alignmentStrategic company alignmentCombine company-wide JavaScript design patterns, Combine company-wide JavaScript design patterns, components, and frameworks into "Atlas"components, and frameworks into "Atlas"

Core set of system classes and objects in Core set of system classes and objects in JavaScript: JavaScript:

Namespaces, Eventing Model, Component ModelNamespaces, Eventing Model, Component ModelInheritance and interfaces, EnumerationsInheritance and interfaces, EnumerationsXMLHTTP stackXMLHTTP stack

7

Web Mail Client Web Mail Client ArchitectureArchitectureBuilt upon "Atlas", Part IIBuilt upon "Atlas", Part II

"Atlas" Bindings"Atlas" BindingsJavaScript object that encapsulates functionality that can be JavaScript object that encapsulates functionality that can be attached to DOM elements declaratively or programmaticallyattached to DOM elements declaratively or programmatically

Interface to browser eventsInterface to browser events

Unify client event model: use "Atlas" Bindings to capture and Unify client event model: use "Atlas" Bindings to capture and drive all UX inputdrive all UX input

Command BindingsCommand Bindings

<div class='Command' FireAnt:commandname='Reply'><img <div class='Command' FireAnt:commandname='Reply'><img src="images/i_reply.gif"><% src="images/i_reply.gif"><% Response.Write(StaticContentHelper.GetString("ReadMessage.Reply")); Response.Write(StaticContentHelper.GetString("ReadMessage.Reply")); %></div>%></div><div class='Command' FireAnt:commandname='ReadMessage' <div class='Command' FireAnt:commandname='ReadMessage' FireAnt:keycode='13' FireAnt:layout='InboxDefault' FireAnt:keycode='13' FireAnt:layout='InboxDefault' style='display:none'></div>style='display:none'></div>

<web:binding selector=".Command" type="FireAnt.Command" <web:binding selector=".Command" type="FireAnt.Command" namespace="FireAnt"></web:binding>namespace="FireAnt"></web:binding>

8

Web Mail Client Web Mail Client ArchitectureArchitectureBuilt upon "Atlas", Part IIIBuilt upon "Atlas", Part IIICommand Binding code-behindCommand Binding code-behindFireAnt.Command = function(boundElem, rgParams)FireAnt.Command = function(boundElem, rgParams){{ //boilerplate binding setup//boilerplate binding setup FireAnt.Command.inherit(this);FireAnt.Command.inherit(this); var m_obj = this, m_sCommandName = null, m_sKeyCode = null, m_sLayout = null, var m_obj = this, m_sCommandName = null, m_sKeyCode = null, m_sLayout = null, m_bCtrlKey = null;m_bCtrlKey = null; //public//public this.Init = function()this.Init = function() {{ //get the name of the command//get the name of the command m_sCommandName = rgParams.commandname.toString();m_sCommandName = rgParams.commandname.toString();

//connect a click handler//connect a click handler switch(boundElem.tagName)switch(boundElem.tagName) {{ case "SELECT":case "SELECT": boundElem.attachEvent("onchange", onchange);boundElem.attachEvent("onchange", onchange); boundElem.attachEvent("onmousewheel", onmousewheel);boundElem.attachEvent("onmousewheel", onmousewheel); break;break; default:default: boundElem.attachEvent("onclick", onclick);boundElem.attachEvent("onclick", onclick); break;break; }}......

9

Web Mail Client Web Mail Client ArchitectureArchitectureBuilt upon "Atlas", Part IVBuilt upon "Atlas", Part IVAutoComplete Binding:AutoComplete Binding:

<div unselectable="on" class="AutoCompleteList" FireAnt:id="<%=this.listId<div unselectable="on" class="AutoCompleteList" FireAnt:id="<%=this.listId%>" FireAnt:size="<%=this.listSize%>" style="position:absolute; overflow-%>" FireAnt:size="<%=this.listSize%>" style="position:absolute; overflow-x:hidden; overflow-y:scroll; display:none;">x:hidden; overflow-y:scroll; display:none;">

<web:binding selector=".AutoCompleteList" type="FireAnt.AutoCompleteList" <web:binding selector=".AutoCompleteList" type="FireAnt.AutoCompleteList" namespace="FireAnt"></web:binding>namespace="FireAnt"></web:binding>

Folder List Binding:Folder List Binding:<div id="ctlFolderList" class="FolderList Menu" tabindex="0" <div id="ctlFolderList" class="FolderList Menu" tabindex="0" FireAnt:paging=falseFireAnt:paging=falseFireAnt:multiselect=false FireAnt:itemtype="FolderItem“ FireAnt:multiselect=false FireAnt:itemtype="FolderItem“ FireAnt:menuname="Folders“ FireAnt:menuname="Folders“ FireAnt:menucommands="{key:'rename',cmd:'RenameFolder',icon:'i_rename.gif'FireAnt:menucommands="{key:'rename',cmd:'RenameFolder',icon:'i_rename.gif',label:‘Rename Folder'}, ,label:‘Rename Folder'}, {key:'delete',cmd:'DeleteFolder',icon:'i_folder_delete.gif',label:‘Delete {key:'delete',cmd:'DeleteFolder',icon:'i_folder_delete.gif',label:‘Delete Folder'}"></div>Folder'}"></div>

<web:binding selector=".FolderList" type="FireAnt.FolderList" <web:binding selector=".FolderList" type="FireAnt.FolderList" namespace="FireAnt"></web:binding>namespace="FireAnt"></web:binding>

10

Web Mail Client Web Mail Client ArchitectureArchitectureBuilt upon "Atlas", Part VBuilt upon "Atlas", Part V

What did "Atlas" solve for Web Mail Beta?What did "Atlas" solve for Web Mail Beta?"Atlas" Script Core"Atlas" Script Core

JavaScript extensions and common shared frameworkJavaScript extensions and common shared framework

Target single browser platform (compatibility layer)Target single browser platform (compatibility layer)

Web Mail's JavaScript classes, eventing model, and Web Mail's JavaScript classes, eventing model, and communications core directly leverage "Atlas"communications core directly leverage "Atlas"

"Atlas" Bindings"Atlas" BindingsConsistent, declarative design patternConsistent, declarative design pattern

Component design and reuseComponent design and reuse

No use of loose html attributes for eventsNo use of loose html attributes for events

Clean separation of UI and JavaScript codeClean separation of UI and JavaScript code

Cross-browser implementation of a DHTML behaviorCross-browser implementation of a DHTML behavior

Automation interface for testabilityAutomation interface for testability

11

Web Mail UX AutomationWeb Mail UX Automation

Walter HsuehWalter HsuehDevelopment LeadDevelopment LeadHotmail FrontdoorHotmail Frontdoor

12

Web Mail Client Web Mail Client ArchitectureArchitectureXMLHTTP RequirementsXMLHTTP Requirements

"FireAnt Proxy-Protocol" (FPP) design"FireAnt Proxy-Protocol" (FPP) designNon-postback design requires a browser client to invoke Non-postback design requires a browser client to invoke server functionality by XMLHTTPserver functionality by XMLHTTPAn auto-generated and easily maintainable set of client An auto-generated and easily maintainable set of client proxiesproxies representing server methods and server objects representing server methods and server objectsA A protocolprotocol that minimizes the over-the-wire cost that minimizes the over-the-wire cost

FPP Server PlatformFPP Server PlatformFramework for exposing C# methods and objects that Framework for exposing C# methods and objects that they can be called from JavaScriptthey can be called from JavaScriptSynchronous / Asynchronous C# methods are supported.Synchronous / Asynchronous C# methods are supported.

FPP Client PlatformFPP Client PlatformJavaScript components built upon "Atlas" Script CoreJavaScript components built upon "Atlas" Script CoreOOP supportOOP supportXMLHTTP supportXMLHTTP support

13

Web Mail Client Web Mail Client Architecture Architecture XMLHTTP Platform DesignXMLHTTP Platform DesignJavaScript JavaScript

Browser Browser ApplicationApplication

Atlas Script CoreAtlas Script Core

C# ApplicatonC# Applicaton

FPP PackagerFPP Packager

FPP JavaScript generatorFPP JavaScript generator

FPP DemarshallerFPP Demarshaller

FPP Parameter ParserFPP Parameter Parser

FPP Stub DispatcherFPP Stub Dispatcher

JavaScript eval()JavaScript eval()JavaScript stubsJavaScript stubs

14

Web Mail Client Web Mail Client Architecture Architecture XMLHTTP Implementation IXMLHTTP Implementation I

C# objects and methods are tagged as FPP C# objects and methods are tagged as FPP exportableexportable

Proxies are generatedProxies are generatedJavaScript objects and upstream serializationJavaScript objects and upstream serialization

Server demarshalling from JavaScriptServer demarshalling from JavaScript

Server marshalling into JavaScriptServer marshalling into JavaScript

JavaScript eval() operates as client JavaScript eval() operates as client demarshallerdemarshaller

System.Enum (C#) -> Web.Flags (JS)System.Enum (C#) -> Web.Flags (JS)

System.Object supportedSystem.Object supported

IDictionary -> JavaScript associative arrayIDictionary -> JavaScript associative array

15

Web Mail Client Web Mail Client Architecture Architecture XMLHTTP Implementation IIXMLHTTP Implementation II

C# method: GetFolderDataC# method: GetFolderData

[FppMethodBegin("GetFolderData", FppHttpVerbType.GET, 0)][FppMethodBegin("GetFolderData", FppHttpVerbType.GET, 0)]public IAsyncResult BeginGetFolderData(public IAsyncResult BeginGetFolderData( Guid folderId,Guid folderId, SortBy sortBy,SortBy sortBy, uint count,uint count, bool ascendingOrder,bool ascendingOrder, ListNavigateDirection direction,ListNavigateDirection direction, uint pageSize,uint pageSize, int totalMessages,int totalMessages, Guid anchorMessageID,Guid anchorMessageID, Guid selectedMessageID,Guid selectedMessageID, AsyncCallback asyncCallback,AsyncCallback asyncCallback, Object asyncStateObject asyncState ))

[FppMethodEnd("GetFolderData")][FppMethodEnd("GetFolderData")]public BootstrapData EndGetFolderData(IAsyncResult ar)public BootstrapData EndGetFolderData(IAsyncResult ar)

16

Web Mail Client Web Mail Client Architecture Architecture XMLHTTP Implementation IIIXMLHTTP Implementation III

JavaScript Proxy:JavaScript Proxy:

registerNamespace("HM");registerNamespace("HM");HM = new Web.Network.FppProxy();HM = new Web.Network.FppProxy();......HM.ListNavigateDirection = HM.ListNavigateDirection = Web.Flags.create("FirstPage",0,"LastPage",1,"NextPage",2,"PreviousPage",3,Web.Flags.create("FirstPage",0,"LastPage",1,"NextPage",2,"PreviousPage",3,"CurrentPage",4);"CurrentPage",4);HM.FolderType = Web.Flags.create("SYSTEM",0,"USER_DEFINED",1,"OIM",2);HM.FolderType = Web.Flags.create("SYSTEM",0,"USER_DEFINED",1,"OIM",2);......HM.registerFppClass("microsoft_msn_hotmail_bootstrapdata",HM.registerFppClass("microsoft_msn_hotmail_bootstrapdata",[Web.Network.FppProxy.__array("Folders"),Web.Network.FppProxy.__string("Me[Web.Network.FppProxy.__array("Folders"),Web.Network.FppProxy.__string("Meter"),Web.Network.FppProxy.__custom(HM.microsoft_msn_hotmail_hotmailaddrester"),Web.Network.FppProxy.__custom(HM.microsoft_msn_hotmail_hotmailaddressbookcontactsandgroups, sbookcontactsandgroups, "Contacts"),Web.Network.FppProxy.__array("Headers"),Web.Network.FppProxy._"Contacts"),Web.Network.FppProxy.__array("Headers"),Web.Network.FppProxy.__string("MessageHtml"),Web.Network.FppProxy.__string("FirstMessageId"),Web_string("MessageHtml"),Web.Network.FppProxy.__string("FirstMessageId"),Web.Network.FppProxy.__custom(HM.microsoft_msn_hotmail_bootstrapconfiguration.Network.FppProxy.__custom(HM.microsoft_msn_hotmail_bootstrapconfiguration, , "Configuration"),Web.Network.FppProxy.__primitive("SelectedMessageIndex")]"Configuration"),Web.Network.FppProxy.__primitive("SelectedMessageIndex")]););......

17

Web Mail Client Web Mail Client Architecture Architecture XMLHTTP Implementation IVXMLHTTP Implementation IV

Server demarshalling (inbound) stub:Server demarshalling (inbound) stub:public static IAsyncResult public static IAsyncResult Begin_Microsoft_Msn_Hotmail_MailBox_GetFolderData(Microsoft.Msn.Hotmail.MailBox Begin_Microsoft_Msn_Hotmail_MailBox_GetFolderData(Microsoft.Msn.Hotmail.MailBox @__fppMailBoxService, string folderId, string sortBy, string count, string @__fppMailBoxService, string folderId, string sortBy, string count, string ascendingOrder, string direction, string pageSize, string totalMessages, string ascendingOrder, string direction, string pageSize, string totalMessages, string anchorMessageID, string selectedMessageID, System.AsyncCallback asyncCallback, object anchorMessageID, string selectedMessageID, System.AsyncCallback asyncCallback, object asyncState)asyncState){{ // unmarshall parameters// unmarshall parameters System.Guid @__folderId = FireAntParamParser.ParseGuid(folderId);System.Guid @__folderId = FireAntParamParser.ParseGuid(folderId); Microsoft.Msn.Hotmail.SortBy @__sortBy = (Microsoft.Msn.Hotmail.SortBy) Microsoft.Msn.Hotmail.SortBy @__sortBy = (Microsoft.Msn.Hotmail.SortBy) Enum.Parse(typeof(Microsoft.Msn.Hotmail.SortBy), sortBy);Enum.Parse(typeof(Microsoft.Msn.Hotmail.SortBy), sortBy); uint @__count = (System.UInt32) uint @__count = (System.UInt32) Convert.ChangeType(FireAntParamParser.UnEscapeText(count), typeof(System.UInt32));Convert.ChangeType(FireAntParamParser.UnEscapeText(count), typeof(System.UInt32)); bool @__ascendingOrder = (System.Boolean) bool @__ascendingOrder = (System.Boolean) Convert.ChangeType(FireAntParamParser.UnEscapeText(ascendingOrder), Convert.ChangeType(FireAntParamParser.UnEscapeText(ascendingOrder), typeof(System.Boolean));typeof(System.Boolean));

......

// invoke __fppMailBoxService.BeginGetFolderData// invoke __fppMailBoxService.BeginGetFolderData return __fppMailBoxService.BeginGetFolderData(__folderId, __sortBy, __count, return __fppMailBoxService.BeginGetFolderData(__folderId, __sortBy, __count, __ascendingOrder, __direction, __pageSize, __totalMessages, __anchorMessageID, __ascendingOrder, __direction, __pageSize, __totalMessages, __anchorMessageID, __selectedMessageID, asyncCallback, asyncState);__selectedMessageID, asyncCallback, asyncState);}}

18

Web Mail Client Web Mail Client Architecture Architecture XMLHTTP Implementation VXMLHTTP Implementation VServer outbound stub:Server outbound stub:public static Microsoft.Msn.Hotmail.BootstrapData public static Microsoft.Msn.Hotmail.BootstrapData End_Microsoft_Msn_Hotmail_MailBox_GetFolderData(Microsoft.Msn.Hotmail.MailBox End_Microsoft_Msn_Hotmail_MailBox_GetFolderData(Microsoft.Msn.Hotmail.MailBox @__fppMailBoxService, out object[] outRefParams, System.IAsyncResult ar)@__fppMailBoxService, out object[] outRefParams, System.IAsyncResult ar){{ // invoke __fppMailBoxService.EndGetFolderData// invoke __fppMailBoxService.EndGetFolderData Microsoft.Msn.Hotmail.BootstrapData returnObject = Microsoft.Msn.Hotmail.BootstrapData returnObject = @__fppMailBoxService.EndGetFolderData(ar);@__fppMailBoxService.EndGetFolderData(ar); // create out/ref param holder object[]// create out/ref param holder object[] outRefParams = new object[0];outRefParams = new object[0]; return returnObject;return returnObject;}}

Server outbound ToJavaScript():Server outbound ToJavaScript():case "Microsoft.Msn.Hotmail.MailBox.GetFolderData":case "Microsoft.Msn.Hotmail.MailBox.GetFolderData":{{ stubReturn = stubReturn = _Stubs.End_Microsoft_Msn_Hotmail_MailBox_GetFolderData(microsoft_msn_hotmail_mailbox, _Stubs.End_Microsoft_Msn_Hotmail_MailBox_GetFolderData(microsoft_msn_hotmail_mailbox, out outRefParams, ar);out outRefParams, ar); break;break;}}......fppReturnValue = FppReturnPackage.ToJavaScript(fppStatus, stubReturn, outRefParams, fppReturnValue = FppReturnPackage.ToJavaScript(fppStatus, stubReturn, outRefParams, fppEx, string.Empty, fppProfile, _FireAntConvert.Converter);fppEx, string.Empty, fppProfile, _FireAntConvert.Converter);return fppReturnValue;return fppReturnValue;

19

Hotmail Server LoadHotmail Server LoadPerformance/Scalability Performance/Scalability RequirementsRequirements

Mail StatisticsMail Statistics~200M active users~200M active users

On a typical day...On a typical day...3.3B email messages3.3B email messages

1.5B spam messages blocked at router1.5B spam messages blocked at router1.0B deleted messages (detected as spam)1.0B deleted messages (detected as spam)0.5B sent to Junk Mail Folder0.5B sent to Junk Mail Folder

>100M sent messages/day>100M sent messages/day80M-100M logins/day80M-100M logins/day5000 peak logins/sec5000 peak logins/sec

20

Web Mail Server Web Mail Server Architecture Architecture Data requirementsData requirements

Average byte size of messages increasingAverage byte size of messages increasingIO (e.g., TCP, HTTP, disk) is limiting factorIO (e.g., TCP, HTTP, disk) is limiting factorDo not want to block threads waiting for Do not want to block threads waiting for long-running operations long-running operations

Thread exhaustionThread exhaustionInefficient CPU utilizationInefficient CPU utilization

MIME is a very inefficient storage formatMIME is a very inefficient storage format> 1.33x size increase due to encoding, MIME part > 1.33x size increase due to encoding, MIME part duplication.duplication.

Original MIME text must be preservedOriginal MIME text must be preservedStreamed and asynchronous approaches to Streamed and asynchronous approaches to manipulating large MIME buffersmanipulating large MIME buffers

21

Web Mail Server Web Mail Server Architecture Architecture Asynchronous Design, Part IAsynchronous Design, Part I

Fully asynchronous programming model Fully asynchronous programming model Increased data and IO access patternsIncreased data and IO access patternsSynchronous design limitations in existing HotmailSynchronous design limitations in existing Hotmail

Fixed-Size ASP.NET ThreadpoolFixed-Size ASP.NET ThreadpoolConsidered dedicated IO threadpool but not necessary for Considered dedicated IO threadpool but not necessary for performance objectivesperformance objectives

Async design avoids blocking on threads Async design avoids blocking on threads High throughput and thread utilizationHigh throughput and thread utilization

Async design drives good design patternsAsync design drives good design patternsLimits the use of global variablesLimits the use of global variablesPassing state ("context") from callback to callbackPassing state ("context") from callback to callbackexecution components and callbacksexecution components and callbacks

22

Web Mail Server Web Mail Server Architecture Architecture Asynchronous Design, Part IIAsynchronous Design, Part II

Significantly more difficult programming model Significantly more difficult programming model than synchronous designthan synchronous designDifficult to infer the call sequenceDifficult to infer the call sequence

Partial callstacks in async componentsPartial callstacks in async componentsRequires a callgraph structure to follow sequence Requires a callgraph structure to follow sequence of callback componentsof callback components

Harder to debug and profileHarder to debug and profileEntry and exit points split across functionsEntry and exit points split across functions

Deadlock and race conditions on thread attach Deadlock and race conditions on thread attach and detachand detachReconstruction of global variables (e.g., Reconstruction of global variables (e.g., HttpContext.Current) on thread entry/exitHttpContext.Current) on thread entry/exit

23

Web Mail Beta StatusWeb Mail Beta Status

Internally used within MicrosoftInternally used within Microsoft

Beta rollout underway, ramp up to Beta rollout underway, ramp up to scalescale

Sign up at the end of this session Sign up at the end of this session with your @hotmail.com account if with your @hotmail.com account if you would like to be part of the beta you would like to be part of the beta program.program.

Questions? [email protected]? [email protected]

24

Community ResourcesCommunity Resources

At PDCAt PDC[PRS312] ASP.NET: Future Directions for [PRS312] ASP.NET: Future Directions for Developing Rich Web Applications with Atlas Developing Rich Web Applications with Atlas (Part 1)(Part 1)

Wednesday 9/14 at 5:00pmWednesday 9/14 at 5:00pm

[PRS220]: ASP.NET: Future Directions for [PRS220]: ASP.NET: Future Directions for Developing Rich Web Applications with Atlas Developing Rich Web Applications with Atlas (Part 2)(Part 2)

Thursday 9/15 at 11:30amThursday 9/15 at 11:30am

[PRSL04] MSN: Extending Start.com Using [PRSL04] MSN: Extending Start.com Using WidgetsWidgets

Thursday 9/15 at 1:00pmThursday 9/15 at 1:00pm

Ask The Experts table: Thursday 9/15, 6:30pm-Ask The Experts table: Thursday 9/15, 6:30pm-9:00pm9:00pmPRS Track lounge: Wednesday 9/14 2:30-5pmPRS Track lounge: Wednesday 9/14 2:30-5pm

After PDCAfter PDCIf you missed this related session, watch it on If you missed this related session, watch it on the DVDthe DVD

© 2005 Microsoft Corporation. All rights reserved.This presentation is for informational purposes only. Microsoft makes no warranties, express or implied, in this summary.