Dump Object for ComObject using dynamic?

前端 未结 1 573
眼角桃花
眼角桃花 2021-01-15 13:56

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 h

1条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-15 14:08

    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 GetTypeInfoGuid = (Func)Delegate.CreateDelegate(typeof(Func), 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);
    

    0 讨论(0)
提交回复
热议问题