none
"Suggested" Custom Property Works, custom Custom Property throws error - Word 2010, VS 2010, C# RRS feed

  • Question

  • I am trying to add some custom properties to a Word 2007 or Word 2010 document using the following code:

    Word.Document document = Globals.ThisAddIn.Application.ActiveDocument;

    Office.DocumentProperties oDocCustomProps = document.CustomDocumentProperties;
    oDocCustomProps.Add("username", false, MsoDocProperties.msoPropertyTypeString, "blah", Type.Missing);

    It throws the very generic exception: 

    Error HRESULT E_FAIL has been returned from a call to a COM component.

    If, however, I change the last line of code to use one of their "suggested" names for the custom property, such as "Owner" instead of "username", then it works, despite the fact that "Owner" did not exist as an entry in the custom.xml file prior to the code's execution.

    The line that fails does put the name of the property into the custom.xml file, but messes up on the value. Whereas it should look like this:

    <property name="username" pid="4" fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"><vt:lpwstr>blah<vt:lpwstr/></property>

    what is actually produced looks like this:

    <property name="username" pid="4" fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"><vt:lpwstr/></property>

    Any thoughts or suggestions? Is there something specific that I need to do to create the "name" of the custom property before giving it a value and adding it to the custom.xml file? Also, should the fmtid be different for each entry? Right now, my custom.xml file is showing four entries, and they all have the same value for fmtid (although some of those were created through Word 2010, not through C# code, so I would assume Word knows what it's doing - maybe). Could this be part of the problem?

    Thanks!

    Friday, May 25, 2012 4:51 PM

Answers

  • Hi Lynae,

    Thanks for posting in the MSDN Forum.

    I suppost you do in in a Console application or Windows forum application. I can reproduce your error in that place. I would suggest develop a add-in project to do it or create a word document-level application to approach it. Following snippet works fine on my side.

    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Xml.Linq;
    using Microsoft.Office.Tools.Word;
    using Microsoft.VisualStudio.Tools.Applications.Runtime;
    using Office = Microsoft.Office.Core;
    using Word = Microsoft.Office.Interop.Word;
    using log4net;
    using MyLog4Net;
    
    namespace WordDocument10
    {
        public partial class ThisDocument
        {
            private ILog Log = MyLog4Net.MyLog4Net.GetMyLog(typeof(ThisDocument));
    
            private void ThisDocument_Startup(object sender, System.EventArgs e)
            {
                try
                {
                    Office.DocumentProperties dps = Application.ActiveDocument
                        .CustomDocumentProperties;
                    Log.Info("Get Properties");
                    if (dps != null)
                    {
                        Office.DocumentProperty dp = dps.Add("userName", false,
            Microsoft.Office.Core.MsoDocProperties.msoPropertyTypeString,
            "White Papers", missing);
                        Log.Info("Set Property");
                        dp.Value = "Hello World!";
                        Log.Info("Value is : " + dp.Value);
                    }
                    else
                    {
                        Log.Error("There has no customized properties");
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(string.Format("{0}\n{1}", ex.Message, ex.StackTrace));
                }
            }
    
            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
        }
    }

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Monday, May 28, 2012 5:25 AM
    Moderator
  • Thank you so much for your help and suggestions! As it turns out, implementing some of the code differences (mainly from Tom_Xu's sample code) helped me to realize that I was not correctly testing for the previous existence of the custom property. I was testing for the existence of the value, not the existence of the property itself. So if the property was created with an empty value, and I then tried to "add" the property, that's when the generic error was thrown. Fixed now and working!

    Thanks again!
    • Marked as answer by Lynae Kamm Friday, June 1, 2012 3:29 PM
    Wednesday, May 30, 2012 7:42 PM

All replies

  • Also, I tried using Word (both 2007 and 2010) to record a macro while creating the custom property of "username" to see what code Word thought should be generated... but the macro recording completely ignored it. The things I did before and after creating the custom property got recorded, but creating a custom property entry seems to be completely invisible to the macro. Bizarre.
    Friday, May 25, 2012 5:03 PM
  • Hi Lynae,

    Thanks for posting in the MSDN Forum.

    I suppost you do in in a Console application or Windows forum application. I can reproduce your error in that place. I would suggest develop a add-in project to do it or create a word document-level application to approach it. Following snippet works fine on my side.

    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Xml.Linq;
    using Microsoft.Office.Tools.Word;
    using Microsoft.VisualStudio.Tools.Applications.Runtime;
    using Office = Microsoft.Office.Core;
    using Word = Microsoft.Office.Interop.Word;
    using log4net;
    using MyLog4Net;
    
    namespace WordDocument10
    {
        public partial class ThisDocument
        {
            private ILog Log = MyLog4Net.MyLog4Net.GetMyLog(typeof(ThisDocument));
    
            private void ThisDocument_Startup(object sender, System.EventArgs e)
            {
                try
                {
                    Office.DocumentProperties dps = Application.ActiveDocument
                        .CustomDocumentProperties;
                    Log.Info("Get Properties");
                    if (dps != null)
                    {
                        Office.DocumentProperty dp = dps.Add("userName", false,
            Microsoft.Office.Core.MsoDocProperties.msoPropertyTypeString,
            "White Papers", missing);
                        Log.Info("Set Property");
                        dp.Value = "Hello World!";
                        Log.Info("Value is : " + dp.Value);
                    }
                    else
                    {
                        Log.Error("There has no customized properties");
                    }
                }
                catch (Exception ex)
                {
                    Log.Error(string.Format("{0}\n{1}", ex.Message, ex.StackTrace));
                }
            }
    
            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
        }
    }

    Have a good day,

    Tom


    Tom Xu [MSFT]
    MSDN Community Support | Feedback to us

    Monday, May 28, 2012 5:25 AM
    Moderator
  • Here's something I put together to store custom settings against a Word template. It uses the Globals.ThisDocument.Variables collection to store custom properties in a serialized form.. it's very useful for storing any serialized type into a document. It's a generic class so can be instantiated with a type of your choice. I call it from another class called SettingsHelper which has similar properties to this one :-

    PS. Don't forget to replace "Espirito.Research.WordTemplate" with your assembly name.

     

            public static bool CustomRightPaneActive
            {
                get { return new SettingsAccessor<bool>().GetSetting("CustomRightPaneActive", true); }
                set { new SettingsAccessor<bool>().SetSetting("CustomRightPaneActive", value); }
            }

    Heres the implementation

        internal class SettingsAccessor<T>
        {
            internal T DefaultValue { get; set; }

            internal void SetSetting(string settingName, object value)
            {
                try
                {
                    Globals.ThisDocument.Variables[settingName].Delete();
                }
                catch
                {
                    //Always delete the previous setting as there doesn't appear to be a way to check if one exists! Good old VSTO / Interop.
                }

                //Store as serialized XML
                if (value != null)
                {
                    XmlSerializer s = new XmlSerializer(value.GetType(), "Espirito.Research.WordTemplate");
                    using (StringWriter writer = new StringWriter())
                    {
                        s.Serialize(writer, value);
                        Globals.ThisDocument.Variables.Add(settingName, writer.ToString());
                    }
                }
            }

            internal T GetSetting(string settingName)
            {
                Type type = typeof(T);

                try
                {
                    //De serialize
                    string value = Globals.ThisDocument.Variables[settingName].Value;
                    XmlSerializer s = new XmlSerializer(typeof(T), "Espirito.Research.WordTemplate");
                    using (StringReader reader = new StringReader(value))
                    {
                        object returnValue = s.Deserialize(reader);
                        return (T)returnValue;
                    }
                }
                catch
                {
                    //Doesn't exist.. create.
                    Globals.ThisDocument.Variables.Add(settingName, string.Empty);
                    return default(T);
                }            
            }

            internal T GetSetting(string settingName, T defaultValue)
            {
                Type type = typeof(T);

                try
                {
                    //De serialize
                    string value = Globals.ThisDocument.Variables[settingName].Value;
                    XmlSerializer s = new XmlSerializer(typeof(T), "Espirito.Research.WordTemplate");
                    using (StringReader reader = new StringReader(value))
                    {
                        object returnValue = s.Deserialize(reader);
                        return (T)returnValue;
                    }
                }
                catch (InvalidOperationException)
                {
                    Globals.ThisDocument.Variables[settingName].Delete();
                    //Doesn't exist.. create using default..
                    Globals.ThisDocument.Variables.Add(settingName, defaultValue);
                    return defaultValue;
                }
                catch
                {
                    //Doesn't exist.. create using default..
                    Globals.ThisDocument.Variables.Add(settingName, defaultValue);
                    return defaultValue;
                }
            }
        }


    Monday, May 28, 2012 12:20 PM
  • Thank you so much for your help and suggestions! As it turns out, implementing some of the code differences (mainly from Tom_Xu's sample code) helped me to realize that I was not correctly testing for the previous existence of the custom property. I was testing for the existence of the value, not the existence of the property itself. So if the property was created with an empty value, and I then tried to "add" the property, that's when the generic error was thrown. Fixed now and working!

    Thanks again!
    • Marked as answer by Lynae Kamm Friday, June 1, 2012 3:29 PM
    Wednesday, May 30, 2012 7:42 PM