January 2006 - Posts

SYMPTOMS

There are several conditions that may cause this issue. Each known condition is presented with a short explanation and a possible workaround.
Application Pool Recycling
Server Farms or Server Clusters
Form Posts
Proxy servers and Virus Scanners

CAUSE

The issue presents itself with a message similar to the following:
 
_ASP.report_aspx === System.Web.HttpException: Invalid_Viewstate_Client_Disconnected Client IP: 141.150.12.56 Port: 62164 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322) ViewState:
 dDwtNjY5ODIyMjI4O3Q8cDxsPFNUQVJUREFURTtFTkREQVRFO1BMVVN
 
 
Background
Viewstate represents the state of the page when it was last processed on the server. The contents of the page are stored in a container and moved to and from the server on each request. By default, ASP.NET validates the contents of viewstate to ensure that it has not been tampered. If this validation test fails, an invalid viewstate exception is thrown. Some known issues that cause this test to fail are listed below.
 
Application Pool Recycling
            IIS 6 will periodically recycle the application pool to maintain the health of the application pool. At the instance when the application pool is being recycled, browser requests may sometimes result in an invalid viewstate error. The fix in this case is to adjust the settings on the application pools so that recycling is less likely to occur at peak periods. This issue represents a bug since the application pool is supposed to gracefully handle this condition.
 
Server Farms or Server Clusters
Applications running in a server cluster must have all the machineKey configurations set to the same validationKey. The default autogenerate key cannot be used in a cluster environment. The registry keys responsible for autogeneration are listed here:

HKEY_LOCAL_MACHINE\Software\Microsoft\ASP.NET
HKEY_CURRENT_USER\Software\Microsoft\ASP.NET

When the machineKey is set to AutoGenerate, the key information is stored in the HKEY_CURRENT_USER hive for the account running the process. For W2k3 servers, this is the Network Service account. Otherwise, the account is ASP.NET machine account. When the process launches, ASP.NET will use the HKEY_CURRENT_USER registry key if it is available. If this key is not available, the
HKEY_LOCAL_MACHINE key will be used.  If neither registry key exists, the process creates the key in the HKEY_LOCAL_MACHINE hive. If these conditions fail, the process creates a brand new set of keys.

When the application pool is running under a user account, the above keys are not generated leading to an intermittent invalid viewstate error.

The workaround is to use a specific key in the machine.config to prevent automatic key generation on each process start.  The key must be exactly 128 bits made up of random characters and inserted into the configuration file on each webserver experiencing the problem.
 
Form Posts
Viewstate can only be posted back to the same page.  Attempting to post an aspx form to another page will fail with a viewstate invalid exception. This behavior is by design.
 
 
One other remedy involves disabling the Machine Authentication Check. Unless, you implement a back up authentication mechanism, you should refrain from this approach. Machine Authentication Checks are important in reducing the attack surface of ASP.NET applications.
 
Proxy servers and Virus Scanners
A firewall and/or antivirus software can tamper with the viewstate resulting in an invalid viewstate exception being thrown.

RESOLUTION

1. Adjust the settings on the application pools so that recycling is less likely to occur at peak periods.
2. Use a specific key in the machine.config to prevent automatic key generation on each process start
3. Only post to the same ASPX page
4. Turn off firewall and antivirus software

MORE INFORMATION

Issues that haven't been covered here should be submitted to Microsoft product support for further consideration

Abstract

The new rules allow for one computer to share its installed O.W.C. interactive license with an unlimited number of computers giving these computers the capability to experience an interactive O.W.C. application even though these computers do not have a qualifying Microsoft product or a valid license installed. You may recall that static, non-interactive use of the Office Web Components is always free for versions 10 thru 11 of the O.W.C. but interactive usage requires a license. Licensing information for the components can be found on the Microsoft Office website and in Community solutions article – 555075 in sufficient detail. The new licensing rules do not apply to O.W.C. version 9 and will not be considered here. The new licensing terms do have serious restrictions and these will be examined here.

Examining the new rules

Interactivity can be extended thru shared licensing. There are some overriding qualifications and conditions which regulate its use. A shared license may only be extended to another desktop if there is explicit intent thru policy or otherwise to install a qualifying product at a later date. This option is only extended to business entities that own an Enterprise, Select, or Maintenance Agreement for Microsoft Office. As an example, let’s consider a corporate intranet scenario where an application developed with the O.W.C. needs to be deployed in an interactive manner. The corporate entity fully intends to install qualifying products such as Microsoft Excel on all desktops and possesses an Enterprise level agreement. The installation forms part of the new corporate policy to upgrade all Microsoft Excel software to the most recent version. Let’s assume further, that the company is fairly large so that the roll out will take a significant amount of time and will be done in phases. In this case, the corporate entity has the option to use shared licensing to allow users who aren’t yet part of the roll out to participate in a full interactive experience.
 
The licensing requirements are straightforward. The business entity must fully intend to install qualifying products on every desktop which will experience interactivity. You may take from this discussion that shared licensing is best suited for a corporate setting where a guarantee can be made either with policy or otherwise that each desktop will eventually install a qualifying product. An example of policy may be an Enterprise level Agreement. Because of these restrictions, shared licensing cannot legally be used in an internet scenario. The overriding concern here is that a guarantee cannot be made either thru policy or otherwise that the client computers accessing the components will, at some point in time, install a qualifying product to cause interactivity. Consequently, all internet shared licensing is necessarily a breach of the new licensing terms of use for O.W.C. versions 10 and 11.
 
For Independent Software Vendors, the licensing requirements are the same. The rule of thumbs to use is:
Does a formal Enterprise, Select, or Maintenance agreement exist?
Can the business entity guarantee thru policy or otherwise that every computer will eventually install a qualifying product?
 
Because of the nature of the ISV business, most ISV's cannot make that guarantee. Rather than leaving your business entity open to litigation, you should use the O.W.C. in a non-interactive manner. Remember, non-interactive usage is always free and requires none of the above qualifying conditions. It does require installation of the Office Web Components. Shared licensing is best suited for intranet scenarios and mostly apply to corporate settings for the previously stated reasons.

HOW TO: implement shared licensing

Shared licensing is implemented thru a special file called the License Package File (LPK). The LPK is implemented using an License Package Authoring Tool which is a free utility provided by Microsoft available on the Microsoft website or here. A discussion on how to use the LPK Tool is provided in the documentation which comes with the tool and will not be provided here. This license file allows one computer with a valid installation of the O.W.C. and a qualifying product such as Microsoft Excel or valid O.W.C. license to share its valid license with another client computer in a temporary manner. This special licensing file or LPK must be placed on the server serving the web pages which contain the O.W.C. component. The path to this file is exposed to the browser thru a special parameter inside an object manager tag. Usually, the developer writing the application hard codes the object manager inside the web page using the special object tag. The object manager tag must be the first tag inside the web page and it may not be customized with attributes such as width and back color for example. Consider an O.W.C. object manager embedded in a webpage which may look like the following:
 
<OBJECT CLASSID="clsid:5220cb21-c88d-11cf-b347-00aa00a28331">
   <PARAM NAME="LPKPath" VALUE="LPKfilename.LPK">
</OBJECT>
 
 
At run-time the hosting container, usually a browser, examines this object tag embedded in the webpage. The browser extracts the path and attempts to find the LPK file pointed to by the path. The path variable is relative and the value name should not be changed. Failure to find this path results in view-only mode use. If the file is found, it is parsed to retrieve a special key which is used as a hash into the permissions manager. A successful hash value results in an O.W.C. component able to render in interactive mode, otherwise, the component renders in view-only mode. Observe that the LPK file is the instrument used to grant interactive use so the necessary precautions should be taken to secure the contents of the LPK file on the server.
 
The implications of these rule changes are subtle but very important. Notice that interactivity no longer depends on a qualifying Microsoft Office product installed on the computer. The license object manager now has the option to retrieve a valid client license from the server and share it with the requesting client to allow interactivity. This retrieval is automatic and occurs when a hosting container detects that the webpage contains an embedded object manager. The hosting container takes care of the permissions request and passes the license key obtained from the LPK file to the license manager for authentication.
 
It’s important to point out that if the object tag is tampered with, the licensing hash will fail and only render in view-only mode. A number of view-only problems can be resolved simply by verifying that the CLASSID is correct and that the path pointed to is valid. Visual studio contains an annoying bug which will remove the path variable from the object manager tag resulting in non-interactive use any time the aspx file is changed. Remember that the default mode is view-only or non-interactivity for versions 10 and 11 of the Office Web Components.
 
Hopefully, this article has shed some light on interactive usage for the O.W.C.. For qualifying products which install a valid O.W.C. license, please consult the Microsoft office website.

 

Article ID : 555082
Last Review : April 13, 2004
Revision : 1.0

Abstract

Session and its events are sometimes peculiar at best and down right complex at worst. The complexity stems from the need to service different state providers in the same architecture. This discussion examines some of the underlying reasons why session end and session start events don’t always seem to fire as a couple. The glue which holds the matrix together is an often overlooked participant called session id. Here is how the session id plays a pivotal role in this new world. A session start event fires for each new request. But, session end event fires based on the state of the session id and not necessarily on the termination of each request. This is interesting because it implies that a session end event does not necessarily follow a session start event. This behavior is often the cause of much confusion relating to the session end events. 
 
 

How does session abandon work?

The abandon method will destroy the session by sending a new session id cookie with an old expiration date. Then, abandon destroys the dictionary associated with the session. Whether session end event fires next is entirely dependent on the session id. It bears repeating: If the session id is regenerated, session end event fires. If the session id is not regenerated, session end fails to fire. So in one case, calling session abandon will invoke the session end event. And in another case, calling session abandon does not cause the session end event to fire. If the session id is being re-used then this session id is kept until the browser is closed even though a new page is requested. As long as the conditions are satisfied, the session id remains. Here is the complete test case example to force session end to fire.
 
private void Page_Load(object sender, System.EventArgs e)
{
Session["test"] = "test";
}
 
private void Button1_Click(object sender, System.EventArgs e)
{
Session.Abandon();
}
 
When the Button1_Click event fires off due to the user clicking a button on the form,
1 At least one request has completed successfully. This is the page_load routine executing successfully.
2 The session dictionary has been written to at least once. This is the page_load routine executing the single line of code.
 
We've looked at the session song and dance in enough detail to allow you to understand when these events fire. The song and dance is based on a set of rules. Once these rules are applied, the expected behavior is achieved. Failure to understand the rules leads to confusion and ill-behaved code.
 

Symptoms

Permutations of this bug appear to occur when paging is used and viewstate is turned on in the datagrid. Seemingly random exceptions occur with this error string:
Invalid CurrentPageIndex value. It must be >= 0 and < the PageCount

CAUSE

Because of viewstate, the grid tries to remember where it was the last time the page was posted. If the postback returns less paged data than the previous page, this exception will be thrown. Consider a datagrid with enough data to fill 4 pages. The user navigates to page four, clicks a button which causes a postback. This time around, the data isn’t sufficient to fill four pages, but rather 3 pages are returned. During the render phase, the grid will attempt to navigate to page 4. There is no page 4. An exception is thrown because the currentpageindex 4 must be greater than or equal to 0 and less than the page count 3.
 

RESOLUTION

There are three approaches to fix this problem.
        
When you bind in your page event handler simply set the currentpageindex = 0. This guarantees that the index is always within the correct bounds. If there is no data returned the data grid is not displayed. The down side is that the user is always returned to page 0 on the returned data – not an overly heavy price to pay for application stability. 
 
Allow the error to occur and then catch the exception in a try block resetting the index to 0 and rebinding the grid
try
{
      //bind code
}
catch(ArgumentOutOfRangeException)
{
 datagrid1.currentpageindex = 0;
        //re-bind code
}

This is a modification of item 1. The catch block fires and incurs a performance penalty only when the exception is thrown.
     
Turn off viewstate. Turning off viewstate implies that the control will no longer be able to maintain its state. The onus is on the programmer to write code to maintain this state and re-hook events.

MORE INFORMATION

Is this a bug? I argue that it is not. The run-time cannot uncover the programmer’s intent when this condition is met. It cannot assume that the last page in the collection is what is intended so it shouldn’t navigate to the last page returned. This assumption would not hold true for every condition. For example if you were on page 100, and 50 pages of data were returned for a new request, it is flawed logic to forcibly navigate the user to page 50 or page 1. All the grid assumes is that you want to be back on page 100 on the returned data. This assumption is based on viewstate. If that page is not there, then the run-time correctly assumes that there is an error or ambiguity in the programmer’s logic and promptly throws an argumentoutofrange exception.
 
Let’s put this discussion into a philosophical light. A teacher instructs you to open your text book to page 35. You open up the text book and discover that there are 25 pages. Is this your fault? If it is, then it is bug. If it is not, and the fault lies with the teacher then she has created an ambiguous situation. What should you do? Inform her that there is an ambiguous situation? Open up the book to the first page? I believe the run-time makes the correct assumption by indicating that there is an ambiguous condition by promptly throwing a nasty exception.
Author: Alvin Bruney MVP
Article ID : 555162
Last Review : July 15, 2004

MORE INFORMATION

Custom drawing works thru the Office Web Component event model. The event architecture allows the O.W.C. component to respond to events - interactivity. Interactivity is strictly regulated thru the licensing terms and conditions. Licensing is covered in Community Solutions Article - 555075 
             http://support.microsoft.com/default.aspx?scid=kb;en-us;555075
 and Microsoft Knowledge Base Article - 243006
             http://support.microsoft.com/default.aspx?scid=kb;EN-US;q243006.
 
Preparing for events in a windows form
 
Applications using the O.W.C. thru Visual Studio.NET cannot respond to events by default. The problem lies outside the O.W.C.  The current work-around to attach events to the O.W.C. is provided in the following article:  328275 HOW TO: Handle Events for the Office Web Components in Visual Studio .NET http://support.microsoft.com/?id=328275

Assuming you have followed the article and your application is now setup to respond to events correctly, open visual studio to a new windows forms project. Either the OWC10 or OWC11 component may be used. This article will focus on OWC11 which ships with Microsoft Office 2003. The code and explanations do not apply to OWC9. This code will not work in a web-form architecture. The reasons are explained later in this article.
 
Find and drop the OWC11 chart component onto the designer surface. You may have to add the O.W.C. chart component to the toolbox if it is not already present using the tools menu of Visual Studio.
 
The following will suffice:
   private AxMicrosoft.Office.Interop.OWC.AxChartSpace objChart;
 
This object declaration will only compile after you have followed the steps outlined above in Preparing for events in a windows form. The code which loads the chart is placed in the applications Form1_load event handler routine. This event fires off when the form loads. In this case, the data to load the chart is contrived, literal data for illustrative purposes. Let's have a look.
 
Form1's code:

  objChart.HasChartSpaceTitle       = true;

   objChart.ChartSpaceTitle.Caption  = "Advanced custom drawing with .Net ";
 
   //Add a chart and a series to the chart
   objChart.Charts.Add(0);
   objChart.Charts[0].HasLegend = true;
   objChart.Charts[0].Type   = OWC11.ChartChartTypeEnum.chChartTypePie;
   OWC11.ChSeries series      = objChart.Charts[0].SeriesCollection.Add(0);
 
   //Set the data for the chart to literal comma-delimited string
   series.SetData(OWC11.ChartDimensionsEnum.chDimCategories, (int)OWC11.ChartSpecialDataSourcesEnum.chDataLiteral,
    "Week 1,Week 2,Week 3,Week 4,Week 5,Week 6");

 
   series.SetData(OWC11.ChartDimensionsEnum.chDimValues,  (int) 
   OWC11.ChartSpecialDataSourcesEnum.chDataLiteral,  "11,25,16,14,8,20");  

 
Without hooking into the event model, the previous code produces a plain chart with six slices and a corresponding legend with entries equal to the number of categories. Notice the use of constants for the SetData method. The use of constants are recommended because constants ensure type safety and reduce invalid input. Now, we customize the chart to respond to events to provide a rich, interactive experience to the user. This will set our application apart from the usual run-of-the-mill charts.
 
Our first modification is to enhance the chart with an information panel. The code which makes this possible will be placed inside the AfterFinalRender event. This event fires once just before the chart is rendered to the canvas. Typically, you will place code in this routine to tweak the look and feel of the chart just before it is rendered to the drawing surface.
 
Form1_AfterFinalRender's code

   //let's customize the background to show a two color gradient 
   owc11.ChInterior font = e.drawObject.Interior as owc11.ChInterior;
   font.SetSolid((object)"gainsboro");
   font.SetPatterned(owc11.ChartPatternTypeEnum.chPattern80Percent,(object)"black","gainsboro");
   font.SetTwoColorGradient(owc11.ChartGradientStyleEnum.chGradientDiagonalUp, owc11.ChartGradientVariantEnum.chGradientVariantEdges,(object)"gainsboro",(object)"gray");
 
   //adjust the position so it sits on the bottom right hand corner of the drawing surface
   e.drawObject.DrawRectangle(20, 50, 260, 260);
   e.drawObject.DrawText("Profit Analysis",25,60);
 
   owc11.ChFont infopane =  e.drawObject.Font;
   infopane.Color        = "green";
   infopane.Bold         = true;
   infopane.Underline  = owc11.UnderlineStyleEnum.owcUnderlineStyleDouble;
   infopane.Italic         = true;
   e.drawObject.DrawText("1st Qtr",25,80);
 
Reviewing the code snippet, a font object is extracted from the intrinsic interior object of the chart component. This font object exposes all the goodies which allow us to customize the text used on the drawing surface. The rectangular information panel is statically placed on the drawing surface for illustrative purposes. In a real world application, you may need to calculate the position at run-time. The PlotArea object represents the canvas on which the chart is being rendered. You can determine the width and height of the PlotArea and adjust your calculations accordingly. Consider:
 
   OWC11.ChPlotArea plotDimension = objChart.Charts[0].PlotArea;
 
where plotDimension would contain a Left, Right, Top and Bottom property for this purpose. It may also help to know the dimension of the chart area. Recall that the chart area lies inside the plotarea surface.
 
Finally, we need to customize each slice. The AfterRender event is the appropriate place for our code. This event fires for each slice as it is being rendered. We intend to keep this simple by drawing an ellipse with our company's logo as the background in the center of each slice. The background is limited to gif and jpg types only.
 
Form1_AfterRender's code:
    //use the font object again for customization
      owc11.ChInterior font = e.drawObject.Interior as owc11.ChInterior;
      if(font == null)
         return;
 
      font.SetTextured((object)@"C:\Documents and Settings\User\My Documents\My Pictures\logo.gif",
      owc11.ChartTextureFormatEnum.chTile, 1, owc11.ChartTexturePlacementEnum.chFront);
     
      //calculate the center of the current slice

      owc11.ChPoint slice = (owc11.ChPoint)e.chartObject;
      int x = slice.Left + (slice.Right-slice.Left)/2;
      int y = slice.Top +(slice.Bottom - slice.Top)/2;
 
      e.drawObject.DrawEllipse(x-10, y-10, x+10, y+10);

 
The ChPoint is a structure which encapsulates information about the Chart Object. The ChPoint structure internally represents the co-ordinates of the Chart Object as vector quantities. The O.W.C. makes extensive use of vector quantities because of the need to render in 3 dimensions. Vectors provide for greater freedom and flexibility as well. For instance, you may be able to place your text on the outside edge of the pie chart for each slice as it is being rendered by doing some simple vector arithmetic.
 
Typically, the chart plots the data labels as a percentage of the slice at the center of each slice. If you intend to custom draw, you should disable the default behavior by turning it off so that the chart remains clean and uncluttered.
 
Find and remove a line of code similar to the following:
  SeriesObject.DataLabelsCollection.Add();
 
If labels have been added at design time, you may disable them with the following snippet of code:

  dl = objChart.Charts[0].SeriesCollection[0].DataLabelsCollection[0];
  for(int i=0;i<dl.Count;i++)
      dl[i].Visible=false;

 
One final note on customization. Typically, a chart doesn't display labels on its surface proper since the likelihood of multiple series in one chart is a real possibility, resulting in a cluttered chart. The default is to place the labels in the legend. To change the way the legend displays entries consider:
 
      objChart.SeriesCollection[index].Caption = "hello, world";
 
If you absolutely must have labels on the chart surface proper, you will have to use custom drawing to write text on the chart surface.
 
Preparing for events in a web-form
 
As previously mentioned, the code approach listed above is not expected to work in a web-form environment. The answer lies in the fundamental difference in the web architecture. A chart created in a web-form using the code-behind approach on the server is streamed out to the client as an image file, either a gif or jpg. Other formats are not supported. Once this image is sent to the client, no relationship or connection to the server is maintained. This behavior is imposed by the stateless nature of the web; a client connects to the server, makes a request and disconnects. No state information is preserved.
 
One approach is to abandon server-side chart generation and load the data into the chart from the client instead. This approach would involve dropping the O.W.C. chart control onto the web-form. You would need to load the data into the chart using client-side script. Then you can use scripting to respond to the various events the chart object exposes using this convention:
 
     Private Sub ChartSpace_AfterRender(ByVal drawObject As ChChartDraw, ByVal chartObject As Object)
 
Client-side charting automation is very well documented in the help files accompanying the Office Web Components and will not be covered here. For online documentation, visit this link: http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q290/3/48.asp&NoWebContent=1
 
Be aware though that automating the O.W.C. component constitutes interactivity and, as such, is subject to the terms and conditions of the licensing agreement.
 
Summary
 
This article provides a good starting point for customization of the O.W.C. chart. The O.W.C. chart component can plot a large number of different types of charts. Custom drawing allows you to customize each one of these charts. In addition, you can handle multiple events such as mouse down and mouse over events. The windows forms architecture offers a richer event model than the web-forms architecture and is easier to program against. As the web-forms architecture matures, you should expect this gap to shrink.
 
Credits
 
This article was prepared with the help of Microsoft Product support.

 
SUMMARY
This article describes how to use and server document class for application caching and document management without running an instance of the Microsoft Office on the web server.

Abstract

Perhaps you’ve heard of Visual Studio Tools for Office System 2005 (VSTO). In fact, I’d venture a guess that some of you may have toyed with it or even started serious development based on the tool suite. For those of you who haven’t, Visual Studio Tools for Office System 2005 allows developers to harness the power of Microsoft Office in enterprise level applications.
 
Currently, the VSTO tool suite supports Microsoft Excel, Word, InfoPath and Outlook. These applications may be built using either C# or Visual Basic.NET (Visual Basic). While most of the development targets windows forms and thick client development, one important piece focuses on server-side development.
 

Testing Microsoft Office automation thru VSTO

VSTO-based applications can be manipulated without the need to start an instance of Microsoft Office on the server. This paper demonstrates that an instance of Office is not needed for document automation by developing an application that manipulates documents on the server. We then create another application that can access and modify the data. Finally, we write a third piece - a watchdog process - that simply monitors the server for an instance of Microsoft Office. If an instance is detected, our watchdog process will notify us.
 
Here is the code for the watchdog process.
 
using System;
using System.Runtime.InteropServices;
 
namespace WatchDog
{
    classProgram
    {
        staticvoid Main(string[] args)
        {
            object automator = null;
            while (automator == null)
            {
                try
                {
                    automator = Marshal.GetActiveObject("Word.Application");
                    Console.WriteLine("An instance of Office is running.");
                    Console.Read();
                    Marshal.ReleaseComObject(automator);
                }
                catch (System.Runtime.InteropServices.COMException)
                {
                    //Microsoft Word is not running
                    Console.WriteLine("Watching...");
                }
            }
        }
    }
}
Listing 1-0 Watchdog process code

 
The watchdog application is conceptually simple. A while loop drives the process. During each iteration, the code searches for an instance of Microsoft Word. If no instance exists, the application prints an appropriate message and continues monitoring. To test watchdog, compile and fire the application. While the application is running, open Microsoft Word and you should be notified that an instance is running. Remember, for a Microsoft Office automation application to scale well, it must necessarily avoid creating an instance of Microsoft Word on the server.
 
Here is the code to create and house the VSTO based data. Create a new VSTO based project and enter the following code.
 
using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;
 
namespace WordDocument1
{
    public partial class ThisDocument
    {
        [Cached]
        public DataSet data;
        private void ThisDocument_Startup(object sender, System.EventArgs e)
        {
            data = new DataSet();
            data.ReadXml("sampledata.xml");
        }
 
        private void ThisDocument_Shutdown(object sender, System.EventArgs e)
        {
        }
    }
}
Listing 1-1 Application data

 
This application sets up the necessary plumbing that allows data to be stored inside a VSTO-based application. The first line of code declares a public dataset with a cached attribute. The cached attribute informs VSTO that the data contained in the dataset should be stored in a special way so that it is accessible outside the application.
 
Next, the code loads some arbitrary data, sampledata.xml, into the dataset. In the real world, this application would typically read from a data store and apply some sort of business logic to the data. This isn’t the real world so we skip the business logic piece.
 
Our final piece of code simply tries to access the data. Here is the code.
 
Public void ManipulateData()
{
            DataSet newDataSet;
            //point to the doc file in the debugger directory
            String fileName = “C:\WordDocument1\bin\debug\WordDocument1.doc”;
            if (ServerDocument.IsCacheEnabled(fileName))
            {
                ServerDocument servDoc = null;
                try
                {
                    servDoc = newServerDocument(fileName);
                    newDataSet = new System.Data.DataSet();
 
                    //grab the namespace and the class that contains the cached data
                    CachedDataHostItem hostI = servDoc.CachedData.HostItems["WordDocument1.ThisDocument"];
 
                    CachedDataItem dataI = hostI.CachedData[0];
                    // load the data
                    if (null != dataI.Xml && null != dataI.Schema)
                    {
                        System.IO.StringReader xmlReader = new System.IO.StringReader(dataI.Xml);
                        System.IO.StringReader schemaReader = new System.IO.StringReader(dataI.Schema);
                    
                        newDataSet.ReadXmlSchema(schemaReader);
                        newDataSet.ReadXml(xmlReader);
                        
                        if(newDataSet != null && newDataSet.Tables != null && newDataSet.Tables.Count > 0)
                        {
                              // Modify the data by adding some arbitrary information
                              foreach (DataRow row in newDataSet.Tables[0].Rows)
                           {
                                row[0] = “my new value goes here”;
                           }
                       }
 
                        dataI.SerializeDataInstance(newDataSet);
                        servDoc.Save();
                    }
                }
                finally
                {
                    if (servDoc != null)
                        servDoc.Close();
                }
            }
}
Listing 1-2 Application to manipulate data

 
The code isn’t that difficult to follow. First, the code tests to see if the VSTO based application contains a data cache. The data cache is a new container that is able to access and manipulate the data inside a VSTO-based application. If the document supports VSTO data caching, an instance of the ServerDocument class is created. This is a special class that is able to manipulate the actual data inside the application.
 
Notice how the code uses a special naming syntax “WordDocument1.ThisDocument” to access the data. This is because the data that is displayed in a VSTO-based Microsoft word document is no longer stored inside the worddocument1.doc file. It is now stored in a special container accessible thru the serverdocument class using the syntax mentioned here.
Once access to the data is obtained, the code can simply read the data into a dataset and manipulate it. Finally, the serverdocument class’ save method is used to write the changed data in the dataset back into the application store.
 
So let’s fire it up. Run the application watchdog first to begin monitoring for an instance of Microsoft Office. Then run the code in listing 1-1 so that data can be loaded. Finally, fire up the code in listing 1-2 so that the data can be manipulated.
 
After running the test harness you will find that the process executes successfully without notification from the watchdog process. The test harness demonstrates that Microsoft Office is not required to manipulate data contained in VSTO-based applications on the server.
 
The new VSTO design is actually the reason for this improvement. VSTO now separates content from presentation. This divorced architecture allows calling code to service data contained inside VSTO-based applications without the need to start an instance of either Microsoft Office or Microsoft Excel. Because an instance of Office is avoided during the automation, then the scalability problems that accompany Office automation disappear entirely.
 
The code presented demonstrates that the divorced architecture actually works and is scalable – although we haven’t tested the scalability claim. However, there are a couple of drawbacks to this silver bullet. The data must be created using Visual Studio Tools for the Office System 2005 since it needs to support data caching. This is quite a shortcoming because it necessarily means that you must migrate your applications to VSTO solutions first if you intend to take advantage of data caching. Also, Datacaching is only supported in Microsoft Word and Microsoft Excel. There is no support for datacaching in Microsoft Infopath or Microsoft Outlook.
 
Another drawback is that the VSTO tools suite is not free. When compared to the regular Office development based on COM that is essentially free, the zero cash cost can seem like an unnecessary investment. Still, if you have a requirement for a highly scalable piece of Office automation software, VSTO seems like a good alternative.
So I just got this new blog toy, and this is my first post. I'm really not into blogging  but I'll give this one my best effort.