Accessing Excel Custom Document Properties programmatically

0 votes
asked Jul 16, 2009 by dan-mcclain

I'm trying to add custom properties to a workbook I have created programmatically. I have a method in place for getting and setting properties, but the problem is the workbook is returning null for the CustomDocumentProperties property. I cannot figure out how to initialize this property so that I can add and retrieve properties from the workbook. Microsoft.Office.Core.DocumentProperties is an interface, so I cant go and do the following

if(workbook.CustomDocumentProperties == null)
    workbook.CustomDocumentProperties = new DocumentProperties;

Here is the code I have to get and set the properties:

     private object GetDocumentProperty(string propertyName, MsoDocProperties type)
    {
        object returnVal = null;

        Microsoft.Office.Core.DocumentProperties properties;
        properties = (Microsoft.Office.Core.DocumentProperties)workBk.CustomDocumentProperties;

        foreach (Microsoft.Office.Core.DocumentProperty property in properties)
        {
            if (property.Name == propertyName && property.Type == type)
            {
                returnVal = property.Value;
            }
            DisposeComObject(property);
        }

        DisposeComObject(properties);

        return returnVal;
    }

    protected void SetDocumentProperty(string propertyName, string propertyValue)
    {
        DocumentProperties properties;
        properties = workBk.CustomDocumentProperties as DocumentProperties;

        bool propertyExists = false;
        foreach (DocumentProperty prop in properties)
        {
            if (prop.Name == propertyName)
            {
                prop.Value = propertyValue;
                propertyExists = true;
            }
            DisposeComObject(prop);

            if(propertyExists) break;
        }

        if (!propertyExists)
        {
            properties.Add(propertyName, false, MsoDocProperties.msoPropertyTypeString, propertyValue, Type.Missing);
        }

        DisposeComObject(propertyExists);

    }

The line properties = workBk.CustomDocumentProperties as DocumentProperties; always set properties to null.

This is using Microsoft.Office.Core v12.0.0.0 and Microsoft.Office.Interop.Excell v12.0.0.0 (Office 2007)

4 Answers

0 votes
answered Jul 16, 2009 by gary-mcgill

I looked at my own code and can see that I access the properties using late binding. I can't remember why, but I'll post some code in case it helps.

object properties = workBk.GetType().InvokeMember("CustomDocumentProperties", BindingFlags.Default | BindingFlags.GetProperty, null, workBk, null);

object property = properties.GetType().InvokeMember("Item", BindingFlags.Default | BindingFlags.GetProperty, null, properties, new object[] { propertyIndex });

object propertyValue = property.GetType().InvokeMember("Value", BindingFlags.Default | BindingFlags.GetProperty, null, propertyWrapper.Object, null);

EDIT: ah, now I remember why. :-)

EDIT 2: Jimbojones' answer - to use the dynamic keyword - is a better solution (if you value ease-of-use over the performance overhead of using dynamic).

0 votes
answered Jul 16, 2009 by dan-mcclain

I found the solution here.

Here is the code I ended up with:

    public void SetDocumentProperty(string propertyName, string propertyValue)
    {
        object oDocCustomProps = workBk.CustomDocumentProperties;
        Type typeDocCustomProps = oDocCustomProps.GetType();

        object[] oArgs = {propertyName,false,
                 MsoDocProperties.msoPropertyTypeString,
                 propertyValue};

        typeDocCustomProps.InvokeMember("Add", BindingFlags.Default |
                                   BindingFlags.InvokeMethod, null,
                                   oDocCustomProps, oArgs);

    }

    private object GetDocumentProperty(string propertyName, MsoDocProperties type)
    {
        object returnVal = null;

        object oDocCustomProps = workBk.CustomDocumentProperties;
        Type typeDocCustomProps = oDocCustomProps.GetType();


        object returned = typeDocCustomProps.InvokeMember("Item", 
                                    BindingFlags.Default |
                                   BindingFlags.GetProperty, null,
                                   oDocCustomProps, new object[] { propertyName });

        Type typeDocAuthorProp = returned.GetType();
        returnVal = typeDocAuthorProp.InvokeMember("Value",
                                   BindingFlags.Default |
                                   BindingFlags.GetProperty,
                                   null, returned,
                                   new object[] { }).ToString();

        return returnVal;
    }

Some exception handling is necessary to hand if the property doesnt exist when retrieved

0 votes
answered Jul 13, 2011 by cori

Late answer to this question, but I worked out a simpler method for adding custom DocumentProperties that might be of use to someone in the future.

My problem was that calling the Add() method with the System type supplied by System.String.GetType() triggered a COMException: Type mismatch. Referring to the link in the previous answers it's clear that this method expects an Office-specific type, so the code that ended up working for me was:

var custProps = (Office.DocumentProperties)this.CustomDocumentProperties;
custProps.Add( "AProperty", false, MsoDocProperties.msoPropertyTypeString, "AStringProperty" );

Because it's a CustomDocumentProperty Office will add the custom property without difficulty, but if you need to check for existence or validate the value when the CustomDocumentProperty might not exist you'll have to catch a System.ArgumentException.

EDIT

As pointed out in Oliver Bock's comment, this is an Office 2007 and up only solution, as far as I know.

0 votes
answered Jul 5, 2012 by jimbojones

If you are targetting .NET 4.0, you can use the dynamic key word for late binding

 Document doc = GetActiveDocument();
 if ( doc != null )
 {
     dynamic properties = doc.CustomDocumentProperties;
     foreach (dynamic p in properties)
     {
         Console.WriteLine( p.Name + " " + p.Value);
     }
 }
Welcome to Q&A, where you can ask questions and receive answers from other members of the community.
Website Online Counter

...