While there are many guides for Browser Helper Objects, i have a hard time finding resources on how to implement scriptable objects (i.e. besides the main control) for content plugins (i.e. embedded in website).
To avoid misunderstandings: the question is about scriptable objects that a plugin object may return to the scripts in a website, e.g. from a method call.
While i guess scriptability in general for these probably works via the usual IDispatch
, i don't see how events are to be handled (i.e. for attachEvent
). Are you supposed to implement that manually (e.g. handle calls to attachEvent
explicitly) or are there only certain Interfaces that you have to implement?
Update (issuing events from plugins)
Ok, so with gf's additional comments, I see that the desired mechanism is the opposite- issuing events from components provided outside of the DOM.
This is similar in respect to how MSHTML events can be handled, but the plugin's objects will need to inspect objects given to it using a different mechanism.
On the objects that provide events (or support attaching objects for events), provide attachEvent and detachEvent methods through IDispatch (and through dual-interface if desired). If you want this to appear seamless with HTML elements, then you should declare them the same way as they are provided on IHTMLElement. The DISPIDs don't have to match but the order of arguments and types should match.
attachEvent Method (IHTMLElement2) @ MSDN
detachEvent Method (IHTMLElement2) @ MSDN
(from MSHTML.IDL in the platform SDK)
When you receive a call via attachEvent, you will need to associate the event name with the object received. Similarly, you will need to clear an association of the object to event name when you receive a call via detachEvent.
When you look to issue an event, examine all the objects you have stored for methods that should be called, matching your event. In theory you don't have to use the same method name as your event name, but in practice it will be easier to maintain and manage if you do. First examine IDispatch itself, calling GetIDsOfNames() to find an exact match for your event. If not, examine IDispatchEx and look for an expando method via GetDispID() that is a match for your event.
IDispatch Interface @ MSDN
IDispatch::GetIDsOfNames @ MSDN (locate recipient event method)
IDispatchEx Interface @ MSDN
IDispatchEx::GetDispID @ MSDN (locate recipient event method)
Last, once you have located the handler from one or the other, call the associated Invoke() method.
IDispatch::Invoke @ MSDN
IDispatchEx::InvokeEx @ MSDN
Initial (handling predefined MSHTML events)
Most event handler objects are created manually, as this allows them to accept MSHTML events via IDispatch when receiving calls through attachEvent() or when assigned to event properties. This mechanism varies from the typical ConnectionPointContainer to EventSink setup that is prevalent in COM. However, creating an object to handle these events is simpler. There are some key differences you should note if you are going to create such an object to handle an event.
The first constraint is that the DISPID and method name of the event received must match the DISPID and method for the event received. Documentation is somewhat sparse on this, but the best place to resolve the correct DISPID is to look at the C++ header files. If you have the Microsoft Platform SDK installed, you can look inside the include subdirectory at the file <mshtmdid.h> (short for MSHTML Dispatch ID). It contains a list of all the relevant MSHTML dispatch IDs.
The second constraint is that IE/MSHTML does not call the binary version of methods declared in vtable-based interfaces, so the call will arrive via IDispatch::Invoke(). This might be an issue for you if your desired framework for COM doesn't take care of routing both types of calls to the same handler in your code.
To create a handler object, you need to create a COM object that supports IUnknown, IDispatch, and IObjectSafety. IUnknown is implicit for either of the other interfaces, but don't forget IObjectSafety.
Not specifically required, but your object should be an apartment-threaded object, to avoid marshaling headaches. Since calls are direct to IDispatch via a VARIANT wrapper, you may encounter issues if you are doing things that require multiple apartments or if you are trying to use free-threaded components. Most frameworks create objects for this model or suggest this type by default (VB6, Delphi, MFC, ATL).
Definitions in the C++ header file mentioned above correspond exactly to items listed on IHTMLElement. Here is one specific item to get you started.
First, the event off of the HTML DOM element.
onclick Property (IHTMLElement) @ MSDN
We note that the name of this property is onclick. Now, to the header file.
MSHTMDID.H @ DDART.NET
The matching item we want is DISPID_EVMETH_ONCLICK (Note the heuristic here: Dispatch ID => Event Method => OnClick). According to the source file, it reuses an existing definition by macro definition.
Some of the definitions overlap or recycle the same DISPIDs defined for general ActiveX/OLE control use. DISPID_CLICK is defined in OleCtl.h, so let's head over there to track down the final value. This header file is also available in the Platform SDK and is also included by default in VC++ installations, at least as far back as VC++ 6.0, as I recall.
OLECTL.H @ DDART.NET
The DISPID we want is -600.
Now, in the IDL for your component, you will either need to declare a method called onclick(), that has this DISPID value; or you will need to handle this DISPID in your handler for IDispatch::Invoke(). If you are using ATL, it doesn't hurt to declare the method and provide dual-layout. Other implementations may vary.
The rest of your development should be typical for scripting objects in Internet Explorer. Also note that most of these DISPIDs fall in the negative range, to avoid collisions with user-defined DISPIDs.