javascriptactivex

Extending an ActiveXObject in javascript


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.


Solution

  • 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); };