none
VSTO Outlook Addin - Why doesn't my Property Page show up?

    Question

  • When I click Tools|Options here's what happens:

    1. The following event handler executes:

    void ThisApplication_OptionsPagesAdd(Outlook.PropertyPages pages)
    {
       pages.Add(new ViewPoint.PropertyTab(), "");
    }

    2.     bool m_Dirty = false;
    OK, class is obviously initializing...

    3. Then InitializeComponent();
    OK, class is obviously initializing...

    But then, that's it! No call into the TabCaption method as I would expect, to get the tab caption for the property page. And the tab doesn't appear! I have no idea what I'm doing wrong. I copied this code (see below) straight out of Visual Studio Tools for Office by Eric Carter and Eric Lippert, but I just can't get this to work!

    Thanks for your help!

    - Joseph Geretz -

    public partial class PropertyTab : UserControl, Outlook.PropertyPage
    {
        const int C_CaptionDispID = -518;
        bool m_Dirty = false;

        public PropertyTab()
        {
            InitializeComponent();
        }

        void Outlook.PropertyPage.Apply()
        {
        }

        void Outlook.PropertyPage.GetPageInfo(ref string helpFile, ref int helpContext)
        {
        }

        bool Outlook.PropertyPage.Dirty
        {
            get
            {
                return m_Dirty;
            }
        }

        [DispId(C_CaptionDispID)]
        public string TabCaption
        {
            get
            {
                return "ViewPoint";
            }
        }
    }
    Tuesday, May 23, 2006 3:32 PM

Answers

  • Here's the problem:
     
    VSTO projects require the following attribute preceding the UserControl class definition:
     
    [ComVisible(true)]

    'Be nice if the book I'm working from, Visual Studio Tools for Office by Carter and Lippert, would have gotten this right. You'd think that two authors collaborating  together could turn out accurate documentation! I guess they couldn't be bothered to actually write the sample code - easier to copy and paste it from the last book they wrote on Extensibility AddIns. Grrrrr - cost me 4 days :-(
     
    Hope this helps someone else out there with the same problem.
     
    - Joe Geretz -
    Friday, May 26, 2006 2:47 AM

All replies

  • I've tracked this down to an exception on the following line of code:
     
    pages.Add(new ViewPoint.PropertyTab(), "");
     
    Here's the error:
     
    System.Runtime.InteropServices.COMException was caught
      Message="The operation failed."
      Source="Microsoft Office Outlook"
      ErrorCode=-1802485758
      StackTrace:
           at Microsoft.Office.Interop.Outlook.PropertyPages.Add(Object Page, String Title)
           at ViewPoint.ThisApplication.ThisApplication_OptionsPagesAdd(PropertyPages pages) in C:\Documents and Settings\Administrator.INTERNAL\My Documents\Visual Studio 2005\Projects\ViewPoint\ViewPoint\ThisApplication.cs:line 51
     
    Not much information. Where do I go from here?
     
    Thanks for your help.
    Tuesday, May 23, 2006 11:17 PM
  • Another thing I notice is that in the following event:
     
    void ThisApplication_OptionsPagesAdd(Outlook.PropertyPages pages)
    {
      pages.Add(new ViewPoint.PropertyTab(), "");
    }
     
    the pages parameter is passed in with a count of 0. Is this correct? Does this reflect the number of custom property pages (in which case 0 is probably correct) or should it reflect the number of 'built in' property pages, in which case 0 would not be the expected value?
     
    Thanks for your help,
    Tuesday, May 23, 2006 11:49 PM
  • I'm at my wits end with this. Clearly, there are going to be some differences between ny project, which uses the VSTO approach and the sample project whch uses Extensibility.

    http://www.codeproject.com/dotnet/PropertyPages.asp

    Other than that though, I've done my utmost to implement a common codebase between both my project and the sample project which establishes a workable baseline. Here's what i'm looking at:

    Inside ThisApplication_Startup, which excutes when Outlook starts and my AddIn loads, I execute the following code:

    this.OptionsPagesAdd +=
        new ApplicationEvents_11_OptionsPagesAddEventHandler
            (ThisApplication_OptionsPagesAdd);

    Clearly, this is effective, since subsequently, when I click on Tools|Options inside Outlook, control jumps into my event handler:

    private void ThisApplication_OptionsPagesAdd(PropertyPages pages)
    {
        try
        {
            pages.Add(new PropertyTab(), "ViewPoint");
        }
        catch (System.Exception e)
        {
            MessageBox.Show("Hmm, an error occurred!");
        }
    }

    Having gotten this far, the only two relevant objects at this point will be the pages object and the PropertyTab UserControl. These are the only objects which are relevant to the statement which is failing:

    pages.Add(new PropertyTab(), "ViewPoint");

    the object 'pages' which is passed in as a parameter is formally defined as Microsoft.Office.Interop.Outlook.PropertyPages. This is correct, as far as I know.

    As for the PropertyTab class, I copied this entirely from the sample project into a blank class file and simply made the following minimal changes.

    1. Changed the namespace to ViewPoint
    2. Changed the name of the class to PropertyTab

    3a. Added: using Microsoft.Office.Interop.Outlook;
    3b. Changed:
        public class MyOptionPage : System.Windows.Forms.UserControl, Outlook.PropertyPage
        to
        public class PropertyTab : System.Windows.Forms.UserControl, PropertyPage

    This bothers me just a bit. Without these changes I get compile errors - The type or namespace name 'Outlook' could not be found (are you missing a using directive or an assembly reference?). I do not fully understand this as my project is utilizing the same references as the sample project. I attempted to add a using directive for Microsoft.Office.Interop, thinking that this would legalize the syntax Outlook.PropertyPage, but it did not. Ultimately, I simply added the fully qualified using directive, using Microsoft.Office.Interop.Outlook and removed the Outlook qualifier from the Interface definition. This led to a clean compile. I'm assuming this is correct, because if I look for a definition of PropertyPage, I get the following:

    using System;
    using System.Runtime.InteropServices;

    namespace Microsoft.Office.Interop.Outlook
    {
        [TypeLibType(4096)]
        [Guid("0006307E-0000-0000-C000-000000000046")]
        public interface PropertyPage
        {
            [DispId(8449)]
            bool Dirty { get; }

            void Apply();
            void GetPageInfo(ref string HelpFile, ref int HelpContext);
        }
    }

    This is the exact same definition I see when I inspect the definition of this interface in the sample project.

    I know that when executing pages.Add(new PropertyTab(), "ViewPoint"); a new instance of PropertyTab is created because I sccessfully step through the following code in the debugger:

    public PropertyTab()
    {
        InitializeComponent();
        isDirty = false;
    }


    ultimately though, after all this work, pages.Add(new PropertyTab(), "ViewPoint"); trips the exact same same error: The operation failed.

    I'm baffled. The pages collection is passed in to the event handler. I'm attempting the Add method, supplying a User Control which meets the required definition (copied from another project in which it works) yet in my project I get 'The operation failed'.

    Where do I go from here?

    For completeness, I'm pasting the entire property page user control class below my signature.

    Thanks for any help which you can provide!

    - Joe Geretz -

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using Microsoft.Office.Interop.Outlook;

    namespace ViewPoint
    {
        public class PropertyTab : System.Windows.Forms.UserControl, PropertyPage
        {

            private bool isDirty;
            private System.Windows.Forms.Button button1;
            private PropertyPageSite ppSite;

            private System.ComponentModel.Container components = null;

            public PropertyTab()
            {
                InitializeComponent();
                isDirty = false;
            }

            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    if (components != null)
                    {
                        components.Dispose();
                    }
                }
                base.Dispose(disposing);
            }

            #region Component Designer generated code
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                this.button1 = new System.Windows.Forms.Button();
                this.SuspendLayout();
                //
                // button1
                //
                this.button1.Location = new System.Drawing.Point(32, 56);
                this.button1.Name = "button1";
                this.button1.TabIndex = 0;
                this.button1.Text = "button1";
                this.button1.Click += new System.EventHandler(this.button1_Click);
                //
                // MyOptionPage
                //
                this.Controls.Add(this.button1);
                this.Name = "MyOptionPage";
                this.Load += new System.EventHandler(this.MyOptionPage_Load);
                this.ResumeLayout(false);

            }
            #endregion

            #region PropertyPage Members

            public bool Dirty
            {
                get
                {
                    return isDirty;
                }
            }

            public void GetPageInfo(ref string HelpFile, ref int HelpContext)
            {
                // TODO:  Add MyOptionPage.GetPageInfo implementation
            }

            public void Apply()
            {
                MessageBox.Show("Hello World");

            }

            #endregion

            private void button1_Click(object sender, System.EventArgs e)
            {
                isDirty = true;
                ppSite.OnStatusChange();
            }

            private void MyOptionPage_Load(object sender, System.EventArgs e)
            {
                Type myType = typeof(System.Object);
                string assembly =
                System.Text.RegularExpressions.Regex.Replace(myType.Assembly.CodeBase, "mscorlib.dll", "System.Windows.Forms.dll");
                assembly = System.Text.RegularExpressions.Regex.Replace(assembly, "file:///", "");
                assembly = System.Reflection.AssemblyName.GetAssemblyName(assembly).FullName;
                Type unmanaged =
                    Type.GetType(System.Reflection.Assembly.CreateQualifiedName
                    (assembly, "System.Windows.Forms.UnsafeNativeMethods"));
                Type oleObj = unmanaged.GetNestedType("IOleObject");
                System.Reflection.MethodInfo mi = oleObj.GetMethod("GetClientSite");
                object myppSite = mi.Invoke(this, null);
                this.ppSite = (PropertyPageSite)myppSite;
            }

            [DispId(-518)]
            public string Caption
            {
                get { return "ViewPoint"; }
            }
        }
    }

    Thursday, May 25, 2006 12:58 AM
  • Here's the problem:
     
    VSTO projects require the following attribute preceding the UserControl class definition:
     
    [ComVisible(true)]

    'Be nice if the book I'm working from, Visual Studio Tools for Office by Carter and Lippert, would have gotten this right. You'd think that two authors collaborating  together could turn out accurate documentation! I guess they couldn't be bothered to actually write the sample code - easier to copy and paste it from the last book they wrote on Extensibility AddIns. Grrrrr - cost me 4 days :-(
     
    Hope this helps someone else out there with the same problem.
     
    - Joe Geretz -
    Friday, May 26, 2006 2:47 AM
  • Just to let you know.... It did help somebody else...

    Thanks to your four day effort, i solved this in 10 minutes just reading this thread.

    Thanks for posting the solution as well.. ;-)

    best regards
    Anders
    Monday, June 12, 2006 8:36 AM
  • Thanks Joe for writting the solution as well. It helped me too to find the solution.


    Nikolas
    Thursday, October 05, 2006 2:53 PM
  • This post was a great help to me as well - thanks for figuring it out.

     

    -Andrew

    Tuesday, November 14, 2006 8:26 PM
  • Thanks! Helped me too, unfortunately, it took me 2 hours to find this page... google didnt do a very good job indexing it! (blame it on the tools (c;)
    Wednesday, December 20, 2006 10:06 AM
  • Thanks mate. It helped me too to solve an error with too less information...

    -- Marino
    Wednesday, November 11, 2009 9:53 PM
  • Thanks so much Joe.. Even i spent two days to find this simple solution :)

    -- Sanjay BK

    • Proposed as answer by Sanjay BK Friday, October 05, 2012 9:36 AM
    • Unproposed as answer by Sanjay BK Friday, October 05, 2012 9:36 AM
    Friday, October 05, 2012 9:34 AM
  • Thanks so much Joe.. Even i spent two days to find this simple solution :)

    -- Sanjay BK

    Below is the working property page:

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;

    namespace Outlook_PropertyPage
    {
        [ComVisible(true)]
    public class MyOptionPage : System.Windows.Forms.UserControl , Microsoft.Office.Interop.Outlook.PropertyPage 
    {
    private bool isDirty;
    private System.Windows.Forms.Button button1;
    private Microsoft.Office.Interop.Outlook.PropertyPageSite ppSite;

    private System.ComponentModel.Container components = null;

    public MyOptionPage()
    {
    InitializeComponent();
    isDirty = false;
    }


    protected override void Dispose( bool disposing )
    {
    if( disposing )
    {
    if(components != null)
    {
    components.Dispose();
    }
    }
    base.Dispose( disposing );
    }

    #region Component Designer generated code
    /// <summary> 
    /// Required method for Designer support - do not modify 
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
                this.button1 = new System.Windows.Forms.Button();
                this.SuspendLayout();
                // 
                // button1
                // 
                this.button1.Location = new System.Drawing.Point(32, 56);
                this.button1.Name = "button1";
                this.button1.Size = new System.Drawing.Size(75, 23);
                this.button1.TabIndex = 0;
                this.button1.Text = "button1";
                this.button1.Click += new System.EventHandler(this.button1_Click);
                // 
                // MyOptionPage
                // 
                this.Controls.Add(this.button1);
                this.Name = "MyOptionPage";
                this.Load += new System.EventHandler(this.MyOptionPage_Load);
                this.ResumeLayout();
    }

    #endregion

    #region PropertyPage Members

    public bool Dirty
    {
    get
    {
    return isDirty;
    }
    }

    public void GetPageInfo(ref string HelpFile, ref int HelpContext)
    {
    // TODO:  Add MyOptionPage.GetPageInfo implementation
    }

    public void Apply()
    {
    MessageBox.Show("Hello World");

    }

    #endregion

    private void button1_Click(object sender, System.EventArgs e)
    {
    isDirty = true;
    ppSite.OnStatusChange();
    }

    private void MyOptionPage_Load(object sender, System.EventArgs e)
    {
    Type myType = typeof(System.Object); 
    string assembly = 
    System.Text.RegularExpressions.Regex.Replace(myType.Assembly.CodeBase, "mscorlib.dll", "System.Windows.Forms.dll");
    assembly = System.Text.RegularExpressions.Regex.Replace(assembly, "file:///", "");
    assembly = System.Reflection.AssemblyName.GetAssemblyName(assembly).FullName;
    Type unmanaged =
    Type.GetType(System.Reflection.Assembly.CreateQualifiedName
    (assembly, "System.Windows.Forms.UnsafeNativeMethods"));
    Type oleObj = unmanaged.GetNestedType("IOleObject");
    System.Reflection.MethodInfo mi = oleObj.GetMethod("GetClientSite");
    object myppSite = mi.Invoke(this, null);
    this.ppSite = (Microsoft.Office.Interop.Outlook.PropertyPageSite)myppSite;
    }

    [DispId(-517)]
    public string Caption
    {
    get{return "Attachment Manager";}
    }
    }
    }

    Friday, October 05, 2012 9:37 AM