Tag Archives: firefox

Detecting dot net CLR version on client machine in Firefox

Update [November 24, 2009]: Folks it’s been a while since Microsoft launched Silverlight. Then came what is referred to as Microsoft .NET Framework Assistant add-on for Firefox (http://msdn.microsoft.com/en-us/library/cc716877.aspx). This does what my original article attempted. Someday I’ll post a new article on how to use it. As of now, this article is deprecated.

 

Recently I came across the need for my ASP.NET application running on Firefox browser to tell me the version of .NET CLR version on the client side. On IE it’s a breeze because the browser identifier string has the CLR version and is neatly encapsulated in the Browser object on the server side. However the object doesn’t work for non-IE browsers.

Initial googling did not return anything available ready-made, so the requirement was shelved due to lack of time. Couple of weeks back I got enough time to look-up what it takes to create a Firefox plugin.

Firefox architecture is extremely extensible, however I was disappointed to find different kinds of ‘plugin’ mechanisms for different purposes. Maybe I don’t understand it all that well but anyway I got really confused between an XPCOM component (extension) or Plugin architecture.

After reading thru a lot of documentation I felt XPCOM component (extension) is what I needed to create. I am still not 100% sure it is THE right approach. If someone can do the same using the ‘plugin’ architecture please let me know.

A very nice article outlining creation of XPCOM components using VC++ Express helped me create and build the interface required. The article also has links to a sample solution that built without a hitch after I had setup the external environment correctly (for the first time in life I got a C++ solution in Visual Studio working). I used the sample as a prototype for my solution.
Took me a lot of further reading to actually realize a very simple thing. After building an XPCOM interface (in C++) it is not necessary you have to code the implementation in C++. It can be done in javascript as well.
Once I realized that, this article showed the path forward. XPCOM has established API to access windows registry. Though I would really have loved to do all this in C++ (curiosity killed the cat) I succumbed to the temptation of using Javascript.

This link outlines Microsoft’s recommendations on detecting .NET CLR versions thru registry entries. I was a little irritated to find almost each version of CLR being written to a new location in the registry making the component totally at mercy of Microsoft in future. I did not have enough patience to write code for version 1.x. The code I wrote works for .NET version 2 and 3. Not sure if it will work for 3.5 or future versions.

The final outcome is just about functional. It can be termed as an alpha release. Here are the steps to DIY. I am not publishing a package/bundle/xpi because somehow I couldn’t create one that installs in Firefox. Kept giving errors while installation. But if you place the pieces in the right place the functionality works.

So whatever is presented below is completely on an ‘AS IS’ basis. I’ve tested it on Vista Business/XP Pro and Win2003 Server and Firefox 2.x. It did not hose my Firefox installations in either of those machines. Please don’t sue me if it messes up your Firefox.

Now to the ‘real’ stuff.

Step 1: The XPCOM Interface:
a. Create and IDL file as follows:

/**
Dot net detection IDL by Sumit Kumar Maitra.
August 2007
Pune, India

References/Acknowledgement:
http://developer.mozilla.org/en/docs/How_to_Build_an_XPCOM_Component_in_Javascript
http://www.codeproject.com/dotnet/DetectDotNet.asp
http://msdn2.microsoft.com/en-us/library/aa480198.aspx#netfx30_topic14
http://developer.mozilla.org/en/docs/Accessing_the_Windows_Registry_Using_XPCOM#Reading_Registry_Values
http://developer.mozilla.org/en/docs/install.rdf
*/

#include "nsISupports.idl"

[scriptable, uuid(429E4518-4D9B-11DC-BB8A-307E55D89593)]
interface IDotnetDetect : nsISupports
{
AString GetDotNetVersion();
};

b. The idl is compiled into an xpt file and the required headers are also generated as follows:


/*
* DO NOT EDIT  THIS FILE IS GENERATED FROM dotnetdetectComp.idl
*/

#ifndef __gen_dotnetdetectComp_h__
#define __gen_dotnetdetectComp_h__

#ifndef __gen_nsISupports_h__
#include "nsISupports.h"
#endif

/* For IDL files that don't want to include root IDL files. */
#ifndef NS_NO_VTABLE
#define NS_NO_VTABLE
#endif

/* starting interface:����¯�¿�½���¯���¿���½ ����¯�¿�½���¯���¿���½ ����¯�¿�½���¯���¿���½  IDotnetDetect */
#define IDOTNETDETECT_IID_STR "429e4518-4d9b-11dc-bb8a-307e55d89593"

#define IDOTNETDETECT_IID \
{0x429e4518, 0x4d9b, 0x11dc, \
{ 0xbb, 0x8a, 0x30, 0x7e, 0x55, 0xd8, 0x95, 0x93 }}

class NS_NO_VTABLE IDotnetDetect : public nsISupports

{
public:
NS_DEFINE_STATIC_IID_ACCESSOR(IDOTNETDETECT_IID)
/* AString GetDotNetVersion (); */
NS_IMETHOD GetDotNetVersion(nsAString & _retval) = 0;
};

/* Use this macro when declaring classes that implement this interface. */
#define NS_DECL_IDOTNETDETECT \
NS_IMETHOD GetDotNetVersion(nsAString & _retval);

/* Use this macro to declare functions that forward the behavior of this interface to another object. */
#define NS_FORWARD_IDOTNETDETECT(_to) \
NS_IMETHOD GetDotNetVersion(nsAString & _retval) { return _to GetDotNetVersion(_retval); }

/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
#define NS_FORWARD_SAFE_IDOTNETDETECT(_to) \
NS_IMETHOD GetDotNetVersion(nsAString & _retval) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetDotNetVersion(_retval); }

#if 0
/* generated class template below */

/* End of implementation class template. */
#endif

#endif /* __gen_dotnetdetectComp_h__ */

c. You will notice that the header also contains a template for the class to be used for implementing the interfaces. It looks as follows:


/* Use the code below as a template for the implementation class for this interface. */

/* Header file */
class _MYCLASS_ : public IDotnetDetect
{
public:
NS_DECL_ISUPPORTS
NS_DECL_IDOTNETDETECT

_MYCLASS_();

private:
~_MYCLASS_();

protected:
/* additional members */
};

/* Implementation file */
NS_IMPL_ISUPPORTS1(_MYCLASS_, IDotnetDetect)

_MYCLASS_::_MYCLASS_()
{
/* member initializers and constructor code */
}

_MYCLASS_::~_MYCLASS_()
{
/* destructor code */
}

/* AString GetDotNetVersion (); */
NS_IMETHODIMP _MYCLASS_::GetDotNetVersion(nsAString & _retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}

d. If you intend to implement the interfaces in C++ use the given template to save yourself that much typing.

Step 2: XPCOM implementation using Javascript

The javascript to detect dot net CLR versions is as follows


/***********************************************************
constants
***********************************************************/

// reference to the interface defined in dotnetDetect.idl
const IDotnetDetect = Components.interfaces.IDotnetDetect;

// reference to the required base interface that all components must support
const nsISupports = Components.interfaces.nsISupports;

// UUID uniquely identifying our component
const CLASS_ID = Components.ID("{429E4518-4D9B-11DC-BB8A-307E55D89593}");

// description
const CLASS_NAME = "DotnetDetect";

// textual unique identifier
const CONTRACT_ID = "@sumitmaitra.com/dotnetdetect;1";

/***********************************************************
class definition
***********************************************************/

//class constructor
function DotnetDetect()
{
};

// class definition
DotnetDetect.prototype = {

// define the function we want to expose in our interface
GetDotNetVersion: function()
{
var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
.createInstance(Components.interfaces.nsIWindowsRegKey);

var detectedCLRversions = "Unknown";

// Detect Version 2 and 3 of CLR Runtime
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP", wrk.ACCESS_READ);

var detectedCLRversionsCount = 0;

for (var i = wrk.childCount - 1; i >= 0; i--)
{
var name   = wrk.getChildName(i);
var subkey = wrk.openChild(name, wrk.ACCESS_READ);
var isInstalled = "0"
var valueName = "Install";
if (subkey.hasValue(valueName)) // .NET version 2.x and 1.1.x
{
isInstalled = subkey.readIntValue(valueName);
}
else //.NET version 3.x
{
var setupKey = subkey.openChild("Setup", wrk.ACCESS_READ);
valueName = "InstallSuccess";
isInstalled = setupKey.readIntValue(valueName);
}
//Alert(valueName + ' ' + isInstalled);
if(isInstalled == 1)
{
if(detectedCLRversionsCount == 0)
{
detectedCLRversions = name;
}
else
{
detectedCLRversions = detectedCLRversions + ' ' + name;
}
detectedCLRversionsCount = detectedCLRversionsCount + 1;
}
subkey.close();
}

wrk.close();
return detectedCLRversions ;
},

QueryInterface: function(aIID)
{
if (!aIID.equals(IDotnetDetect) &&
!aIID.equals(nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};

/***********************************************************
//class factory

//This object is a member of the global-scope Components.classes.
//It is keyed off of the contract ID. Eg:

myDotnetDetect = Components.classes["@sumitmaitra.com/dotnetdetect;1"].
createInstance(Components.interfaces.IDotnetDetect);

***********************************************************/
var DotnetDetectFactory = {
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return (new DotnetDetect()).QueryInterface(aIID);
}
};

/***********************************************************
module definition (xpcom registration)
***********************************************************/
var DotnetDetectModule = {
registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
{
aCompMgr = aCompMgr.
QueryInterface(Components.interfaces.nsIComponentRegistrar);
aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME,
CONTRACT_ID, aFileSpec, aLocation, aType);
},

unregisterSelf: function(aCompMgr, aLocation, aType)
{
aCompMgr = aCompMgr.
QueryInterface(Components.interfaces.nsIComponentRegistrar);
aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
},

getClassObject: function(aCompMgr, aCID, aIID)
{
if (!aIID.equals(Components.interfaces.nsIFactory))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

if (aCID.equals(CLASS_ID))
return DotnetDetectFactory;

throw Components.results.NS_ERROR_NO_INTERFACE;
},

canUnload: function(aCompMgr) { return true; }
};

/***********************************************************
module initialization

When the application registers the component, this function
is called.
***********************************************************/
function NSGetModule(aCompMgr, aFileSpec) { return DotnetDetectModule; }

Step 3: Putting the pieces in the right locations. Now due to the lack of a working installable package you have to place all the pieces in the right locations manually. So here are the steps:

1. Copy the xpt and javascript file to “[Mozilla Firefox install directory]\components” folder.
2. Restart Firefox (if simple restart doesn’t work restart the machine).

Step 4: Testing it out

1. Save the following as an HTML file


netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var oMyComponent = Components.classes['@sumitmaitra.com/dotnetdetect;1'].createInstance(Components.interfaces.IDotnetDetect);
function btnReverse_onclick()
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
try
{
var s = oMyComponent.GetDotNetVersion();
document.thisForm.txtResult.value = s;
}
catch(e)
{
alert(e);
}
}
<form>
Dot Net CLR versions detected:
</form>

Part of the above is copy/paste from another sample so don’t mind the naming convention.

2. I am guessing here but I believe because we didn’t ‘install’ the extension Firefox gives a security warning every-time the page is loaded. If you ask it not to bother you it won’t in the future.

3. Click on the button and checkout the versions of CLR installed.

Well, sometime in the near future I hope to iron out the installer issues and submit this as a ‘legal’ extension to ‘Mozilla’. If anyone takes the reference of this article and does it first, I will greatly appreciate a note of acknowledgment. Everything here is free for use (even commercially), just drop me a mail or put it in comments saying you used it.
Any tips and trick on how to package this into a well behaving Firefox extension will also be highly appreciated.

Finally I repeat, everything here is AS IS. All trademarks acknowledged.

Tagged , , , ,
%d bloggers like this: