Gadgets: Registering managed objects

Andrew posted about the PowerShell Gadget he recently built. Its a pretty cool piece of kit really exercising all of the possibilities in hosting .NET within the Sidebar infrastructure. In fact its currently my most actively used Gadget!

There are some good examples in there are how to wire up to .NET eventing, managed interop and use of platform invoke. AP spent quite a bit of time refactoring this and learning a lot of about the Gadget host in the process – I think he is now the guru of managed gadgets :)

One of the things that initially presented itself through the use of a managed component that needs to be registered through COM is that you need some way of installing that component on to the system in the first place. Normally you would build a MSI to do this, however we found an example of how this can be circumvented and you can have the Gadget “self register” a COM object itself allowing you to retain the .gadget drag and drop packaging and install experience.

In order to do this, we have an initial check to validate the existence of the COM object on the system and if it doesn’t exist we execute a script to add the required registry keys which register the ProgId and CLSID on the system.

In the PowerShell gadget, this is done on the Gadget.html file as a script directive:

<script>  
  InitializeInterop();
</script>

That in turn simply wraps the creation of the required COM object in a try/catch block to validate its existence.

try
{
  var proxy = new ActiveXObject("Mindscape.ConsoleHost");
  proxy = null;
  return true;
}
catch (e)
{
  return false;
}

If it doesn’t exist the registration steps required are:

codeBase = System.Gadget.path + "\\Bin\\Mindscape.Gadgets.PowerShell.dll"; 
 
shell = new ActiveXObject("WScript.Shell");
 
shell.RegWrite(root + "Mindscape.ConsoleHolder\\",
                    "Mindscape.Gadgets.PowerShell.ConsoleHolder");
shell.RegWrite(root + "Mindscape.ConsoleHolder\\CLSID\\",
                    "{91267DB9-9914-4524-A757-9024B99D02E9}");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\",
                    "Mindscape.Gadgets.PowerShell.ConsoleHolder");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\",
                    "mscoree.dll");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\ThreadingModel",
                   "Both");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\Class",
                    "Mindscape.Gadgets.PowerShell.ConsoleHolder");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\Assembly",
                    "Mindscape.Gadgets.PowerShell, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5bd33ec22e477ed5");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\RuntimeVersion",
                    "v2.0.50727");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\CodeBase",
                    "file:///" + codeBase);
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\1.0.0.0\\Class",
                    "Mindscape.Gadgets.PowerShell.ConsoleHolder");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\1.0.0.0\\Assembly",
                    "Mindscape.Gadgets.PowerShell, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5bd33ec22e477ed5");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\1.0.0.0\\RuntimeVersion",
                    "v2.0.50727");
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\InprocServer32\\1.0.0.0\\CodeBase",
                    "file:///" + codeBase);
shell.RegWrite(root + "CLSID\\{91267DB9-9914-4524-A757-9024B99D02E9}\\ProgId\\",
                    "Mindscape.ConsoleHolder");
shell.RegWrite(root + "Component Categories\\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}\\0    ",
                    ".NET Category");

We are using the WScript.Shell object to RegWrite the associated entries. Most importantly with the managed component we are setting the CodeBase entry to specifically identify the location of the managed DLL on the system (because we don’t have it registered in the GAC and the Sidebar process is not going to locate it in a known search path)

You would run through these steps for each managed object you want to register. You need to know the CLSID and ProgId of your Gadget.

You will notice that we use use a root specifier to direct where exactly we want to load this in the registry. The code calling our Registration function makes up to 2 calls to try and register the Gadget because of UAC.

try
{
  Register("HKCR\\");
}
catch (e)
{
  try
  {
    Register("HKCU\\Software\\Classes\\");
  }
  catch (e)
  {
  }
}

Initially we try and register it globally in HKEY_CLASSES_ROOT, but if you are running as a standard user or with UAC enabled you wont be able to do this. So if we hit an exception on this call we try again but this time using the HKEY_CURRENT_USER area to register it for the current user only.

Using this approach you can now register your managed objects as part of your Gadgets install. You could also use the reverse approach to unregister when your Gadget closes down but keep in mind that the Sidebar will lock managed components so the Gadget uninstall will still not work correctly while the assembly is locked.

Happy Gadget coding! :)

Tagged as Lab Samples

Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top