Archive for the ‘COM’ Category

C# .Net – How to capture a close button press on a Office Custom Task pane

April 27, 2011

When developing our office custom task pane we wanted to control whether pane was displayed when the office application started.

Initially we implemented a ribbon menu that toggled the panes visibility. When the user toggled the pane, we stored the visibility in the registry so that the next time the office application started we would only show the pane if it was required.

But, if the user closed the pane via its close button rather than the toggle button then we would not update the registry.

The solution was to update the registry when the pane visibility changed rather than when the toggle method is called.

There are a couple of events exposed on _CustomTaskPaneEvents_Event interface:

  1. DockPositionStateChange
  2. VisibleStateChange

The VisibleStateChange event is fired when ever the pane shows or is removed.

public void CTPFactoryAvailable( ICTPFactory CTPFactoryInst ) {
    CTP = CTPFactoryInst.CreateCTP(
        typeof( OfficeTaskPanelControl ).FullName,
        "Perfion", Type.Missing );

CTP.VisibleStateChange += CTP_VisibleStateChange;

    CTP.Visible = true;
    CTP.Width = 250;
}

void CTP_VisibleStateChange( CustomTaskPane CustomTaskPaneInst ) {
    // Update the registry at this point. RegHlp.Set("PanelVisibility", "" + CTP.Visible );
}
Advertisements

C# .Net – Excel Com Issue solved

March 25, 2011

Today I ran into an issue calling Excel via COM from C# .Net.

We had a situation where calling the com code worked fine on some machines and crashed on others.

MSexcel.Range cell = getTableCell( rowIndex, columnIndex );
cell.Value2 = "Test";

The call to set property Value2 would on some machines throw the following exception:

0x800A03EC at System.RuntimeType.ForwardCallToInvokeMember( String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData) 

I then tried to get Value2 from the cell and that threw the following exception:

“Old format or invalid type library”. (Exception from HRESULT: 0x80028018 (TYPE_E_INVDATAREAD)) at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData) 

After a lot of searching I came across an article which solves the issue.

The problem seems to be when the installed version of Excel has a different locale to that of the current user.

I have created a wrapper class that can be used to wrap any of the Excel objects so that you can safely call the properties and methods.

using System.Text;
using MSexcel = Microsoft.Office.Interop.Excel;
using System.Globalization;
using System.Reflection;
namespace ExcelWrapper {
    public class PExcelOb {
        object officeObject;
        Type type;
        public static CultureInfo CultureInfo = null;
        public static void SetUpCulture( MSexcel.Application app ) {
            int culture = app.LanguageSettings.get_LanguageID(
                Microsoft.Office.Core.MsoAppLanguageID.msoLanguageIDUI );
            CultureInfo = new CultureInfo( culture );
        }
        public PExcelOb( object officeObject ) {
            if( CultureInfo == null ) {
                throw new Exception( "SetUpCulture has not been called" );
            }
            this.officeObject = officeObject;
            type = officeObject.GetType();
        }
        public object Invoke( string method, params object[] paramters ) {
            object results = null;
            results = type.InvokeMember(
                method, BindingFlags.InvokeMethod, null,
                officeObject, paramters, CultureInfo );
            return results;
        }
        public object this[string name] {
            get {
                return type.InvokeMember(
                    name, BindingFlags.GetProperty, null,
                    officeObject, null, CultureInfo );
            }
            set {
                object[] parameters = new object[1];
                parameters[0] = value;
                type.InvokeMember(
                    name, BindingFlags.SetProperty, null,
                    officeObject, parameters, CultureInfo );
            }
        }
    }
}

To use the wrapper you need to first setup the Culture such:

PExcelOb.SetUpCulture( Application );

Where Application is the Excel application object.

e.g.

MSexcel.Application 

Then create an instance of the wrapper around the Excel object.

Is replaced with the following:

PExcelOb cell_Safe = new PExcelOb( cell );
cell_Safe["Value2"] = "Test";

You can call a method such:

PExcelOb activeSheet_Safe = newPExcelOb( activeSheet );
activeSheet_Safe.Invoke( "Paste", null );