Javascript – Extending an ActiveXObject in javascript

activexjavascriptPROXY

I want to add some functionality track certain calls to ActiveX object methods in javascript.

I usually create my activeX object like this:
var tconn = new ActiveXObject("Tconnector");

I need to log every time the open method is called on tconn and all other instances of that activeX control.

I cant modify tconn's prototype because it does not have one!

I think that i can create a dummy ActiveXObject function that creates a proxy object to proxy calls to the real one. Can you help me do that?

Note: writing a direct wrapper is out of question, because there are already 1000s of calls to this activeX within the application.

Best Answer

You can in fact override ActiveXObject().

This means you can try to build a transparent proxy object around the actual object and hook on method calls. This would mean you'd have to build a proxy around every method and property your ActiveX object has, unless you are absolutely sure there is no code whatsoever calling a particular method or property.

I've built a small wrapper for the "MSXML2.XMLHTTP" object. There are probably all kinds of problems you can run into, so take that with a grain of salt:

var ActualActiveXObject = ActiveXObject;

var ActiveXObject = function(progid) {
  var ax = new ActualActiveXObject(progid);

  if (progid.toLowerCase() == "msxml2.xmlhttp") {
    var o = {
      _ax: ax,
      _status: "fake",
      responseText: "",
      responseXml: null,
      readyState: 0,
      status: 0,
      statusText: 0,
      onReadyStateChange: null
      // add the other properties...
    };
    o._onReadyStateChange = function() {
      var self = o;
      return function() {
        self.readyState   = self._ax.readyState;
        self.responseText = self._ax.responseText;
        self.responseXml  = self._ax.responseXml;
        self.status       = self._ax.status;
        self.statusText   = self._ax.statusText;
        if (self.onReadyStateChange) self.onReadyStateChange();
      }
    }();
    o.open = function(bstrMethod, bstrUrl, varAsync, bstrUser, bstrPassword) {
      varAsync = (varAsync !== false);
      this._ax.onReadyStateChange = this._onReadyStateChange
      return this._ax.open(bstrMethod, bstrUrl, varAsync, bstrUser, bstrPassword);
    };
    o.send = function(varBody) {
      return this._ax.send(varBody);
    };
    // add the other methods...
  }
  else {
    var o = ax;
  }

  return o;
}

function Test() {
  var r = new ActiveXObject('Msxml2.XMLHTTP');

  alert(r._status);  // "fake"

  r.onReadyStateChange = function() { alert(this.readyState); };
  r.open("GET", "z.xml");
  r.send();

  alert(r.responseText);
}

Disclaimer: Especially the async/onReadyStateChange handling probably isn't right, and the code may have other issues as well. As I said, it's just an idea. Handle with care.

P.S.: A COM object is case-insensitive when it comes to method- and property names. This wrapper is (as all JavaScript) case-sensitive. For example, if your code happens to call both "Send()" and "send()", you will need a skeleton "Send()" method in the wrapper as well:

o.Send = function() { return this.send.apply(this, arguments); };