none
VSTO 2005 Word Bookmarks

    Question

  • On the client side I created a Word Document Project using "add the datasource to the project, add the bookmark controls and then databind the bookmark controls to the fields in the datasource" and "you should be able to simply drag and drop a field from the datasource to the document to create a bound bookmark control." (thanks to Ken Laws 24 June 2005). I used the DataSet Wizard to create a strongly typed dataset connected to the DB. The drag and drop worked fine.

    On the server side I used a trigger to indicate added DB records and cached the record in a copy of the client side word document.

    On the client side I used caspol to provide access to the server side folder containing the word documents.

    When I open the server word documents on the client I get an exception:

    Office document Customization is not available.


    ************** Exception Text **************
    System.ArgumentNullException: Value cannot be null.
    Parameter name: type
       at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
       at System.Xml.Serialization.XmlSerializer..ctor(Type type)
       at Microsoft.VisualStudio.Tools.Applications.Runtime.AppInfo.CreateCachedDataObject(IAppInfo appInfo, String id, String dataType)
       at Microsoft.VisualStudio.Tools.Applications.Runtime.AppInfo.CreateCachedDataObject(IAppInfo appInfo, String hostItem, String id, String dataType)
       at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.AttemptToLoadCachedDataFromAppInfo()
       at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.Microsoft.VisualStudio.Tools.Applications.Runtime.ICachedDataProvider.get_IsCacheInitialized()
       at WordDocument1.ThisDocument.InitializeCachedData()
       at WordDocument1.ThisDocument.Initialize()
       at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.CreateStartupObject(EntryPoint entryPoint, Dependency dependency, Assembly objectAssembly)
       at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.ConfigureAppDomain()
       at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.LoadAssembliesAndConfigureAppDomain(IHostServiceProvider serviceProvider)
       at Microsoft.VisualStudio.Tools.Applications.Runtime.AppDomainManagerInternal.ExecuteCustomization(IHostServiceProvider serviceProvider)
     

    The ex-client source document on the server side opens on the client side without the exception (it has an uninitialized data cach) (the assembly runs). The server generated documents containing a data cach (one DB record) upon opening, all provide the above exception (the assembly fails to run). I used VSTOApplicationManifestEditor to inspect the data cach.

    On the client side (ThisDocument) do I need additional code to access the cached data in the opened document and bind it to the bookmarks?

    This is a learning project. I have zip files if anyone would like to have a look at the code.

    Thanks,

    Kevin

    Wednesday, January 25, 2006 10:51 PM

Answers

  • Hi Kevin,

    If you have already sent your files to Ade, he may have answered your question. I just want to point you to an old thread that may address the source of your issue.

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=207500&SiteID=1

    Briefly, when you open the Word doc with a populated data cache, the VSTO loader tries to deserializes the XML in your doc's data cache. Seems like VSTO fails to deserialize the data cache because one of the data types stored in your data set (or the type of the data set itself) can not be found (loaded).

    The most likely reason is that type of the dataset that you have cached is defined in a different assembly from the type of dataset you are trying to deserialize to on client-side (because they are defined in two different assemblies => two different types).

    You may want to define your dataset in one assembly and reference it from both the server app and the Word customization.

    Hope this helps, otherwise Ade is there :-) 

    Friday, January 27, 2006 6:52 AM

All replies

  • Hi Kevin,

    "On the server side I used a trigger to indicate added DB records and cached the record in a copy of the client side word document"

    1. Could you explain a bit how you are adding the new record to the data cache of the word document on the server side?

    2. Could you paste the XML (including data cache) that you see when you open the offending document in the Application Manifest Editor?

    Thanks

    Thursday, January 26, 2006 11:07 PM
  •  

    If you could send me the code that would be most helpful.

    ade.miller at 
    microsoft.com

     

    Thanks,

     

    Ade

    Friday, January 27, 2006 12:40 AM
  • Hi Apurva

    Thank you for your reply. Here is the server code:

            public void GenerateJobSheet(string sJobNumber)
            {
                try
                {
                    //Open server document, and load in data
                    string sPath = _documentDataSource.DownloadPath;
                    string sourcePath = sPath + "WordDocument1.doc";
                    string destinationPath = sPath + "Job Sheet " + sJobNumber + ".doc";
                    File.Copy(sourcePath, destinationPath, true);

                    if (ServerDocument.IsCacheEnabled(destinationPath))
                    {
                        //Store data in the cache
                        serverReport = new Microsoft.VisualStudio.Tools.Applications.Runtime.ServerDocument(destinationPath);

                        CachedDataHostItem cachedView;
                        CachedDataItem cachedItemJob;

                        string viewName = "WordDocument1.ThisDocument";
                        string itemNameJob = "jobsDBDataSet";

                        cachedView = serverReport.CachedData.HostItems[viewName];
                        cachedItemJob = cachedView.CachedData[itemNameJob];

                        cachedItemJob.SerializeDataInstance(_documentDataSource);

                        serverReport.Save();
                    }
                }

    The output from ApplicationManifestEditor (pasting the picture probably did not work):

    Manifest

    Application Information:

    ApplicationManifestName:                     WordDocument1.dll

    ApplicationManifestVersion:                   1.0.0.9

    DeploymentManifestName:                     file:\\ptcorkg1\C$\DME\WordDocument1.Application

    Customization Assembly Information:

    AssemblyName:                                    WordDocument1.dll

    AssemblyPath:                                      WordDocument1_1.0.0.9\WordDocument1.dll

    AssemblyVersion:                                  1.0.0.0

    PublicKeyToken:                                     07f86aabdb9de1fe

    Entry Point Information:

    EntryPoints:                                           String Array[]

    [0]                                                          WordDocument1.ThisDocument

    Cached Data

    - WordDocument1.ThisDocument

              - jobsDBDataset

                     JOBS

                      CHANGED

    -----------------------------------------------------------------------------------------------------------------

     JobNumber    JobDescription   Category   ISGroup   sApplication    sModule    sFunction   

    18900               PT1 Report         R                3              PT1              REPORT      Assets

    ------------------------------------------------------------------------------------------------------------------------------------------------------

    All nicely set out in a Tab Windows Form

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odc_vsto2005_ta/html/Office_vstoApplicationManifestEditor.asp

    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" manifestVersion="1.0">
      <assemblyIdentity name="WordDocument1.dll" version="1.0.0.9" />
      <asmv2:entryPoint name="Startup" dependencyName="dependency0">
        <asmv2:clrClassInvocation class="WordDocument1.ThisDocument" />
      </asmv2:entryPoint>
      <asmv2:dependency asmv2:name="dependency0">
        <asmv2:dependentAssembly>
          <assemblyIdentity name="WordDocument1" version="1.0.0.0" publicKeyToken="07f86aabdb9de1fe" />
        </asmv2:dependentAssembly>
        <asmv2:installFrom codebase="WordDocument1_1.0.0.9\WordDocument1.dll" />
      </asmv2:dependency>
      <asmv2:installFrom codebase="
    file://ptcorkg1/C$/DME/WordDocument1.application" />
    </assembly>

    Adding the data to the document on the server: see code above which I believe to be standard code.

    The single record seems to be properly cached in the document(see screen typed above).

    On the client side: WordDocument1.dll code:

    namespace WordDocument1
    {
        public partial class ThisDocument
        {
            private bool bCached = false;

            private void ThisDocument_Startup(object sender, System.EventArgs e)
            {
                bCached = (this.jobsDBDataSet.IsInitialized);
                try
                {
                    if (!bCached)
                    {
                        this.jobsTableAdapter.Fill(this.jobsDBDataSet.JOBS);
                        MessageBox.Show("Loaded JOBS table");
                    }
                    else
                    {
                        MessageBox.Show("Document cach already loaded");
                    }
                }

    I used the DataSouce Wizard to create the data source. Drag n Drop the bookmarks to the document. Drag n Drop from the DB fields to the bookmarks.

    Hope the above answers your queries.

    Regards

    Kevin

    Friday, January 27, 2006 1:21 AM
  • Hi Kevin,

    If you have already sent your files to Ade, he may have answered your question. I just want to point you to an old thread that may address the source of your issue.

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=207500&SiteID=1

    Briefly, when you open the Word doc with a populated data cache, the VSTO loader tries to deserializes the XML in your doc's data cache. Seems like VSTO fails to deserialize the data cache because one of the data types stored in your data set (or the type of the data set itself) can not be found (loaded).

    The most likely reason is that type of the dataset that you have cached is defined in a different assembly from the type of dataset you are trying to deserialize to on client-side (because they are defined in two different assemblies => two different types).

    You may want to define your dataset in one assembly and reference it from both the server app and the Word customization.

    Hope this helps, otherwise Ade is there :-) 

    Friday, January 27, 2006 6:52 AM
  • This is very likely to be the deserialization having an issue re constructing the type and is a pain.

    The suggested solution is how i have done this in the past, bit of a pain, would be useful for the loader not to worry on this.

    Rgds

    Friday, January 27, 2006 10:21 PM
    Moderator
  • Hi Apurva,

    The above datasets were both generated by the wizard and differed only in that the client dataset had only one table and the server dataset had that same table plus one other. The client connection string contained "Network Library=dbnmpntw" and the server did not. I made the server and the client the same but this was not enough to get the Word Document to populate with data.

    I am using SQL Server 2000.

    I decided to begin from the beginning and proceeded as follows:

    I created a new separate windows class project on the client (containing Office 2003) to build an assembly containing only the dataset (jobsDBDataSet).

    Next I created the client project -> Office -> Word, Added the reference to jobsDBDataSet, drag n drop from the toolbox, the DataSet and the BindingSource to the Word Document page, set the DataSet properties "Modifiers" to Public and "CachedInDocument" to true, set the BindingSource properties "DataSource" to jobsDBDataSet, "DataMember" to JOBS, then drag n drop the bookmarks onto the document page, set each bookmark control properties "Databindings" - "Text" to jobsDBbindingSource and the DB field eg "JobNumber" , finally compiled the project.

    The server project was modified to reference the assembly from the separate DataSet project as below:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using System.Xml;
    using System.Data;
    using System.Reflection;
    using Microsoft.VisualStudio.Tools.Applications.Runtime;
    using System.Diagnostics;
    using System.Threading;


    namespace DMEJobSheet
    {
        public class WordServer
        {
            private jobsDBLibrary.jobsDBDataSetTableAdapters.CHANGEDTableAdapter _adapterChange;
            private jobsDBLibrary.jobsDBDataSetTableAdapters.JOBSTableAdapter _adapter;
            private jobsDBLibrary.jobsDBDataSet.JOBSDataTable _dataTable;
            private jobsDBLibrary.jobsDBDataSet.CHANGEDDataTable _dataTableChange;
            public jobsDBLibrary.jobsDBDataSet _documentDataSource;
            private string _downloadPath = string.Empty;
            private string sJobNumber = string.Empty;
            private string sApplication = string.Empty;
            private string sModule = string.Empty;
            private string sFunction = string.Empty;
            ServerDocument serverReport = null;

            public string DownloadPath
            {
                get
                {
                    return (System.Configuration.ConfigurationManager.AppSettings.Get("downloadPath").ToString());
                }
            }

        public WordServer()
        {
            try
            {
                jobsDBLibrary.jobsDBDataSet dataSource = new jobsDBLibrary.jobsDBDataSet();
                DataRow drNew;
                DMEprocess oDME = new DMEprocess();
                _documentDataSource = new jobsDBLibrary.jobsDBDataSet();
                _dataTableChange = dataSource.CHANGED;
                _dataTable = dataSource.JOBS;
                _adapterChange = new jobsDBLibrary.jobsDBDataSetTableAdapters.CHANGEDTableAdapter();
                _adapter = new jobsDBLibrary.jobsDBDataSetTableAdapters.JOBSTableAdapter();
                while (true)
                {
                    _documentDataSource.Clear();
                    dataSource.Clear();
                    _adapterChange.Fill(_dataTableChange);
                    _adapter.Fill(_dataTable);
                    if (_dataTableChange.Count > 0)
                    {
                        foreach (DataRow dr in _dataTableChange.Rows)
                        {
                            sJobNumber = dr["JobNumber"].ToString();
                            drNew = _dataTable.Rows.Find(sJobNumber);
                            if (drNew != null)
                            {
                                sApplication = drNew["sApplication"].ToString();
                                sModule = drNew["sModule"].ToString();
                                sFunction = drNew["sFunction"].ToString();
                                _documentDataSource.Tables["JOBS"].ImportRow(drNew);
                                dr.Delete();
                                this.GenerateJobSheet(sJobNumber);
                            }
                        }
                        _adapterChange.Update(_dataTableChange);
                        _dataTableChange.Clear();
                    }
                }
            }
            catch (System.Data.SqlClient.SqlException MySqlException)
            {
                string sMessage = string.Empty;
                jobsDBLibrary.jobsDBDataSet dataSource = new jobsDBLibrary.jobsDBDataSet();
                string sPath = DownloadPath;
                if (!System.IO.Directory.Exists(sPath))
                {
                    System.IO.Directory.CreateDirectory(sPath);
                }
                string sPathAndFile = sPath + "\\" + System.DateTime.Now.ToString("ddMMMyyyy") + "A-Exception.log";
                TextWriter oTR = File.AppendText(sPathAndFile);
                foreach (System.Data.SqlClient.SqlError myError in MySqlException.Errors)
                {
                    sMessage = "Error: " + myError.Message +
                        "\nClass: " + myError.Class +
                        "\nLineNumber: " + myError.LineNumber +
                        "\nNumber: " + myError.Number +
                        "\nProcedure: " + myError.Procedure +
                        "\nServer: " + myError.Server +
                        "\nSource: " + myError.Source +
                        "\nState: " + myError.State + "\n";
                    oTR.WriteLine(sMessage);
                }
                oTR.Close();
            }
            catch (Exception e)
            {
                string sMessage = e.Message + " " + e.Source;
                jobsDBLibrary.jobsDBDataSet dataSource = new jobsDBLibrary.jobsDBDataSet();
                string sPath = DownloadPath;
                if (!System.IO.Directory.Exists(sPath))
                {
                    System.IO.Directory.CreateDirectory(sPath);
                }
                string sPathAndFile = sPath + "\\" + System.DateTime.Now.ToString("ddMMMyyyy") + "B-Exception.log";
                TextWriter oTR = File.AppendText(sPathAndFile);
                oTR.WriteLine(sMessage);
                oTR.Close();
            }
        }

            public void GenerateJobSheet(string sJobNumber)
            {
                try
                {
                    //Open server document, and load in data
                    string sPath = DownloadPath;
                    string sourcePath = sPath + "jobsDocument.doc";
                    string destinationPath = sPath + "Job Sheet " + sJobNumber + ".doc";
                    File.Copy(sourcePath, destinationPath, true);

                    if (ServerDocument.IsCacheEnabled(destinationPath))
                    {
                        //Store data in the cache
                        serverReport = new Microsoft.VisualStudio.Tools.Applications.Runtime.ServerDocument(destinationPath);

                        CachedDataHostItem cachedView;
                        CachedDataItem cachedItemJob;

                        string viewName = "jobsDocument.ThisDocument";
                        string itemNameJob = "jobsDBDataSet";

                        cachedView = serverReport.CachedData.HostItems[viewName];
                        cachedItemJob = cachedView.CachedData[itemNameJob];

                        cachedItemJob.SerializeDataInstance(_documentDataSource);

                        serverReport.Save();
                    }
                }
                catch (Exception e)
                {
                    string sMessage = e.Message + " " + e.Source;
                    jobsDBLibrary.jobsDBDataSet dataSource = new jobsDBLibrary.jobsDBDataSet();
                    string sPath = DownloadPath;
                    if (!System.IO.Directory.Exists(sPath))
                    {
                        System.IO.Directory.CreateDirectory(sPath);
                    }
                    string sPathAndFile = sPath + "\\" + System.DateTime.Now.ToString("ddMMMyyyy") + "B-Exception.log";
                    TextWriter oTR = File.AppendText(sPathAndFile);
                    oTR.WriteLine(sMessage);
                    oTR.Close();
                }
                finally
                {
                    serverReport.Close();
                }
            }
        }
    }

    ============================================================

    The client side code was not altered in anyway:

    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 jobsDocument
    {
        public partial class ThisDocument
        {
            private void ThisDocument_Startup(object sender, System.EventArgs e)
            {
            }

            private void ThisDocument_Shutdown(object sender, System.EventArgs e)
            {
            }

            #region VSTO Designer generated code

            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InternalStartup()
            {
                this.Startup += new System.EventHandler(ThisDocument_Startup);
                this.Shutdown += new System.EventHandler(ThisDocument_Shutdown);
            }

            #endregion
        }
    }

    Accessing the Word Document eg "Job Number 19004.doc" on the server from the client works 100%. The opened document has the DB data displayed in the bookmarks.

    To Microsoft: Why are the DataSets generated by the wizard (client and server) not acceptable to VSTO? Are they not identical? Must one always generate the dataset in a separate assembly?

    Thank you Apurva.

    Kevin Gordon

    New Zealand.

     

     

    Tuesday, January 31, 2006 8:40 PM