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 ActiveXO
The problem here is that it seems that IE wont allow saving of the original activXObject constructor and will give a stack overflow (;-) upon creating the ActualActiveXObject. it seems that this is special for the ActivX because it is working when doing that with other javascript objects.
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();
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); };