NetOfficeFw / NetOffice

🌌 Create add-ins and automation code for Microsoft Office applications.
MIT License
697 stars 143 forks source link

Exception thrown with Visio Page.GetFormulas #176

Closed netoffice-bot closed 1 year ago

netoffice-bot commented 8 years ago

Issue by saveenr Sun, 04 Sep 2016 06:37:30 GMT Originally opened as https://netoffice.codeplex.com/workitem/22311


I am exploring using NetOffice.Visio for my project VisioAutomation: https://github.com/saveenr/VisioAutomation

Here's some code which works with Microsoft.Office.Interop.Visio

        // using IVisio = Microsoft.Office.Interop.Visio;
        var app = new IVisio.Application();
        var doc = app.Documents.Add("");
        var page = app.ActivePage;
        var shape = page.DrawRectangle(0, 0, 2, 3);
        shape.Text = "With Microsoft.Office.Interop.Visio";

        var SID_SRCStream = new short[4];
        SID_SRCStream[0] = (short)shape.ID16;
        SID_SRCStream[1] = (short)IVisio.VisSectionIndices.visSectionObject;
        SID_SRCStream[2] = (short)IVisio.VisRowIndices.visRowFill;
        SID_SRCStream[3] = (short)IVisio.VisCellIndices.visFillForegnd;

        System.Array a;
        page.GetFormulasU(SID_SRCStream, out a);

But the same code throws an exception with NetOffice.Visio (for .NET 4.0)

        // using IVisioNetOffice = NetOffice.VisioApi;
        var app = new IVisioNetOffice.Application();
        var doc = app.Documents.Add("");
        var page = app.ActivePage;
        var shape = page.DrawRectangle(0, 0, 2, 3);
        shape.Text = "With NetOffice";

        var SID_SRCStream = new short[4];
        SID_SRCStream[0] = (short)shape.ID16;
        SID_SRCStream[1] = (short)IVisioNetOffice.Enums.VisSectionIndices.visSectionObject;
        SID_SRCStream[2] = (short)IVisioNetOffice.Enums.VisRowIndices.visRowFill;
        SID_SRCStream[3] = (short)IVisioNetOffice.Enums.VisCellIndices.visFillForegnd;

        object[] a;
        page.GetFormulasU(SID_SRCStream, out a);

The exception occurs in GetFormulas the inner exception says "{"Type mismatch. (Exception from HRESULT: 0x80020005 (DISP_E_TYPEMISMATCH))"}".

I suspect the problem is with the second parameter because setformulas seems to work with the same SID_SRCStream.

Any ideas what is wrong in my code?

netoffice-bot commented 8 years ago

Comment by Sun, 04 Sep 2016 10:04:07 GMT


This is an issue in NetOffice 1.7.3

NetOffice want replace null arguments with Type.Missing which is not a good idea in that case. Please try object[] a = new object[0]; and let me know how it works.

Sebastian

netoffice-bot commented 8 years ago

Comment by Sun, 04 Sep 2016 23:03:14 GMT


Thanks for the quick reply - I tried the code below...

        object[] a = new object[0];
        page.GetFormulasU(SID_SRCStream, out a);

And it continues to give the same error.

netoffice-bot commented 8 years ago

Comment by Mon, 05 Sep 2016 00:26:27 GMT


I must to ask for it . It is definitely the same kind of exception? BTW: Any kind of stack trace want very helpful here.

I see 2 more possible reasons here.

1) NetOffice failed to marshal(back) an array as an output argument here. arrays in COM Interop is a very very special topic. (the stack trace can clearify that, remove any private informations if you, want i want only see the last netoffice calls)

2) The Visio dev team does something wrong in the IDispatch implementation of GetFormulas( this is a common scenario, i find a lot of bugs all the NetOffice years here in all Office applications except Excel - the "primusin parvum")

I'm sorry to say that i dont have visio installed on my current system. (I'm a freelance programmer and not in my country right now) Please give me one day to fix this to reproduce your problem and don let me alone here. This is probably an important issue in NetOffice.

Please try this static method instead with "a" as null value (for diagnostics only, i'm sure that fails but i can make sure a possible third kind of error is impossible)

public static void GetFormulas(NetOffice.VisioApi.IVPage page, Int16[] sID_SRCStream, out object[] formulaArray) { ParameterModifier[] modifiers = Invoker.CreateParamModifiers(false,true); formulaArray = null; object[] paramsArray = new object[]{sID_SRCStream, formulaArray}; Invoker.Method(page, "GetFormulas", paramsArray, modifiers); formulaArray = (object[])paramsArray[1]; }

Thanks and i work for this issue in high priority. *Sebastian

netoffice-bot commented 8 years ago

Comment by Mon, 05 Sep 2016 07:10:32 GMT


Thanks Sebastian,

I've added an attachment to this comment with the stacktraces.

I tried the code you provided ...

            var  modifiers = NetOffice.Invoker.CreateParamModifiers(false, true);
            object[] formulaArray = null;
            object[] paramsArray = new object[] { SID_SRCStream, formulaArray };
            NetOffice.Invoker.Method(page, "GetFormulas", paramsArray, modifiers);
            formulaArray = (object[])paramsArray[1];
            object[] a = new object[0];
            page.GetFormulasU(SID_SRCStream, out a);

but it won't compile to to the Invoker.Method line...

Severity    Code    Description Project File    Line    Suppression State
Error   CS0120  An object reference is required for the non-static field, method, or property 'Invoker.Method(COMObject, string, object[], ParameterModifier[])'    ConsoleApplication1 d:\test_visio\ConsoleApplication1\ConsoleApplication1\Program.cs    77  Active
netoffice-bot commented 8 years ago

Comment by Mon, 05 Sep 2016 07:14:10 GMT


Minor clarification, the attachment with the exception is right under the Description of the Issue at the top of this thread

netoffice-bot commented 8 years ago

Comment by Tue, 06 Sep 2016 20:49:10 GMT


I can reproduce the issue right now.

In order to continute to work with NetOffice asap i create a bypass for you as linq extension(see attachment) This should work in all Visio versions(*1) (11 to 16) - but only true tested in 16-2016 (sry i'm not at home)

I want create a C++ COM server now with the same method signature as IVPage::GetFormulas and IDispatch support to analyze the problem with a C# dumy client. I'll be back as fast i can. If you have problems with the given bypass or other/new problems - please let me know.

*Sebastian

(*1) = The bypass use the early bind implementation behind in a lightweight way. This works versionless and fine when the interface id and the method dispid/method signature never has been changed. IVPage interface id and GetFormula method never has been changed.

netoffice-bot commented 8 years ago

Comment by Tue, 06 Sep 2016 20:50:56 GMT


second try to give the attachment (javascript issues)

netoffice-bot commented 8 years ago

Comment by Tue, 06 Sep 2016 20:51:42 GMT


using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.CompilerServices;
using Visio = NetOffice.VisioApi;

namespace Foo // change to what you want/need
{
    public static class IVPageExtensions
    {
        [DefaultMember("Name"), Guid("000D0709-0000-0000-C000-000000000046"), TypeLibType(4176)]
        [ComImport]
        private interface IVPageVTable
        {
            [DispId(32)]
            [MethodImpl(MethodImplOptions.InternalCall)]
            void GetFormulas([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_I2)] [In] ref Array SID_SRCStream, 
                [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] out Array formulaArray);
        }

        public static void GetFormulas(this Visio.IVPage page, Int16[] sID_SRCStream, out object[] formulaArray)
        {
            formulaArray = null;

            Array arg1 = sID_SRCStream as Array;
            if(null == arg1)
                arg1 = new Array[0];

            IVPageVTable proxy = page.UnderlyingObject as IVPageVTable;
            if (null != proxy)
            {
                Array formulas = null;
                proxy.GetFormulas(ref arg1, out formulas);
                if (null != formulas)
                {                    
                    formulaArray = new object[formulas.Length];
                    for (int i = 0; i < formulas.Length; i++)
                        formulaArray[i] = formulas.GetValue(i);
                }
            }
            else
                throw new InvalidCastException("Unable to cast underlying proxy into interop interface");    
        }
    }
}
netoffice-bot commented 8 years ago

Comment by Thu, 08 Sep 2016 08:03:38 GMT


Thanks, Sebastian. I tried the code you provided - no exception is thrown but it sets returned value for formulaArray is set to null (instead of an array with a single formula value)

One thing that may be affecting it is that on the current machine I tried it on a machine with both Visio 2010 and Visio 2016 and when I ran the code Visio 2010 kept launching. Tomorrow I'll try on a machine that only has Visio 2016 installed.

netoffice-bot commented 8 years ago

Comment by Sat, 10 Sep 2016 19:36:59 GMT


OK, I finally got to a machine with Visio 2016 only installed - same results. formulaArray is set to null. As I step through the code it the line "proxy.GetFormulas(ref arg1, out formulas)" always sets formulas to null

netoffice-bot commented 8 years ago

Comment by Sat, 10 Sep 2016 22:01:46 GMT


I'm sorry to say that the bypass didnt works. Nothing is called in that way. Thats why is always null. I can reproduce the error in late binding. Currently i create a C++ com server with same argument signature to see what happen in the backend. if everything is good in my backend implementation for the arguments - it is a bug in visio, otherwise COM Interop does some magic here which i can find in the IL code for interop calling and my backend implementation. I hope i have some answers tomorrow in the evening.

Sebastian

jozefizso commented 1 year ago

Closing as Visio library was removed.