Retrieve DISPID of outgoing dispinterface member

551 views Asked by At

I'm stuck on this one.

Given three variables:

  • an IDispatch* to a connectable object
  • the IID (DIID) of an outgoing dispinterface on that object
  • the name of a member defined by the dispinterface

How can resolve the name to a DISPID?

  • pDispatch->GetIDsOfNames(...) returns DISP_E_UNKNOWNNAME, as I would expect (outgoing interfaces aren't implemented by the connectable object)
  • I need to support scenarios where 0 clients have yet connected to the outgoing interface, so I can't enumerate the existing connection points in order to call GetIDsOfNames on one of them (I'm not even sure this would work)
  • In order to perform manual reflection, I would need the dispinterface's ITypeInfo. I could get this from the coclass's ITypeInfo. However pDispatch->GetTypeInfo(0, ...) returns the ITypeInfo for the IDispatch implementation (as per the documentation), not the ITypeInfo for the coclass. (And there are no other ITypeInfos exposed by this object's IDispatch implementation.)
1

There are 1 answers

0
Simon Mourier On

If the object is "quite standard", then it should be possible.

From the object/IDispatch interface, you should be able to get to the TLB (type library). From the type library, you should be able to browse all coclasses, and get interfaces that these coclasses implement. You need to get to interface you have the IID for, browse the member and get the one you're interested.

There are many cases where this just won't work. Here is a console sample I've put up that works with a shell object. I've written it in C# because it's easier, but there's nothing you can't do with a decent language. I've used an old TLBINF32.DLL com utility library (only x86 unfortunately) I talk about in my answer to this question here on SO: How to read COM TypeLib with C# or C++?

    static void Main(string[] args)
    {
        // create a sample object every one has
        object o = Activator.CreateInstance(Type.GetTypeFromProgID("shell.application")); // for example
        TLIApplication app = new TLIApplication();

        // not sure, but I believe in pure COM it's calling IDispatch::GetTypeInfo & ITypeInfo::GetContainingTypeLib 
        TypeLibInfo tli = app.InterfaceInfoFromObject(o).Parent;

        // this is the guid for DShellFolderViewEvents
        int dispid = GetDispId(tli, new Guid("{62112AA2-EBE4-11CF-A5FB-0020AFE7292D}"), "SelectionChanged");
        Console.WriteLine("dispid:" + dispid); // should display 200
    }

    public static int GetDispId(TypeLibInfo tlb, Guid diid, string memberName)
    {
        // browse all coclasses
        // in pure COM this is ITypeLib::GetTypeInfo
        foreach (CoClassInfo ti in tlb.CoClasses)
        {
            // browse all interfaces in those coclasses
            // in pure COM this is ITypeInfo::GetRefTypeOfImplType 
            foreach (InterfaceInfo itf in ti.Interfaces)
            {
                // only select [source] interfaces (events)
                // this test is optional since the diid is unique
                // in pure COM this is ITypeInfo::GetImplTypeFlags
                if (((ImplTypeFlags)itf.AttributeMask & ImplTypeFlags.IMPLTYPEFLAG_FSOURCE) != ImplTypeFlags.IMPLTYPEFLAG_FSOURCE)
                    continue;

                if (new Guid(itf.GUID) == diid)
                {
                    // in pure COM this is ITypeInfo::GetTypeAttr & ITypeInfo::GetFuncDesc
                    foreach (MemberInfo mi in itf.Members)
                    {
                        if (mi.Name == memberName)
                            return mi.MemberId;
                    }
                }
            }
        }
        return -1;
    }