Dump Object for ComObject using dynamic?
Asked Answered
A

1

2

I'm trying (without luck) to implement an "Object Dumper" for objects I'm accessing in the Office Type Library.

It must be possibly, because VS's debug window has a "dynamic view" for the System.__ComObject objects that effectively does what I want.

Any ideas?

Anhedral answered 15/1, 2012 at 12:17 Comment(2)
Reflection doesn't work on late-bound COM objects, the kind you get when you use the dynamic keyword. You got the debug view from using the interop library. There's very little reason to use late binding with Office apps.Ferrick
>> There's very little reason to use late binding with Office apps. I don't need it for run time, just to inspect what the hell office is passing back to me. >> You got the debug view from using the interop library That makes sense. I'm guessing Debug View is enumerating the interfaces and seeing what will cast. Maybe I can do something along those lines ...Anhedral
M
4

I have also created a method for getting an interface that can be used for accessing the object. Use:

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

You have to have an IDispatch interface in your code:

[Guid("00020400-0000-0000-C000-000000000046"), ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IDispatch 
{ 
    [PreserveSig] 
    int GetTypeInfoCount(out int pctinfo); 

    [PreserveSig] 
    int GetTypeInfo( 
        [MarshalAs(UnmanagedType.U4)] int iTInfo, 
        [MarshalAs(UnmanagedType.U4)] int lcid, 
        out System.Runtime.InteropServices.ComTypes.ITypeInfo ppTInfo); 

    [PreserveSig] 
    int GetIDsOfNames( 
        ref Guid riid, 
        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr)] 
        string[] rgszNames, 
        int cNames, 
        int lcid, 
        [MarshalAs(UnmanagedType.LPArray)] int[] rgDispId); 

    [PreserveSig] 
    int Invoke( 
        int dispIdMember, 
        ref Guid riid, 
        uint lcid, 
        ushort wFlags, 
        ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, 
        out object pVarResult, 
        ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo, 
        IntPtr[] puArgErr); 
}

My method casts COM object to IDispatch, gains unmanaged type info, gets object type GUID from non-public Marshal method GetTypeInfoGuid and then searchs it in current AppDomain's assemblies.

internal static Func<ITypeInfo,Guid> GetTypeInfoGuid = (Func<ITypeInfo,Guid>)Delegate.CreateDelegate(typeof(Func<ITypeInfo,Guid>), typeof(Marshal).GetMethod("GetTypeInfoGuid", BindingFlags.NonPublic | BindingFlags.Static, null, new[]{typeof(ITypeInfo)}, null), true);
public static Type GetCOMObjectType(object comobject)
{
    if(!Marshal.IsComObject(comobject))
    {
        throw new ArgumentException("This is not COM object.", "comobject");
    }
    IDispatch dispatch = (IDispatch)comobject;
    ITypeInfo info;
    dispatch.GetTypeInfo(0,0, out info);
    Guid guid = GetTypeInfoGuid(info);
    foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies())
    {
        foreach(Type t in a.GetTypes())
        {
            if(t.IsInterface && t.IsImport && t.GUID == guid)
            {
                return t;
            }
        }
    }
    return Type.GetTypeFromCLSID(guid);
}

If the appropriate type isn't found, the method will return type of System.__ComObject. However, you can get GUID property value from it, so at least you will get the GUID. Usage:

object controls = axWindowsMediaPlayer1.cdromCollection;
Type t = GetCOMObjectType(controls);
Console.WriteLine(t);
Console.WriteLine(t.GUID);
/*Output:
  WMPLib.IWMPCdromCollection
  ee4c8fe2-34b2-11d3-a3bf-006097c9b344 */

Hope this helps. Good luck. ☺

Edit: Found an easier but slower method:

object controls = axWindowsMediaPlayer1.cdromCollection;
IDispatch dispatch = (IDispatch)controls;
ITypeInfo info;
dispatch.GetTypeInfo(0,0, out info);
Type t = Marshal.GetTypeForITypeInfo(Marshal.GetIUnknownForObject(info));
Console.WriteLine(t);
Macrobiotic answered 4/6, 2012 at 15:25 Comment(1)
ITypeInfo is a ComObject so it's necessary to release it using Marshal.ReleaseComObjectCaiaphas

© 2022 - 2024 — McMap. All rights reserved.