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.
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); };
                        Thank you very much for your wrapper. With your help I was able to create a xmlrequest detector for IE and FF and the rest.
I have added a version (combined from another example) that works for FF , IE and the rest of the gang,
if(window.XMLHttpRequest)
{
var XMLHttpRequest = window.XMLHttpRequest;
// mystery: for some reason, doing "var oldSend = XMLHttpRequest.prototype.send;" and 
//  calling it at the end of "newSend" doesn't work...
var startTracing = function () {
    XMLHttpRequest.prototype.uniqueID = function() {
        // each XMLHttpRequest gets assigned a unique ID and memorizes it 
        //  in the "uniqueIDMemo" property
        if (!this.uniqueIDMemo) {
            this.uniqueIDMemo = Math.floor(Math.random() * 1000);
        }
        return this.uniqueIDMemo;
    }
    // backup original "open" function reference
    XMLHttpRequest.prototype.oldOpen = XMLHttpRequest.prototype.open;
    var newOpen = function(method, url, async, user, password) {
        console.log("[" + this.uniqueID() + "] intercepted open (" + 
                    method + " , " + 
                    url + " , " + 
                    async + " , " + 
                    user + " , " + 
                    password + ")");
        this.oldOpen(method, url, async, user, password);
    }
    XMLHttpRequest.prototype.open = newOpen;
    // backup original "send" function reference
    XMLHttpRequest.prototype.oldSend = XMLHttpRequest.prototype.send;
    var newSend = function(a) {
        console.log("[" + this.uniqueID() + "] intercepted send (" + a + ")");
        var xhr = this;
        var onload = function() { 
            console.log("[" + xhr.uniqueID() + "] intercepted load: " + 
                    xhr.status + 
                    " " + xhr.responseText); 
        };
        var onerror = function() { 
            console.log("[" + xhr.uniqueID() + "] intercepted error: " + 
                    xhr.status); 
        };
        xhr.addEventListener("load", onload, false);
        xhr.addEventListener("error", onerror, false);
        this.oldSend(a);
    }
    XMLHttpRequest.prototype.send = newSend;
}
startTracing();
}
else if (window.ActiveXObject) {
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
        };
        o._onReadyStateChange = function() {
            var self = o;
            return function() {
            self.readyState   = self._ax.readyState;
            if (self.readyState == 4) {
                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) {
            console.log("intercepted open (" + 
                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);
        };
    }
    else
        var o = ax;
    return o;
}
}
                        A little fix for "The data necessary to complete this operation is not yet available" in IE6 - waiting for completeness before population of reponse properties:
self.readyState   = self._ax.readyState;
 if (self.readyState == 4) {
  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();
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With