Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / ATL

Using COM in Gadgets without Installation

4.75/5 (10 votes)
5 Jun 20074 min read 2   1.5K  
This article shows how to create gadgets that use COM, without requiring installation or administrator permission.

Screenshot - Workometer.jpg

Introduction

This article shows how any COM component can be used in a Vista Sidebar gadget and be packaged into the gadget ZIP-file so it can be deployed using the usual "one click" deployment without requiring any special MSI installation or administrator permission.

To install the gadget, download the Workometer.zip file, rename the file extension to ".gadget" and double click it.

Background

For the Swiss gadget competition 2007, I wanted to write a special gadget. I had in mind a kind of gauge that displays how fast you can type and some statistics about how many keys you press during a typical workday. For that, I needed to use a system wide keyboard & mouse hook, using the Windows API function SetWindowsHookEx.

I knew I'd have to write a COM component that takes care of the statistics, but how could I publish the gadget in the Windows Live gallery if a COM component is required to be installed and registered, before the gadget works? And what about uninstalling?

It took me a while to get it working, but if you know how, it's quite easy. I thought I'd share this with you, because people here at CodeProject helped me get there.

Using the COM Component in JavaScript

I had the choice to write a COM component in C++/ATL or C#/.NET. Both are pretty straight forward so I won't explain that in this article. There are other great articles for that. Anyway, SetWindowsHookEx is not available in .NET, so I'd have to use Interop. I decided to write the component in plain and simple C++/ATL. The entire source is downloadable from the link above.

The component exposes an Interface called IGauge with a few methods like StartMeasure, StopMeasure, keySpeed, keyCount, etc. The JavaScript code used to access the component looks like this:

JavaScript
var workGauge = new ActiveXObject("Workometer.Gauge"); 
workGauge.StartMeasure();

If you have registered the COM component on your machine, that's all you need. However, we don't want to have to register it. Read on.

Temporarily Register the Component using JavaScript and WScript

The trick is to write the registry entries which COM components usually write into the registry when you call regsvr32 or regasm before you instantiate the component. Here are the required registry entries for Workometer.

[HKEY_CURRENT_USER\SOFTWARE\Classes\Workometer.Gauge]
@="Workometer Gauge"

[HKEY_CURRENT_USER\SOFTWARE\Classes\Workometer.Gauge\CLSID]
@="{205BC886-A9AA-4C96-B93C-564CCBA8BD83}"

[HKEY_CURRENT_USER\Software\Classes\CLSID\
{205BC886-A9AA-4C96-B93C-564CCBA8BD83}]
@="Workometer.Gauge"

[HKEY_CURRENT_USER\Software\Classes\CLSID\
{205BC886-A9AA-4C96-B93C-564CCBA8BD83}\InprocServer32]
@="C:\Users\...\Workometer.dll"
ThreadingModel="apartment"

Notice that the entries are not in the HKEY_CLASSES_ROOT section of the registry. To write entries there, you would need to run in administrator mode. It surprised me that HKEY_CURRENT_USER could be used, but it works.

To write the entries into the registry using JavaScript, use the WScript.Shell object. The WScript object exists on all Vista machines and contains the functions RegWrite and RegDelete which you can use for that purpose. Here's how you call it:

var wshShell = new ActiveXObject("WScript.Shell"); 
wshShell.RegWrite ("HKCU\\SOFTWARE\\Classes\\Workometer.Gauge\\", 
    "Workometer Gauge");

There's one more small thing. The full path name of the DLL must be in the registry. To get the path of the DLL, we use the function System.Gadget.path which is automatically available to all gadgets.

Let's Wrap It Up

Below, you'll find the entire code required to load the Workometer COM component. Basically, when the gadget gets loaded, I write the required entries into the registry, create the object with ActiveXObject, then immediately delete the registry entries again, so nothing is left behind. That's the whole story.

JavaScript
var workGauge;

loadWorkGauge();

function loadWorkGauge()
{
    // dynamic register (write the registry entries into current user
    var wshShell = new ActiveXObject("WScript.Shell"); 
    var rootCls = "HKCU\\Software\\Classes\\CLSID\\
        {205BC886-A9AA-4C96-B93C-564CCBA8BD83}\\";
    var rootPid = "HKCU\\SOFTWARE\\Classes\\Workometer.Gauge\\";
    wshShell.RegWrite (rootPid, "Workometer Gauge");
    wshShell.RegWrite (rootPid+"CLSID\\", "
        {205BC886-A9AA-4C96-B93C-564CCBA8BD83}");
    wshShell.RegWrite (rootCls, "Workometer.Gauge");
    wshShell.RegWrite (rootCls+"InprocServer32\\", 
        System.Gadget.path+"\\Workometer.dll");
    wshShell.RegWrite (rootCls+"InprocServer32\\ThreadingModel", "apartment");

    // create the COM object and store it in global variable
    workGauge = new ActiveXObject("Workometer.Gauge"); 

    // delete all the entries again
    wshShell.RegDelete (rootCls+"InprocServer32\\");
    wshShell.RegDelete (rootCls);
    wshShell.RegDelete (rootPid+"CLSID\\");
    wshShell.RegDelete (rootPid);
}

When you pack your gadget files into a ZIP file (.gadget) for deployment, the code above assumes the DLL is in the root of the ZIP file, the same place where the gadget.xml file is.

Loading C#/.NET Components

Although I haven't tried it, I'm certain you can also load .NET assemblies that expose COM objects in the same way. For assemblies two or three more lines are required to be written into the registry. To find out which lines are required, create the .NET assembly and register it with regasm, then have a look at the entries in the registry.

History

  • 6th June, 2007: Initial post

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below. A list of licenses authors might use can be found here.