Simple Human Workflow with Windows Workflow Foundation
Performing Human Workflow with Windows Workflow Foundation using Simple Mail Transfer Protocol and Office Live Communication Server
Chris J.T. Auld
Kognition Consulting Limited
Windows Workflow Foundation (WF)
Simple Mail Transfer Protocal (SMTP)
Office Live Communication Server
Summary: Coordinate the interactions between real human beings in software using features found in Windows Workflow Foundation. (25 printed pages)
Click here to download SetupEmailSample.msi.
Workflow: the Human Aspect
About the Sample Application
Using the Sample Activities
Workflow: the Human Aspect
Increasingly we need to be able to coordinate the interactions between real human actors in software—humans are, of course, a key participant in almost every software system. In this article we will discuss why we want to implement human workflow. We'll look at some of the common challenges faced when involving humans in structured workflow systems. Finally, we'll look at a sample application that models a leave (or vacation) approval workflow showing some techniques for communicating with human workflow actors using mechanisms familiar to them.
What Is Human Workflow?
Human workflow involves coordinating business processes that involve people. Within human workflow these people communicate with various systems and other people in a business process implemented in software using a workflow model. Using a model, we can take pre-built units of behavior and define a workflow that coordinates them. The key to human workflow is that those units of behavior represent not only system performed actions, but also actions and decisions undertaken by human actors.
There are many other factors in human workflow solutions, such as a business user interface, end user task management, aggregation of events for monitoring, and management by IT operations. In this simple project we are not implementing these or a server application for executing workflows. Instead, we are trying to give you a developer introduction to a simple way to start implementing human workflow.
Let's think about a very simple example of human workflow—leave approval. In almost every case in which employees want to take leave from their employer, they will need to go through some sort of approval process. Depending on the size of the organization, this may range from a simple knock on their manager's door to a sophisticated human resources software package. Whatever the case, it involves the interaction of a number of people: the employee, his or her manager, and possibly other members of the organization such as human resources or payroll staff. It's a process that can potentially span several days or even weeks. The process may even need to change partway through—consider what would happen should a manager leave mid-process before they the application is approved.
Why Use Workflow
This simple workflow could be quite easily created in code. So what do we gain by using a workflow model for our system? In particular, what specific advantages does a model offer to human workflow?
The hard part of implementing the previously described workflow is not inherent in the process being modeled. Rather, it is all the difficulty involved in making the process robust to the vagaries of human and computer system behavior. Workflow involving humans often spans days or weeks; how do we make the system robust enough to survive a system reboot? What happens if people change their minds?
Windows Workflow Foundation provides the plumbing for dealing with these issues. It is able to serialize half-complete workflows out to persistent storage. It has support for compensation so that we can define the action to be taken in exception cases. Say we have modeled a leave approval workflow like that in the sample code. You cannot just 'rollback' human actors like you would a database if their leave is cancelled, but you can set out a known set of actions to take in such a situation that will compensate for the changed decision.
Flexible Flow Control
Human beings are non-deterministic—they literally have 'minds of their own.' What would happen if the manager in our example decided halfway through the process that another manager needed to review the document? By using a workflow model we can build systems that can cope with change and these sorts of exceptional cases. In many systems it is these edge cases that pose the biggest risk and cost to running the business.
Windows Workflow Foundation allows running workflows to be reconfigured on the fly. New steps can be added and redundant steps skipped, all while maintaining visibility and an audit of the actions taken through the monitoring mechanism. This ability to change means that we can reduce the number of scenarios in which users abandon the system and revert to some sort of 'out of band' process, like a phone call or a walk down the corridor to their colleagues' office.
Long Running and Stateful
Processes involving human interaction tend to take a relatively long period of time. You would struggle to get three business executives to sign off on a major financial transaction in under 10 days. Three distributed databases, on the other hand, can easily communicate and commit a transaction in less than 10 seconds. Programming frameworks have not traditionally dealt with long-running work particularly well. We usually have to write a lot of code to ensure that information is persisted out to disk while waiting on human input, and we're generally responsible for waking the process back up again once we have received it.
Windows Workflow Foundation provides a framework for executing these long running business processes. It provides logic for managing the persistence of state while waiting for external events.
Imagine the difficulty of building an application without being able to see your code in action in the debugger. Yet we deploy complex systems that provide almost no process visibility to end users, managers and administrators.
Using the monitoring capability of Windows Workflow Foundation we can allow users to see where they are in the workflow process; management can use the monitoring dataset to pull summary reports for an overall picture of the system operation and finally administrators can view historical information to determine what might have caused aberrant behavior.
Design Time Transparency
Place a business executive in front of a whiteboard and ask them to describe a critical business process for you and nine times out of ten they will draw you some sort of flow diagram. Likewise, when we need to explain to a human user what their role is in a complex system we will often do so by way of some sort of diagram. In Windows Workflow Foundation, we can model our application logic using the same sort of diagrammatic style that we use to communicate business logic with our users.
About the Sample Application
The goal of the sample application is to demonstrate how to involve human actors in a simple workflow scenario. The scenario is the approval of leave for an employee. The employee submits a leave request and this is forwarded, by email or instant messenger, to their manager. If the manager takes too long to reply then the request is escalated the next level of management.
The important thing to note here is that we use communication mechanisms that are familiar to the user. Instant messenger and email are the communications mechanisms over which we'd expect ad hoc approval processes to be carried out. As such they make ideal candidates when our goal is to bring an otherwise ad-hoc process under the control of workflow.
The sample doesn't show you how to build a server application that can host workflows like this—it does however include a simple windows forms application to host the workflows for demonstration purposes.
The following diagram shows the workflow that we are modeling:
The workflow that is run by default uses email to route the requests. See the section below on the Real Time Communication activity library for details on how to enable instant message communications with Live Communications Server 2005 in the workflow.
Installing and Running the Sample Application
When you run the installer for the sample application it will ask for some sample email addresses during the install process. These email addresses are used to configure the App.config file and a simple XML file (OrgChart.xml) that is used to represent a virtual organizational hierarchy. You can read about more detail on OrgChart.xml below.
The installed sample uses Microsoft Outlook for sending and receiving email so that you can try it out with minimal configuration. You should have at least one other person with email that you can test the leave approvals with. The sample also has code that can use Microsoft Exchange Server or SMTP Services in Internet Information Server for email. You can configure these separately.
After opening the HumanEmailWorkflow solution you should run the HumanWorkflowWinformDemo project.
Before submitting a leave request using the application you should ensure that Microsoft Outlook is running. The Leave Form will be pre-populated with the some of the details specified at install time ready to run the sample.
You may see warning boxes pop up in Outlook. This is the Outlook security model kicking in to restrict our application from running what it thinks may be malicious code. Take a look at the section below titled 'Preventing Outlook Warning Boxes' for information on how to stop these appearing in a production application, but, for the moment you need to allow the application to access Outlook address details:
You will also need to let Outlook send mail on your behalf:
Based on the values you entered at setup time the sample application will submit the leave request first to the 'Manager' and then to the 'Grand Manager' specified in the sample OrgChart.xml. Follow the instructions in the email to approve or decline the request. The sample is setup such that it can be easily used with a single email address acting as Employee, Manager and Grand Manager—obviously in a real world environment these would be different addresses.
Stepping Through the Workflow
You may want to set some breakpoints in the running workflow to gain a better understanding of when messages are sent and received. Windows Workflow Foundation provides a graphical debugger—this means that you can set breakpoints graphically on activities in the workflow designer. Start by opening the EmailWorkflow.cs file from the LeaveWorkflows project.
This will open the EmailWorkflow into the workflow designer. You can then set a breakpoint on an activity by either using the right click context menu or pressing F9.
You can see that a breakpoint has been set on the activity—there is now a red breakpoint dot on the left hand side.
When you run the application from within Visual Studio the debugger will break on this activity and from there you can step through the code and the workflow definition.
Using the Sample Activities
The key to the sample application is a set of eight custom activities. These activities provide example implementations for two key aspects of functionality needed for many human workflow solutions.
Directory Lookup. The ability to discover relationships between people and systems is critical. In most modern IT systems this data is stored in a directory such as Microsoft Active Directory. The sample includes activities to lookup user and relationship information from AD.
Communication. Human workflow participants need to be able to communicate between each other and with the system. The sample activities demonstrate how to communicate with users as part of the workflow. The communication mechanisms supported are email (via Outlook, or the Windows SMTP Service in Internet Information Server or Microsoft Exchange using WebDAV) and instant messages (via Microsoft Office Live Communications Server 2005).
Using the Active Directory Activity
In order for our applications to help manage human interactions we need a mechanism to retrieve details about our human actors and the relationships between them. In most organizations the primary electronic store of information about identities and their relationships is Active Directory. The Active Directory Lookup activity library allows you to retrieve details about users from Active Directory. It also lets you retrieve lists of users based on their relationships. So, for example, we can retrieve all the direct reports for a given manager or all the users in a given group. Then, for each of those users we can retrieve their details from AD—information like names and email address.
In order to provide support for organizations without Active Directory the activity library also allows the use of an XML file to represent the directory structure. An example XML file called 'OrgChart.xml' is provided with the sample application.
The Active Directory library is made up of two activities. The DirectoryLookupActivity is used to lookup relationships between users. The UserDetailsLookupActivity is used to retrieve detailed information on a given user.
The DirectoryLookupActivity and UserDetailsLookupActivity share a number of properties.
|DirectoryUri||String||Location of the directory.
|QuerySource||ADActivityLookupSource||Enum indicating the source of the directory lookup to perform. Either:
ActiveDirectory or XmlFile.
Both activities also share a common pair of events.
|Queried||EventHandler||This event is raised after the query has completed against the directory.|
|Querying||EventHandler||This event is raised just prior to the query being executed against the directory.|
Using the DirectoryLookupActivity
The DirectoryLookupActivity is used to retrieve a list of one or more users accounts based on relationships declared in the directory. Relationships include things such as groups and management hierarchies. The activity uses an object name (group or user) and a query type to retrieve a list of accounts.
As well as the common properties listed above, the DirectoryLookupActivity exposes the following properties.
|QueryResults||ArrayList||This property is populated with the results of the query. A list of strings representing user account names.|
|QueryType||ADActivityLookupType||Enum indicating the type of directory lookup to perform. Either:
Manager—Returns the users manager, or;
Subordinates—Returns the users directly subordinate to this user, or;
Peers—Returns the users peers, i.e. those managed by the same manager, or;
GroupExpansion—Returns the users who are members of the provided group.
|Query||String||The object name to query on.
Using the UserDetailsLookupActivity
The UserDetailsLookupActivity is used to retrieve the details of a given user account from the directory. Basic user information is included in the sample implementation but this can be easily extended to retrieve other information stored in the directory. The activity uses a user account name to retrieve the details.
|Query||String||The object name to query on.
|RetrievedUserData||UserData||This property is populated with the results of the query. That is the user that is found in the directory. The UserData type is a simple object with public properties. This allows easy binding to the attributes of the user within workflows that use this activity.|
Using the Mail Activity
As discussed above, email is a key technology for enabling the interaction between human actors. This activity library provides support to allow workflows to communicate with human actors via email. This activity also provides a demonstration of how runtime services can be used to support communications into and out of our workflows. The library includes a pair of activities, SendEmail and SmtpMailReceived, used for sending and receiving email respectively. These activities are built as sub-classes of two out of the box activities CallExternalMethod and HandleExternalEvent. We could have simply used these activities in our workflow and referenced the service interface. However, by building sub-classes we are able to achieve a much richer experience when the activities are used in the designer. This makes the activities easier to reuse in other workflow applications.
Workflow instances run within the context of the workflow runtime. As a general rule when they communicate with the outside world, that is other systems or human actors, they should do so through the runtime. To do this an activity can ask the runtime to provide a service that implements a specific interface. The activity can then use this interface to send and receive communications. Using this approach provides a level of intermediation between activities that allow communication and the mechanism by which they communicate. It means, for example, that the host can provide a different implementation of a service depending on whether it is running on a server or a client. The Smtp Mail Activity provided supports three different implementations of the SmtpMailService interface. Take a look in the SmtpMailServices project and you will find:
SmtpMailDropService. This service sends mail directly via an Smtp server. It receives mail by monitoring the drop directory of the Windows Smtp Service. This service is suitable for server side production use.
SmtpOutlookMailService. This service sends and receives mail by communicating with Outlook using the Outlook object model. This service will show a warning box as it accesses Outlook information unless run in the context of a trusted add in. It is not suitable for use on the server, but it is ideal to be incorporated into an Outlook add-in using either Visual Studio Tools for the Office System 2005 or a Visual Studio Shared Add In extensibility project.
ExchangeWebDavMailService. This service sends mail using directly via an Smtp server. It receives mail by monitoring Microsoft Exchange. It is able to monitor specific folders in an Exchange account for more flexible routing. This service is suitable for production use server-side or client-side—it also requires that you have a Microsoft Exchange Server available.
This activity library allows a workflow to participate as an actor in an email conversation by sending and receiving emails. The emails are correlated through the use of a unique identifier attached to the message. By using correlation we can map inbound emails to specific receive activities and in doing so implement basic conversational semantics. So, for example we can send a question by email to a given human actor and then receive their answer to that question back via email. The example workflow included with the sample code shows how this is possible for asking if a leave application can be approved. Correlation is particularly important where you may need to send a message to more than one person at the same time—say in a parallel document approval scenario.
The correlation mechanism uses a subscription service so that we can use a single identifier (a GUID) to map messages back to both the correct running workflow instance and the appropriate email receive activity in that instance. The GUID is attached to the message in a number of places including headers and at the end of the subject line. By using a GUID attached to the message we can potentially have multiple messages waiting on the same user and still be able to correlate the message replies correctly.
The SendEmail and SmtpMailReceived share a number of properties.
|To||String||The To field for the email. You should populate this on the SendEmail activity. On SmtpMailReceived it will be populated on receipt of the message.|
|From||String||The From field for the email. Set this before SendEmail is executed. On receipt of an email it will be populated on SmtpMailReceived.
|Body||String||The body of the email. As above you should populate this on SendEmail. It will be populated on SmtpMailReceived on receipt of the message.
See the special notes relating to templating the message body in the section on the SendEmail activity below.
|MailType||SmtpEMailType||This property indicates the type of the body of the message, either PlainText of HtmlText.|
|Subject||String||The Subject field of the email. As per body above. Subject cannot be templated.|
|MessageId||Guid||This is the unique identifier used to correlate messages. You should generate a new Guid for each message pair that you wish to send and receive.|
The events for both activities are simply the standard events for CallExternalMethod and HandleExternalEvent. See the documentation for these activites for more information.
Using the SendEmail activity
The SendEmail is used to send an email to a user. It uses a template mechanism to allow information from a piece of XML to be inserted into a message template body.
|XmlData||IXPathNavigable||This property may be set to contain an object implementing IXPathNavigable that contains arbitrary data. This data can then be inserted into the message body using the templating mechanism described below.|
Templating the Message Body
The SendEmail activity allows the message body to be written as a template. The template will be filled with data from the XmlData property prior to the mail being sent when the activity executed. The template fields are simple XPath statements. So for example, given the following XML in the XmlData property:
<leaveRequest> <applicantAccount> SueM </applicantAccount> <applicantEMail> email@example.com </applicantEMail> <leaveComment> Please grant me this leave </leaveComment> <startDate> 11 February 2006 </startDate> <endDate> 20 February 2006 </endDate> <responseFrom> Dave </responseFrom> </leaveRequest>
This is an example template example template:
<%/leaveRequest/applicantAccount%> has requested leave for the period <%/leaveRequest/startDate%> to <%/leaveRequest/endDate%>. This request has been escalated to you because it timed out while waiting for their manager, <%/leaveRequest/responseFrom%>."
Comment attached to the request
To approve this request reply with 'Approved', to decline this request reply with 'Declined'.
Using the SmtpMailReceived activity
SmtpMailReceived is used to receive an email into the workflow. It is a sub class of the HandleExternalEvent activity. It provides a regular expression property that is used to determine whether a message contains certain text—this allows for basic processing of message data. In the sample application it is used to determine whether the response has Approved or Declined the leave.
|Regex||String||This property may be set to a regular expression by the developer.|
|RegexMatched||Boolean||When the activity is executed, that is a message is received, the value of the Regex property will be matched against the message body that is received. This property will be set to true or false based on that match.|
Using SendEmail and SmtpMailReceived together
The SendEmail and SmtpMailReceived activities are designed to be used together in pairs to create conversational semantics. The goal of the two activities is to allow us to send a message to a user via email and to identify the reply to that specific message—as noted above this is achieved by using the message correlation capabilities built into Windows Workflow Foundation.
Let's look at how to build a very simple send/receive pairing.
Start by dragging some activities onto a blank workflow. We want the workflow to look something like the following with a SendEmail activity followed by a SmtpMailReceived activity and a DelayActivity within a Listen activity.
This pattern of placing a HandleExternalEventActivity (or a subclass of it in our case) alongside a DelayActivity in a listen block is one that you will see quite commonly in scenarios where you are messaging outside the workflow. It allows us to specify a timeout, using the DelayActivity, so that we can continue processing if we fail to receive a reply in a specified period of time.
Now we need to correlate the send and receive activities. To do this we need a CorrelationToken and also a GUID— we will pass the GUID out with the message to allow it to be identified on return.
Start by setting the correlation token property of the two activites. Be sure to set the owner activity of the token to the Workflow1 workflow.
Then we just need to create a MessageId GUID to uniquely identify the message pair. You can just type a variable name into the MessageId property and hit enter. The workflow designer will automatically declare and initialize a variable for you in the code file. For the second activity of the pair you can select the same variable.
It is important to ensure that we have a valid GUID. If we allow the designer to declare the variable in the code behind file for us we will end up with a declaration like this:
private Guid messageGuid = default(System.Guid);
This will initialize the GUID with a default value which is all zeros. This is obviously not suitable for our unique identifier requirements. Instead we should initialize with a new GUID like this:
private Guid messageGuid = Guid.NewGuid();
You should now be able to run this very simple workflow to examine how the send and receive pair works. Be sure to add the appropriate services in your runtime host. The easiest way to do this would be to simply re-use the host code that is included in the sample code.
Preventing Outlook Warning Boxes
When you first ran the sample code you would have noticed that Microsoft Outlook opened a number of warning dialog boxes. These dialogs are a by-design feature of Outlook called the Outlook Object Model Guard designed to reduce the risk of malicious code sending and receiving emails without the user knowing. In order to prevent these dialogs from appearing you need to write your application such that Outlook treats it as trusted code—in most cases this will be by creating an Outlook add-in.
An Outlook add-in can avoid showing the warning dialog boxes Outlook add-ins can avoid displaying the security warning dialog box by obtaining references to Outlook objects from the trusted Outlook.Application object. In .NET you can create an add-in either as a COM add-in or as a Visual Studio Tools for Office (VSTO) add in.
If you create your add-in as a COM add-in by implementing the IDTExtensibility2 interface, then you should use the Application object returned from the OnConnection method. For more information, see the topic What's New in Microsoft Office Outlook 2003 for Developers?, specifically under the heading "Revised and Improved Security Model."
If you are creating an Outlook add-in using Visual Studio Tools for Office, then you should use the ThisApplication object provided by the project template. For more information, see Specific Security Considerations for Office Solutions, specifically under the heading "Minimizing Object Model Guard Warnings."
It's important to note that even with the use of trusted code the Outlook Object Model Guard may still restrict your code. If Outlook is used with Exchange, then obtaining all Outlook objects from ThisApplication does not guarantee that your add-in will be able to access the entire Outlook object model. For example, if an Exchange administrator sets Outlook to automatically deny all attempts to access address information using the Outlook object model, then Outlook will not allow the previous code example to access the To property, even though the code example uses the trusted ThisApplication object.
Using the RTC Communication Activities
The Real Time Communication (RTC) Library is built to communicate with users over a real time communications protocol such as instant messenger. Using these activities a workflow can send an instant message to a user which pops up as a toast on their desktop. The workflow can also wait for an instant message response from the user and take action based on the message received. As with the Smtp library above it uses pluggable services. In this case a single service is provided to support communications with Microsoft Office Live Communications Server 2005. Because it relies on having Live Communications Server 2005 installed, the sample application does not use this activity in the default workflow. If you want to see it in action then you should change the following line of code in LeaveForm.cs to create a LeaveWorkflow type instead of an EmailWorkflow type.
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LeaveWorkflow),parms);
You will also need to uncomment the RtcService declaration in the same file.
The RTC Communication Library consists of four activites.
SetStatus. This activity sets the status for the workflow Rtc user—that is the user logged in as the RtcService. Using this activity you can set the status of a user to be Away/Online etc…
GetStatus. This activity gets the online status of another user. In the sample LeaveWorkflow we use it to check if the user is Online before we send them a message.
SendMessage. This activity is similar to the SendEmail message above. It is used to send a templated message to a user.
RtcMessageReceived. Once again, similar to the corresponding Smtp activity, this activity provides a mechanism to receive a message and match the response against a regular expression.
Configuring Live Communications Server
In order to use the RTC components in your own application you will need to be able to access a Microsoft Live Communications Server installation. If you do not have LCS running in your network already you can download a trial version from http://www.microsoft.com/office/livecomm/prodinfo/trial.mspx.
You will need to create a domain account for your workflow so that the service can login to LCS. You should update the runtime host application in the sample code with this authentication information. You should also ensure that the LCS user used for the workflow has already added to their contacts the other users in the organization with whom you wish to communicate—this is important as by default LCS will only allow you to see the online status of users in your contact list.
Correlation in the RTC Activity
Unlike email an instant messenger message does not afford us anywhere convenient to attach a correlation identifier. Because we can expect IM messages to be replied to fairly promptly and in order we simply correlate messages based on the ID of the user with who we are communicating.
GetStatus, SendMessage and ReceiveMessage share a common property.
|Uri||String||This property applies to all but the SetStatus activity. It represents the SIP URI for the user—this is the IM address for the user.
With CheckStatus it is the user whose status is to be checked.
With SendMessage and RtcMessageReceived it is the user to who the message has been sent or received from.
Using the SetStatus activity
The SetStatus activity is used to set the status of the workflow service. You can use this activity to make the workflow appear away or offline to other messenger users.
|Status||RTCCORELib.RTC_PRESENCE_STATUS||This is the constant representing the status to which the user should be set.
Permitted values are:
Using the GetStatus activity
The GetStatus activity is used to retrieve the status of another user. You can use this activity to determine when other users are online or offline and take actions based on this information.
|ReturnValue||RTCCORELib.RTC_PRESENCE_STATUS||After the activity is executed this property will be set to the status value of the user passed in the Uri property.|
Using the SendMessage activity
The SendMessage activity is used to send an instant message via a runtime service that implements IRtcCommunication. This activity is very similar to SendEmail as described above. It includes XML based templating support. Refer to the documentation for the properties of SendEmail for more details.
Using the RtcMessageReceived activity
The RtcMessageReceived activity is used to send an instant message via a runtime service that implements IRtcCommunication. This activity is very similar to SendEmail as described above. It includes XML based templating support. Refer to the documentation for the properties of SmtpMailReceived for more details.
Extending the Rtc Activites
The Rtc Activity library supplied with the sample code is designed to work with Microsoft Office Live Communication Server. You may wish to extend the library to work with your own preferred instant messaging system or even a mobile Short Message Service (SMS) gateway. To do this simply implement the interfaces found in the RtcCommunicationInterfaces project. If your chosen IM provider does not support status/presence information then you should still implement stubs IRtcGetStatus and IRtcSetStatus.
Putting it all Together
We can combine the two methods of communication along with the directory lookup activites so that we first try an MSN message and if this fails we then send an email. To do this we will use some of the out of the box workflow activities. If you open the LeaveWorkflow.cs file from the LeaveWorkflows project you will see how these activities can be combined.
We first check if the user we are trying to message has their status set to Online.
If they are online then we will send them an instant message and wait for a reply.
We use a SendMessage activity to send the message and then use a ListenActivity to either listen for a reply, or to timeout based on a configurable delay. If we receive a reply we use a Code activity to set a Boolean flag and continue. If we time out while waiting for a reply then we continue and will send an email instead.
Looking at the properties SendMessage and RtcMessageReceived pair you will notice that they reference the same CorrelationToken set. This allows the send and receive messages to be correlated as described above.
If they are not online then we will go straight on to sending them an email message.
We then use a very similar process to send the email message and wait for a reply.
In this article we've looked at how Windows Workflow Foundation can be used to involve human actors in workflow applications. We've discussed some of the key advantages in using a workflow model rather than simply writing code from scratch.
The sample code for this article demonstrated how to communicate with humans as part of a workflow process. We saw that by using protocols such as email and instant messenger we can allow humans to communicate as easily within workflows as they do with each other.
About the author
Chris J.T. Auld is the Managing Director and Chief Software Architect at Kognition Consulting Limited (www.kognition.co.nz) in Wellington, New Zealand. His company provides training, consulting and development on the .NET platform. Chris is a frequent speaker at Microsoft events on Smart Client, Smart Device and Windows Workflow Foundation Development. Chris is a Microsoft Regional Director for New Zealand and a Microsoft MVP for Windows Mobile Devices.