2015-11-05

Connecting to ActiveX control events in IE11 mode

I have had the unfortunate recent experience of trying to get a web-based application that uses a commercial ActiveX control to work in IE11. In previous versions of IE (10 and earlier), one can use the attachEvent() method to add a callback to an ActiveX control-defined event. In the application I am working on, a dynamic <object> element is constructed to instantiate the control on a page. Then, attachEvent() is used to add callbacks to the events provided by the control.

For example, if control represent a reference to the <object> node, the following is how I attached to an event:

control.addEvent('FileChanged', myfunc);

In IE11 standards (edge) mode, addEvent() is not available since it is non-standard, where MS states to use addEventListener() instead. Unfortunately, addEventListener() does not work with ActiveX control events.

After some searching around, it appears there exists a way to attach to ActiveX events by creating a function whose name is the ID of the control, followed by "::" and the event name. For example, if the ID of the control is "myAxId" (when using the <object>, the ID is specified by the id attribute of the element), and the event name is "FileChanged", you can do:

function myAxId::FileChanged() { ... }

There is also a way to use <script> tags to bind to ActiveX events (see https://stackoverflow.com/a/152724/719934).

The problem is these are static-based approaches and do not work well when ActiveX controls are instantiated dynamically. Therefore, based on on the approach used at https://stackoverflow.com/a/10557222/719934, I created the following generic JavaScript function for connecting to an ActiveX-defined event:

function connectToAXEvent(o, etype, func)
{
  if (o.attachEvent) {
    o.attachEvent(etype, func);
  } else {
    var id = o.id, f = func;
    eval('(function(){'+
      'function '+id+'::'+etype+'(){'+
        'f.apply(f,arguments);'+
       '}'+
    '})();');
  }
}

The parameter o is the DOM node for the ActiveX control (i.e. the DOM node for the <object> element that instantiates the control). The etype parameter is the name of the event to bind to. The func parameter is the function to be called when the event is triggered.

For cases where you may already have the ID, you can change the function so it takes an ID string vs the <object> node.

So far, the function has worked in all IE modes. Of course, the function is only invoked in IE browsing contexts since other browsers do not support ActiveX. I do find it ironic that IE11 edge mode, which attempts to be standards compliant requires the creation of a non-standard function identifier ("::" is not allowed in function names) to connect to an ActiveX event. It would have been nice if MS supported the use of addEventListener() for ActiveX events.

2 comments:

  1. Hi Earl,

    A bit but a wonderful blog you have written. I was actually looking for something like this to work for me as we have been running our web app in Comparability mode till now (IE7 mode) and on IE our ActiveX control was working all fine as AttachEvent was working fine to add event listener with no issues at all.

    But now on IE 11 browser with Comparability mode not set, AttachEvent stopped working and we were looking for an alternate mechanism to work for us. Your code snippet here works all fine to attach an event listener, but we still require to remove the event listener in certain situation. So by any chance do you have any code snippet which can be used for Detaching the event listener for an ActiveX control at the runtime using JavaScript or JQuery.

    Any kind of help would be really appreciated.

    Kind Regards
    Kapil

    ReplyDelete
    Replies
    1. You cannot remove function declarations. Not knowing the specifics of what your needs are, not sure if you have any options. If it is the case where you do not want a listener to perform some action, you could, in your listener, check for a state indicator if the listener should actually do something or not. For example:

      if (do_operation_check()) {
      // do stuff here...
      } else {
      // do nothing
      }

      Where "do_operation_check()" is whatever expression you require to determine if the listener should do something.

      Delete