CaubleStone Ink

.Net development and other geeky stuff

Tutorial – Google Desktop Sidebar – Hello World Part 1, A simple plug-in

Posted on December 10th, 2006


Requirements

.Net Framework SDK 1.1
Google Desktop SDK

Downloads

The zip file listed here is all inclusive of all four tutorial parts. We broke up the article into four parts for ease of reading. Included in the zip is a Word doc version of all four articles.

Download (231KB)

Purpose

In this article I intend to show you how to build a simple Google Desktop Sidebar (GDS) component. The entire source is in C# 1.1. We assume that all developers looking at this article are familiar with writing code in C# and the .Net framework. For this component we will be doing a simple Hello World type sample. We will show screenshots where appropriate. Otherwise we assume you know your way around.

What it shows

We intend to show you how to hook up a basic no frills .Net C# GDS component. What we have done is setup some basic modules under the folder GoogleBase that you can re-use in any component that you develop. These files wrap up all the API and com interaction that Google had setup in the samples sdk. Now it’s easy for you to use. In the article we will show you how to setup the component. Work with a title changing. Adjusting Layout formats (the canned ones). Adding content items. Using the default reader pane, and setting up Property pages.

This tutorial will walk you through the steps taken to get where the sample code is.

Project Setup

To start with create a new solution with and add a Class Library project. If you have not downloaded the code for this article please do so now as it will have some base files you will need to get going. Also, you need to make sure you have the Google Desktop SDK installed for this to work.

Copy and include the GoogleBase folder and all of it’s contents from the zip file into your project. These modules setup the following, ImageConverter object for getting your resource streams down to COM layer, the COM API, structures, etc needed by the GDK, and the base property page object that you can inherit from in order to create your property pages.

Once you have the files include you need to add some references to your project. The first two references are needed for the plug-in to work. In the references window click on the COM Tab and find the following two objects (figure 1) and add them to your project.

Once you have selected those objects it will create the necessary Interop dll’s needed since we are interacting with COM objects instead of managed code.

You also need to include the following references:

  • System.Drawing
  • System.Windows.Forms

Once that is done you need to set your project properties. The Google Sidebar is a COM object so we need to build a COM library. To do so we need to enable that in our project properties. So open your project properties and set the basics like Assembly name, root namespace, etc. Once you have that done goto the Configuration Properties -> Build section and at the bottom of your options change the Register for COM Interop to TRUE. This will enable us to build a com library.

That’s the basic setup for the project. Now we need to start putting together the basic plug-in.

Compilation Notes

When building a COM library you will need some registration functions in your code to allow it to hook in properly. We will show that after we get the basic plug-in up and running with a title.

However, once you compile and register your component with the GD Sidebar you cannot modify it unless you exit the Sidebar application. This will remove the hooks so you can re-compile and re-register. Tedious process if you are doing a lot of testing but it works.

Your first plug-in

Ok, lets setup our first plug-in. Create a new Class file in your project and the following includes for the file.

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;

// used for Registry / Com interop registration
using Microsoft.Win32;
using Google.Base;
// Base module to ease plug-in development
using Google.Base.Utils;
// Base module to ease plug-in development
using GoogleDesktopAPILib;
// Google Type libraries
using GoogleDesktopDisplayLib;
// Google Type libraries

Once you have these in let’s create our first plug-in class. For your class, add the following inheritance chain (or cut / paste and change your class name):

public class HelloWorldPlugin : GoogleDesktopDisplayPluginHelperClass, IGoogleDesktopDisplayPluginHandler, ISpecifyPropertyPages, IObjectWithSite, IPersistStreamInit

Once you have the class inheritance model in place use the Class view to go in and implement all the interfaces.

Now let’s setup some of our base internal values that we will use with our com object. These values will be used with the registration and read by the GDS when you view available plug-ins.

// since this is a com object we can store some info as static members.
static string baseTitle = "Hello World";
static string pluginName = "Hello World";
static string pluginDesc = "Hello World sample written in C#";

There is one other item you must add and it is extremely important. All COM objects need a GUID. So let’s add ours. Using the GUIDGEN utility from the framework choose the registry format to generate and take the value and add it to our class in the following ways:

[GuidAttribute("A674FDFF-D816-4eaf-9D5E-97317158733E")]
public class HelloWorldPlugin : GoogleDesktopDisplayPluginHelperClass, IGoogleDesktopDisplayPluginHandler, ISpecifyPropertyPages, IObjectWithSite, IPersistStreamInit
{
  // You must set a control GUID and it must match the guid attribute in order to work static string     controlGuid = "{A674FDFF-D816-4eaf-9D5E-97317158733E}";
}

The GUID’s in both places need to match. Without this you will be unable to register and use your plug-in.

You will also need to add the following member variables to the class:

// Member variables needed to hook into the Sidebar api
// this retrieves the site info for this plug-in.
private IGoogleDesktopDisplaySite displaySite;

// setup the layout options so the sidebar api knows how to render our information.
private GoogleDesktopDisplayContentItemLayout layout;

What this does is allows your app to hook into the panel that is showing your information. You will notice that we set up above a baseTitle object as static. You do not need to do this if you plan to have your title bar be dynamic. However it aids in this demonstration. The layout property will be used in a later tutorial.

Ok, now that we have some member variables, our interfaces implemented (roughly) and a class to use let’s build the constructor. Here we will setup an initial layout format as well as hook in the About box information. Real quick here about the about box. There are three distinct areas that text is put. The first line will use your logo if any and display your main info like a company name. If you use two new line commands it will kick your text down to the main content area.

Here is our constructor:

public HelloWorldPlugin()
{
  // setup some basic info.
  StringBuilder sb = new StringBuilder();
  sb.Append("Hello World Plug-in by CaubleStone Ink");
  sb.Append("//n//n");
  sb.Append("Shows how to setup basic functionality");
  sb.Append("for a sidebar plug-in.//n");
  this.layout = GoogleDesktopDisplayContentItemLayout.GDD_CONTENT_ITEM_LAYOUT_NOWRAP_ITEMS;   this.about_text = sb.ToString();
}

What we have done here is setup our basic info using a StringBuilder and setting our basic layout to just the plain jane no wrap value. Similar to the TODO list from Google.

Now lets walk through setting up the interfaces. By default you can leave all the interfaces alone however you may need to add a few new keywords on some to aid in the code generation and to help avoid memory leaks.

We are going to start with the IObjectWithSite interface class. All we need to do here is add the following so our app can get a handle to the display surface that the GDS will be using.

public void GetPageContainer(out Object site)
{
  // TODO: Add HelloWorldPlugin.GetPageContainer implementation
  site = null;

}

public void SetSite(Object site)
{
  displaySite = (IGoogleDesktopDisplaySite)site;
}

That’s it. We are slowly on our way to a plug-in. Now on to the next interface IGoogleDesktopDisplayPluginHandler. In this interface we will hook up our Title for our plug-in. What we plan on having is the plug-in adjust the title from Hello World to Hello World – Min when it’s minimized or maximized.

public new void OnCommand(GoogleDesktopDisplayPluginCommand command)
{
  if (command == GoogleDesktopDisplayPluginCommand.GDD_CMD_ABOUT_DLG)
  {
    // always throw this exception when you dont intend to override the default behavior
    // and you want the plug-in helper to do it[ES][SQ]s usual stuff.
    throw new NotImplementedException();
  }
}

public new void OnDisplayStateChange(GoogleDesktopDisplayTileDisplayState display_state)
{
  if (display_state == GoogleDesktopDisplayTileDisplayState.GDD_TILE_DISPLAY_STATE_MINIMIZED ||        display_state == GoogleDesktopDisplayTileDisplayState.GDD_TILE_DISPLAY_STATE_RESTORED)
  {
    // switching between minimized and normal mode, so update the title
    this.GetTitle();
  }
}

Notice that we have the new keyword on both of these methods. That is so it hides the underlying implementation (which there is none since it’s an interface) and helps us to prevent memory leaks. What we are doing is checking the value of display_state which comes in from the interface call. If the component has been minimized or restored it will change our title.

Let’s get our GetTitle function in here.

public void GetTitle()
{
  this.title = baseTitle;
  if (displaySite != null && displaySite.display_state ==  GoogleDesktopDisplayTileDisplayState.GDD_TILE_DISPLAY_STATE_MINIMIZED)
  {
    this.title = baseTitle + " - Min";
  }
}

What we are doing here is setting the underlying classes title property. We are starting with our baseTitle static variable and then appending the “- Min” to it if we are minimized.

Now we are finished with our basic code. Let’s get our registration functions in place so we can see the final results.

COM Registration functions

#region Registration functions
/// <summary>
/// Called when the plug-in is registered with the system. We add a few registry
/// keys and register with GoogleDesktop as a plug-in.
/// </summary>
/// <param name="t" /></param>
[ComRegisterFunctionAttribute]
static void RegisterFunction(Type t)
{
  try
  {
    // Set the "MiscStatus" value in registry to a valid value string keyName = @"CLSID\" + t.GUID.ToString("B");

    using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(keyName, true))
    {
      key.CreateSubKey("Control").Close();
      using (RegistryKey subkey = key.CreateSubKey("MiscStatus"))
      {
        subkey.SetValue("", "whateveryouwant");
      }
      key.CreateSubKey("Control").Close();
      key.SetValue("",pluginName);
      using (RegistryKey subkey = key.CreateSubKey("Description"))
      {
        subkey.SetValue("", pluginDesc);
      }
    }

    // Create the registrar object
    GoogleDesktopRegistrarClass registrar = new GoogleDesktopRegistrarClass();

    // Start component registration by specifying our attributes
    object[] descriptions = { "Title", pluginName, "Description", pluginName, "Icon", "" };

    registrar.StartComponentRegistration(controlGuid, descriptions);
    // A single component can register for multiple plugins with Google Desktop.
    // Here we register a single display plug-in.
    IGoogleDesktopRegisterDisplayPlugin displayRegistration =
        (IGoogleDesktopRegisterDisplayPlugin)         registrar.GetRegistrationInterface("GoogleDesktop.DisplayPluginRegistration");

    displayRegistration.RegisterPlugin(controlGuid, false);
    // Done with component registration.
    registrar.FinishComponentRegistration();
  }
  catch (Exception e)
  {
    MessageBox.Show("Exception thrown during registration. Description=" + e.Message);
  }
}

/// <summary>
/// Called when the plug-in is unregistered. We unregister our plug-in with
/// GoogleDesktop.
/// </summary>
/// <param name=""t"" /></param>
[ComUnregisterFunctionAttribute]
static void UnregisterFunction(Type t)
{
  try
  {
    // Create the registrar object
    GoogleDesktopRegistrarClass registrar = new GoogleDesktopRegistrarClass();
    // Unregister ourselves
    registrar.UnregisterComponent(controlGuid);
  }
  catch (Exception e)
  {
    MessageBox.Show("Exception thrown during registration. Description=" + e.Message);
  }
}
#endregion

What this block of code does is writes to the registry values needed to register your component for COM interop. By using the GoogleDesktopRegistarClass it also registers your COM object for use and visibility by the GDK. For the most part you can just copy / paste whenever you build a plug-in.

Compiling and Running

Ok, now that you have all the code in place it’s time to compile and run your application. If your build was successful you should see the following:

Once you get this message hit yes. If you have the Google Sidebar running you should see it show up immediately. If not startup the Sidebar and if not there add the panel.

Based on what we have done you should see something like the following:

Normal

Minimized

What you are seeing is the Hello World plug-in with only a title. The title will change when you Minimize or Restore the panel.

What you have learned

In this part of our tutorial you have learned the basics of creating a very simple plug-in for the Google Desktop Sidebar using C#. As you can see it only took minimal setup steps thanks to some of the items that Google provided and we broke out for you.

In our next article we will cover adding content items, basic format to the plug-in.

Errata

04-12-2006

Thanks to M. Loeffen for catching this. If you were working through the examples you would indeed recieve a compile error because we did not define the layout property. We have adjusted the tutorial to add this variable to our main plugin class. If you want to see it here this is what you need to add:

  // setup the layout options so the sidebar api knows how to render our information.
  private GoogleDesktopDisplayContentItemLayout layout;