1

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.)
Michael Gunter
  • 12,528
  • 1
  • 24
  • 58
  • My first thought would be to QueryInteface using the DIID and then calling GetIDsofNames() on the returned dispInterface... – Joseph Willcoxson Nov 19 '14 at 18:47
  • Outgoing interfaces are not implemented by the source object. QueryInterface on any outgoing interface IID will return E_NOINTERFACE. – Michael Gunter Nov 19 '14 at 19:34
  • Well, then you're in for a lot of work> ;) Have you tried calling ITypeInfo::GetContainingTypeLib() to get the type library and then iterating the type library until you find the typeinfo you want and then getting its ids? It seems sort of brute force, but I would think it would work... – Joseph Willcoxson Nov 19 '14 at 19:42
  • Problem there is it assumes the dispinterface is in the same type library. If it is not, then I think I'm SOL. – Michael Gunter Nov 19 '14 at 19:54
  • Well, in the registry, you could always look in the HKEY_CLASSES_ROOT\Interfaces key to see if there is an entry for your interface, and also if there is a TypeLib key for it. Are you trying to do this dynamically, or are you comfortable assuming that the interface isn't going to change. You can use Visual Studio tool OleView to inspect the interface. – Joseph Willcoxson Nov 19 '14 at 19:59
  • It's dynamic, so I don't know the interfaces ahead of time. I probably can (and have to) use the registry in the case when the interface isn't defined in the same type library, since I believe that's the way most ITypeLib implementations work anyway. – Michael Gunter Nov 19 '14 at 20:03
  • If you are lucky, the object implements `IProvideClassInfo::GetClassInfo`. Otherwise, there are heuristics that might or might not work. `ITypeInfo::GetContainingTypeLib` has already been mentioned. Or, if you know the `CLSID`, you could try and dig a corresponding `LIBID` from the registry and load it with `LoadRegTypeLib`. The object may implement `IPersist`, giving you its `CLSID`. – Igor Tandetnik Nov 20 '14 at 06:28

1 Answers1

0

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;
    }
Community
  • 1
  • 1
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298