Understanding what things mean in context: Dispatch interfaces


Remember that you have to understand what things mean in context. For example, the IActiveMovie3 interface has a method called get_MediaPlayer. If you come into this method without any context, you might expect it to return a pointer to an IMediaPlayer interface, yet the header file says that it returns a pointer to an IDispatch interface instead. If you look at the bigger picture, you'll see why this makes sense.

IActiveMovie3 is an IDispatch interface. As you well know, the IDispatch interface's target audience is scripting languages, primarily classic Visual Basic (and to a lesser degree, JScript). Classic Visual Basic is a dynamically-typed language, wherein nearly all variables are merely "objects", the precise type of which is not known until run-time. A statically-typed language will complain at compile time that you are invoking a method on an object that doesn't support that method or that you are passing the wrong number or type of operands to a method. A dynamically-typed language, on the other hand, doesn't check until the line of code is actually executed whether the method exists, and if it does, whether you called it correctly.

When working with IDispatch and dynamically-typed languages, therefore, the natural unit of currency for objects is the IDispatch. All objects take the form of IDispatch. Objects that produce other objects will produce IDispatch interfaces, because that's what the scripting engine is expecting.

That's why the get_MediaPlayer method returns an IDispatch. Because that's what the scripting engine expects. And, if you are familiar with the context, it's also what you should expect.

A tell-tale sign of this context comes from the name "get_MediaPlayer". This name does not follow the COM function naming convention but rather is a constructed name for the C/C++ binding of the "get" property. C/C++ bindings are the assembly language of OLE automation: You're operating with the nuts and bolts of OLE automation, and if you want to play at this level, you're going to have to know how to use a screwdriver.

Comments (23)
  1. KiwiBlue says:

    Is there an IScaryMovie interface?

  2. Doogal says:

    So couldn’t IMediaPlayer inherit from IDispatch and get_MediaPlayer return an IMediaPlayer interface? Presumably the scripting engine could cope with that and everybody would be happy?

  3. Erbo says:

    Doogal: Certainly it could; such interfaces are called "dual interfaces." (I can’t find much info on IMediaPlayer in particular, but it looks like it is NOT a dual interface.)

    However, even if you’re dealing with dual interfaces, you can’t be sure that any random IDispatch is an instance of your desired dual interface; you have to QueryInterface it and find out.

  4. Sebastian Redl says:

    A search for IActiveMovie3 in MSDN online turns up nothing. How can I find more information on this interface? (Such as its nature: COM interface, Dual interface, pure Dispatch interface.)

  5. Lewis Jones says:

    Re: Sebastian Redl

    Looking in OLE/COM object Viewer (my favorite way to hack into COM objects), it does appear that IActiveMovie3 is a dispinterface (although it’s attributes marks it as dual, so I would think you should be able to use it via vtable IUnknown access).

    It’s on the ActiveMovie Type Library 2.0:

    amcompat.tlb

    It will list the methods available there.

  6. Lewis Jones says:

    Another thing is that by having it return an IDispatch object, that means that if something "better" comes out that implements IActiveMovie3, it isn’t locked into returning an object that implements IMediaPlayer…

    That is why using TypeName() (in "classic" VB terms) is useful – to make sure the object you are getting is the one you think you are getting.

    Pity that getting the type name of an IDispatch object in C++ is such a @!#$#!@ pain (Watch, someone will just post a 1-line Win32 API call that I missed… I hope…)

  7. wpoust says:

    "and if you want to play at this level, you’re going to have to know how to use a screwdriver."

    Screwdriver? I don’t think so. A plastic spork (spoon/fork) seems more like a better analogy for IDispatch.

  8. PatriotB says:

    A blog entry, in 2006, involving ActiveMovie?? Only on Raymond’s blog. (Or, I could see Larry blogging about this too.) Keep it up! :)

  9. kbiel says:

    >You’re operating with the nuts and bolts of OLE automation, and if you want to play at this level, you’re going to have to know how to use a screwdriver.

    And how does one use a screwdriver with nuts and bolts?

  10. Starfish says:

    >And how does one use a screwdriver with nuts and bolts?

    If it is a bolt with a drive, insert the screwdriver and turn.

  11. Anonymous Coward says:

    If a new version of IActiveMovie3 has a get_MediaPlayer that returns anything other than an IMediaPlayer, then all client code that expects the IDispatch to actually be an IMediaPlayer will break horribly. It will get the interface, see if it’s the IMediaPlayer it expected, and if not then the code won’t know what to do with the object it actually got, and then won’t work.

    A more likely option is for either IActiveMovie4 to have a get_IMediaPlayerNewVersion or for IActiveMovie3 to get a new get_IShinyNewThing method. (The latter would worry me a bit, though.)

  12. rodrigostrauss says:

    Classic Visual Basic (unless Classic Visual Basic == VBScript) is not a dynamic typed language. Visual Basic is a strong typed language (using Option Explicit) and you can use Object the same way you can use Object in VB8/C#.

    VB6 complains at compile time about wrong functions names, since it imports the TLB metadata and knows the object in advance. And it accesses the objects using custom interfaces via vtable, not using IDispacth.

    Are we talking about the same VB?

  13. BryanK says:

    But VB6’s "Object" type is exactly equivalent to an IDispatch. VB6 can’t consume IUnknowns, the "return" type of everything has to be either IDispatch or in a TLB so it can check it out at compile time.

    VBScript, however, is much like JavaScript in that it’s weakly typed (everything’s an IDispatch).

  14. Bill says:

    "You’re operating with the nuts and bolts of OLE automation, and if you want to play at this level, you’re going to have to know how to use a screwdriver."

    Reminded me of:

    "If we hit that bullseye, the rest of the dominos will fall like a house of cards. Checkmate."

  15. grg says:

    Classic Visual Basic (unless Classic Visual Basic == VBScript) is not a dynamic typed language. Visual Basic is a strong typed language (using Option Explicit) and you can use Object the same way you can use Object in VB8/C#.

    I think VB doesn’t try to optimize the code even with option explicit. He is still dynamic internally. That’s only a little syntactic sugar for the programmer.

  16. Cooney says:

    Screwdriver? I don’t think so. A plastic spork (spoon/fork) seems more like a better analogy for IDispatch.

    No, a driver really is the best analogy – you have a generic handle that you can attach things to: phillips head, 3/8" socket, chainsaw…

  17. Jamie Anderson says:

    From memory, the VB6 "Object" type is actually a variant, and may hold an IDispatch item.

    Option Explicit simply turned on checks to make sure that variables were declared before using them. Otherwise, if you typo’d a variable name, the code would compile just fine.

    VB6 did allow you to specify explicit types, such as "this is a string". However, it did so much implicit conversion that you could quite easily assign most variable types to most other types.

  18. rodrigostrauss says:

    Hey guys, like you all I’m a C++ programmer, but I’ve programmed VB before (looks like you don’t). I’m not sure it’s a quality, but… :-)

    Object is a VARIANT, not an IDispatch. When you use early binding (adding a TLB reference to VB) all calls are vtable based (no IDispatch), just like a pObj->Method();. A very short WinDbg session can show you this. It’s good to remember that the MFC ActiveX wrapper is IDispatch based – thus slower than VB.

    You can’t use a raw IUnknown pointer in VB6 because it’s not a valid automation type. But you can make your interface dual, VB6 can use a custom interface if it’s automation compatible.

    VB6 is not dynamic internally. If you look at the VB6 folder you will found C2.EXE, the Visual C++ backend. I don’t know if it’s exactly the same backed used by VC6, but you can get some weird logs with Visual C++ messages compiling VB6 programs.

    The get_MediaPlayer return IDispatch to be used by script languages (VB6 is not among them) like VBS and JS.

    All VB do is calling a VariantChangeType if you try to assign variables from different types. It sucks, but doesn’t make VB6 dynamic typed.

  19. PatriotB says:

    After thinking about this a bit more, I’ve thought of what I’d wager is the *real* reason why that property returns an IDispatch instead of an IMediaPlayer: to prevent type library pollution (terminology i just made up).

    Think of it this way. If the property explicitly was defined as an IMediaPlayer, that means that the IMediaPlayer interface would have to be referenced by the ActiveMovie type library. And by extension, every interface/enum/etc. that IMediaPlayer referred to. Which would probably be the equivalent of the entire MediaPlayer type library.

    So if you added a reference to the ActiveMovie type library to your VB6 project, you’d have all the MediaPlayer interfaces pulled in as well, whether or not you ever intended to use the get_MediaPlayer property.

    Defining it as a dispatch not ony makes it "future-safe" but makes it easier to draw lines between the two type libraries.

  20. BryanK says:

    No, the Object type is not the VARIANT type. Try to run this code in VB6:

    Dim x as Object

    x = 1

    MsgBox x

    and you’ll get an error on the "x = 1" line, saying "object variable or With block variable not set". This is because VB6 is trying to get the default property of the x object, but x is Nothing, so it can’t. If you change the code to:

    Dim x as Object

    Set x = 1

    MsgBox x

    then you’ll get a "Type Mismatch" error on the "Set x = 1" line at runtime.

    Changing the Dim to "Dim x as Variant" works fine (in the "x = 1" case, not the "Set" case).

  21. BryanK says:

    Oops, the "Type Mismatch" error on the "Set x = 1" line occurs at compile time, not runtime.

    Still, Objects are not VARIANTs.

    (Note, however, that a VARIANT (in C++, or a Variant in VB6) can hold an Object (IDispatch). But the reverse is not true.)

  22. rodrigostrauss says:

    You’re right, I missed the point, Object is IDispatch. But when you declare a variable without type (the way you MUST do in VBScript), it’s a VARIANT and not Object.

  23. BryanK says:

    Yes, that’s true, "Dim x" creates a Variant-type variable. (Probably because a C++ VARIANT can contain anything: byte, short, long, BSTR, float, double, IUnknown, etc., so "Dim x" gives x the most general type available.)

Comments are closed.