[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1086
  • Last Modified:

How can I create com object instance using the class name, for a child object to get property values

I need to create an instance of a com object (say a PowerPoint shape) using the fully qualified class name ("Microsoft.Office.Interop.PowerPoint.Shape").  I am wanting to enumerate through all properties of a shape and also get the values for child objects.  I have the first part working fine, meaning I can get the values for properties for a shape object.  Where I am stuck is getting the properties for child objects of a shape.  
For example: if the shape is a textbox object I want to get the values for each property in the "fonts" object.  I need to be able to create an instance of the fonts object  to get the font specific properties (name, size, bold, etc.)

As this is a com interface object the System.Type returns "System.__ComObject". By enumerating the PropertyInfo collection I can get the fully qualified class name for each property and get the System.Type for the property.

I have tried to get the instance by enumerating the assemblies using Assembly.GetExecutingAssembly().GetReferencedAssemblies() but the class name is not in the list to be able to load the instance for the class type.

I need to be able to loop through not only the shape object, but all child objects as well and list the property values.  I have already defined what properties are applicable for each object type, I just need to be able to dynamically creat the child object instance.

I anyone has any suggestions on accomplishing this, other that a huge case block for each posible object type, it would be greatly appreciated.

I am working in Visual Studio 2010, VB.net, Reflection, PowerPoint 2010, Win7
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports Scripting
Imports Microsoft.Office.Core
Imports Microsoft.Office.Interop.PowerPoint

Thanks,
0
izzycamm
Asked:
izzycamm
  • 6
  • 4
1 Solution
 
Bob LearnedCommented:
What you are trying to achieve is a fairly tricky operation, so I would love to ask why you are looking to do this?  I might have something buried deep within my bag-o-tricks.
0
 
izzycammAuthor Commented:
I have a system where I take a PowerPoint presentation, store the properties of the shapes in a series of definition tables and dynamically rebuild the presentation, replacing any data values to be presented from Sql data.  I also set colors and shapes in the presentation based on the data to be presented.  

I have a process in place to extract the properties for the shapes I currently use into the definition tables.  This is working great and I am able to build  the presentations quickly and acurately.

I am now looking to add the functionality for dynamic charting to the functional library.  I can manually enter all of the many underlying properties for a chart, such as axes, series, legends, etc.  I now have a need to do this for a presentation with a lot of charts to be build dynamically.  Not only the data to chart may change, but the axes scales, series colors, etc. Basicly I am looking to expand my "scrape" process to get these child shape properies.  Manually setting up the defintion tabled for thr shapes for this presentation is a tedious process.  I like to automate processes like this whenever possible. (what I call "creative laziness")

If you have any thoughts on creating the child shape instances to nest the scrape process i would be greatly appreciative.  The main problem is that the shapes are a com interface and I need to create the instance from the fully qualified class name, which I can get from the parent's property info.

As I said earlier, my process works I just want to expand it to the next level.  I am close, but not there yet.

Thanks
0
 
Bob LearnedCommented:
I found my ComReflection class in my bag-o-tricks.

using System;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Reflection;
using ComTypes = System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices.ComTypes;
using System.Security;

namespace Com.Services
{

    public class ComReflection
    {


        /// <summary>
        /// CoCreateInstance: creates a new instance of a COM class, without an interop type, returning the default RCW.
        /// </summary>
        /// <param name="clsid">The CLSID of the class to create</param>
        /// <param name="punkOuter">If Nothing, indicates no aggregation. Otherwise, the IUnknown of the aggregate object</param>
        /// <param name="context">From <a href="http://msdn.microsoft.com/en-us/library/ms693716(VS.85).aspx">CLSTX</a></param>
        /// <param name="iid">The IID to be returned from this function</param>
        /// <returns>An 'iid' interface to the newly created COM object</returns>
        [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)]
        [return: MarshalAs(UnmanagedType.Interface)]
        public static extern object CoCreateInstance(ref Guid clsid, [MarshalAs(UnmanagedType.Interface)]
            object punkOuter, int context, ref Guid iid);

        //
        public static Guid IID_NULL = new Guid("00000000-0000-0000-C000-000000000000");
        public static Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");

        private string dummyString;
        private int dummyInt;
        private IntPtr dummyIntPtr;
        private uint dummyUint;

        ///// <summary>
        ///// IDispatch: this is a managed version of the IDispatch interface
        ///// </summary>
        ///// <remarks>We don't use GetIDsOfNames or Invoke, and so haven't bothered with correct signatures for them.</remarks>
        //[ComImport(), Guid("00020400-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        //public interface IDispatch
        //{
        //    void GetTypeInfoCount(ref uint pctinfo);
        //    void GetTypeInfo(uint itinfo, uint lcid, ref IntPtr pptinfo);
        //    void GetIDsOfNames_unused();
        //    void Invoke(int dispIdMember, ref Guid riid, uint lcid, ushort dwFlags, ref ComTypes.DISPPARAMS pDispParams,
        //        ref VARIANT pVarResult,
        //        ref IntPtr pExcepInfo, ref uint pArgErr);
        //}
        //[ComImport, SuppressUnmanagedCodeSecurity, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
        //internal interface IDispatch
        //{
        //    [SecurityCritical]
        //    void GetTypeInfoCount(out uint pctinfo);
        //    [SecurityCritical]
        //    void GetTypeInfo(uint iTInfo, int lcid, out ITypeInfo info);
        //    [SecurityCritical]
        //    void GetIDsOfNames(ref Guid iid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, 
        //        SizeParamIndex = 2)] string[] names, uint cNames, int lcid, 
        //        [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] int[] rgDispId);
        //    [SecurityCritical]
        //    void Invoke(int dispIdMember, ref Guid riid, int lcid, ComTypes.INVOKEKIND wFlags,
        //        ref ComTypes.DISPPARAMS pDispParams, ref VARIANT pvarResult, IntPtr pExcepInfo, IntPtr puArgErr);
        //}
        [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
        public interface IDispatch
        {
            int GetTypeInfoCount();
            [return: MarshalAs(UnmanagedType.Interface)]
            ITypeInfo GetTypeInfo([In, MarshalAs(UnmanagedType.U4)] int iTInfo, [In, MarshalAs(UnmanagedType.U4)] int lcid);
            [PreserveSig]
            int GetIDsOfNames([In] ref Guid riid, [In, MarshalAs(UnmanagedType.LPArray)] string[] rgszNames, [In, MarshalAs(UnmanagedType.U4)] int cNames, [In, MarshalAs(UnmanagedType.U4)] int lcid, [Out, MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
            [PreserveSig]
            int Invoke(int dispIdMember, [In] ref Guid riid, [In, MarshalAs(UnmanagedType.U4)] int lcid, [In, MarshalAs(UnmanagedType.U4)] int dwFlags, [In, Out] ComTypes.DISPPARAMS pDispParams, 
                [In, Out] ref object pVarResult, [In, Out] ComTypes.EXCEPINFO pExcepInfo, [Out] out IntPtr[] pArgErr);
        }



        /// <summary>
        /// VARIANT: this is equivalent to "Object" in VB. It's the universal variable type for COM.
        /// </summary>
        /// <remarks>The "vt" flag determines which of the other fields have meaning. vt is a VarEnum.</remarks>
        [StructLayoutAttribute(LayoutKind.Explicit, Size = 16)]
        public struct VARIANT
        {
            [FieldOffsetAttribute(0)]
            public ushort vt;
            [FieldOffsetAttribute(2)]
            public ushort wReserved1;
            [FieldOffsetAttribute(4)]
            public ushort wReserved2;
            [FieldOffsetAttribute(6)]
            public ushort wReserved3;
            //
            [FieldOffsetAttribute(8)]
            public long llVal;
            [FieldOffsetAttribute(8)]
            public int lVal;
            [FieldOffsetAttribute(8)]
            public byte bVal;
            [FieldOffsetAttribute(8)]
            public short iVal;
            [FieldOffsetAttribute(8)]
            public float fltVal;
            [FieldOffsetAttribute(8)]
            public double dblVal;
            [FieldOffsetAttribute(8)]
            public short boolVal;
            [FieldOffsetAttribute(8)]
            public int scode;
            [FieldOffsetAttribute(8)]
            public double date;
            [FieldOffsetAttribute(8)]
            public byte cVal;
            [FieldOffsetAttribute(8)]
            public ushort uiVal;
            [FieldOffsetAttribute(8)]
            public uint ulVal;
            [FieldOffsetAttribute(8)]
            public ulong ullVal;
            [FieldOffsetAttribute(8)]
            public int intVal;
            [FieldOffsetAttribute(8)]
            public uint uintVal;
            [FieldOffsetAttribute(8)]

            public IntPtr ptr;
            /// <summary>
            /// GetObject: returns a .NET Object equivalent for this Variant.
            /// </summary>
            public object GetObject()
            {
                // We want to use the handy Marshal.GetObjectForNativeVariant.
                // But this only operates upon an IntPtr to a block of memory.
                // So we first flatten ourselves into that block of memory. (size 16)
                IntPtr ptr = Marshal.AllocCoTaskMem(16);
                Marshal.StructureToPtr(this, ptr, false);
                try
                {
                    return Marshal.GetObjectForNativeVariant(ptr);
                }
                finally
                {
                    Marshal.FreeCoTaskMem(ptr);
                }
            }
        }


        /// <summary>
        /// ReflectOnCOMObjectThroughITypeInfo: given a com object that supports IDispatch, attempts
        /// to get its ITypeInfo interface (which represents the object's entry in its type-library),
        /// and reflect on the object through this.
        /// </summary>
        /// <param name="com">the com object upon which to reflect</param>
        public List<ComObjectInfo> ReflectOnCOMObjectThroughITypeInfo(object com)
        {
            // How do we get ITypeInfo for a COM object?
            // It would be nice to use Marshal.GetITypeInfoForType. But that fails when the com object
            // doesn't have an interop assembly (e.g. when the com object was created for us
            // by native code). So instead we have to use IDispatch::GetTypeInfo.
            IDispatch dispatch = (IDispatch)com;
            ITypeInfo typeInfo = this.GetTypeInformation(dispatch, com);

            // to release the AddRef that GetTypeInfo did for us.

            List<ComObjectInfo> objectInfoList = new List<ComObjectInfo>();

            AddTypeInfoToDump(typeInfo);
            while (typeInfosToDump.Count > 0)
            {
                ComObjectInfo objectInfo = this.DumpTypeInfo(typeInfosToDump.Dequeue());

                objectInfoList.Add(objectInfo);
            }

            return objectInfoList;
        }

        private ITypeInfo GetTypeInformation(IDispatch dispatch, object com)
        {

            int count = dispatch.GetTypeInfoCount();
            if (count < 1)
                throw new ArgumentException("No type info", "com");
            ITypeInfo typeinfo = dispatch.GetTypeInfo(0, 0);
            return typeinfo;
        }


        /// <summary>
        /// DumpType: prints information about an ITypeInfo type to the console -- name, inheritance, members
        /// </summary>
        /// <param name="typeInfo">the type to dump</param>

        public ComObjectInfo DumpTypeInfo(ComTypes.ITypeInfo typeInfo)
        {
            string typeName;

            typeInfo.GetDocumentation(-1, out typeName, out dummyString, out dummyInt, out dummyString);

            ComObjectInfo objectInfo = new ComObjectInfo(typeName);

            // TypeAttr: contains general information about the type
            IntPtr pTypeAttr = default(IntPtr);
            typeInfo.GetTypeAttr(out pTypeAttr);
            ComTypes.TYPEATTR typeAttr = (ComTypes.TYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(ComTypes.TYPEATTR));
            typeInfo.ReleaseTypeAttr(pTypeAttr);


            // Inheritance:
            for (int iImplType = 0; iImplType < typeAttr.cImplTypes; iImplType++)
            {
                int href = 0;
                typeInfo.GetRefTypeOfImplType(iImplType, out href);
                // "href" is an index into the list of type descriptions within the type library.
                ComTypes.ITypeInfo implTypeInfo = null;
                typeInfo.GetRefTypeInfo(href, out implTypeInfo);
                // And GetRefTypeInfo looks up the index to get an ITypeInfo for it.
                string implTypeName = "";
                implTypeInfo.GetDocumentation(-1, out implTypeName, out dummyString, out dummyInt, out dummyString);

                objectInfo.Implements.Add(implTypeName);
            }


            // Function/Sub/Property members:
            // Note that property accessors are flattened, e.g. for a property "Fred as Integer"
            // it will be represented as two members "[Get] Function Fred() As Integer", and "[Put] Sub Fred(Integer)"
            // Each member is uniquely identified by an integer "MEMID".
            // This memid is what's used e.g. when invoking the member.

            for (int iFunc = 0; iFunc < typeAttr.cFuncs; iFunc++)
            {
                // FUNCDESC is the key datastructure here:
                IntPtr pFuncDesc = default(IntPtr);
                typeInfo.GetFuncDesc(iFunc, out pFuncDesc);
                ComTypes.FUNCDESC funcDesc = (ComTypes.FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(ComTypes.FUNCDESC));

                // Each function notionally has a list of names associated with it. I'll just pick the first.
                string[] names = { "" };
                typeInfo.GetNames(funcDesc.memid, names, 1, out dummyInt);
                string funcName = names[0];

                StringBuilder sb = new StringBuilder(funcName);

                //sb.Append("(");

                // Function formal parameters:
                //short cParams = funcDesc.cParams;
                //for (int iParam = 0; iParam <= cParams - 1; iParam++)
                //{
                //    ComTypes.ELEMDESC elemDesc = (ComTypes.ELEMDESC)Marshal.PtrToStructure(
                //        new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + Marshal.SizeOf(typeof(ComTypes.ELEMDESC)) * iParam),
                //        typeof(ComTypes.ELEMDESC));
                //        sb.Append(", ");

                //    if (((int)elemDesc.desc.paramdesc.wParamFlags & 2) != 0)
                //        sb.Append("out ");

                //    sb.Append(DumpTypeDesc(elemDesc.tdesc, typeInfo));
                //}

                //sb.Append(")");

                // And print out the rest of the function's information:
                if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYGET) != 0)
                    sb.Append("Get ");
                else if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT) != 0)
                    sb.Append("Set ");
                else if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF) != 0)
                    sb.Append("Set ");

                bool isSub = (funcDesc.elemdescFunc.tdesc.vt == (short)VarEnum.VT_VOID);

                objectInfo.Methods.Add(sb.ToString());

                typeInfo.ReleaseFuncDesc(pFuncDesc);
            }


            // Field members:
            for (int iVar = 0; iVar <= typeAttr.cVars - 1; iVar++)
            {
                IntPtr pVarDesc = default(IntPtr);
                typeInfo.GetVarDesc(iVar, out pVarDesc);
                ComTypes.VARDESC varDesc = (ComTypes.VARDESC)Marshal.PtrToStructure(pVarDesc, typeof(ComTypes.VARDESC));
                typeInfo.ReleaseVarDesc(pVarDesc);
                string[] names = { "" };
                typeInfo.GetNames(varDesc.memid, names, 1, out dummyInt);
                string varName = names[0];
                objectInfo.Fields.Add(varName);
            }

            return objectInfo;
        }

        /// <summary>
        /// DumpTypeDesc: given a TYPEDESC, dumps it out into a string e.g. "Ref Int" or
        /// "Array of MyTypeInfo". Also calls AddTypeInfoToDump for every ITypeInfo encountered.
        /// </summary>
        /// <param name="tdesc">the TYPEDESC to dump</param>
        /// <param name="context">the ITypeInfo that contained this TYPEDESC, for context</param>
        /// <returns>a string representation of the TYPEDESC</returns>
        public string DumpTypeDesc(ComTypes.TYPEDESC tdesc, ComTypes.ITypeInfo context)
        {
            VarEnum vt = (VarEnum)tdesc.vt;

            ComTypes.TYPEDESC tdesc2;
            switch (vt)
            {

                case VarEnum.VT_PTR:
                    tdesc2 = (ComTypes.TYPEDESC)Marshal.PtrToStructure(tdesc.lpValue, typeof(ComTypes.TYPEDESC));

                    return "Ref " + DumpTypeDesc(tdesc2, context);
                case VarEnum.VT_USERDEFINED:
                    int href = Convert.ToInt32(tdesc.lpValue.ToInt64() & int.MaxValue);
                    ComTypes.ITypeInfo refTypeInfo = null;
                    context.GetRefTypeInfo(href, out refTypeInfo);
                    AddTypeInfoToDump(refTypeInfo);
                    string refTypeName = "";
                    refTypeInfo.GetDocumentation(-1, out refTypeName, out dummyString, out dummyInt, out dummyString);

                    return refTypeName;
                case VarEnum.VT_CARRAY:
                    tdesc2 = (ComTypes.TYPEDESC)Marshal.PtrToStructure(tdesc.lpValue, typeof(ComTypes.TYPEDESC));
                    return "Array of " + DumpTypeDesc(tdesc2, context);
                // lpValue is actually an ARRAYDESC structure, which also has information on the array dimensions,
                // but alas .Net doesn't predefine ARRAYDESC.

                case VarEnum.VT_VOID:
                    // e.g. IUnknown::QueryInterface(Ref GUID, out Ref Ref Void)
                    return "Void";
                case VarEnum.VT_VARIANT:
                    return "Object";
                case VarEnum.VT_UNKNOWN:

                    return "IUnknown*";
                case VarEnum.VT_BSTR:
                    return "String";
                case VarEnum.VT_LPWSTR:
                    return "wchar*";
                case VarEnum.VT_LPSTR:

                    return "char*";
                case VarEnum.VT_HRESULT:

                    return "HResult";
                case VarEnum.VT_BOOL:
                    return "Bool";
                case VarEnum.VT_I1:
                    return "SByte";
                case VarEnum.VT_UI1:
                    return "Byte";
                case VarEnum.VT_I2:
                    return "Short";
                case VarEnum.VT_UI2:
                    return "UShort";
                case VarEnum.VT_I4:
                case VarEnum.VT_INT:
                    // I don't know the difference
                    return "Integer";
                case VarEnum.VT_UI4:
                case VarEnum.VT_UINT:
                    // I don't know the difference
                    return "UInteger";
                case VarEnum.VT_I8:
                    return "Long";
                case VarEnum.VT_UI8:

                    return "ULong";
                default:
                    // There are many other VT_s that I haven't special-cased yet.
                    // That's just because I haven't encountered them yet in my test-cases.
                    return vt.ToString();
            }
        }


        Queue<ComTypes.ITypeInfo> typeInfosToDump = new Queue<ComTypes.ITypeInfo>();
        HashSet<string> typeInfosDumped = new HashSet<string>();
        //
        public void AddTypeInfoToDump(ComTypes.ITypeInfo typeInfo)
        {
            string typeName = "";
            typeInfo.GetDocumentation(-1, out typeName, out dummyString, out dummyInt, out dummyString);
            if (typeInfosDumped.Contains(typeName))
                return;
            typeInfosToDump.Enqueue(typeInfo);
            typeInfosDumped.Add(typeName);
        }

        /// <summary>
        /// DumpCOMObject: given a COM object, performs reflection on it and prints out all the
        /// properties it can (and also parameterless functions named "Is*" or "Get*")
        /// </summary>
        /// <param name="com">the COM object to dump</param>
        /// <param name="Depth">for visual formatting, says how far to indent</param>

        public void DumpCOMObject(object com, int Depth = 0)
        {
            // We'll only display the top-level fields and one level down:
            if (Depth > 1)
                return;
            string prefix = "";
            for (int i = 1; i <= Depth; i++)
            {
                prefix += ".   ";
            }

            // First, fetch the runtime type of this COM object:
            IDispatch dispatch = (IDispatch)com;
            ITypeInfo typeInfo = this.GetTypeInformation(dispatch, com);
            // to release the AddRef that GetTypeInfo did for us.

            // We'll use this id to identify which object and interface we're retrieving properties from:
            string id = "";
            typeInfo.GetDocumentation(-1, out id, out dummyString, out dummyInt, out dummyString);
            IntPtr iunk = Marshal.GetIUnknownForObject(id);
            id = "#" + iunk.ToString() + "_" + id;
            Marshal.Release(iunk);

            // TypeAttr: contains general information about the type
            IntPtr pTypeAttr = default(IntPtr);
            typeInfo.GetTypeAttr(out pTypeAttr);
            ComTypes.TYPEATTR typeAttr = (ComTypes.TYPEATTR)Marshal.PtrToStructure(pTypeAttr, typeof(ComTypes.TYPEATTR));
            typeInfo.ReleaseTypeAttr(pTypeAttr);


            // Properties and parameterless functions:

            for (int iFunc = 0; iFunc <= typeAttr.cFuncs - 1; iFunc++)
            {
                // FUNCDESC is the key datastructure here:
                IntPtr pFuncDesc = default(IntPtr);
                typeInfo.GetFuncDesc(iFunc, out pFuncDesc);
                ComTypes.FUNCDESC funcDesc = (ComTypes.FUNCDESC)Marshal.PtrToStructure(pFuncDesc, typeof(ComTypes.FUNCDESC));
                typeInfo.ReleaseFuncDesc(pFuncDesc);

                // Each function notionally has a list of names associated with it. I'll just pick the first.
                string[] names = { "" };
                typeInfo.GetNames(funcDesc.memid, names, 1, out dummyInt);
                string funcName = names[0];

                // We'll only try to retrieve things that are likely to be side-effect-free properties:
                if ((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYGET) == 0 && !(funcName.StartsWith("[Gg]et"))
                    && !(funcName.StartsWith("[Ii]s")))
                    continue;
                if (funcDesc.cParams > 0)
                    continue;
                VarEnum returnType = (VarEnum)funcDesc.elemdescFunc.tdesc.vt;
                if (returnType == VarEnum.VT_VOID)
                    continue;
                string returnTypeName = DumpTypeDesc(funcDesc.elemdescFunc.tdesc, typeInfo);

                // And we'll only try to evaluate the easily-evaluatable properties:
                VarEnum[] dumpableTypes = new VarEnum[] {
                VarEnum.VT_BOOL,
                VarEnum.VT_BSTR,
                VarEnum.VT_CLSID,
                VarEnum.VT_DATE,
                VarEnum.VT_DECIMAL,
                VarEnum.VT_FILETIME,
                VarEnum.VT_HRESULT,
                VarEnum.VT_I1,
                VarEnum.VT_I2,
                VarEnum.VT_I4,
                VarEnum.VT_I8,
                VarEnum.VT_INT,
                VarEnum.VT_LPSTR,
                VarEnum.VT_LPWSTR,
                VarEnum.VT_R4,
                VarEnum.VT_R8,
                VarEnum.VT_UI1,
                VarEnum.VT_UI2,
                VarEnum.VT_UI4,
                VarEnum.VT_UI8,
                VarEnum.VT_UINT,
                VarEnum.VT_USERDEFINED
            };

                bool typeIsDumpable = dumpableTypes.Contains(returnType);
                if (returnType == VarEnum.VT_PTR)
                {
                    ComTypes.TYPEDESC ptrType = (ComTypes.TYPEDESC)Marshal.PtrToStructure(funcDesc.elemdescFunc.tdesc.lpValue, typeof(ComTypes.TYPEDESC));
                    if (ptrType.vt == (short)VarEnum.VT_USERDEFINED)
                        typeIsDumpable = true;
                }

                // Here's how we fetch an arbitrary property from a COM object, identified by its MEMID.
                object val = null;
                if (typeIsDumpable)
                {
                    ComTypes.DISPPARAMS dispParams = new ComTypes.DISPPARAMS
                    {
                        cArgs = 0,
                        cNamedArgs = 0
                    };
                    int dispatchType = (int)((funcDesc.invkind & ComTypes.INVOKEKIND.INVOKE_PROPERTYGET) != 0 ? 2 : 1);
           // void Invoke(int dispIdMember, ref Guid riid, int lcid, ComTypes.INVOKEKIND wFlags, ref ComTypes.DISPPARAMS pDispParams, IntPtr pvarResult, IntPtr pExcepInfo, IntPtr puArgErr);

                    object varResult = this.InvokeGet(dispatch, funcDesc.memid);
                    //dispatch.Invoke(funcDesc.memid, ref IID_NULL, 0, dispatchType, dispParams, ref varResult,
                    //    exceptionInfo, out dummyIntPtr);
                    //val = varResult.GetObject();
                    //if (varResult.vt == (ushort)VarEnum.VT_PTR && varResult.ptr != IntPtr.Zero)
                    //{
                    //    Marshal.Release(varResult.ptr);
                    //}
                }
                else
                {
                    val = string.Format("[{0}]", (VarEnum)funcDesc.elemdescFunc.tdesc.vt);
                }

                // And dump out the property. We'll recurse into the object properties.
                Console.WriteLine("{0}{1}.{2} = {4}   As {3}", prefix, id, funcName, returnTypeName, val);
                if (returnType == VarEnum.VT_PTR && val != null)
                    DumpCOMObject(com, Depth + 1);

            }


        }

        public object InvokeGet(IDispatch dispatch, int memId)
        {
            IntPtr[] pArgErr = default(IntPtr[]);
            object pVarResult =  new object();
            ComTypes.DISPPARAMS pDispParams = new ComTypes.DISPPARAMS();
            ComTypes.EXCEPINFO pExcepInfo = new ComTypes.EXCEPINFO();

            Guid guid = new Guid();
            int result = dispatch.Invoke(memId, ref guid, 0,
                         (ushort)ComTypes.INVOKEKIND.INVOKE_PROPERTYGET,
                         pDispParams, ref pVarResult, pExcepInfo, out pArgErr);

            return pVarResult;
        }
    }

}

Open in new window

0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
Bob LearnedCommented:
Untested VB.NET conversion, using Telerik Code Converter:

Imports System
Imports System.Linq
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.Collections.Generic
Imports System.Reflection
Imports ComTypes = System.Runtime.InteropServices.ComTypes
Imports System.Runtime.InteropServices.ComTypes
Imports System.Security

Namespace Com.Services

	Public Class ComReflection


		''' <summary>
		''' CoCreateInstance: creates a new instance of a COM class, without an interop type, returning the default RCW.
		''' </summary>
		''' <param name="clsid">The CLSID of the class to create</param>
		''' <param name="punkOuter">If Nothing, indicates no aggregation. Otherwise, the IUnknown of the aggregate object</param>
		''' <param name="context">From <a href="http://msdn.microsoft.com/en-us/library/ms693716(VS.85).aspx">CLSTX</a></param>
		''' <param name="iid">The IID to be returned from this function</param>
		''' <returns>An 'iid' interface to the newly created COM object</returns>
		<DllImport("ole32.dll", ExactSpelling := True, PreserveSig := False)> _
		Public Shared Function CoCreateInstance(ByRef clsid As Guid, <MarshalAs(UnmanagedType.[Interface])> punkOuter As Object, context As Integer, ByRef iid As Guid) As <MarshalAs(UnmanagedType.[Interface])> Object
		End Function

		'
		Public Shared IID_NULL As New Guid("00000000-0000-0000-C000-000000000000")
		Public Shared IID_IUnknown As New Guid("00000000-0000-0000-C000-000000000046")

		Private dummyString As String
		Private dummyInt As Integer
		Private dummyIntPtr As IntPtr
		Private dummyUint As UInteger

		'''// <summary>
		'''// IDispatch: this is a managed version of the IDispatch interface
		'''// </summary>
		'''// <remarks>We don't use GetIDsOfNames or Invoke, and so haven't bothered with correct signatures for them.</remarks>
		'[ComImport(), Guid("00020400-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		'public interface IDispatch
		'{
		'    void GetTypeInfoCount(ref uint pctinfo);
		'    void GetTypeInfo(uint itinfo, uint lcid, ref IntPtr pptinfo);
		'    void GetIDsOfNames_unused();
		'    void Invoke(int dispIdMember, ref Guid riid, uint lcid, ushort dwFlags, ref ComTypes.DISPPARAMS pDispParams,
		'        ref VARIANT pVarResult,
		'        ref IntPtr pExcepInfo, ref uint pArgErr);
		'}
		'[ComImport, SuppressUnmanagedCodeSecurity, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
		'internal interface IDispatch
		'{
		'    [SecurityCritical]
		'    void GetTypeInfoCount(out uint pctinfo);
		'    [SecurityCritical]
		'    void GetTypeInfo(uint iTInfo, int lcid, out ITypeInfo info);
		'    [SecurityCritical]
		'    void GetIDsOfNames(ref Guid iid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, 
		'        SizeParamIndex = 2)] string[] names, uint cNames, int lcid, 
		'        [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] int[] rgDispId);
		'    [SecurityCritical]
		'    void Invoke(int dispIdMember, ref Guid riid, int lcid, ComTypes.INVOKEKIND wFlags,
		'        ref ComTypes.DISPPARAMS pDispParams, ref VARIANT pvarResult, IntPtr pExcepInfo, IntPtr puArgErr);
		'}
		<ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")> _
		Public Interface IDispatch
			Function GetTypeInfoCount() As Integer
			Function GetTypeInfo(<[In], MarshalAs(UnmanagedType.U4)> iTInfo As Integer, <[In], MarshalAs(UnmanagedType.U4)> lcid As Integer) As <MarshalAs(UnmanagedType.[Interface])> ITypeInfo
			<PreserveSig> _
			Function GetIDsOfNames(<[In]> ByRef riid As Guid, <[In], MarshalAs(UnmanagedType.LPArray)> rgszNames As String(), <[In], MarshalAs(UnmanagedType.U4)> cNames As Integer, <[In], MarshalAs(UnmanagedType.U4)> lcid As Integer, <Out, MarshalAs(UnmanagedType.LPArray)> rgDispId As Integer()) As Integer
			<PreserveSig> _
			Function Invoke(dispIdMember As Integer, <[In]> ByRef riid As Guid, <[In], MarshalAs(UnmanagedType.U4)> lcid As Integer, <[In], MarshalAs(UnmanagedType.U4)> dwFlags As Integer, <[In], Out> pDispParams As ComTypes.DISPPARAMS, <[In], Out> ByRef pVarResult As Object, _
				<[In], Out> pExcepInfo As ComTypes.EXCEPINFO, <Out> pArgErr As IntPtr()) As Integer
		End Interface



		''' <summary>
		''' VARIANT: this is equivalent to "Object" in VB. It's the universal variable type for COM.
		''' </summary>
		''' <remarks>The "vt" flag determines which of the other fields have meaning. vt is a VarEnum.</remarks>
		<StructLayoutAttribute(LayoutKind.Explicit, Size := 16)> _
		Public Structure [VARIANT]
			<FieldOffsetAttribute(0)> _
			Public vt As UShort
			<FieldOffsetAttribute(2)> _
			Public wReserved1 As UShort
			<FieldOffsetAttribute(4)> _
			Public wReserved2 As UShort
			<FieldOffsetAttribute(6)> _
			Public wReserved3 As UShort
			'
			<FieldOffsetAttribute(8)> _
			Public llVal As Long
			<FieldOffsetAttribute(8)> _
			Public lVal As Integer
			<FieldOffsetAttribute(8)> _
			Public bVal As Byte
			<FieldOffsetAttribute(8)> _
			Public iVal As Short
			<FieldOffsetAttribute(8)> _
			Public fltVal As Single
			<FieldOffsetAttribute(8)> _
			Public dblVal As Double
			<FieldOffsetAttribute(8)> _
			Public boolVal As Short
			<FieldOffsetAttribute(8)> _
			Public scode As Integer
			<FieldOffsetAttribute(8)> _
			Public [date] As Double
			<FieldOffsetAttribute(8)> _
			Public cVal As Byte
			<FieldOffsetAttribute(8)> _
			Public uiVal As UShort
			<FieldOffsetAttribute(8)> _
			Public ulVal As UInteger
			<FieldOffsetAttribute(8)> _
			Public ullVal As ULong
			<FieldOffsetAttribute(8)> _
			Public intVal As Integer
			<FieldOffsetAttribute(8)> _
			Public uintVal As UInteger

			<FieldOffsetAttribute(8)> _
			Public ptr As IntPtr
			''' <summary>
			''' GetObject: returns a .NET Object equivalent for this Variant.
			''' </summary>
			Public Function GetObject() As Object
				' We want to use the handy Marshal.GetObjectForNativeVariant.
				' But this only operates upon an IntPtr to a block of memory.
				' So we first flatten ourselves into that block of memory. (size 16)
				Dim ptr As IntPtr = Marshal.AllocCoTaskMem(16)
				Marshal.StructureToPtr(Me, ptr, False)
				Try
					Return Marshal.GetObjectForNativeVariant(ptr)
				Finally
					Marshal.FreeCoTaskMem(ptr)
				End Try
			End Function
		End Structure


		''' <summary>
		''' ReflectOnCOMObjectThroughITypeInfo: given a com object that supports IDispatch, attempts
		''' to get its ITypeInfo interface (which represents the object's entry in its type-library),
		''' and reflect on the object through this.
		''' </summary>
		''' <param name="com">the com object upon which to reflect</param>
		Public Function ReflectOnCOMObjectThroughITypeInfo(com As Object) As List(Of ComObjectInfo)
			' How do we get ITypeInfo for a COM object?
			' It would be nice to use Marshal.GetITypeInfoForType. But that fails when the com object
			' doesn't have an interop assembly (e.g. when the com object was created for us
			' by native code). So instead we have to use IDispatch::GetTypeInfo.
			Dim dispatch As IDispatch = DirectCast(com, IDispatch)
			Dim typeInfo As ITypeInfo = Me.GetTypeInformation(dispatch, com)

			' to release the AddRef that GetTypeInfo did for us.

			Dim objectInfoList As New List(Of ComObjectInfo)()

			AddTypeInfoToDump(typeInfo)
			While typeInfosToDump.Count > 0
				Dim objectInfo As ComObjectInfo = Me.DumpTypeInfo(typeInfosToDump.Dequeue())

				objectInfoList.Add(objectInfo)
			End While

			Return objectInfoList
		End Function

		Private Function GetTypeInformation(dispatch As IDispatch, com As Object) As ITypeInfo

			Dim count As Integer = dispatch.GetTypeInfoCount()
			If count < 1 Then
				Throw New ArgumentException("No type info", "com")
			End If
			Dim typeinfo As ITypeInfo = dispatch.GetTypeInfo(0, 0)
			Return typeinfo
		End Function


		''' <summary>
		''' DumpType: prints information about an ITypeInfo type to the console -- name, inheritance, members
		''' </summary>
		''' <param name="typeInfo">the type to dump</param>

		Public Function DumpTypeInfo(typeInfo As ComTypes.ITypeInfo) As ComObjectInfo
			Dim typeName As String

			typeInfo.GetDocumentation(-1, typeName, dummyString, dummyInt, dummyString)

			Dim objectInfo As New ComObjectInfo(typeName)

			' TypeAttr: contains general information about the type
			Dim pTypeAttr As IntPtr = Nothing
			typeInfo.GetTypeAttr(pTypeAttr)
			Dim typeAttr As ComTypes.TYPEATTR = DirectCast(Marshal.PtrToStructure(pTypeAttr, GetType(ComTypes.TYPEATTR)), ComTypes.TYPEATTR)
			typeInfo.ReleaseTypeAttr(pTypeAttr)


			' Inheritance:
			For iImplType As Integer = 0 To typeAttr.cImplTypes - 1
				Dim href As Integer = 0
				typeInfo.GetRefTypeOfImplType(iImplType, href)
				' "href" is an index into the list of type descriptions within the type library.
				Dim implTypeInfo As ComTypes.ITypeInfo = Nothing
				typeInfo.GetRefTypeInfo(href, implTypeInfo)
				' And GetRefTypeInfo looks up the index to get an ITypeInfo for it.
				Dim implTypeName As String = ""
				implTypeInfo.GetDocumentation(-1, implTypeName, dummyString, dummyInt, dummyString)

				objectInfo.[Implements].Add(implTypeName)
			Next


			' Function/Sub/Property members:
			' Note that property accessors are flattened, e.g. for a property "Fred as Integer"
			' it will be represented as two members "[Get] Function Fred() As Integer", and "[Put] Sub Fred(Integer)"
			' Each member is uniquely identified by an integer "MEMID".
			' This memid is what's used e.g. when invoking the member.

			For iFunc As Integer = 0 To typeAttr.cFuncs - 1
				' FUNCDESC is the key datastructure here:
				Dim pFuncDesc As IntPtr = Nothing
				typeInfo.GetFuncDesc(iFunc, pFuncDesc)
				Dim funcDesc As ComTypes.FUNCDESC = DirectCast(Marshal.PtrToStructure(pFuncDesc, GetType(ComTypes.FUNCDESC)), ComTypes.FUNCDESC)

				' Each function notionally has a list of names associated with it. I'll just pick the first.
				Dim names As String() = {""}
				typeInfo.GetNames(funcDesc.memid, names, 1, dummyInt)
				Dim funcName As String = names(0)

				Dim sb As New StringBuilder(funcName)

				'sb.Append("(");

				' Function formal parameters:
				'short cParams = funcDesc.cParams;
				'for (int iParam = 0; iParam <= cParams - 1; iParam++)
				'{
				'    ComTypes.ELEMDESC elemDesc = (ComTypes.ELEMDESC)Marshal.PtrToStructure(
				'        new IntPtr(funcDesc.lprgelemdescParam.ToInt64() + Marshal.SizeOf(typeof(ComTypes.ELEMDESC)) * iParam),
				'        typeof(ComTypes.ELEMDESC));
				'        sb.Append(", ");

				'    if (((int)elemDesc.desc.paramdesc.wParamFlags & 2) != 0)
				'        sb.Append("out ");

				'    sb.Append(DumpTypeDesc(elemDesc.tdesc, typeInfo));
				'}

				'sb.Append(")");

				' And print out the rest of the function's information:
				If (funcDesc.invkind And ComTypes.INVOKEKIND.INVOKE_PROPERTYGET) <> 0 Then
					sb.Append("Get ")
				ElseIf (funcDesc.invkind And ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT) <> 0 Then
					sb.Append("Set ")
				ElseIf (funcDesc.invkind And ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF) <> 0 Then
					sb.Append("Set ")
				End If

				Dim isSub As Boolean = (funcDesc.elemdescFunc.tdesc.vt = CShort(VarEnum.VT_VOID))

				objectInfo.Methods.Add(sb.ToString())

				typeInfo.ReleaseFuncDesc(pFuncDesc)
			Next


			' Field members:
			For iVar As Integer = 0 To typeAttr.cVars - 1
				Dim pVarDesc As IntPtr = Nothing
				typeInfo.GetVarDesc(iVar, pVarDesc)
				Dim varDesc As ComTypes.VARDESC = DirectCast(Marshal.PtrToStructure(pVarDesc, GetType(ComTypes.VARDESC)), ComTypes.VARDESC)
				typeInfo.ReleaseVarDesc(pVarDesc)
				Dim names As String() = {""}
				typeInfo.GetNames(varDesc.memid, names, 1, dummyInt)
				Dim varName As String = names(0)
				objectInfo.Fields.Add(varName)
			Next

			Return objectInfo
		End Function

		''' <summary>
		''' DumpTypeDesc: given a TYPEDESC, dumps it out into a string e.g. "Ref Int" or
		''' "Array of MyTypeInfo". Also calls AddTypeInfoToDump for every ITypeInfo encountered.
		''' </summary>
		''' <param name="tdesc">the TYPEDESC to dump</param>
		''' <param name="context">the ITypeInfo that contained this TYPEDESC, for context</param>
		''' <returns>a string representation of the TYPEDESC</returns>
		Public Function DumpTypeDesc(tdesc As ComTypes.TYPEDESC, context As ComTypes.ITypeInfo) As String
			Dim vt As VarEnum = DirectCast(tdesc.vt, VarEnum)

			Dim tdesc2 As ComTypes.TYPEDESC
			Select Case vt

				Case VarEnum.VT_PTR
					tdesc2 = DirectCast(Marshal.PtrToStructure(tdesc.lpValue, GetType(ComTypes.TYPEDESC)), ComTypes.TYPEDESC)

					Return "Ref " + DumpTypeDesc(tdesc2, context)
				Case VarEnum.VT_USERDEFINED
					Dim href As Integer = Convert.ToInt32(tdesc.lpValue.ToInt64() And Integer.MaxValue)
					Dim refTypeInfo As ComTypes.ITypeInfo = Nothing
					context.GetRefTypeInfo(href, refTypeInfo)
					AddTypeInfoToDump(refTypeInfo)
					Dim refTypeName As String = ""
					refTypeInfo.GetDocumentation(-1, refTypeName, dummyString, dummyInt, dummyString)

					Return refTypeName
				Case VarEnum.VT_CARRAY
					tdesc2 = DirectCast(Marshal.PtrToStructure(tdesc.lpValue, GetType(ComTypes.TYPEDESC)), ComTypes.TYPEDESC)
					Return "Array of " + DumpTypeDesc(tdesc2, context)
				' lpValue is actually an ARRAYDESC structure, which also has information on the array dimensions,
				' but alas .Net doesn't predefine ARRAYDESC.

				Case VarEnum.VT_VOID
					' e.g. IUnknown::QueryInterface(Ref GUID, out Ref Ref Void)
					Return "Void"
				Case VarEnum.VT_VARIANT
					Return "Object"
				Case VarEnum.VT_UNKNOWN

					Return "IUnknown*"
				Case VarEnum.VT_BSTR
					Return "String"
				Case VarEnum.VT_LPWSTR
					Return "wchar*"
				Case VarEnum.VT_LPSTR

					Return "char*"
				Case VarEnum.VT_HRESULT

					Return "HResult"
				Case VarEnum.VT_BOOL
					Return "Bool"
				Case VarEnum.VT_I1
					Return "SByte"
				Case VarEnum.VT_UI1
					Return "Byte"
				Case VarEnum.VT_I2
					Return "Short"
				Case VarEnum.VT_UI2
					Return "UShort"
				Case VarEnum.VT_I4, VarEnum.VT_INT
					' I don't know the difference
					Return "Integer"
				Case VarEnum.VT_UI4, VarEnum.VT_UINT
					' I don't know the difference
					Return "UInteger"
				Case VarEnum.VT_I8
					Return "Long"
				Case VarEnum.VT_UI8

					Return "ULong"
				Case Else
					' There are many other VT_s that I haven't special-cased yet.
					' That's just because I haven't encountered them yet in my test-cases.
					Return vt.ToString()
			End Select
		End Function


		Private typeInfosToDump As New Queue(Of ComTypes.ITypeInfo)()
		Private typeInfosDumped As New HashSet(Of String)()
		'
		Public Sub AddTypeInfoToDump(typeInfo As ComTypes.ITypeInfo)
			Dim typeName As String = ""
			typeInfo.GetDocumentation(-1, typeName, dummyString, dummyInt, dummyString)
			If typeInfosDumped.Contains(typeName) Then
				Return
			End If
			typeInfosToDump.Enqueue(typeInfo)
			typeInfosDumped.Add(typeName)
		End Sub

		''' <summary>
		''' DumpCOMObject: given a COM object, performs reflection on it and prints out all the
		''' properties it can (and also parameterless functions named "Is*" or "Get*")
		''' </summary>
		''' <param name="com">the COM object to dump</param>
		''' <param name="Depth">for visual formatting, says how far to indent</param>

		Public Sub DumpCOMObject(com As Object, Optional Depth As Integer = 0)
			' We'll only display the top-level fields and one level down:
			If Depth > 1 Then
				Return
			End If
			Dim prefix As String = ""
			For i As Integer = 1 To Depth
				prefix += ".   "
			Next

			' First, fetch the runtime type of this COM object:
			Dim dispatch As IDispatch = DirectCast(com, IDispatch)
			Dim typeInfo As ITypeInfo = Me.GetTypeInformation(dispatch, com)
			' to release the AddRef that GetTypeInfo did for us.

			' We'll use this id to identify which object and interface we're retrieving properties from:
			Dim id As String = ""
			typeInfo.GetDocumentation(-1, id, dummyString, dummyInt, dummyString)
			Dim iunk As IntPtr = Marshal.GetIUnknownForObject(id)
			id = "#" + iunk.ToString() + "_" + id
			Marshal.Release(iunk)

			' TypeAttr: contains general information about the type
			Dim pTypeAttr As IntPtr = Nothing
			typeInfo.GetTypeAttr(pTypeAttr)
			Dim typeAttr As ComTypes.TYPEATTR = DirectCast(Marshal.PtrToStructure(pTypeAttr, GetType(ComTypes.TYPEATTR)), ComTypes.TYPEATTR)
			typeInfo.ReleaseTypeAttr(pTypeAttr)


			' Properties and parameterless functions:

			For iFunc As Integer = 0 To typeAttr.cFuncs - 1
				' FUNCDESC is the key datastructure here:
				Dim pFuncDesc As IntPtr = Nothing
				typeInfo.GetFuncDesc(iFunc, pFuncDesc)
				Dim funcDesc As ComTypes.FUNCDESC = DirectCast(Marshal.PtrToStructure(pFuncDesc, GetType(ComTypes.FUNCDESC)), ComTypes.FUNCDESC)
				typeInfo.ReleaseFuncDesc(pFuncDesc)

				' Each function notionally has a list of names associated with it. I'll just pick the first.
				Dim names As String() = {""}
				typeInfo.GetNames(funcDesc.memid, names, 1, dummyInt)
				Dim funcName As String = names(0)

				' We'll only try to retrieve things that are likely to be side-effect-free properties:
				If (funcDesc.invkind And ComTypes.INVOKEKIND.INVOKE_PROPERTYGET) = 0 AndAlso Not (funcName.StartsWith("[Gg]et")) AndAlso Not (funcName.StartsWith("[Ii]s")) Then
					Continue For
				End If
				If funcDesc.cParams > 0 Then
					Continue For
				End If
				Dim returnType As VarEnum = DirectCast(funcDesc.elemdescFunc.tdesc.vt, VarEnum)
				If returnType = VarEnum.VT_VOID Then
					Continue For
				End If
				Dim returnTypeName As String = DumpTypeDesc(funcDesc.elemdescFunc.tdesc, typeInfo)

				' And we'll only try to evaluate the easily-evaluatable properties:
				Dim dumpableTypes As VarEnum() = New VarEnum() {VarEnum.VT_BOOL, VarEnum.VT_BSTR, VarEnum.VT_CLSID, VarEnum.VT_DATE, VarEnum.VT_DECIMAL, VarEnum.VT_FILETIME, _
					VarEnum.VT_HRESULT, VarEnum.VT_I1, VarEnum.VT_I2, VarEnum.VT_I4, VarEnum.VT_I8, VarEnum.VT_INT, _
					VarEnum.VT_LPSTR, VarEnum.VT_LPWSTR, VarEnum.VT_R4, VarEnum.VT_R8, VarEnum.VT_UI1, VarEnum.VT_UI2, _
					VarEnum.VT_UI4, VarEnum.VT_UI8, VarEnum.VT_UINT, VarEnum.VT_USERDEFINED}

				Dim typeIsDumpable As Boolean = dumpableTypes.Contains(returnType)
				If returnType = VarEnum.VT_PTR Then
					Dim ptrType As ComTypes.TYPEDESC = DirectCast(Marshal.PtrToStructure(funcDesc.elemdescFunc.tdesc.lpValue, GetType(ComTypes.TYPEDESC)), ComTypes.TYPEDESC)
					If ptrType.vt = CShort(VarEnum.VT_USERDEFINED) Then
						typeIsDumpable = True
					End If
				End If

				' Here's how we fetch an arbitrary property from a COM object, identified by its MEMID.
				Dim val As Object = Nothing
				If typeIsDumpable Then
					Dim dispParams As New ComTypes.DISPPARAMS() With { _
						.cArgs = 0, _
						.cNamedArgs = 0 _
					}
					Dim dispatchType As Integer = CInt(If((funcDesc.invkind And ComTypes.INVOKEKIND.INVOKE_PROPERTYGET) <> 0, 2, 1))
					' void Invoke(int dispIdMember, ref Guid riid, int lcid, ComTypes.INVOKEKIND wFlags, ref ComTypes.DISPPARAMS pDispParams, IntPtr pvarResult, IntPtr pExcepInfo, IntPtr puArgErr);

						'dispatch.Invoke(funcDesc.memid, ref IID_NULL, 0, dispatchType, dispParams, ref varResult,
						'    exceptionInfo, out dummyIntPtr);
						'val = varResult.GetObject();
						'if (varResult.vt == (ushort)VarEnum.VT_PTR && varResult.ptr != IntPtr.Zero)
						'{
						'    Marshal.Release(varResult.ptr);
						'}
					Dim varResult As Object = Me.InvokeGet(dispatch, funcDesc.memid)
				Else
					val = String.Format("[{0}]", DirectCast(funcDesc.elemdescFunc.tdesc.vt, VarEnum))
				End If

				' And dump out the property. We'll recurse into the object properties.
				Console.WriteLine("{0}{1}.{2} = {4}   As {3}", prefix, id, funcName, returnTypeName, val)
				If returnType = VarEnum.VT_PTR AndAlso val IsNot Nothing Then
					DumpCOMObject(com, Depth + 1)

				End If
			Next


		End Sub

		Public Function InvokeGet(dispatch As IDispatch, memId As Integer) As Object
			Dim pArgErr As IntPtr() = Nothing
			Dim pVarResult As New Object()
			Dim pDispParams As New ComTypes.DISPPARAMS()
			Dim pExcepInfo As New ComTypes.EXCEPINFO()

			Dim guid As New Guid()
			Dim result As Integer = dispatch.Invoke(memId, guid, 0, CUShort(ComTypes.INVOKEKIND.INVOKE_PROPERTYGET), pDispParams, pVarResult, _
				pExcepInfo, pArgErr)

			Return pVarResult
		End Function
	End Class

End Namespace

'=======================================================
'Service provided by Telerik (www.telerik.com)
'Conversion powered by NRefactory.
'Twitter: @telerik, @toddanglin
'Facebook: facebook.com/telerik
'=======================================================

Open in new window

0
 
Bob LearnedCommented:
Also, if you need to create instances of COM objects, and you have a ProgID, then you can use the Type.GetTypeFromProgId method to get a type that you can use with Activator.CreateInstance.

Common Interactions with COM Objects
http://www.informit.com/articles/article.aspx?p=27219&seqNum=8

Dim t = Type.GetTypeFromProgID("SAPI.SpVoice")

Open in new window

0
 
izzycammAuthor Commented:
Thank you for the assistance.  The code you provided in both VB and C# reference a "ComObjectInfo" namespace.  Do you have the code for this to create the referenced namespace/class?

This looks I may be able to work with it to accomplish what I need to do, but I will need this information.

Thanks
0
 
Bob LearnedCommented:
Ooops, missed that one:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Com.Services
{
    public class ComObjectInfo
    {

        public string TypeName { get; private set; }

        public List<string> Implements { get; private set; }
        public List<string> Methods { get; private set; }
        public List<string> Fields { get; private set; }
        
        public ComObjectInfo(string typeName)
        {
            this.TypeName = typeName;

            this.Implements = new List<string>();
            this.Methods = new List<string>();
            this.Fields = new List<string>();
        }

    }
}

Open in new window

0
 
Bob LearnedCommented:
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text

Namespace Com.Services
	Public Class ComObjectInfo

		Public Property TypeName() As String
			Get
				Return m_TypeName
			End Get
			Private Set
				m_TypeName = Value
			End Set
		End Property
		Private m_TypeName As String

		Public Property [Implements]() As List(Of String)
			Get
				Return m_Implements
			End Get
			Private Set
				m_Implements = Value
			End Set
		End Property
		Private m_Implements As List(Of String)
		Public Property Methods() As List(Of String)
			Get
				Return m_Methods
			End Get
			Private Set
				m_Methods = Value
			End Set
		End Property
		Private m_Methods As List(Of String)
		Public Property Fields() As List(Of String)
			Get
				Return m_Fields
			End Get
			Private Set
				m_Fields = Value
			End Set
		End Property
		Private m_Fields As List(Of String)

		Public Sub New(typeName As String)
			Me.TypeName = typeName

			Me.[Implements] = New List(Of String)()
			Me.Methods = New List(Of String)()
			Me.Fields = New List(Of String)()
		End Sub

	End Class
End Namespace

'=======================================================
'Service provided by Telerik (www.telerik.com)
'Conversion powered by NRefactory.
'Twitter: @telerik, @toddanglin
'Facebook: facebook.com/telerik
'=======================================================

Open in new window

0
 
izzycammAuthor Commented:
Thanks for the additional information.  I will see if this solves my issue.
0
 
izzycammAuthor Commented:
This seems to be the information I need to move forward in the right direction.  I still need to teak this for my project, but a big help in understanding what needs to be done.

Thanks
0

Featured Post

Get your Disaster Recovery as a Service basics

Disaster Recovery as a Service is one go-to solution that revolutionizes DR planning. Implementing DRaaS could be an efficient process, easily accessible to non-DR experts. Learn about monitoring, testing, executing failovers and failbacks to ensure a "healthy" DR environment.

  • 6
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now